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 static void SetAnimStatus_BeforeFadingOut()
633 global.anim_status = GAME_MODE_PSEUDO_FADING;
636 static void SetAnimStatus_AfterFadingIn()
638 global.anim_status = global.anim_status_next;
640 // force update of global animation status in case of rapid screen changes
641 redraw_mask = REDRAW_ALL;
645 void FadeIn(int fade_mask)
648 DrawMaskedBorder(REDRAW_ALL);
651 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
652 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
654 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
658 FADE_SXSIZE = FULL_SXSIZE;
659 FADE_SYSIZE = FULL_SYSIZE;
661 SetAnimStatus_AfterFadingIn();
664 void FadeOut(int fade_mask)
666 SetAnimStatus_BeforeFadingOut();
669 DrawMaskedBorder(REDRAW_ALL);
672 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
673 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
675 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
677 global.border_status = game_status;
680 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
682 static struct TitleFadingInfo fading_leave_stored;
685 fading_leave_stored = fading_leave;
687 fading = fading_leave_stored;
690 void FadeSetEnterMenu()
692 fading = menu.enter_menu;
694 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
697 void FadeSetLeaveMenu()
699 fading = menu.leave_menu;
701 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
704 void FadeSetEnterScreen()
706 fading = menu.enter_screen[game_status];
708 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
711 void FadeSetNextScreen()
713 fading = menu.next_screen[game_status];
715 // (do not overwrite fade mode set by FadeSetEnterScreen)
716 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
719 void FadeSetLeaveScreen()
721 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
724 void FadeSetFromType(int type)
726 if (type & TYPE_ENTER_SCREEN)
727 FadeSetEnterScreen();
728 else if (type & TYPE_ENTER)
730 else if (type & TYPE_LEAVE)
734 void FadeSetDisabled()
736 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
738 fading = fading_none;
741 void FadeSkipNextFadeIn()
743 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
746 void FadeSkipNextFadeOut()
748 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
751 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
753 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
755 return (graphic == IMG_UNDEFINED ? NULL :
756 graphic_info[graphic].bitmap != NULL || redefined ?
757 graphic_info[graphic].bitmap :
758 graphic_info[default_graphic].bitmap);
761 Bitmap *getBackgroundBitmap(int graphic)
763 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
766 Bitmap *getGlobalBorderBitmap(int graphic)
768 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
771 Bitmap *getGlobalBorderBitmapFromGameStatus()
774 (game_status == GAME_MODE_MAIN ||
775 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
776 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
777 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
778 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
781 return getGlobalBorderBitmap(graphic);
784 void SetWindowBackgroundImageIfDefined(int graphic)
786 if (graphic_info[graphic].bitmap)
787 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
790 void SetMainBackgroundImageIfDefined(int graphic)
792 if (graphic_info[graphic].bitmap)
793 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
796 void SetDoorBackgroundImageIfDefined(int graphic)
798 if (graphic_info[graphic].bitmap)
799 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
802 void SetWindowBackgroundImage(int graphic)
804 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
807 void SetMainBackgroundImage(int graphic)
809 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
812 void SetDoorBackgroundImage(int graphic)
814 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
817 void SetPanelBackground()
819 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
821 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
822 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
824 SetDoorBackgroundBitmap(bitmap_db_panel);
827 void DrawBackground(int x, int y, int width, int height)
829 /* "drawto" might still point to playfield buffer here (hall of fame) */
830 ClearRectangleOnBackground(backbuffer, x, y, width, height);
832 if (IN_GFX_FIELD_FULL(x, y))
833 redraw_mask |= REDRAW_FIELD;
834 else if (IN_GFX_DOOR_1(x, y))
835 redraw_mask |= REDRAW_DOOR_1;
836 else if (IN_GFX_DOOR_2(x, y))
837 redraw_mask |= REDRAW_DOOR_2;
838 else if (IN_GFX_DOOR_3(x, y))
839 redraw_mask |= REDRAW_DOOR_3;
842 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
844 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
846 if (font->bitmap == NULL)
849 DrawBackground(x, y, width, height);
852 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
854 struct GraphicInfo *g = &graphic_info[graphic];
856 if (g->bitmap == NULL)
859 DrawBackground(x, y, width, height);
862 static int game_status_last = -1;
863 static Bitmap *global_border_bitmap_last = NULL;
864 static Bitmap *global_border_bitmap = NULL;
865 static int real_sx_last = -1, real_sy_last = -1;
866 static int full_sxsize_last = -1, full_sysize_last = -1;
867 static int dx_last = -1, dy_last = -1;
868 static int dxsize_last = -1, dysize_last = -1;
869 static int vx_last = -1, vy_last = -1;
870 static int vxsize_last = -1, vysize_last = -1;
872 boolean CheckIfGlobalBorderHasChanged()
874 // if game status has not changed, global border has not changed either
875 if (game_status == game_status_last)
878 // determine and store new global border bitmap for current game status
879 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
881 return (global_border_bitmap_last != global_border_bitmap);
884 boolean CheckIfGlobalBorderRedrawIsNeeded()
886 // if game status has not changed, nothing has to be redrawn
887 if (game_status == game_status_last)
890 // redraw if last screen was title screen
891 if (game_status_last == GAME_MODE_TITLE)
894 // redraw if global screen border has changed
895 if (CheckIfGlobalBorderHasChanged())
898 // redraw if position or size of playfield area has changed
899 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
900 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
903 // redraw if position or size of door area has changed
904 if (dx_last != DX || dy_last != DY ||
905 dxsize_last != DXSIZE || dysize_last != DYSIZE)
908 // redraw if position or size of tape area has changed
909 if (vx_last != VX || vy_last != VY ||
910 vxsize_last != VXSIZE || vysize_last != VYSIZE)
916 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
919 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
921 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
924 void RedrawGlobalBorder()
926 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
928 RedrawGlobalBorderFromBitmap(bitmap);
930 redraw_mask = REDRAW_ALL;
933 static void RedrawGlobalBorderIfNeeded()
935 if (game_status == game_status_last)
938 // copy current draw buffer to later copy back areas that have not changed
939 if (game_status_last != GAME_MODE_TITLE)
940 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
942 if (CheckIfGlobalBorderRedrawIsNeeded())
944 // redraw global screen border (or clear, if defined to be empty)
945 RedrawGlobalBorderFromBitmap(global_border_bitmap);
947 // copy previous playfield and door areas, if they are defined on both
948 // previous and current screen and if they still have the same size
950 if (real_sx_last != -1 && real_sy_last != -1 &&
951 REAL_SX != -1 && REAL_SY != -1 &&
952 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
953 BlitBitmap(bitmap_db_store, backbuffer,
954 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
957 if (dx_last != -1 && dy_last != -1 &&
958 DX != -1 && DY != -1 &&
959 dxsize_last == DXSIZE && dysize_last == DYSIZE)
960 BlitBitmap(bitmap_db_store, backbuffer,
961 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
963 if (vx_last != -1 && vy_last != -1 &&
964 VX != -1 && VY != -1 &&
965 vxsize_last == VXSIZE && vysize_last == VYSIZE)
966 BlitBitmap(bitmap_db_store, backbuffer,
967 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
969 redraw_mask = REDRAW_ALL;
972 game_status_last = game_status;
974 global_border_bitmap_last = global_border_bitmap;
976 real_sx_last = REAL_SX;
977 real_sy_last = REAL_SY;
978 full_sxsize_last = FULL_SXSIZE;
979 full_sysize_last = FULL_SYSIZE;
982 dxsize_last = DXSIZE;
983 dysize_last = DYSIZE;
986 vxsize_last = VXSIZE;
987 vysize_last = VYSIZE;
992 RedrawGlobalBorderIfNeeded();
994 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
995 /* (when entering hall of fame after playing) */
996 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
998 /* !!! maybe this should be done before clearing the background !!! */
999 if (game_status == GAME_MODE_PLAYING)
1001 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1002 SetDrawtoField(DRAW_FIELDBUFFER);
1006 SetDrawtoField(DRAW_BACKBUFFER);
1010 void MarkTileDirty(int x, int y)
1012 redraw_mask |= REDRAW_FIELD;
1015 void SetBorderElement()
1019 BorderElement = EL_EMPTY;
1021 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1023 for (x = 0; x < lev_fieldx; x++)
1025 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1026 BorderElement = EL_STEELWALL;
1028 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1034 void FloodFillLevel(int from_x, int from_y, int fill_element,
1035 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1036 int max_fieldx, int max_fieldy)
1040 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1041 static int safety = 0;
1043 /* check if starting field still has the desired content */
1044 if (field[from_x][from_y] == fill_element)
1049 if (safety > max_fieldx * max_fieldy)
1050 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1052 old_element = field[from_x][from_y];
1053 field[from_x][from_y] = fill_element;
1055 for (i = 0; i < 4; i++)
1057 x = from_x + check[i][0];
1058 y = from_y + check[i][1];
1060 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1061 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1067 void SetRandomAnimationValue(int x, int y)
1069 gfx.anim_random_frame = GfxRandom[x][y];
1072 int getGraphicAnimationFrame(int graphic, int sync_frame)
1074 /* animation synchronized with global frame counter, not move position */
1075 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1076 sync_frame = FrameCounter;
1078 return getAnimationFrame(graphic_info[graphic].anim_frames,
1079 graphic_info[graphic].anim_delay,
1080 graphic_info[graphic].anim_mode,
1081 graphic_info[graphic].anim_start_frame,
1085 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1086 Bitmap **bitmap, int *x, int *y,
1087 boolean get_backside)
1089 struct GraphicInfo *g = &graphic_info[graphic];
1090 Bitmap *src_bitmap = g->bitmap;
1091 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1092 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1093 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1095 // if no in-game graphics defined, always use standard graphic size
1096 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1097 tilesize = TILESIZE;
1099 if (tilesize == gfx.standard_tile_size)
1100 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1101 else if (tilesize == game.tile_size)
1102 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1104 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1106 if (g->offset_y == 0) /* frames are ordered horizontally */
1108 int max_width = g->anim_frames_per_line * g->width;
1109 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1111 src_x = pos % max_width;
1112 src_y = src_y % g->height + pos / max_width * g->height;
1114 else if (g->offset_x == 0) /* frames are ordered vertically */
1116 int max_height = g->anim_frames_per_line * g->height;
1117 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1119 src_x = src_x % g->width + pos / max_height * g->width;
1120 src_y = pos % max_height;
1122 else /* frames are ordered diagonally */
1124 src_x = src_x + frame * g->offset_x;
1125 src_y = src_y + frame * g->offset_y;
1128 *bitmap = src_bitmap;
1129 *x = src_x * tilesize / g->tile_size;
1130 *y = src_y * tilesize / g->tile_size;
1133 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1134 int *x, int *y, boolean get_backside)
1136 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1140 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1141 Bitmap **bitmap, int *x, int *y)
1143 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1146 void getFixedGraphicSource(int graphic, int frame,
1147 Bitmap **bitmap, int *x, int *y)
1149 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1152 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1154 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1157 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1158 int *x, int *y, boolean get_backside)
1160 struct GraphicInfo *g = &graphic_info[graphic];
1161 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1162 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1164 if (TILESIZE_VAR != TILESIZE)
1165 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1168 *bitmap = g->bitmap;
1170 if (g->offset_y == 0) /* frames are ordered horizontally */
1172 int max_width = g->anim_frames_per_line * g->width;
1173 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1175 *x = pos % max_width;
1176 *y = src_y % g->height + pos / max_width * g->height;
1178 else if (g->offset_x == 0) /* frames are ordered vertically */
1180 int max_height = g->anim_frames_per_line * g->height;
1181 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1183 *x = src_x % g->width + pos / max_height * g->width;
1184 *y = pos % max_height;
1186 else /* frames are ordered diagonally */
1188 *x = src_x + frame * g->offset_x;
1189 *y = src_y + frame * g->offset_y;
1192 *x = *x * TILESIZE_VAR / g->tile_size;
1193 *y = *y * TILESIZE_VAR / g->tile_size;
1196 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1198 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1201 void DrawGraphic(int x, int y, int graphic, int frame)
1204 if (!IN_SCR_FIELD(x, y))
1206 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1207 printf("DrawGraphic(): This should never happen!\n");
1212 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1215 MarkTileDirty(x, y);
1218 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1221 if (!IN_SCR_FIELD(x, y))
1223 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1224 printf("DrawGraphic(): This should never happen!\n");
1229 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1231 MarkTileDirty(x, y);
1234 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1240 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1242 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1245 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1251 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1252 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1255 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1258 if (!IN_SCR_FIELD(x, y))
1260 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1261 printf("DrawGraphicThruMask(): This should never happen!\n");
1266 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1269 MarkTileDirty(x, y);
1272 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1275 if (!IN_SCR_FIELD(x, y))
1277 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1278 printf("DrawGraphicThruMask(): This should never happen!\n");
1283 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1285 MarkTileDirty(x, y);
1288 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1294 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1296 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1300 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1301 int graphic, int frame)
1306 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1308 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1312 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1314 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1316 MarkTileDirty(x / tilesize, y / tilesize);
1319 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1325 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1326 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1329 void DrawMiniGraphic(int x, int y, int graphic)
1331 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1332 MarkTileDirty(x / 2, y / 2);
1335 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1340 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1341 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1344 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1345 int graphic, int frame,
1346 int cut_mode, int mask_mode)
1351 int width = TILEX, height = TILEY;
1354 if (dx || dy) /* shifted graphic */
1356 if (x < BX1) /* object enters playfield from the left */
1363 else if (x > BX2) /* object enters playfield from the right */
1369 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1375 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1377 else if (dx) /* general horizontal movement */
1378 MarkTileDirty(x + SIGN(dx), y);
1380 if (y < BY1) /* object enters playfield from the top */
1382 if (cut_mode == CUT_BELOW) /* object completely above top border */
1390 else if (y > BY2) /* object enters playfield from the bottom */
1396 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1402 else if (dy > 0 && cut_mode == CUT_ABOVE)
1404 if (y == BY2) /* object completely above bottom border */
1410 MarkTileDirty(x, y + 1);
1411 } /* object leaves playfield to the bottom */
1412 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1414 else if (dy) /* general vertical movement */
1415 MarkTileDirty(x, y + SIGN(dy));
1419 if (!IN_SCR_FIELD(x, y))
1421 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1422 printf("DrawGraphicShifted(): This should never happen!\n");
1427 width = width * TILESIZE_VAR / TILESIZE;
1428 height = height * TILESIZE_VAR / TILESIZE;
1429 cx = cx * TILESIZE_VAR / TILESIZE;
1430 cy = cy * TILESIZE_VAR / TILESIZE;
1431 dx = dx * TILESIZE_VAR / TILESIZE;
1432 dy = dy * TILESIZE_VAR / TILESIZE;
1434 if (width > 0 && height > 0)
1436 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1441 dst_x = FX + x * TILEX_VAR + dx;
1442 dst_y = FY + y * TILEY_VAR + dy;
1444 if (mask_mode == USE_MASKING)
1445 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1448 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1451 MarkTileDirty(x, y);
1455 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1456 int graphic, int frame,
1457 int cut_mode, int mask_mode)
1462 int width = TILEX_VAR, height = TILEY_VAR;
1465 int x2 = x + SIGN(dx);
1466 int y2 = y + SIGN(dy);
1468 /* movement with two-tile animations must be sync'ed with movement position,
1469 not with current GfxFrame (which can be higher when using slow movement) */
1470 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1471 int anim_frames = graphic_info[graphic].anim_frames;
1473 /* (we also need anim_delay here for movement animations with less frames) */
1474 int anim_delay = graphic_info[graphic].anim_delay;
1475 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1477 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1478 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1480 /* re-calculate animation frame for two-tile movement animation */
1481 frame = getGraphicAnimationFrame(graphic, sync_frame);
1483 /* check if movement start graphic inside screen area and should be drawn */
1484 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1486 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1488 dst_x = FX + x1 * TILEX_VAR;
1489 dst_y = FY + y1 * 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(x1, y1);
1501 /* check if movement end graphic inside screen area and should be drawn */
1502 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1504 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1506 dst_x = FX + x2 * TILEX_VAR;
1507 dst_y = FY + y2 * TILEY_VAR;
1509 if (mask_mode == USE_MASKING)
1510 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1513 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1516 MarkTileDirty(x2, y2);
1520 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1521 int graphic, int frame,
1522 int cut_mode, int mask_mode)
1526 DrawGraphic(x, y, graphic, frame);
1531 if (graphic_info[graphic].double_movement) /* EM style movement images */
1532 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1534 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1537 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1538 int frame, int cut_mode)
1540 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1543 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1544 int cut_mode, int mask_mode)
1546 int lx = LEVELX(x), ly = LEVELY(y);
1550 if (IN_LEV_FIELD(lx, ly))
1552 SetRandomAnimationValue(lx, ly);
1554 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1555 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1557 /* do not use double (EM style) movement graphic when not moving */
1558 if (graphic_info[graphic].double_movement && !dx && !dy)
1560 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1561 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1564 else /* border element */
1566 graphic = el2img(element);
1567 frame = getGraphicAnimationFrame(graphic, -1);
1570 if (element == EL_EXPANDABLE_WALL)
1572 boolean left_stopped = FALSE, right_stopped = FALSE;
1574 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1575 left_stopped = TRUE;
1576 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1577 right_stopped = TRUE;
1579 if (left_stopped && right_stopped)
1581 else if (left_stopped)
1583 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1584 frame = graphic_info[graphic].anim_frames - 1;
1586 else if (right_stopped)
1588 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1589 frame = graphic_info[graphic].anim_frames - 1;
1594 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1595 else if (mask_mode == USE_MASKING)
1596 DrawGraphicThruMask(x, y, graphic, frame);
1598 DrawGraphic(x, y, graphic, frame);
1601 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1602 int cut_mode, int mask_mode)
1604 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1605 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1606 cut_mode, mask_mode);
1609 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1612 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1615 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1618 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1621 void DrawLevelElementThruMask(int x, int y, int element)
1623 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1626 void DrawLevelFieldThruMask(int x, int y)
1628 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1631 /* !!! implementation of quicksand is totally broken !!! */
1632 #define IS_CRUMBLED_TILE(x, y, e) \
1633 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1634 !IS_MOVING(x, y) || \
1635 (e) == EL_QUICKSAND_EMPTYING || \
1636 (e) == EL_QUICKSAND_FAST_EMPTYING))
1638 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1643 int width, height, cx, cy;
1644 int sx = SCREENX(x), sy = SCREENY(y);
1645 int crumbled_border_size = graphic_info[graphic].border_size;
1648 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1650 for (i = 1; i < 4; i++)
1652 int dxx = (i & 1 ? dx : 0);
1653 int dyy = (i & 2 ? dy : 0);
1656 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1659 /* check if neighbour field is of same crumble type */
1660 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1661 graphic_info[graphic].class ==
1662 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1664 /* return if check prevents inner corner */
1665 if (same == (dxx == dx && dyy == dy))
1669 /* if we reach this point, we have an inner corner */
1671 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1673 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1674 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1675 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1676 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1678 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1679 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1682 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1687 int width, height, bx, by, cx, cy;
1688 int sx = SCREENX(x), sy = SCREENY(y);
1689 int crumbled_border_size = graphic_info[graphic].border_size;
1690 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1691 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1694 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1696 /* draw simple, sloppy, non-corner-accurate crumbled border */
1698 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1699 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1700 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1701 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1703 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1704 FX + sx * TILEX_VAR + cx,
1705 FY + sy * TILEY_VAR + cy);
1707 /* (remaining middle border part must be at least as big as corner part) */
1708 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1709 crumbled_border_size >= TILESIZE / 3)
1712 /* correct corners of crumbled border, if needed */
1714 for (i = -1; i <= 1; i += 2)
1716 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1717 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1718 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1721 /* check if neighbour field is of same crumble type */
1722 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1723 graphic_info[graphic].class ==
1724 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1726 /* no crumbled corner, but continued crumbled border */
1728 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1729 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1730 int b1 = (i == 1 ? crumbled_border_size_var :
1731 TILESIZE_VAR - 2 * crumbled_border_size_var);
1733 width = crumbled_border_size_var;
1734 height = crumbled_border_size_var;
1736 if (dir == 1 || dir == 2)
1751 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1753 FX + sx * TILEX_VAR + cx,
1754 FY + sy * TILEY_VAR + cy);
1759 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1761 int sx = SCREENX(x), sy = SCREENY(y);
1764 static int xy[4][2] =
1772 if (!IN_LEV_FIELD(x, y))
1775 element = TILE_GFX_ELEMENT(x, y);
1777 /* crumble field itself */
1778 if (IS_CRUMBLED_TILE(x, y, element))
1780 if (!IN_SCR_FIELD(sx, sy))
1783 for (i = 0; i < 4; i++)
1785 int xx = x + xy[i][0];
1786 int yy = y + xy[i][1];
1788 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1791 /* check if neighbour field is of same crumble type */
1792 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1793 graphic_info[graphic].class ==
1794 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1797 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1800 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1801 graphic_info[graphic].anim_frames == 2)
1803 for (i = 0; i < 4; i++)
1805 int dx = (i & 1 ? +1 : -1);
1806 int dy = (i & 2 ? +1 : -1);
1808 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1812 MarkTileDirty(sx, sy);
1814 else /* center field not crumbled -- crumble neighbour fields */
1816 for (i = 0; i < 4; i++)
1818 int xx = x + xy[i][0];
1819 int yy = y + xy[i][1];
1820 int sxx = sx + xy[i][0];
1821 int syy = sy + xy[i][1];
1823 if (!IN_LEV_FIELD(xx, yy) ||
1824 !IN_SCR_FIELD(sxx, syy))
1827 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1830 element = TILE_GFX_ELEMENT(xx, yy);
1832 if (!IS_CRUMBLED_TILE(xx, yy, element))
1835 graphic = el_act2crm(element, ACTION_DEFAULT);
1837 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1839 MarkTileDirty(sxx, syy);
1844 void DrawLevelFieldCrumbled(int x, int y)
1848 if (!IN_LEV_FIELD(x, y))
1851 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1852 GfxElement[x][y] != EL_UNDEFINED &&
1853 GFX_CRUMBLED(GfxElement[x][y]))
1855 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1860 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1862 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1865 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1868 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1869 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1870 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1871 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1872 int sx = SCREENX(x), sy = SCREENY(y);
1874 DrawGraphic(sx, sy, graphic1, frame1);
1875 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1878 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1880 int sx = SCREENX(x), sy = SCREENY(y);
1881 static int xy[4][2] =
1890 for (i = 0; i < 4; i++)
1892 int xx = x + xy[i][0];
1893 int yy = y + xy[i][1];
1894 int sxx = sx + xy[i][0];
1895 int syy = sy + xy[i][1];
1897 if (!IN_LEV_FIELD(xx, yy) ||
1898 !IN_SCR_FIELD(sxx, syy) ||
1899 !GFX_CRUMBLED(Feld[xx][yy]) ||
1903 DrawLevelField(xx, yy);
1907 static int getBorderElement(int x, int y)
1911 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1912 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1913 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1914 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1915 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1916 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1917 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1919 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1920 int steel_position = (x == -1 && y == -1 ? 0 :
1921 x == lev_fieldx && y == -1 ? 1 :
1922 x == -1 && y == lev_fieldy ? 2 :
1923 x == lev_fieldx && y == lev_fieldy ? 3 :
1924 x == -1 || x == lev_fieldx ? 4 :
1925 y == -1 || y == lev_fieldy ? 5 : 6);
1927 return border[steel_position][steel_type];
1930 void DrawScreenElement(int x, int y, int element)
1932 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1933 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1936 void DrawLevelElement(int x, int y, int element)
1938 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1939 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1942 void DrawScreenField(int x, int y)
1944 int lx = LEVELX(x), ly = LEVELY(y);
1945 int element, content;
1947 if (!IN_LEV_FIELD(lx, ly))
1949 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1952 element = getBorderElement(lx, ly);
1954 DrawScreenElement(x, y, element);
1959 element = Feld[lx][ly];
1960 content = Store[lx][ly];
1962 if (IS_MOVING(lx, ly))
1964 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1965 boolean cut_mode = NO_CUTTING;
1967 if (element == EL_QUICKSAND_EMPTYING ||
1968 element == EL_QUICKSAND_FAST_EMPTYING ||
1969 element == EL_MAGIC_WALL_EMPTYING ||
1970 element == EL_BD_MAGIC_WALL_EMPTYING ||
1971 element == EL_DC_MAGIC_WALL_EMPTYING ||
1972 element == EL_AMOEBA_DROPPING)
1973 cut_mode = CUT_ABOVE;
1974 else if (element == EL_QUICKSAND_FILLING ||
1975 element == EL_QUICKSAND_FAST_FILLING ||
1976 element == EL_MAGIC_WALL_FILLING ||
1977 element == EL_BD_MAGIC_WALL_FILLING ||
1978 element == EL_DC_MAGIC_WALL_FILLING)
1979 cut_mode = CUT_BELOW;
1981 if (cut_mode == CUT_ABOVE)
1982 DrawScreenElement(x, y, element);
1984 DrawScreenElement(x, y, EL_EMPTY);
1987 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1988 else if (cut_mode == NO_CUTTING)
1989 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1992 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1994 if (cut_mode == CUT_BELOW &&
1995 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1996 DrawLevelElement(lx, ly + 1, element);
1999 if (content == EL_ACID)
2001 int dir = MovDir[lx][ly];
2002 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2003 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2005 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2008 else if (IS_BLOCKED(lx, ly))
2013 boolean cut_mode = NO_CUTTING;
2014 int element_old, content_old;
2016 Blocked2Moving(lx, ly, &oldx, &oldy);
2019 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2020 MovDir[oldx][oldy] == MV_RIGHT);
2022 element_old = Feld[oldx][oldy];
2023 content_old = Store[oldx][oldy];
2025 if (element_old == EL_QUICKSAND_EMPTYING ||
2026 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2027 element_old == EL_MAGIC_WALL_EMPTYING ||
2028 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2029 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2030 element_old == EL_AMOEBA_DROPPING)
2031 cut_mode = CUT_ABOVE;
2033 DrawScreenElement(x, y, EL_EMPTY);
2036 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2038 else if (cut_mode == NO_CUTTING)
2039 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2042 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2045 else if (IS_DRAWABLE(element))
2046 DrawScreenElement(x, y, element);
2048 DrawScreenElement(x, y, EL_EMPTY);
2051 void DrawLevelField(int x, int y)
2053 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2054 DrawScreenField(SCREENX(x), SCREENY(y));
2055 else if (IS_MOVING(x, y))
2059 Moving2Blocked(x, y, &newx, &newy);
2060 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2061 DrawScreenField(SCREENX(newx), SCREENY(newy));
2063 else if (IS_BLOCKED(x, y))
2067 Blocked2Moving(x, y, &oldx, &oldy);
2068 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2069 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2073 void DrawSizedElement(int x, int y, int element, int tilesize)
2077 graphic = el2edimg(element);
2078 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2081 void DrawMiniElement(int x, int y, int element)
2085 graphic = el2edimg(element);
2086 DrawMiniGraphic(x, y, graphic);
2089 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2092 int x = sx + scroll_x, y = sy + scroll_y;
2094 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2095 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2096 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2097 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2099 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2102 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2104 int x = sx + scroll_x, y = sy + scroll_y;
2106 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2107 DrawMiniElement(sx, sy, EL_EMPTY);
2108 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2109 DrawMiniElement(sx, sy, Feld[x][y]);
2111 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2114 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2115 int x, int y, int xsize, int ysize,
2116 int tile_width, int tile_height)
2120 int dst_x = startx + x * tile_width;
2121 int dst_y = starty + y * tile_height;
2122 int width = graphic_info[graphic].width;
2123 int height = graphic_info[graphic].height;
2124 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2125 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2126 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2127 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2128 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2129 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2130 boolean draw_masked = graphic_info[graphic].draw_masked;
2132 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2134 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2136 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2140 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2141 inner_sx + (x - 1) * tile_width % inner_width);
2142 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2143 inner_sy + (y - 1) * tile_height % inner_height);
2146 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2149 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2153 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2154 int x, int y, int xsize, int ysize, int font_nr)
2156 int font_width = getFontWidth(font_nr);
2157 int font_height = getFontHeight(font_nr);
2159 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2160 font_width, font_height);
2163 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2165 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2166 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2167 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2168 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2169 boolean no_delay = (tape.warp_forward);
2170 unsigned int anim_delay = 0;
2171 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2172 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2173 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2174 int font_width = getFontWidth(font_nr);
2175 int font_height = getFontHeight(font_nr);
2176 int max_xsize = level.envelope[envelope_nr].xsize;
2177 int max_ysize = level.envelope[envelope_nr].ysize;
2178 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2179 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2180 int xend = max_xsize;
2181 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2182 int xstep = (xstart < xend ? 1 : 0);
2183 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2185 int end = MAX(xend - xstart, yend - ystart);
2188 for (i = start; i <= end; i++)
2190 int last_frame = end; // last frame of this "for" loop
2191 int x = xstart + i * xstep;
2192 int y = ystart + i * ystep;
2193 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2194 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2195 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2196 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2199 SetDrawtoField(DRAW_FIELDBUFFER);
2201 BlitScreenToBitmap(backbuffer);
2203 SetDrawtoField(DRAW_BACKBUFFER);
2205 for (yy = 0; yy < ysize; yy++)
2206 for (xx = 0; xx < xsize; xx++)
2207 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2209 DrawTextBuffer(sx + font_width, sy + font_height,
2210 level.envelope[envelope_nr].text, font_nr, max_xsize,
2211 xsize - 2, ysize - 2, 0, mask_mode,
2212 level.envelope[envelope_nr].autowrap,
2213 level.envelope[envelope_nr].centered, FALSE);
2215 redraw_mask |= REDRAW_FIELD;
2218 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2222 void ShowEnvelope(int envelope_nr)
2224 int element = EL_ENVELOPE_1 + envelope_nr;
2225 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2226 int sound_opening = element_info[element].sound[ACTION_OPENING];
2227 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2228 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2229 boolean no_delay = (tape.warp_forward);
2230 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2231 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2232 int anim_mode = graphic_info[graphic].anim_mode;
2233 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2234 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2236 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2238 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2240 if (anim_mode == ANIM_DEFAULT)
2241 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2243 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2246 Delay(wait_delay_value);
2248 WaitForEventToContinue();
2250 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2252 if (anim_mode != ANIM_NONE)
2253 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2255 if (anim_mode == ANIM_DEFAULT)
2256 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2258 game.envelope_active = FALSE;
2260 SetDrawtoField(DRAW_FIELDBUFFER);
2262 redraw_mask |= REDRAW_FIELD;
2266 static void setRequestBasePosition(int *x, int *y)
2268 int sx_base, sy_base;
2270 if (request.x != -1)
2271 sx_base = request.x;
2272 else if (request.align == ALIGN_LEFT)
2274 else if (request.align == ALIGN_RIGHT)
2275 sx_base = SX + SXSIZE;
2277 sx_base = SX + SXSIZE / 2;
2279 if (request.y != -1)
2280 sy_base = request.y;
2281 else if (request.valign == VALIGN_TOP)
2283 else if (request.valign == VALIGN_BOTTOM)
2284 sy_base = SY + SYSIZE;
2286 sy_base = SY + SYSIZE / 2;
2292 static void setRequestPositionExt(int *x, int *y, int width, int height,
2293 boolean add_border_size)
2295 int border_size = request.border_size;
2296 int sx_base, sy_base;
2299 setRequestBasePosition(&sx_base, &sy_base);
2301 if (request.align == ALIGN_LEFT)
2303 else if (request.align == ALIGN_RIGHT)
2304 sx = sx_base - width;
2306 sx = sx_base - width / 2;
2308 if (request.valign == VALIGN_TOP)
2310 else if (request.valign == VALIGN_BOTTOM)
2311 sy = sy_base - height;
2313 sy = sy_base - height / 2;
2315 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2316 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2318 if (add_border_size)
2328 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2330 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2333 void DrawEnvelopeRequest(char *text)
2335 int last_game_status = game_status; /* save current game status */
2336 char *text_final = text;
2337 char *text_door_style = NULL;
2338 int graphic = IMG_BACKGROUND_REQUEST;
2339 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2340 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2341 int font_nr = FONT_REQUEST;
2342 int font_width = getFontWidth(font_nr);
2343 int font_height = getFontHeight(font_nr);
2344 int border_size = request.border_size;
2345 int line_spacing = request.line_spacing;
2346 int line_height = font_height + line_spacing;
2347 int max_text_width = request.width - 2 * border_size;
2348 int max_text_height = request.height - 2 * border_size;
2349 int line_length = max_text_width / font_width;
2350 int max_lines = max_text_height / line_height;
2351 int text_width = line_length * font_width;
2352 int width = request.width;
2353 int height = request.height;
2354 int tile_size = MAX(request.step_offset, 1);
2355 int x_steps = width / tile_size;
2356 int y_steps = height / tile_size;
2357 int sx_offset = border_size;
2358 int sy_offset = border_size;
2362 if (request.centered)
2363 sx_offset = (request.width - text_width) / 2;
2365 if (request.wrap_single_words && !request.autowrap)
2367 char *src_text_ptr, *dst_text_ptr;
2369 text_door_style = checked_malloc(2 * strlen(text) + 1);
2371 src_text_ptr = text;
2372 dst_text_ptr = text_door_style;
2374 while (*src_text_ptr)
2376 if (*src_text_ptr == ' ' ||
2377 *src_text_ptr == '?' ||
2378 *src_text_ptr == '!')
2379 *dst_text_ptr++ = '\n';
2381 if (*src_text_ptr != ' ')
2382 *dst_text_ptr++ = *src_text_ptr;
2387 *dst_text_ptr = '\0';
2389 text_final = text_door_style;
2392 setRequestPosition(&sx, &sy, FALSE);
2394 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2396 for (y = 0; y < y_steps; y++)
2397 for (x = 0; x < x_steps; x++)
2398 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2399 x, y, x_steps, y_steps,
2400 tile_size, tile_size);
2402 /* force DOOR font inside door area */
2403 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
2405 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2406 line_length, -1, max_lines, line_spacing, mask_mode,
2407 request.autowrap, request.centered, FALSE);
2409 SetGameStatus(last_game_status); /* restore current game status */
2411 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2412 RedrawGadget(tool_gadget[i]);
2414 // store readily prepared envelope request for later use when animating
2415 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2417 if (text_door_style)
2418 free(text_door_style);
2421 void AnimateEnvelopeRequest(int anim_mode, int action)
2423 int graphic = IMG_BACKGROUND_REQUEST;
2424 boolean draw_masked = graphic_info[graphic].draw_masked;
2425 int delay_value_normal = request.step_delay;
2426 int delay_value_fast = delay_value_normal / 2;
2427 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2428 boolean no_delay = (tape.warp_forward);
2429 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2430 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2431 unsigned int anim_delay = 0;
2433 int tile_size = MAX(request.step_offset, 1);
2434 int max_xsize = request.width / tile_size;
2435 int max_ysize = request.height / tile_size;
2436 int max_xsize_inner = max_xsize - 2;
2437 int max_ysize_inner = max_ysize - 2;
2439 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2440 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2441 int xend = max_xsize_inner;
2442 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2443 int xstep = (xstart < xend ? 1 : 0);
2444 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2446 int end = MAX(xend - xstart, yend - ystart);
2449 if (setup.quick_doors)
2456 for (i = start; i <= end; i++)
2458 int last_frame = end; // last frame of this "for" loop
2459 int x = xstart + i * xstep;
2460 int y = ystart + i * ystep;
2461 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2462 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2463 int xsize_size_left = (xsize - 1) * tile_size;
2464 int ysize_size_top = (ysize - 1) * tile_size;
2465 int max_xsize_pos = (max_xsize - 1) * tile_size;
2466 int max_ysize_pos = (max_ysize - 1) * tile_size;
2467 int width = xsize * tile_size;
2468 int height = ysize * tile_size;
2473 setRequestPosition(&src_x, &src_y, FALSE);
2474 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2476 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2478 for (yy = 0; yy < 2; yy++)
2480 for (xx = 0; xx < 2; xx++)
2482 int src_xx = src_x + xx * max_xsize_pos;
2483 int src_yy = src_y + yy * max_ysize_pos;
2484 int dst_xx = dst_x + xx * xsize_size_left;
2485 int dst_yy = dst_y + yy * ysize_size_top;
2486 int xx_size = (xx ? tile_size : xsize_size_left);
2487 int yy_size = (yy ? tile_size : ysize_size_top);
2490 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2491 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2493 BlitBitmap(bitmap_db_cross, backbuffer,
2494 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2498 redraw_mask |= REDRAW_FIELD;
2503 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2507 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2509 int graphic = IMG_BACKGROUND_REQUEST;
2510 int sound_opening = SND_REQUEST_OPENING;
2511 int sound_closing = SND_REQUEST_CLOSING;
2512 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2513 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2514 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2515 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2516 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2518 if (game_status == GAME_MODE_PLAYING)
2519 BlitScreenToBitmap(backbuffer);
2521 SetDrawtoField(DRAW_BACKBUFFER);
2523 // SetDrawBackgroundMask(REDRAW_NONE);
2525 if (action == ACTION_OPENING)
2527 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2529 if (req_state & REQ_ASK)
2531 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2532 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2534 else if (req_state & REQ_CONFIRM)
2536 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2538 else if (req_state & REQ_PLAYER)
2540 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2541 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2542 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2543 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2546 DrawEnvelopeRequest(text);
2548 if (game_status != GAME_MODE_MAIN)
2552 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2554 if (action == ACTION_OPENING)
2556 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2558 if (anim_mode == ANIM_DEFAULT)
2559 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2561 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2565 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2567 if (anim_mode != ANIM_NONE)
2568 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2570 if (anim_mode == ANIM_DEFAULT)
2571 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2574 game.envelope_active = FALSE;
2576 if (action == ACTION_CLOSING)
2578 if (game_status != GAME_MODE_MAIN)
2581 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2584 // SetDrawBackgroundMask(last_draw_background_mask);
2586 redraw_mask |= REDRAW_FIELD;
2588 if (game_status == GAME_MODE_MAIN)
2593 if (action == ACTION_CLOSING &&
2594 game_status == GAME_MODE_PLAYING &&
2595 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2596 SetDrawtoField(DRAW_FIELDBUFFER);
2599 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2603 int graphic = el2preimg(element);
2605 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2606 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2609 void DrawLevel(int draw_background_mask)
2613 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2614 SetDrawBackgroundMask(draw_background_mask);
2618 for (x = BX1; x <= BX2; x++)
2619 for (y = BY1; y <= BY2; y++)
2620 DrawScreenField(x, y);
2622 redraw_mask |= REDRAW_FIELD;
2625 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2630 for (x = 0; x < size_x; x++)
2631 for (y = 0; y < size_y; y++)
2632 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2634 redraw_mask |= REDRAW_FIELD;
2637 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2641 for (x = 0; x < size_x; x++)
2642 for (y = 0; y < size_y; y++)
2643 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2645 redraw_mask |= REDRAW_FIELD;
2648 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2650 boolean show_level_border = (BorderElement != EL_EMPTY);
2651 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2652 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2653 int tile_size = preview.tile_size;
2654 int preview_width = preview.xsize * tile_size;
2655 int preview_height = preview.ysize * tile_size;
2656 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2657 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2658 int real_preview_width = real_preview_xsize * tile_size;
2659 int real_preview_height = real_preview_ysize * tile_size;
2660 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2661 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2664 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2667 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2669 dst_x += (preview_width - real_preview_width) / 2;
2670 dst_y += (preview_height - real_preview_height) / 2;
2672 for (x = 0; x < real_preview_xsize; x++)
2674 for (y = 0; y < real_preview_ysize; y++)
2676 int lx = from_x + x + (show_level_border ? -1 : 0);
2677 int ly = from_y + y + (show_level_border ? -1 : 0);
2678 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2679 getBorderElement(lx, ly));
2681 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2682 element, tile_size);
2686 redraw_mask |= REDRAW_FIELD;
2689 #define MICROLABEL_EMPTY 0
2690 #define MICROLABEL_LEVEL_NAME 1
2691 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2692 #define MICROLABEL_LEVEL_AUTHOR 3
2693 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2694 #define MICROLABEL_IMPORTED_FROM 5
2695 #define MICROLABEL_IMPORTED_BY_HEAD 6
2696 #define MICROLABEL_IMPORTED_BY 7
2698 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2700 int max_text_width = SXSIZE;
2701 int font_width = getFontWidth(font_nr);
2703 if (pos->align == ALIGN_CENTER)
2704 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2705 else if (pos->align == ALIGN_RIGHT)
2706 max_text_width = pos->x;
2708 max_text_width = SXSIZE - pos->x;
2710 return max_text_width / font_width;
2713 static void DrawPreviewLevelLabelExt(int mode)
2715 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2716 char label_text[MAX_OUTPUT_LINESIZE + 1];
2717 int max_len_label_text;
2718 int font_nr = pos->font;
2721 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2724 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2725 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2726 mode == MICROLABEL_IMPORTED_BY_HEAD)
2727 font_nr = pos->font_alt;
2729 max_len_label_text = getMaxTextLength(pos, font_nr);
2731 if (pos->size != -1)
2732 max_len_label_text = pos->size;
2734 for (i = 0; i < max_len_label_text; i++)
2735 label_text[i] = ' ';
2736 label_text[max_len_label_text] = '\0';
2738 if (strlen(label_text) > 0)
2739 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2742 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2743 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2744 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2745 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2746 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2747 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2748 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2749 max_len_label_text);
2750 label_text[max_len_label_text] = '\0';
2752 if (strlen(label_text) > 0)
2753 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2755 redraw_mask |= REDRAW_FIELD;
2758 static void DrawPreviewLevelExt(boolean restart)
2760 static unsigned int scroll_delay = 0;
2761 static unsigned int label_delay = 0;
2762 static int from_x, from_y, scroll_direction;
2763 static int label_state, label_counter;
2764 unsigned int scroll_delay_value = preview.step_delay;
2765 boolean show_level_border = (BorderElement != EL_EMPTY);
2766 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2767 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2768 int last_game_status = game_status; /* save current game status */
2775 if (preview.anim_mode == ANIM_CENTERED)
2777 if (level_xsize > preview.xsize)
2778 from_x = (level_xsize - preview.xsize) / 2;
2779 if (level_ysize > preview.ysize)
2780 from_y = (level_ysize - preview.ysize) / 2;
2783 from_x += preview.xoffset;
2784 from_y += preview.yoffset;
2786 scroll_direction = MV_RIGHT;
2790 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2791 DrawPreviewLevelLabelExt(label_state);
2793 /* initialize delay counters */
2794 DelayReached(&scroll_delay, 0);
2795 DelayReached(&label_delay, 0);
2797 if (leveldir_current->name)
2799 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2800 char label_text[MAX_OUTPUT_LINESIZE + 1];
2801 int font_nr = pos->font;
2802 int max_len_label_text = getMaxTextLength(pos, font_nr);
2804 if (pos->size != -1)
2805 max_len_label_text = pos->size;
2807 strncpy(label_text, leveldir_current->name, max_len_label_text);
2808 label_text[max_len_label_text] = '\0';
2810 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2811 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2814 SetGameStatus(last_game_status); /* restore current game status */
2819 /* scroll preview level, if needed */
2820 if (preview.anim_mode != ANIM_NONE &&
2821 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2822 DelayReached(&scroll_delay, scroll_delay_value))
2824 switch (scroll_direction)
2829 from_x -= preview.step_offset;
2830 from_x = (from_x < 0 ? 0 : from_x);
2833 scroll_direction = MV_UP;
2837 if (from_x < level_xsize - preview.xsize)
2839 from_x += preview.step_offset;
2840 from_x = (from_x > level_xsize - preview.xsize ?
2841 level_xsize - preview.xsize : from_x);
2844 scroll_direction = MV_DOWN;
2850 from_y -= preview.step_offset;
2851 from_y = (from_y < 0 ? 0 : from_y);
2854 scroll_direction = MV_RIGHT;
2858 if (from_y < level_ysize - preview.ysize)
2860 from_y += preview.step_offset;
2861 from_y = (from_y > level_ysize - preview.ysize ?
2862 level_ysize - preview.ysize : from_y);
2865 scroll_direction = MV_LEFT;
2872 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2875 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2876 /* redraw micro level label, if needed */
2877 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2878 !strEqual(level.author, ANONYMOUS_NAME) &&
2879 !strEqual(level.author, leveldir_current->name) &&
2880 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2882 int max_label_counter = 23;
2884 if (leveldir_current->imported_from != NULL &&
2885 strlen(leveldir_current->imported_from) > 0)
2886 max_label_counter += 14;
2887 if (leveldir_current->imported_by != NULL &&
2888 strlen(leveldir_current->imported_by) > 0)
2889 max_label_counter += 14;
2891 label_counter = (label_counter + 1) % max_label_counter;
2892 label_state = (label_counter >= 0 && label_counter <= 7 ?
2893 MICROLABEL_LEVEL_NAME :
2894 label_counter >= 9 && label_counter <= 12 ?
2895 MICROLABEL_LEVEL_AUTHOR_HEAD :
2896 label_counter >= 14 && label_counter <= 21 ?
2897 MICROLABEL_LEVEL_AUTHOR :
2898 label_counter >= 23 && label_counter <= 26 ?
2899 MICROLABEL_IMPORTED_FROM_HEAD :
2900 label_counter >= 28 && label_counter <= 35 ?
2901 MICROLABEL_IMPORTED_FROM :
2902 label_counter >= 37 && label_counter <= 40 ?
2903 MICROLABEL_IMPORTED_BY_HEAD :
2904 label_counter >= 42 && label_counter <= 49 ?
2905 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2907 if (leveldir_current->imported_from == NULL &&
2908 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2909 label_state == MICROLABEL_IMPORTED_FROM))
2910 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2911 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2913 DrawPreviewLevelLabelExt(label_state);
2916 SetGameStatus(last_game_status); /* restore current game status */
2919 void DrawPreviewLevelInitial()
2921 DrawPreviewLevelExt(TRUE);
2924 void DrawPreviewLevelAnimation()
2926 DrawPreviewLevelExt(FALSE);
2929 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2930 int graphic, int sync_frame,
2933 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2935 if (mask_mode == USE_MASKING)
2936 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2938 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2941 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2942 int graphic, int sync_frame, int mask_mode)
2944 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2946 if (mask_mode == USE_MASKING)
2947 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2949 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2952 inline static void DrawGraphicAnimation(int x, int y, int graphic)
2954 int lx = LEVELX(x), ly = LEVELY(y);
2956 if (!IN_SCR_FIELD(x, y))
2959 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2960 graphic, GfxFrame[lx][ly], NO_MASKING);
2962 MarkTileDirty(x, y);
2965 void DrawFixedGraphicAnimation(int x, int y, int graphic)
2967 int lx = LEVELX(x), ly = LEVELY(y);
2969 if (!IN_SCR_FIELD(x, y))
2972 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2973 graphic, GfxFrame[lx][ly], NO_MASKING);
2974 MarkTileDirty(x, y);
2977 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2979 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2982 void DrawLevelElementAnimation(int x, int y, int element)
2984 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2986 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2989 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2991 int sx = SCREENX(x), sy = SCREENY(y);
2993 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2996 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2999 DrawGraphicAnimation(sx, sy, graphic);
3002 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3003 DrawLevelFieldCrumbled(x, y);
3005 if (GFX_CRUMBLED(Feld[x][y]))
3006 DrawLevelFieldCrumbled(x, y);
3010 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3012 int sx = SCREENX(x), sy = SCREENY(y);
3015 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3018 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3020 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3023 DrawGraphicAnimation(sx, sy, graphic);
3025 if (GFX_CRUMBLED(element))
3026 DrawLevelFieldCrumbled(x, y);
3029 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3031 if (player->use_murphy)
3033 /* this works only because currently only one player can be "murphy" ... */
3034 static int last_horizontal_dir = MV_LEFT;
3035 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3037 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3038 last_horizontal_dir = move_dir;
3040 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3042 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3044 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3050 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3053 static boolean equalGraphics(int graphic1, int graphic2)
3055 struct GraphicInfo *g1 = &graphic_info[graphic1];
3056 struct GraphicInfo *g2 = &graphic_info[graphic2];
3058 return (g1->bitmap == g2->bitmap &&
3059 g1->src_x == g2->src_x &&
3060 g1->src_y == g2->src_y &&
3061 g1->anim_frames == g2->anim_frames &&
3062 g1->anim_delay == g2->anim_delay &&
3063 g1->anim_mode == g2->anim_mode);
3066 void DrawAllPlayers()
3070 for (i = 0; i < MAX_PLAYERS; i++)
3071 if (stored_player[i].active)
3072 DrawPlayer(&stored_player[i]);
3075 void DrawPlayerField(int x, int y)
3077 if (!IS_PLAYER(x, y))
3080 DrawPlayer(PLAYERINFO(x, y));
3083 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3085 void DrawPlayer(struct PlayerInfo *player)
3087 int jx = player->jx;
3088 int jy = player->jy;
3089 int move_dir = player->MovDir;
3090 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3091 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3092 int last_jx = (player->is_moving ? jx - dx : jx);
3093 int last_jy = (player->is_moving ? jy - dy : jy);
3094 int next_jx = jx + dx;
3095 int next_jy = jy + dy;
3096 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3097 boolean player_is_opaque = FALSE;
3098 int sx = SCREENX(jx), sy = SCREENY(jy);
3099 int sxx = 0, syy = 0;
3100 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3102 int action = ACTION_DEFAULT;
3103 int last_player_graphic = getPlayerGraphic(player, move_dir);
3104 int last_player_frame = player->Frame;
3107 /* GfxElement[][] is set to the element the player is digging or collecting;
3108 remove also for off-screen player if the player is not moving anymore */
3109 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3110 GfxElement[jx][jy] = EL_UNDEFINED;
3112 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3116 if (!IN_LEV_FIELD(jx, jy))
3118 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3119 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3120 printf("DrawPlayerField(): This should never happen!\n");
3125 if (element == EL_EXPLOSION)
3128 action = (player->is_pushing ? ACTION_PUSHING :
3129 player->is_digging ? ACTION_DIGGING :
3130 player->is_collecting ? ACTION_COLLECTING :
3131 player->is_moving ? ACTION_MOVING :
3132 player->is_snapping ? ACTION_SNAPPING :
3133 player->is_dropping ? ACTION_DROPPING :
3134 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3136 if (player->is_waiting)
3137 move_dir = player->dir_waiting;
3139 InitPlayerGfxAnimation(player, action, move_dir);
3141 /* ----------------------------------------------------------------------- */
3142 /* draw things in the field the player is leaving, if needed */
3143 /* ----------------------------------------------------------------------- */
3145 if (player->is_moving)
3147 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3149 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3151 if (last_element == EL_DYNAMITE_ACTIVE ||
3152 last_element == EL_EM_DYNAMITE_ACTIVE ||
3153 last_element == EL_SP_DISK_RED_ACTIVE)
3154 DrawDynamite(last_jx, last_jy);
3156 DrawLevelFieldThruMask(last_jx, last_jy);
3158 else if (last_element == EL_DYNAMITE_ACTIVE ||
3159 last_element == EL_EM_DYNAMITE_ACTIVE ||
3160 last_element == EL_SP_DISK_RED_ACTIVE)
3161 DrawDynamite(last_jx, last_jy);
3163 /* !!! this is not enough to prevent flickering of players which are
3164 moving next to each others without a free tile between them -- this
3165 can only be solved by drawing all players layer by layer (first the
3166 background, then the foreground etc.) !!! => TODO */
3167 else if (!IS_PLAYER(last_jx, last_jy))
3168 DrawLevelField(last_jx, last_jy);
3171 DrawLevelField(last_jx, last_jy);
3174 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3175 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3178 if (!IN_SCR_FIELD(sx, sy))
3181 /* ----------------------------------------------------------------------- */
3182 /* draw things behind the player, if needed */
3183 /* ----------------------------------------------------------------------- */
3186 DrawLevelElement(jx, jy, Back[jx][jy]);
3187 else if (IS_ACTIVE_BOMB(element))
3188 DrawLevelElement(jx, jy, EL_EMPTY);
3191 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3193 int old_element = GfxElement[jx][jy];
3194 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3195 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3197 if (GFX_CRUMBLED(old_element))
3198 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3200 DrawGraphic(sx, sy, old_graphic, frame);
3202 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3203 player_is_opaque = TRUE;
3207 GfxElement[jx][jy] = EL_UNDEFINED;
3209 /* make sure that pushed elements are drawn with correct frame rate */
3210 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3212 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3213 GfxFrame[jx][jy] = player->StepFrame;
3215 DrawLevelField(jx, jy);
3219 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3220 /* ----------------------------------------------------------------------- */
3221 /* draw player himself */
3222 /* ----------------------------------------------------------------------- */
3224 graphic = getPlayerGraphic(player, move_dir);
3226 /* in the case of changed player action or direction, prevent the current
3227 animation frame from being restarted for identical animations */
3228 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3229 player->Frame = last_player_frame;
3231 frame = getGraphicAnimationFrame(graphic, player->Frame);
3235 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3236 sxx = player->GfxPos;
3238 syy = player->GfxPos;
3241 if (player_is_opaque)
3242 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3244 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3246 if (SHIELD_ON(player))
3248 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3249 IMG_SHIELD_NORMAL_ACTIVE);
3250 int frame = getGraphicAnimationFrame(graphic, -1);
3252 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3256 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3259 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3260 sxx = player->GfxPos;
3262 syy = player->GfxPos;
3266 /* ----------------------------------------------------------------------- */
3267 /* draw things the player is pushing, if needed */
3268 /* ----------------------------------------------------------------------- */
3270 if (player->is_pushing && player->is_moving)
3272 int px = SCREENX(jx), py = SCREENY(jy);
3273 int pxx = (TILEX - ABS(sxx)) * dx;
3274 int pyy = (TILEY - ABS(syy)) * dy;
3275 int gfx_frame = GfxFrame[jx][jy];
3281 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3283 element = Feld[next_jx][next_jy];
3284 gfx_frame = GfxFrame[next_jx][next_jy];
3287 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3289 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3290 frame = getGraphicAnimationFrame(graphic, sync_frame);
3292 /* draw background element under pushed element (like the Sokoban field) */
3293 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3295 /* this allows transparent pushing animation over non-black background */
3298 DrawLevelElement(jx, jy, Back[jx][jy]);
3300 DrawLevelElement(jx, jy, EL_EMPTY);
3302 if (Back[next_jx][next_jy])
3303 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3305 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3307 else if (Back[next_jx][next_jy])
3308 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3311 /* do not draw (EM style) pushing animation when pushing is finished */
3312 /* (two-tile animations usually do not contain start and end frame) */
3313 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3314 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3316 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3318 /* masked drawing is needed for EMC style (double) movement graphics */
3319 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3320 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3324 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3325 /* ----------------------------------------------------------------------- */
3326 /* draw player himself */
3327 /* ----------------------------------------------------------------------- */
3329 graphic = getPlayerGraphic(player, move_dir);
3331 /* in the case of changed player action or direction, prevent the current
3332 animation frame from being restarted for identical animations */
3333 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3334 player->Frame = last_player_frame;
3336 frame = getGraphicAnimationFrame(graphic, player->Frame);
3340 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3341 sxx = player->GfxPos;
3343 syy = player->GfxPos;
3346 if (player_is_opaque)
3347 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3349 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3351 if (SHIELD_ON(player))
3353 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3354 IMG_SHIELD_NORMAL_ACTIVE);
3355 int frame = getGraphicAnimationFrame(graphic, -1);
3357 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3361 /* ----------------------------------------------------------------------- */
3362 /* draw things in front of player (active dynamite or dynabombs) */
3363 /* ----------------------------------------------------------------------- */
3365 if (IS_ACTIVE_BOMB(element))
3367 graphic = el2img(element);
3368 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3370 if (game.emulation == EMU_SUPAPLEX)
3371 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3373 DrawGraphicThruMask(sx, sy, graphic, frame);
3376 if (player_is_moving && last_element == EL_EXPLOSION)
3378 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3379 GfxElement[last_jx][last_jy] : EL_EMPTY);
3380 int graphic = el_act2img(element, ACTION_EXPLODING);
3381 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3382 int phase = ExplodePhase[last_jx][last_jy] - 1;
3383 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3386 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3389 /* ----------------------------------------------------------------------- */
3390 /* draw elements the player is just walking/passing through/under */
3391 /* ----------------------------------------------------------------------- */
3393 if (player_is_moving)
3395 /* handle the field the player is leaving ... */
3396 if (IS_ACCESSIBLE_INSIDE(last_element))
3397 DrawLevelField(last_jx, last_jy);
3398 else if (IS_ACCESSIBLE_UNDER(last_element))
3399 DrawLevelFieldThruMask(last_jx, last_jy);
3402 /* do not redraw accessible elements if the player is just pushing them */
3403 if (!player_is_moving || !player->is_pushing)
3405 /* ... and the field the player is entering */
3406 if (IS_ACCESSIBLE_INSIDE(element))
3407 DrawLevelField(jx, jy);
3408 else if (IS_ACCESSIBLE_UNDER(element))
3409 DrawLevelFieldThruMask(jx, jy);
3412 MarkTileDirty(sx, sy);
3415 /* ------------------------------------------------------------------------- */
3417 void WaitForEventToContinue()
3419 boolean still_wait = TRUE;
3421 /* simulate releasing mouse button over last gadget, if still pressed */
3423 HandleGadgets(-1, -1, 0);
3425 button_status = MB_RELEASED;
3439 case EVENT_BUTTONPRESS:
3440 case EVENT_KEYPRESS:
3444 case EVENT_KEYRELEASE:
3445 ClearPlayerAction();
3449 HandleOtherEvents(&event);
3453 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3460 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3464 #define MAX_REQUEST_LINES 13
3465 #define MAX_REQUEST_LINE_FONT1_LEN 7
3466 #define MAX_REQUEST_LINE_FONT2_LEN 10
3468 static int RequestHandleEvents(unsigned int req_state)
3470 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3471 local_player->LevelSolved_GameEnd);
3472 int width = request.width;
3473 int height = request.height;
3477 setRequestPosition(&sx, &sy, FALSE);
3479 button_status = MB_RELEASED;
3481 request_gadget_id = -1;
3488 SetDrawtoField(DRAW_FIELDBUFFER);
3490 HandleGameActions();
3492 SetDrawtoField(DRAW_BACKBUFFER);
3494 if (global.use_envelope_request)
3496 /* copy current state of request area to middle of playfield area */
3497 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3505 while (NextValidEvent(&event))
3509 case EVENT_BUTTONPRESS:
3510 case EVENT_BUTTONRELEASE:
3511 case EVENT_MOTIONNOTIFY:
3515 if (event.type == EVENT_MOTIONNOTIFY)
3520 motion_status = TRUE;
3521 mx = ((MotionEvent *) &event)->x;
3522 my = ((MotionEvent *) &event)->y;
3526 motion_status = FALSE;
3527 mx = ((ButtonEvent *) &event)->x;
3528 my = ((ButtonEvent *) &event)->y;
3529 if (event.type == EVENT_BUTTONPRESS)
3530 button_status = ((ButtonEvent *) &event)->button;
3532 button_status = MB_RELEASED;
3535 /* this sets 'request_gadget_id' */
3536 HandleGadgets(mx, my, button_status);
3538 switch (request_gadget_id)
3540 case TOOL_CTRL_ID_YES:
3543 case TOOL_CTRL_ID_NO:
3546 case TOOL_CTRL_ID_CONFIRM:
3547 result = TRUE | FALSE;
3550 case TOOL_CTRL_ID_PLAYER_1:
3553 case TOOL_CTRL_ID_PLAYER_2:
3556 case TOOL_CTRL_ID_PLAYER_3:
3559 case TOOL_CTRL_ID_PLAYER_4:
3570 case EVENT_KEYPRESS:
3571 switch (GetEventKey((KeyEvent *)&event, TRUE))
3574 if (req_state & REQ_CONFIRM)
3579 #if defined(TARGET_SDL2)
3586 #if defined(TARGET_SDL2)
3596 if (req_state & REQ_PLAYER)
3600 case EVENT_KEYRELEASE:
3601 ClearPlayerAction();
3605 HandleOtherEvents(&event);
3610 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3612 int joy = AnyJoystick();
3614 if (joy & JOY_BUTTON_1)
3616 else if (joy & JOY_BUTTON_2)
3622 if (global.use_envelope_request)
3624 /* copy back current state of pressed buttons inside request area */
3625 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3635 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3641 static boolean RequestDoor(char *text, unsigned int req_state)
3643 unsigned int old_door_state;
3644 int last_game_status = game_status; /* save current game status */
3645 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3646 int font_nr = FONT_TEXT_2;
3651 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3653 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3654 font_nr = FONT_TEXT_1;
3657 if (game_status == GAME_MODE_PLAYING)
3658 BlitScreenToBitmap(backbuffer);
3660 /* disable deactivated drawing when quick-loading level tape recording */
3661 if (tape.playing && tape.deactivate_display)
3662 TapeDeactivateDisplayOff(TRUE);
3664 SetMouseCursor(CURSOR_DEFAULT);
3666 #if defined(NETWORK_AVALIABLE)
3667 /* pause network game while waiting for request to answer */
3668 if (options.network &&
3669 game_status == GAME_MODE_PLAYING &&
3670 req_state & REQUEST_WAIT_FOR_INPUT)
3671 SendToServer_PausePlaying();
3674 old_door_state = GetDoorState();
3676 /* simulate releasing mouse button over last gadget, if still pressed */
3678 HandleGadgets(-1, -1, 0);
3682 /* draw released gadget before proceeding */
3685 if (old_door_state & DOOR_OPEN_1)
3687 CloseDoor(DOOR_CLOSE_1);
3689 /* save old door content */
3690 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3691 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3694 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3695 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3697 /* clear door drawing field */
3698 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3700 /* force DOOR font inside door area */
3701 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
3703 /* write text for request */
3704 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3706 char text_line[max_request_line_len + 1];
3712 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3714 tc = *(text_ptr + tx);
3715 // if (!tc || tc == ' ')
3716 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3720 if ((tc == '?' || tc == '!') && tl == 0)
3730 strncpy(text_line, text_ptr, tl);
3733 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3734 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3735 text_line, font_nr);
3737 text_ptr += tl + (tc == ' ' ? 1 : 0);
3738 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3741 SetGameStatus(last_game_status); /* restore current game status */
3743 if (req_state & REQ_ASK)
3745 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3746 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3748 else if (req_state & REQ_CONFIRM)
3750 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3752 else if (req_state & REQ_PLAYER)
3754 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3755 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3756 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3757 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3760 /* copy request gadgets to door backbuffer */
3761 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3763 OpenDoor(DOOR_OPEN_1);
3765 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3767 if (game_status == GAME_MODE_PLAYING)
3769 SetPanelBackground();
3770 SetDrawBackgroundMask(REDRAW_DOOR_1);
3774 SetDrawBackgroundMask(REDRAW_FIELD);
3780 if (game_status != GAME_MODE_MAIN)
3783 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3785 // ---------- handle request buttons ----------
3786 result = RequestHandleEvents(req_state);
3788 if (game_status != GAME_MODE_MAIN)
3793 if (!(req_state & REQ_STAY_OPEN))
3795 CloseDoor(DOOR_CLOSE_1);
3797 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3798 (req_state & REQ_REOPEN))
3799 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3804 if (game_status == GAME_MODE_PLAYING)
3806 SetPanelBackground();
3807 SetDrawBackgroundMask(REDRAW_DOOR_1);
3811 SetDrawBackgroundMask(REDRAW_FIELD);
3814 #if defined(NETWORK_AVALIABLE)
3815 /* continue network game after request */
3816 if (options.network &&
3817 game_status == GAME_MODE_PLAYING &&
3818 req_state & REQUEST_WAIT_FOR_INPUT)
3819 SendToServer_ContinuePlaying();
3822 /* restore deactivated drawing when quick-loading level tape recording */
3823 if (tape.playing && tape.deactivate_display)
3824 TapeDeactivateDisplayOn();
3829 static boolean RequestEnvelope(char *text, unsigned int req_state)
3833 if (game_status == GAME_MODE_PLAYING)
3834 BlitScreenToBitmap(backbuffer);
3836 /* disable deactivated drawing when quick-loading level tape recording */
3837 if (tape.playing && tape.deactivate_display)
3838 TapeDeactivateDisplayOff(TRUE);
3840 SetMouseCursor(CURSOR_DEFAULT);
3842 #if defined(NETWORK_AVALIABLE)
3843 /* pause network game while waiting for request to answer */
3844 if (options.network &&
3845 game_status == GAME_MODE_PLAYING &&
3846 req_state & REQUEST_WAIT_FOR_INPUT)
3847 SendToServer_PausePlaying();
3850 /* simulate releasing mouse button over last gadget, if still pressed */
3852 HandleGadgets(-1, -1, 0);
3856 // (replace with setting corresponding request background)
3857 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3858 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3860 /* clear door drawing field */
3861 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3863 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3865 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3867 if (game_status == GAME_MODE_PLAYING)
3869 SetPanelBackground();
3870 SetDrawBackgroundMask(REDRAW_DOOR_1);
3874 SetDrawBackgroundMask(REDRAW_FIELD);
3880 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3882 // ---------- handle request buttons ----------
3883 result = RequestHandleEvents(req_state);
3885 if (game_status != GAME_MODE_MAIN)
3890 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3894 if (game_status == GAME_MODE_PLAYING)
3896 SetPanelBackground();
3897 SetDrawBackgroundMask(REDRAW_DOOR_1);
3901 SetDrawBackgroundMask(REDRAW_FIELD);
3904 #if defined(NETWORK_AVALIABLE)
3905 /* continue network game after request */
3906 if (options.network &&
3907 game_status == GAME_MODE_PLAYING &&
3908 req_state & REQUEST_WAIT_FOR_INPUT)
3909 SendToServer_ContinuePlaying();
3912 /* restore deactivated drawing when quick-loading level tape recording */
3913 if (tape.playing && tape.deactivate_display)
3914 TapeDeactivateDisplayOn();
3919 boolean Request(char *text, unsigned int req_state)
3921 if (global.use_envelope_request)
3922 return RequestEnvelope(text, req_state);
3924 return RequestDoor(text, req_state);
3927 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3929 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3930 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3933 if (dpo1->sort_priority != dpo2->sort_priority)
3934 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3936 compare_result = dpo1->nr - dpo2->nr;
3938 return compare_result;
3941 void InitGraphicCompatibilityInfo_Doors()
3947 struct DoorInfo *door;
3951 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3952 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3954 { -1, -1, -1, NULL }
3956 struct Rect door_rect_list[] =
3958 { DX, DY, DXSIZE, DYSIZE },
3959 { VX, VY, VXSIZE, VYSIZE }
3963 for (i = 0; doors[i].door_token != -1; i++)
3965 int door_token = doors[i].door_token;
3966 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3967 int part_1 = doors[i].part_1;
3968 int part_8 = doors[i].part_8;
3969 int part_2 = part_1 + 1;
3970 int part_3 = part_1 + 2;
3971 struct DoorInfo *door = doors[i].door;
3972 struct Rect *door_rect = &door_rect_list[door_index];
3973 boolean door_gfx_redefined = FALSE;
3975 /* check if any door part graphic definitions have been redefined */
3977 for (j = 0; door_part_controls[j].door_token != -1; j++)
3979 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3980 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3982 if (dpc->door_token == door_token && fi->redefined)
3983 door_gfx_redefined = TRUE;
3986 /* check for old-style door graphic/animation modifications */
3988 if (!door_gfx_redefined)
3990 if (door->anim_mode & ANIM_STATIC_PANEL)
3992 door->panel.step_xoffset = 0;
3993 door->panel.step_yoffset = 0;
3996 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3998 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3999 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4000 int num_door_steps, num_panel_steps;
4002 /* remove door part graphics other than the two default wings */
4004 for (j = 0; door_part_controls[j].door_token != -1; j++)
4006 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4007 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4009 if (dpc->graphic >= part_3 &&
4010 dpc->graphic <= part_8)
4014 /* set graphics and screen positions of the default wings */
4016 g_part_1->width = door_rect->width;
4017 g_part_1->height = door_rect->height;
4018 g_part_2->width = door_rect->width;
4019 g_part_2->height = door_rect->height;
4020 g_part_2->src_x = door_rect->width;
4021 g_part_2->src_y = g_part_1->src_y;
4023 door->part_2.x = door->part_1.x;
4024 door->part_2.y = door->part_1.y;
4026 if (door->width != -1)
4028 g_part_1->width = door->width;
4029 g_part_2->width = door->width;
4031 // special treatment for graphics and screen position of right wing
4032 g_part_2->src_x += door_rect->width - door->width;
4033 door->part_2.x += door_rect->width - door->width;
4036 if (door->height != -1)
4038 g_part_1->height = door->height;
4039 g_part_2->height = door->height;
4041 // special treatment for graphics and screen position of bottom wing
4042 g_part_2->src_y += door_rect->height - door->height;
4043 door->part_2.y += door_rect->height - door->height;
4046 /* set animation delays for the default wings and panels */
4048 door->part_1.step_delay = door->step_delay;
4049 door->part_2.step_delay = door->step_delay;
4050 door->panel.step_delay = door->step_delay;
4052 /* set animation draw order for the default wings */
4054 door->part_1.sort_priority = 2; /* draw left wing over ... */
4055 door->part_2.sort_priority = 1; /* ... right wing */
4057 /* set animation draw offset for the default wings */
4059 if (door->anim_mode & ANIM_HORIZONTAL)
4061 door->part_1.step_xoffset = door->step_offset;
4062 door->part_1.step_yoffset = 0;
4063 door->part_2.step_xoffset = door->step_offset * -1;
4064 door->part_2.step_yoffset = 0;
4066 num_door_steps = g_part_1->width / door->step_offset;
4068 else // ANIM_VERTICAL
4070 door->part_1.step_xoffset = 0;
4071 door->part_1.step_yoffset = door->step_offset;
4072 door->part_2.step_xoffset = 0;
4073 door->part_2.step_yoffset = door->step_offset * -1;
4075 num_door_steps = g_part_1->height / door->step_offset;
4078 /* set animation draw offset for the default panels */
4080 if (door->step_offset > 1)
4082 num_panel_steps = 2 * door_rect->height / door->step_offset;
4083 door->panel.start_step = num_panel_steps - num_door_steps;
4084 door->panel.start_step_closing = door->panel.start_step;
4088 num_panel_steps = door_rect->height / door->step_offset;
4089 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4090 door->panel.start_step_closing = door->panel.start_step;
4091 door->panel.step_delay *= 2;
4102 for (i = 0; door_part_controls[i].door_token != -1; i++)
4104 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4105 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4107 /* initialize "start_step_opening" and "start_step_closing", if needed */
4108 if (dpc->pos->start_step_opening == 0 &&
4109 dpc->pos->start_step_closing == 0)
4111 // dpc->pos->start_step_opening = dpc->pos->start_step;
4112 dpc->pos->start_step_closing = dpc->pos->start_step;
4115 /* fill structure for door part draw order (sorted below) */
4117 dpo->sort_priority = dpc->pos->sort_priority;
4120 /* sort door part controls according to sort_priority and graphic number */
4121 qsort(door_part_order, MAX_DOOR_PARTS,
4122 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4125 unsigned int OpenDoor(unsigned int door_state)
4127 if (door_state & DOOR_COPY_BACK)
4129 if (door_state & DOOR_OPEN_1)
4130 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4131 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4133 if (door_state & DOOR_OPEN_2)
4134 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4135 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4137 door_state &= ~DOOR_COPY_BACK;
4140 return MoveDoor(door_state);
4143 unsigned int CloseDoor(unsigned int door_state)
4145 unsigned int old_door_state = GetDoorState();
4147 if (!(door_state & DOOR_NO_COPY_BACK))
4149 if (old_door_state & DOOR_OPEN_1)
4150 BlitBitmap(backbuffer, bitmap_db_door_1,
4151 DX, DY, DXSIZE, DYSIZE, 0, 0);
4153 if (old_door_state & DOOR_OPEN_2)
4154 BlitBitmap(backbuffer, bitmap_db_door_2,
4155 VX, VY, VXSIZE, VYSIZE, 0, 0);
4157 door_state &= ~DOOR_NO_COPY_BACK;
4160 return MoveDoor(door_state);
4163 unsigned int GetDoorState()
4165 return MoveDoor(DOOR_GET_STATE);
4168 unsigned int SetDoorState(unsigned int door_state)
4170 return MoveDoor(door_state | DOOR_SET_STATE);
4173 int euclid(int a, int b)
4175 return (b ? euclid(b, a % b) : a);
4178 unsigned int MoveDoor(unsigned int door_state)
4180 struct Rect door_rect_list[] =
4182 { DX, DY, DXSIZE, DYSIZE },
4183 { VX, VY, VXSIZE, VYSIZE }
4185 static int door1 = DOOR_OPEN_1;
4186 static int door2 = DOOR_CLOSE_2;
4187 unsigned int door_delay = 0;
4188 unsigned int door_delay_value;
4191 if (door_state == DOOR_GET_STATE)
4192 return (door1 | door2);
4194 if (door_state & DOOR_SET_STATE)
4196 if (door_state & DOOR_ACTION_1)
4197 door1 = door_state & DOOR_ACTION_1;
4198 if (door_state & DOOR_ACTION_2)
4199 door2 = door_state & DOOR_ACTION_2;
4201 return (door1 | door2);
4204 if (!(door_state & DOOR_FORCE_REDRAW))
4206 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4207 door_state &= ~DOOR_OPEN_1;
4208 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4209 door_state &= ~DOOR_CLOSE_1;
4210 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4211 door_state &= ~DOOR_OPEN_2;
4212 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4213 door_state &= ~DOOR_CLOSE_2;
4216 if (global.autoplay_leveldir)
4218 door_state |= DOOR_NO_DELAY;
4219 door_state &= ~DOOR_CLOSE_ALL;
4222 if (game_status == GAME_MODE_EDITOR)
4223 door_state |= DOOR_NO_DELAY;
4225 if (door_state & DOOR_ACTION)
4227 boolean door_panel_drawn[NUM_DOORS];
4228 boolean panel_has_doors[NUM_DOORS];
4229 boolean door_part_skip[MAX_DOOR_PARTS];
4230 boolean door_part_done[MAX_DOOR_PARTS];
4231 boolean door_part_done_all;
4232 int num_steps[MAX_DOOR_PARTS];
4233 int max_move_delay = 0; // delay for complete animations of all doors
4234 int max_step_delay = 0; // delay (ms) between two animation frames
4235 int num_move_steps = 0; // number of animation steps for all doors
4236 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4237 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4238 int current_move_delay = 0;
4242 for (i = 0; i < NUM_DOORS; i++)
4243 panel_has_doors[i] = FALSE;
4245 for (i = 0; i < MAX_DOOR_PARTS; i++)
4247 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4248 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4249 int door_token = dpc->door_token;
4251 door_part_done[i] = FALSE;
4252 door_part_skip[i] = (!(door_state & door_token) ||
4256 for (i = 0; i < MAX_DOOR_PARTS; i++)
4258 int nr = door_part_order[i].nr;
4259 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4260 struct DoorPartPosInfo *pos = dpc->pos;
4261 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4262 int door_token = dpc->door_token;
4263 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4264 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4265 int step_xoffset = ABS(pos->step_xoffset);
4266 int step_yoffset = ABS(pos->step_yoffset);
4267 int step_delay = pos->step_delay;
4268 int current_door_state = door_state & door_token;
4269 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4270 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4271 boolean part_opening = (is_panel ? door_closing : door_opening);
4272 int start_step = (part_opening ? pos->start_step_opening :
4273 pos->start_step_closing);
4274 float move_xsize = (step_xoffset ? g->width : 0);
4275 float move_ysize = (step_yoffset ? g->height : 0);
4276 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4277 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4278 int move_steps = (move_xsteps && move_ysteps ?
4279 MIN(move_xsteps, move_ysteps) :
4280 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4281 int move_delay = move_steps * step_delay;
4283 if (door_part_skip[nr])
4286 max_move_delay = MAX(max_move_delay, move_delay);
4287 max_step_delay = (max_step_delay == 0 ? step_delay :
4288 euclid(max_step_delay, step_delay));
4289 num_steps[nr] = move_steps;
4293 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4295 panel_has_doors[door_index] = TRUE;
4299 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4301 num_move_steps = max_move_delay / max_step_delay;
4302 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4304 door_delay_value = max_step_delay;
4306 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4308 start = num_move_steps - 1;
4312 /* opening door sound has priority over simultaneously closing door */
4313 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4314 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4315 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4316 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4319 for (k = start; k < num_move_steps; k++)
4321 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4323 door_part_done_all = TRUE;
4325 for (i = 0; i < NUM_DOORS; i++)
4326 door_panel_drawn[i] = FALSE;
4328 for (i = 0; i < MAX_DOOR_PARTS; i++)
4330 int nr = door_part_order[i].nr;
4331 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4332 struct DoorPartPosInfo *pos = dpc->pos;
4333 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4334 int door_token = dpc->door_token;
4335 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4336 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4337 boolean is_panel_and_door_has_closed = FALSE;
4338 struct Rect *door_rect = &door_rect_list[door_index];
4339 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4341 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4342 int current_door_state = door_state & door_token;
4343 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4344 boolean door_closing = !door_opening;
4345 boolean part_opening = (is_panel ? door_closing : door_opening);
4346 boolean part_closing = !part_opening;
4347 int start_step = (part_opening ? pos->start_step_opening :
4348 pos->start_step_closing);
4349 int step_delay = pos->step_delay;
4350 int step_factor = step_delay / max_step_delay;
4351 int k1 = (step_factor ? k / step_factor + 1 : k);
4352 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4353 int kk = MAX(0, k2);
4356 int src_x, src_y, src_xx, src_yy;
4357 int dst_x, dst_y, dst_xx, dst_yy;
4360 if (door_part_skip[nr])
4363 if (!(door_state & door_token))
4371 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4372 int kk_door = MAX(0, k2_door);
4373 int sync_frame = kk_door * door_delay_value;
4374 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4376 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4381 if (!door_panel_drawn[door_index])
4383 ClearRectangle(drawto, door_rect->x, door_rect->y,
4384 door_rect->width, door_rect->height);
4386 door_panel_drawn[door_index] = TRUE;
4389 // draw opening or closing door parts
4391 if (pos->step_xoffset < 0) // door part on right side
4394 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4397 if (dst_xx + width > door_rect->width)
4398 width = door_rect->width - dst_xx;
4400 else // door part on left side
4403 dst_xx = pos->x - kk * pos->step_xoffset;
4407 src_xx = ABS(dst_xx);
4411 width = g->width - src_xx;
4413 if (width > door_rect->width)
4414 width = door_rect->width;
4416 // printf("::: k == %d [%d] \n", k, start_step);
4419 if (pos->step_yoffset < 0) // door part on bottom side
4422 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4425 if (dst_yy + height > door_rect->height)
4426 height = door_rect->height - dst_yy;
4428 else // door part on top side
4431 dst_yy = pos->y - kk * pos->step_yoffset;
4435 src_yy = ABS(dst_yy);
4439 height = g->height - src_yy;
4442 src_x = g_src_x + src_xx;
4443 src_y = g_src_y + src_yy;
4445 dst_x = door_rect->x + dst_xx;
4446 dst_y = door_rect->y + dst_yy;
4448 is_panel_and_door_has_closed =
4451 panel_has_doors[door_index] &&
4452 k >= num_move_steps_doors_only - 1);
4454 if (width >= 0 && width <= g->width &&
4455 height >= 0 && height <= g->height &&
4456 !is_panel_and_door_has_closed)
4458 if (is_panel || !pos->draw_masked)
4459 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4462 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4466 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4468 if ((part_opening && (width < 0 || height < 0)) ||
4469 (part_closing && (width >= g->width && height >= g->height)))
4470 door_part_done[nr] = TRUE;
4472 // continue door part animations, but not panel after door has closed
4473 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4474 door_part_done_all = FALSE;
4477 if (!(door_state & DOOR_NO_DELAY))
4481 if (game_status == GAME_MODE_MAIN)
4484 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4486 current_move_delay += max_step_delay;
4489 if (door_part_done_all)
4494 if (door_state & DOOR_ACTION_1)
4495 door1 = door_state & DOOR_ACTION_1;
4496 if (door_state & DOOR_ACTION_2)
4497 door2 = door_state & DOOR_ACTION_2;
4499 // draw masked border over door area
4500 DrawMaskedBorder(REDRAW_DOOR_1);
4501 DrawMaskedBorder(REDRAW_DOOR_2);
4503 return (door1 | door2);
4506 static boolean useSpecialEditorDoor()
4508 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4509 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4511 // do not draw special editor door if editor border defined or redefined
4512 if (graphic_info[graphic].bitmap != NULL || redefined)
4515 // do not draw special editor door if global border defined to be empty
4516 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4519 // do not draw special editor door if viewport definitions do not match
4523 EY + EYSIZE != VY + VYSIZE)
4529 void DrawSpecialEditorDoor()
4531 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4532 int top_border_width = gfx1->width;
4533 int top_border_height = gfx1->height;
4534 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4535 int ex = EX - outer_border;
4536 int ey = EY - outer_border;
4537 int vy = VY - outer_border;
4538 int exsize = EXSIZE + 2 * outer_border;
4540 if (!useSpecialEditorDoor())
4543 /* draw bigger level editor toolbox window */
4544 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4545 top_border_width, top_border_height, ex, ey - top_border_height);
4546 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4547 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4549 redraw_mask |= REDRAW_ALL;
4552 void UndrawSpecialEditorDoor()
4554 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4555 int top_border_width = gfx1->width;
4556 int top_border_height = gfx1->height;
4557 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4558 int ex = EX - outer_border;
4559 int ey = EY - outer_border;
4560 int ey_top = ey - top_border_height;
4561 int exsize = EXSIZE + 2 * outer_border;
4562 int eysize = EYSIZE + 2 * outer_border;
4564 if (!useSpecialEditorDoor())
4567 /* draw normal tape recorder window */
4568 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4570 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4571 ex, ey_top, top_border_width, top_border_height,
4573 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4574 ex, ey, exsize, eysize, ex, ey);
4578 // if screen background is set to "[NONE]", clear editor toolbox window
4579 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4580 ClearRectangle(drawto, ex, ey, exsize, eysize);
4583 redraw_mask |= REDRAW_ALL;
4587 /* ---------- new tool button stuff ---------------------------------------- */
4592 struct TextPosInfo *pos;
4595 } toolbutton_info[NUM_TOOL_BUTTONS] =
4598 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4599 TOOL_CTRL_ID_YES, "yes"
4602 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4603 TOOL_CTRL_ID_NO, "no"
4606 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4607 TOOL_CTRL_ID_CONFIRM, "confirm"
4610 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4611 TOOL_CTRL_ID_PLAYER_1, "player 1"
4614 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4615 TOOL_CTRL_ID_PLAYER_2, "player 2"
4618 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4619 TOOL_CTRL_ID_PLAYER_3, "player 3"
4622 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4623 TOOL_CTRL_ID_PLAYER_4, "player 4"
4627 void CreateToolButtons()
4631 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4633 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4634 struct TextPosInfo *pos = toolbutton_info[i].pos;
4635 struct GadgetInfo *gi;
4636 Bitmap *deco_bitmap = None;
4637 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4638 unsigned int event_mask = GD_EVENT_RELEASED;
4641 int gd_x = gfx->src_x;
4642 int gd_y = gfx->src_y;
4643 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4644 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4647 if (global.use_envelope_request)
4648 setRequestPosition(&dx, &dy, TRUE);
4650 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4652 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4654 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4655 pos->size, &deco_bitmap, &deco_x, &deco_y);
4656 deco_xpos = (gfx->width - pos->size) / 2;
4657 deco_ypos = (gfx->height - pos->size) / 2;
4660 gi = CreateGadget(GDI_CUSTOM_ID, id,
4661 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4662 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4663 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4664 GDI_WIDTH, gfx->width,
4665 GDI_HEIGHT, gfx->height,
4666 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4667 GDI_STATE, GD_BUTTON_UNPRESSED,
4668 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4669 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4670 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4671 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4672 GDI_DECORATION_SIZE, pos->size, pos->size,
4673 GDI_DECORATION_SHIFTING, 1, 1,
4674 GDI_DIRECT_DRAW, FALSE,
4675 GDI_EVENT_MASK, event_mask,
4676 GDI_CALLBACK_ACTION, HandleToolButtons,
4680 Error(ERR_EXIT, "cannot create gadget");
4682 tool_gadget[id] = gi;
4686 void FreeToolButtons()
4690 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4691 FreeGadget(tool_gadget[i]);
4694 static void UnmapToolButtons()
4698 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4699 UnmapGadget(tool_gadget[i]);
4702 static void HandleToolButtons(struct GadgetInfo *gi)
4704 request_gadget_id = gi->custom_id;
4707 static struct Mapping_EM_to_RND_object
4710 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4711 boolean is_backside; /* backside of moving element */
4717 em_object_mapping_list[] =
4720 Xblank, TRUE, FALSE,
4724 Yacid_splash_eB, FALSE, FALSE,
4725 EL_ACID_SPLASH_RIGHT, -1, -1
4728 Yacid_splash_wB, FALSE, FALSE,
4729 EL_ACID_SPLASH_LEFT, -1, -1
4732 #ifdef EM_ENGINE_BAD_ROLL
4734 Xstone_force_e, FALSE, FALSE,
4735 EL_ROCK, -1, MV_BIT_RIGHT
4738 Xstone_force_w, FALSE, FALSE,
4739 EL_ROCK, -1, MV_BIT_LEFT
4742 Xnut_force_e, FALSE, FALSE,
4743 EL_NUT, -1, MV_BIT_RIGHT
4746 Xnut_force_w, FALSE, FALSE,
4747 EL_NUT, -1, MV_BIT_LEFT
4750 Xspring_force_e, FALSE, FALSE,
4751 EL_SPRING, -1, MV_BIT_RIGHT
4754 Xspring_force_w, FALSE, FALSE,
4755 EL_SPRING, -1, MV_BIT_LEFT
4758 Xemerald_force_e, FALSE, FALSE,
4759 EL_EMERALD, -1, MV_BIT_RIGHT
4762 Xemerald_force_w, FALSE, FALSE,
4763 EL_EMERALD, -1, MV_BIT_LEFT
4766 Xdiamond_force_e, FALSE, FALSE,
4767 EL_DIAMOND, -1, MV_BIT_RIGHT
4770 Xdiamond_force_w, FALSE, FALSE,
4771 EL_DIAMOND, -1, MV_BIT_LEFT
4774 Xbomb_force_e, FALSE, FALSE,
4775 EL_BOMB, -1, MV_BIT_RIGHT
4778 Xbomb_force_w, FALSE, FALSE,
4779 EL_BOMB, -1, MV_BIT_LEFT
4781 #endif /* EM_ENGINE_BAD_ROLL */
4784 Xstone, TRUE, FALSE,
4788 Xstone_pause, FALSE, FALSE,
4792 Xstone_fall, FALSE, FALSE,
4796 Ystone_s, FALSE, FALSE,
4797 EL_ROCK, ACTION_FALLING, -1
4800 Ystone_sB, FALSE, TRUE,
4801 EL_ROCK, ACTION_FALLING, -1
4804 Ystone_e, FALSE, FALSE,
4805 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4808 Ystone_eB, FALSE, TRUE,
4809 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4812 Ystone_w, FALSE, FALSE,
4813 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4816 Ystone_wB, FALSE, TRUE,
4817 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4824 Xnut_pause, FALSE, FALSE,
4828 Xnut_fall, FALSE, FALSE,
4832 Ynut_s, FALSE, FALSE,
4833 EL_NUT, ACTION_FALLING, -1
4836 Ynut_sB, FALSE, TRUE,
4837 EL_NUT, ACTION_FALLING, -1
4840 Ynut_e, FALSE, FALSE,
4841 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4844 Ynut_eB, FALSE, TRUE,
4845 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4848 Ynut_w, FALSE, FALSE,
4849 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4852 Ynut_wB, FALSE, TRUE,
4853 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4856 Xbug_n, TRUE, FALSE,
4860 Xbug_e, TRUE, FALSE,
4861 EL_BUG_RIGHT, -1, -1
4864 Xbug_s, TRUE, FALSE,
4868 Xbug_w, TRUE, FALSE,
4872 Xbug_gon, FALSE, FALSE,
4876 Xbug_goe, FALSE, FALSE,
4877 EL_BUG_RIGHT, -1, -1
4880 Xbug_gos, FALSE, FALSE,
4884 Xbug_gow, FALSE, FALSE,
4888 Ybug_n, FALSE, FALSE,
4889 EL_BUG, ACTION_MOVING, MV_BIT_UP
4892 Ybug_nB, FALSE, TRUE,
4893 EL_BUG, ACTION_MOVING, MV_BIT_UP
4896 Ybug_e, FALSE, FALSE,
4897 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4900 Ybug_eB, FALSE, TRUE,
4901 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4904 Ybug_s, FALSE, FALSE,
4905 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4908 Ybug_sB, FALSE, TRUE,
4909 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4912 Ybug_w, FALSE, FALSE,
4913 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4916 Ybug_wB, FALSE, TRUE,
4917 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4920 Ybug_w_n, FALSE, FALSE,
4921 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4924 Ybug_n_e, FALSE, FALSE,
4925 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4928 Ybug_e_s, FALSE, FALSE,
4929 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4932 Ybug_s_w, FALSE, FALSE,
4933 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4936 Ybug_e_n, FALSE, FALSE,
4937 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4940 Ybug_s_e, FALSE, FALSE,
4941 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4944 Ybug_w_s, FALSE, FALSE,
4945 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4948 Ybug_n_w, FALSE, FALSE,
4949 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4952 Ybug_stone, FALSE, FALSE,
4953 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4956 Ybug_spring, FALSE, FALSE,
4957 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4960 Xtank_n, TRUE, FALSE,
4961 EL_SPACESHIP_UP, -1, -1
4964 Xtank_e, TRUE, FALSE,
4965 EL_SPACESHIP_RIGHT, -1, -1
4968 Xtank_s, TRUE, FALSE,
4969 EL_SPACESHIP_DOWN, -1, -1
4972 Xtank_w, TRUE, FALSE,
4973 EL_SPACESHIP_LEFT, -1, -1
4976 Xtank_gon, FALSE, FALSE,
4977 EL_SPACESHIP_UP, -1, -1
4980 Xtank_goe, FALSE, FALSE,
4981 EL_SPACESHIP_RIGHT, -1, -1
4984 Xtank_gos, FALSE, FALSE,
4985 EL_SPACESHIP_DOWN, -1, -1
4988 Xtank_gow, FALSE, FALSE,
4989 EL_SPACESHIP_LEFT, -1, -1
4992 Ytank_n, FALSE, FALSE,
4993 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4996 Ytank_nB, FALSE, TRUE,
4997 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5000 Ytank_e, FALSE, FALSE,
5001 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5004 Ytank_eB, FALSE, TRUE,
5005 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5008 Ytank_s, FALSE, FALSE,
5009 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5012 Ytank_sB, FALSE, TRUE,
5013 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5016 Ytank_w, FALSE, FALSE,
5017 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5020 Ytank_wB, FALSE, TRUE,
5021 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5024 Ytank_w_n, FALSE, FALSE,
5025 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5028 Ytank_n_e, FALSE, FALSE,
5029 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5032 Ytank_e_s, FALSE, FALSE,
5033 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5036 Ytank_s_w, FALSE, FALSE,
5037 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5040 Ytank_e_n, FALSE, FALSE,
5041 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5044 Ytank_s_e, FALSE, FALSE,
5045 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5048 Ytank_w_s, FALSE, FALSE,
5049 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5052 Ytank_n_w, FALSE, FALSE,
5053 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5056 Ytank_stone, FALSE, FALSE,
5057 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5060 Ytank_spring, FALSE, FALSE,
5061 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5064 Xandroid, TRUE, FALSE,
5065 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5068 Xandroid_1_n, FALSE, FALSE,
5069 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5072 Xandroid_2_n, FALSE, FALSE,
5073 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5076 Xandroid_1_e, FALSE, FALSE,
5077 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5080 Xandroid_2_e, FALSE, FALSE,
5081 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5084 Xandroid_1_w, FALSE, FALSE,
5085 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5088 Xandroid_2_w, FALSE, FALSE,
5089 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5092 Xandroid_1_s, FALSE, FALSE,
5093 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5096 Xandroid_2_s, FALSE, FALSE,
5097 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5100 Yandroid_n, FALSE, FALSE,
5101 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5104 Yandroid_nB, FALSE, TRUE,
5105 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5108 Yandroid_ne, FALSE, FALSE,
5109 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5112 Yandroid_neB, FALSE, TRUE,
5113 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5116 Yandroid_e, FALSE, FALSE,
5117 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5120 Yandroid_eB, FALSE, TRUE,
5121 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5124 Yandroid_se, FALSE, FALSE,
5125 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5128 Yandroid_seB, FALSE, TRUE,
5129 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5132 Yandroid_s, FALSE, FALSE,
5133 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5136 Yandroid_sB, FALSE, TRUE,
5137 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5140 Yandroid_sw, FALSE, FALSE,
5141 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5144 Yandroid_swB, FALSE, TRUE,
5145 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5148 Yandroid_w, FALSE, FALSE,
5149 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5152 Yandroid_wB, FALSE, TRUE,
5153 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5156 Yandroid_nw, FALSE, FALSE,
5157 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5160 Yandroid_nwB, FALSE, TRUE,
5161 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5164 Xspring, TRUE, FALSE,
5168 Xspring_pause, FALSE, FALSE,
5172 Xspring_e, FALSE, FALSE,
5176 Xspring_w, FALSE, FALSE,
5180 Xspring_fall, FALSE, FALSE,
5184 Yspring_s, FALSE, FALSE,
5185 EL_SPRING, ACTION_FALLING, -1
5188 Yspring_sB, FALSE, TRUE,
5189 EL_SPRING, ACTION_FALLING, -1
5192 Yspring_e, FALSE, FALSE,
5193 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5196 Yspring_eB, FALSE, TRUE,
5197 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5200 Yspring_w, FALSE, FALSE,
5201 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5204 Yspring_wB, FALSE, TRUE,
5205 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5208 Yspring_kill_e, FALSE, FALSE,
5209 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5212 Yspring_kill_eB, FALSE, TRUE,
5213 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5216 Yspring_kill_w, FALSE, FALSE,
5217 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5220 Yspring_kill_wB, FALSE, TRUE,
5221 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5224 Xeater_n, TRUE, FALSE,
5225 EL_YAMYAM_UP, -1, -1
5228 Xeater_e, TRUE, FALSE,
5229 EL_YAMYAM_RIGHT, -1, -1
5232 Xeater_w, TRUE, FALSE,
5233 EL_YAMYAM_LEFT, -1, -1
5236 Xeater_s, TRUE, FALSE,
5237 EL_YAMYAM_DOWN, -1, -1
5240 Yeater_n, FALSE, FALSE,
5241 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5244 Yeater_nB, FALSE, TRUE,
5245 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5248 Yeater_e, FALSE, FALSE,
5249 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5252 Yeater_eB, FALSE, TRUE,
5253 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5256 Yeater_s, FALSE, FALSE,
5257 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5260 Yeater_sB, FALSE, TRUE,
5261 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5264 Yeater_w, FALSE, FALSE,
5265 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5268 Yeater_wB, FALSE, TRUE,
5269 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5272 Yeater_stone, FALSE, FALSE,
5273 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5276 Yeater_spring, FALSE, FALSE,
5277 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5280 Xalien, TRUE, FALSE,
5284 Xalien_pause, FALSE, FALSE,
5288 Yalien_n, FALSE, FALSE,
5289 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5292 Yalien_nB, FALSE, TRUE,
5293 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5296 Yalien_e, FALSE, FALSE,
5297 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5300 Yalien_eB, FALSE, TRUE,
5301 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5304 Yalien_s, FALSE, FALSE,
5305 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5308 Yalien_sB, FALSE, TRUE,
5309 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5312 Yalien_w, FALSE, FALSE,
5313 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5316 Yalien_wB, FALSE, TRUE,
5317 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5320 Yalien_stone, FALSE, FALSE,
5321 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5324 Yalien_spring, FALSE, FALSE,
5325 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5328 Xemerald, TRUE, FALSE,
5332 Xemerald_pause, FALSE, FALSE,
5336 Xemerald_fall, FALSE, FALSE,
5340 Xemerald_shine, FALSE, FALSE,
5341 EL_EMERALD, ACTION_TWINKLING, -1
5344 Yemerald_s, FALSE, FALSE,
5345 EL_EMERALD, ACTION_FALLING, -1
5348 Yemerald_sB, FALSE, TRUE,
5349 EL_EMERALD, ACTION_FALLING, -1
5352 Yemerald_e, FALSE, FALSE,
5353 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5356 Yemerald_eB, FALSE, TRUE,
5357 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5360 Yemerald_w, FALSE, FALSE,
5361 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5364 Yemerald_wB, FALSE, TRUE,
5365 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5368 Yemerald_eat, FALSE, FALSE,
5369 EL_EMERALD, ACTION_COLLECTING, -1
5372 Yemerald_stone, FALSE, FALSE,
5373 EL_NUT, ACTION_BREAKING, -1
5376 Xdiamond, TRUE, FALSE,
5380 Xdiamond_pause, FALSE, FALSE,
5384 Xdiamond_fall, FALSE, FALSE,
5388 Xdiamond_shine, FALSE, FALSE,
5389 EL_DIAMOND, ACTION_TWINKLING, -1
5392 Ydiamond_s, FALSE, FALSE,
5393 EL_DIAMOND, ACTION_FALLING, -1
5396 Ydiamond_sB, FALSE, TRUE,
5397 EL_DIAMOND, ACTION_FALLING, -1
5400 Ydiamond_e, FALSE, FALSE,
5401 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5404 Ydiamond_eB, FALSE, TRUE,
5405 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5408 Ydiamond_w, FALSE, FALSE,
5409 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5412 Ydiamond_wB, FALSE, TRUE,
5413 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5416 Ydiamond_eat, FALSE, FALSE,
5417 EL_DIAMOND, ACTION_COLLECTING, -1
5420 Ydiamond_stone, FALSE, FALSE,
5421 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5424 Xdrip_fall, TRUE, FALSE,
5425 EL_AMOEBA_DROP, -1, -1
5428 Xdrip_stretch, FALSE, FALSE,
5429 EL_AMOEBA_DROP, ACTION_FALLING, -1
5432 Xdrip_stretchB, FALSE, TRUE,
5433 EL_AMOEBA_DROP, ACTION_FALLING, -1
5436 Xdrip_eat, FALSE, FALSE,
5437 EL_AMOEBA_DROP, ACTION_GROWING, -1
5440 Ydrip_s1, FALSE, FALSE,
5441 EL_AMOEBA_DROP, ACTION_FALLING, -1
5444 Ydrip_s1B, FALSE, TRUE,
5445 EL_AMOEBA_DROP, ACTION_FALLING, -1
5448 Ydrip_s2, FALSE, FALSE,
5449 EL_AMOEBA_DROP, ACTION_FALLING, -1
5452 Ydrip_s2B, FALSE, TRUE,
5453 EL_AMOEBA_DROP, ACTION_FALLING, -1
5460 Xbomb_pause, FALSE, FALSE,
5464 Xbomb_fall, FALSE, FALSE,
5468 Ybomb_s, FALSE, FALSE,
5469 EL_BOMB, ACTION_FALLING, -1
5472 Ybomb_sB, FALSE, TRUE,
5473 EL_BOMB, ACTION_FALLING, -1
5476 Ybomb_e, FALSE, FALSE,
5477 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5480 Ybomb_eB, FALSE, TRUE,
5481 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5484 Ybomb_w, FALSE, FALSE,
5485 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5488 Ybomb_wB, FALSE, TRUE,
5489 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5492 Ybomb_eat, FALSE, FALSE,
5493 EL_BOMB, ACTION_ACTIVATING, -1
5496 Xballoon, TRUE, FALSE,
5500 Yballoon_n, FALSE, FALSE,
5501 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5504 Yballoon_nB, FALSE, TRUE,
5505 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5508 Yballoon_e, FALSE, FALSE,
5509 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5512 Yballoon_eB, FALSE, TRUE,
5513 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5516 Yballoon_s, FALSE, FALSE,
5517 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5520 Yballoon_sB, FALSE, TRUE,
5521 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5524 Yballoon_w, FALSE, FALSE,
5525 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5528 Yballoon_wB, FALSE, TRUE,
5529 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5532 Xgrass, TRUE, FALSE,
5533 EL_EMC_GRASS, -1, -1
5536 Ygrass_nB, FALSE, FALSE,
5537 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5540 Ygrass_eB, FALSE, FALSE,
5541 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5544 Ygrass_sB, FALSE, FALSE,
5545 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5548 Ygrass_wB, FALSE, FALSE,
5549 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5556 Ydirt_nB, FALSE, FALSE,
5557 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5560 Ydirt_eB, FALSE, FALSE,
5561 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5564 Ydirt_sB, FALSE, FALSE,
5565 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5568 Ydirt_wB, FALSE, FALSE,
5569 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5572 Xacid_ne, TRUE, FALSE,
5573 EL_ACID_POOL_TOPRIGHT, -1, -1
5576 Xacid_se, TRUE, FALSE,
5577 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5580 Xacid_s, TRUE, FALSE,
5581 EL_ACID_POOL_BOTTOM, -1, -1
5584 Xacid_sw, TRUE, FALSE,
5585 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5588 Xacid_nw, TRUE, FALSE,
5589 EL_ACID_POOL_TOPLEFT, -1, -1
5592 Xacid_1, TRUE, FALSE,
5596 Xacid_2, FALSE, FALSE,
5600 Xacid_3, FALSE, FALSE,
5604 Xacid_4, FALSE, FALSE,
5608 Xacid_5, FALSE, FALSE,
5612 Xacid_6, FALSE, FALSE,
5616 Xacid_7, FALSE, FALSE,
5620 Xacid_8, FALSE, FALSE,
5624 Xball_1, TRUE, FALSE,
5625 EL_EMC_MAGIC_BALL, -1, -1
5628 Xball_1B, FALSE, FALSE,
5629 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5632 Xball_2, FALSE, FALSE,
5633 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5636 Xball_2B, FALSE, FALSE,
5637 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5640 Yball_eat, FALSE, FALSE,
5641 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5644 Ykey_1_eat, FALSE, FALSE,
5645 EL_EM_KEY_1, ACTION_COLLECTING, -1
5648 Ykey_2_eat, FALSE, FALSE,
5649 EL_EM_KEY_2, ACTION_COLLECTING, -1
5652 Ykey_3_eat, FALSE, FALSE,
5653 EL_EM_KEY_3, ACTION_COLLECTING, -1
5656 Ykey_4_eat, FALSE, FALSE,
5657 EL_EM_KEY_4, ACTION_COLLECTING, -1
5660 Ykey_5_eat, FALSE, FALSE,
5661 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5664 Ykey_6_eat, FALSE, FALSE,
5665 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5668 Ykey_7_eat, FALSE, FALSE,
5669 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5672 Ykey_8_eat, FALSE, FALSE,
5673 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5676 Ylenses_eat, FALSE, FALSE,
5677 EL_EMC_LENSES, ACTION_COLLECTING, -1
5680 Ymagnify_eat, FALSE, FALSE,
5681 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5684 Ygrass_eat, FALSE, FALSE,
5685 EL_EMC_GRASS, ACTION_SNAPPING, -1
5688 Ydirt_eat, FALSE, FALSE,
5689 EL_SAND, ACTION_SNAPPING, -1
5692 Xgrow_ns, TRUE, FALSE,
5693 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5696 Ygrow_ns_eat, FALSE, FALSE,
5697 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5700 Xgrow_ew, TRUE, FALSE,
5701 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5704 Ygrow_ew_eat, FALSE, FALSE,
5705 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5708 Xwonderwall, TRUE, FALSE,
5709 EL_MAGIC_WALL, -1, -1
5712 XwonderwallB, FALSE, FALSE,
5713 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5716 Xamoeba_1, TRUE, FALSE,
5717 EL_AMOEBA_DRY, ACTION_OTHER, -1
5720 Xamoeba_2, FALSE, FALSE,
5721 EL_AMOEBA_DRY, ACTION_OTHER, -1
5724 Xamoeba_3, FALSE, FALSE,
5725 EL_AMOEBA_DRY, ACTION_OTHER, -1
5728 Xamoeba_4, FALSE, FALSE,
5729 EL_AMOEBA_DRY, ACTION_OTHER, -1
5732 Xamoeba_5, TRUE, FALSE,
5733 EL_AMOEBA_WET, ACTION_OTHER, -1
5736 Xamoeba_6, FALSE, FALSE,
5737 EL_AMOEBA_WET, ACTION_OTHER, -1
5740 Xamoeba_7, FALSE, FALSE,
5741 EL_AMOEBA_WET, ACTION_OTHER, -1
5744 Xamoeba_8, FALSE, FALSE,
5745 EL_AMOEBA_WET, ACTION_OTHER, -1
5748 Xdoor_1, TRUE, FALSE,
5749 EL_EM_GATE_1, -1, -1
5752 Xdoor_2, TRUE, FALSE,
5753 EL_EM_GATE_2, -1, -1
5756 Xdoor_3, TRUE, FALSE,
5757 EL_EM_GATE_3, -1, -1
5760 Xdoor_4, TRUE, FALSE,
5761 EL_EM_GATE_4, -1, -1
5764 Xdoor_5, TRUE, FALSE,
5765 EL_EMC_GATE_5, -1, -1
5768 Xdoor_6, TRUE, FALSE,
5769 EL_EMC_GATE_6, -1, -1
5772 Xdoor_7, TRUE, FALSE,
5773 EL_EMC_GATE_7, -1, -1
5776 Xdoor_8, TRUE, FALSE,
5777 EL_EMC_GATE_8, -1, -1
5780 Xkey_1, TRUE, FALSE,
5784 Xkey_2, TRUE, FALSE,
5788 Xkey_3, TRUE, FALSE,
5792 Xkey_4, TRUE, FALSE,
5796 Xkey_5, TRUE, FALSE,
5797 EL_EMC_KEY_5, -1, -1
5800 Xkey_6, TRUE, FALSE,
5801 EL_EMC_KEY_6, -1, -1
5804 Xkey_7, TRUE, FALSE,
5805 EL_EMC_KEY_7, -1, -1
5808 Xkey_8, TRUE, FALSE,
5809 EL_EMC_KEY_8, -1, -1
5812 Xwind_n, TRUE, FALSE,
5813 EL_BALLOON_SWITCH_UP, -1, -1
5816 Xwind_e, TRUE, FALSE,
5817 EL_BALLOON_SWITCH_RIGHT, -1, -1
5820 Xwind_s, TRUE, FALSE,
5821 EL_BALLOON_SWITCH_DOWN, -1, -1
5824 Xwind_w, TRUE, FALSE,
5825 EL_BALLOON_SWITCH_LEFT, -1, -1
5828 Xwind_nesw, TRUE, FALSE,
5829 EL_BALLOON_SWITCH_ANY, -1, -1
5832 Xwind_stop, TRUE, FALSE,
5833 EL_BALLOON_SWITCH_NONE, -1, -1
5837 EL_EM_EXIT_CLOSED, -1, -1
5840 Xexit_1, TRUE, FALSE,
5841 EL_EM_EXIT_OPEN, -1, -1
5844 Xexit_2, FALSE, FALSE,
5845 EL_EM_EXIT_OPEN, -1, -1
5848 Xexit_3, FALSE, FALSE,
5849 EL_EM_EXIT_OPEN, -1, -1
5852 Xdynamite, TRUE, FALSE,
5853 EL_EM_DYNAMITE, -1, -1
5856 Ydynamite_eat, FALSE, FALSE,
5857 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5860 Xdynamite_1, TRUE, FALSE,
5861 EL_EM_DYNAMITE_ACTIVE, -1, -1
5864 Xdynamite_2, FALSE, FALSE,
5865 EL_EM_DYNAMITE_ACTIVE, -1, -1
5868 Xdynamite_3, FALSE, FALSE,
5869 EL_EM_DYNAMITE_ACTIVE, -1, -1
5872 Xdynamite_4, FALSE, FALSE,
5873 EL_EM_DYNAMITE_ACTIVE, -1, -1
5876 Xbumper, TRUE, FALSE,
5877 EL_EMC_SPRING_BUMPER, -1, -1
5880 XbumperB, FALSE, FALSE,
5881 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5884 Xwheel, TRUE, FALSE,
5885 EL_ROBOT_WHEEL, -1, -1
5888 XwheelB, FALSE, FALSE,
5889 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5892 Xswitch, TRUE, FALSE,
5893 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5896 XswitchB, FALSE, FALSE,
5897 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5901 EL_QUICKSAND_EMPTY, -1, -1
5904 Xsand_stone, TRUE, FALSE,
5905 EL_QUICKSAND_FULL, -1, -1
5908 Xsand_stonein_1, FALSE, TRUE,
5909 EL_ROCK, ACTION_FILLING, -1
5912 Xsand_stonein_2, FALSE, TRUE,
5913 EL_ROCK, ACTION_FILLING, -1
5916 Xsand_stonein_3, FALSE, TRUE,
5917 EL_ROCK, ACTION_FILLING, -1
5920 Xsand_stonein_4, FALSE, TRUE,
5921 EL_ROCK, ACTION_FILLING, -1
5924 Xsand_stonesand_1, FALSE, FALSE,
5925 EL_QUICKSAND_EMPTYING, -1, -1
5928 Xsand_stonesand_2, FALSE, FALSE,
5929 EL_QUICKSAND_EMPTYING, -1, -1
5932 Xsand_stonesand_3, FALSE, FALSE,
5933 EL_QUICKSAND_EMPTYING, -1, -1
5936 Xsand_stonesand_4, FALSE, FALSE,
5937 EL_QUICKSAND_EMPTYING, -1, -1
5940 Xsand_stonesand_quickout_1, FALSE, FALSE,
5941 EL_QUICKSAND_EMPTYING, -1, -1
5944 Xsand_stonesand_quickout_2, FALSE, FALSE,
5945 EL_QUICKSAND_EMPTYING, -1, -1
5948 Xsand_stoneout_1, FALSE, FALSE,
5949 EL_ROCK, ACTION_EMPTYING, -1
5952 Xsand_stoneout_2, FALSE, FALSE,
5953 EL_ROCK, ACTION_EMPTYING, -1
5956 Xsand_sandstone_1, FALSE, FALSE,
5957 EL_QUICKSAND_FILLING, -1, -1
5960 Xsand_sandstone_2, FALSE, FALSE,
5961 EL_QUICKSAND_FILLING, -1, -1
5964 Xsand_sandstone_3, FALSE, FALSE,
5965 EL_QUICKSAND_FILLING, -1, -1
5968 Xsand_sandstone_4, FALSE, FALSE,
5969 EL_QUICKSAND_FILLING, -1, -1
5972 Xplant, TRUE, FALSE,
5973 EL_EMC_PLANT, -1, -1
5976 Yplant, FALSE, FALSE,
5977 EL_EMC_PLANT, -1, -1
5980 Xlenses, TRUE, FALSE,
5981 EL_EMC_LENSES, -1, -1
5984 Xmagnify, TRUE, FALSE,
5985 EL_EMC_MAGNIFIER, -1, -1
5988 Xdripper, TRUE, FALSE,
5989 EL_EMC_DRIPPER, -1, -1
5992 XdripperB, FALSE, FALSE,
5993 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5996 Xfake_blank, TRUE, FALSE,
5997 EL_INVISIBLE_WALL, -1, -1
6000 Xfake_blankB, FALSE, FALSE,
6001 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6004 Xfake_grass, TRUE, FALSE,
6005 EL_EMC_FAKE_GRASS, -1, -1
6008 Xfake_grassB, FALSE, FALSE,
6009 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6012 Xfake_door_1, TRUE, FALSE,
6013 EL_EM_GATE_1_GRAY, -1, -1
6016 Xfake_door_2, TRUE, FALSE,
6017 EL_EM_GATE_2_GRAY, -1, -1
6020 Xfake_door_3, TRUE, FALSE,
6021 EL_EM_GATE_3_GRAY, -1, -1
6024 Xfake_door_4, TRUE, FALSE,
6025 EL_EM_GATE_4_GRAY, -1, -1
6028 Xfake_door_5, TRUE, FALSE,
6029 EL_EMC_GATE_5_GRAY, -1, -1
6032 Xfake_door_6, TRUE, FALSE,
6033 EL_EMC_GATE_6_GRAY, -1, -1
6036 Xfake_door_7, TRUE, FALSE,
6037 EL_EMC_GATE_7_GRAY, -1, -1
6040 Xfake_door_8, TRUE, FALSE,
6041 EL_EMC_GATE_8_GRAY, -1, -1
6044 Xfake_acid_1, TRUE, FALSE,
6045 EL_EMC_FAKE_ACID, -1, -1
6048 Xfake_acid_2, FALSE, FALSE,
6049 EL_EMC_FAKE_ACID, -1, -1
6052 Xfake_acid_3, FALSE, FALSE,
6053 EL_EMC_FAKE_ACID, -1, -1
6056 Xfake_acid_4, FALSE, FALSE,
6057 EL_EMC_FAKE_ACID, -1, -1
6060 Xfake_acid_5, FALSE, FALSE,
6061 EL_EMC_FAKE_ACID, -1, -1
6064 Xfake_acid_6, FALSE, FALSE,
6065 EL_EMC_FAKE_ACID, -1, -1
6068 Xfake_acid_7, FALSE, FALSE,
6069 EL_EMC_FAKE_ACID, -1, -1
6072 Xfake_acid_8, FALSE, FALSE,
6073 EL_EMC_FAKE_ACID, -1, -1
6076 Xsteel_1, TRUE, FALSE,
6077 EL_STEELWALL, -1, -1
6080 Xsteel_2, TRUE, FALSE,
6081 EL_EMC_STEELWALL_2, -1, -1
6084 Xsteel_3, TRUE, FALSE,
6085 EL_EMC_STEELWALL_3, -1, -1
6088 Xsteel_4, TRUE, FALSE,
6089 EL_EMC_STEELWALL_4, -1, -1
6092 Xwall_1, TRUE, FALSE,
6096 Xwall_2, TRUE, FALSE,
6097 EL_EMC_WALL_14, -1, -1
6100 Xwall_3, TRUE, FALSE,
6101 EL_EMC_WALL_15, -1, -1
6104 Xwall_4, TRUE, FALSE,
6105 EL_EMC_WALL_16, -1, -1
6108 Xround_wall_1, TRUE, FALSE,
6109 EL_WALL_SLIPPERY, -1, -1
6112 Xround_wall_2, TRUE, FALSE,
6113 EL_EMC_WALL_SLIPPERY_2, -1, -1
6116 Xround_wall_3, TRUE, FALSE,
6117 EL_EMC_WALL_SLIPPERY_3, -1, -1
6120 Xround_wall_4, TRUE, FALSE,
6121 EL_EMC_WALL_SLIPPERY_4, -1, -1
6124 Xdecor_1, TRUE, FALSE,
6125 EL_EMC_WALL_8, -1, -1
6128 Xdecor_2, TRUE, FALSE,
6129 EL_EMC_WALL_6, -1, -1
6132 Xdecor_3, TRUE, FALSE,
6133 EL_EMC_WALL_4, -1, -1
6136 Xdecor_4, TRUE, FALSE,
6137 EL_EMC_WALL_7, -1, -1
6140 Xdecor_5, TRUE, FALSE,
6141 EL_EMC_WALL_5, -1, -1
6144 Xdecor_6, TRUE, FALSE,
6145 EL_EMC_WALL_9, -1, -1
6148 Xdecor_7, TRUE, FALSE,
6149 EL_EMC_WALL_10, -1, -1
6152 Xdecor_8, TRUE, FALSE,
6153 EL_EMC_WALL_1, -1, -1
6156 Xdecor_9, TRUE, FALSE,
6157 EL_EMC_WALL_2, -1, -1
6160 Xdecor_10, TRUE, FALSE,
6161 EL_EMC_WALL_3, -1, -1
6164 Xdecor_11, TRUE, FALSE,
6165 EL_EMC_WALL_11, -1, -1
6168 Xdecor_12, TRUE, FALSE,
6169 EL_EMC_WALL_12, -1, -1
6172 Xalpha_0, TRUE, FALSE,
6173 EL_CHAR('0'), -1, -1
6176 Xalpha_1, TRUE, FALSE,
6177 EL_CHAR('1'), -1, -1
6180 Xalpha_2, TRUE, FALSE,
6181 EL_CHAR('2'), -1, -1
6184 Xalpha_3, TRUE, FALSE,
6185 EL_CHAR('3'), -1, -1
6188 Xalpha_4, TRUE, FALSE,
6189 EL_CHAR('4'), -1, -1
6192 Xalpha_5, TRUE, FALSE,
6193 EL_CHAR('5'), -1, -1
6196 Xalpha_6, TRUE, FALSE,
6197 EL_CHAR('6'), -1, -1
6200 Xalpha_7, TRUE, FALSE,
6201 EL_CHAR('7'), -1, -1
6204 Xalpha_8, TRUE, FALSE,
6205 EL_CHAR('8'), -1, -1
6208 Xalpha_9, TRUE, FALSE,
6209 EL_CHAR('9'), -1, -1
6212 Xalpha_excla, TRUE, FALSE,
6213 EL_CHAR('!'), -1, -1
6216 Xalpha_quote, TRUE, FALSE,
6217 EL_CHAR('"'), -1, -1
6220 Xalpha_comma, TRUE, FALSE,
6221 EL_CHAR(','), -1, -1
6224 Xalpha_minus, TRUE, FALSE,
6225 EL_CHAR('-'), -1, -1
6228 Xalpha_perio, TRUE, FALSE,
6229 EL_CHAR('.'), -1, -1
6232 Xalpha_colon, TRUE, FALSE,
6233 EL_CHAR(':'), -1, -1
6236 Xalpha_quest, TRUE, FALSE,
6237 EL_CHAR('?'), -1, -1
6240 Xalpha_a, TRUE, FALSE,
6241 EL_CHAR('A'), -1, -1
6244 Xalpha_b, TRUE, FALSE,
6245 EL_CHAR('B'), -1, -1
6248 Xalpha_c, TRUE, FALSE,
6249 EL_CHAR('C'), -1, -1
6252 Xalpha_d, TRUE, FALSE,
6253 EL_CHAR('D'), -1, -1
6256 Xalpha_e, TRUE, FALSE,
6257 EL_CHAR('E'), -1, -1
6260 Xalpha_f, TRUE, FALSE,
6261 EL_CHAR('F'), -1, -1
6264 Xalpha_g, TRUE, FALSE,
6265 EL_CHAR('G'), -1, -1
6268 Xalpha_h, TRUE, FALSE,
6269 EL_CHAR('H'), -1, -1
6272 Xalpha_i, TRUE, FALSE,
6273 EL_CHAR('I'), -1, -1
6276 Xalpha_j, TRUE, FALSE,
6277 EL_CHAR('J'), -1, -1
6280 Xalpha_k, TRUE, FALSE,
6281 EL_CHAR('K'), -1, -1
6284 Xalpha_l, TRUE, FALSE,
6285 EL_CHAR('L'), -1, -1
6288 Xalpha_m, TRUE, FALSE,
6289 EL_CHAR('M'), -1, -1
6292 Xalpha_n, TRUE, FALSE,
6293 EL_CHAR('N'), -1, -1
6296 Xalpha_o, TRUE, FALSE,
6297 EL_CHAR('O'), -1, -1
6300 Xalpha_p, TRUE, FALSE,
6301 EL_CHAR('P'), -1, -1
6304 Xalpha_q, TRUE, FALSE,
6305 EL_CHAR('Q'), -1, -1
6308 Xalpha_r, TRUE, FALSE,
6309 EL_CHAR('R'), -1, -1
6312 Xalpha_s, TRUE, FALSE,
6313 EL_CHAR('S'), -1, -1
6316 Xalpha_t, TRUE, FALSE,
6317 EL_CHAR('T'), -1, -1
6320 Xalpha_u, TRUE, FALSE,
6321 EL_CHAR('U'), -1, -1
6324 Xalpha_v, TRUE, FALSE,
6325 EL_CHAR('V'), -1, -1
6328 Xalpha_w, TRUE, FALSE,
6329 EL_CHAR('W'), -1, -1
6332 Xalpha_x, TRUE, FALSE,
6333 EL_CHAR('X'), -1, -1
6336 Xalpha_y, TRUE, FALSE,
6337 EL_CHAR('Y'), -1, -1
6340 Xalpha_z, TRUE, FALSE,
6341 EL_CHAR('Z'), -1, -1
6344 Xalpha_arrow_e, TRUE, FALSE,
6345 EL_CHAR('>'), -1, -1
6348 Xalpha_arrow_w, TRUE, FALSE,
6349 EL_CHAR('<'), -1, -1
6352 Xalpha_copyr, TRUE, FALSE,
6353 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6357 Xboom_bug, FALSE, FALSE,
6358 EL_BUG, ACTION_EXPLODING, -1
6361 Xboom_bomb, FALSE, FALSE,
6362 EL_BOMB, ACTION_EXPLODING, -1
6365 Xboom_android, FALSE, FALSE,
6366 EL_EMC_ANDROID, ACTION_OTHER, -1
6369 Xboom_1, FALSE, FALSE,
6370 EL_DEFAULT, ACTION_EXPLODING, -1
6373 Xboom_2, FALSE, FALSE,
6374 EL_DEFAULT, ACTION_EXPLODING, -1
6377 Znormal, FALSE, FALSE,
6381 Zdynamite, FALSE, FALSE,
6385 Zplayer, FALSE, FALSE,
6389 ZBORDER, FALSE, FALSE,
6399 static struct Mapping_EM_to_RND_player
6408 em_player_mapping_list[] =
6412 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6416 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6420 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6424 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6428 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6432 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6436 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6440 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6444 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6448 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6452 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6456 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6460 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6464 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6468 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6472 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6476 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6480 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6484 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6488 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6492 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6496 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6500 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6504 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6508 EL_PLAYER_1, ACTION_DEFAULT, -1,
6512 EL_PLAYER_2, ACTION_DEFAULT, -1,
6516 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6520 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6524 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6528 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6532 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6536 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6540 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6544 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6548 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6552 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6556 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6560 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6564 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6568 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6572 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6576 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6580 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6584 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6588 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6592 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6596 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6600 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6604 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6608 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6612 EL_PLAYER_3, ACTION_DEFAULT, -1,
6616 EL_PLAYER_4, ACTION_DEFAULT, -1,
6625 int map_element_RND_to_EM(int element_rnd)
6627 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6628 static boolean mapping_initialized = FALSE;
6630 if (!mapping_initialized)
6634 /* return "Xalpha_quest" for all undefined elements in mapping array */
6635 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6636 mapping_RND_to_EM[i] = Xalpha_quest;
6638 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6639 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6640 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6641 em_object_mapping_list[i].element_em;
6643 mapping_initialized = TRUE;
6646 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6647 return mapping_RND_to_EM[element_rnd];
6649 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6654 int map_element_EM_to_RND(int element_em)
6656 static unsigned short mapping_EM_to_RND[TILE_MAX];
6657 static boolean mapping_initialized = FALSE;
6659 if (!mapping_initialized)
6663 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6664 for (i = 0; i < TILE_MAX; i++)
6665 mapping_EM_to_RND[i] = EL_UNKNOWN;
6667 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6668 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6669 em_object_mapping_list[i].element_rnd;
6671 mapping_initialized = TRUE;
6674 if (element_em >= 0 && element_em < TILE_MAX)
6675 return mapping_EM_to_RND[element_em];
6677 Error(ERR_WARN, "invalid EM level element %d", element_em);
6682 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6684 struct LevelInfo_EM *level_em = level->native_em_level;
6685 struct LEVEL *lev = level_em->lev;
6688 for (i = 0; i < TILE_MAX; i++)
6689 lev->android_array[i] = Xblank;
6691 for (i = 0; i < level->num_android_clone_elements; i++)
6693 int element_rnd = level->android_clone_element[i];
6694 int element_em = map_element_RND_to_EM(element_rnd);
6696 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6697 if (em_object_mapping_list[j].element_rnd == element_rnd)
6698 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6702 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6704 struct LevelInfo_EM *level_em = level->native_em_level;
6705 struct LEVEL *lev = level_em->lev;
6708 level->num_android_clone_elements = 0;
6710 for (i = 0; i < TILE_MAX; i++)
6712 int element_em = lev->android_array[i];
6714 boolean element_found = FALSE;
6716 if (element_em == Xblank)
6719 element_rnd = map_element_EM_to_RND(element_em);
6721 for (j = 0; j < level->num_android_clone_elements; j++)
6722 if (level->android_clone_element[j] == element_rnd)
6723 element_found = TRUE;
6727 level->android_clone_element[level->num_android_clone_elements++] =
6730 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6735 if (level->num_android_clone_elements == 0)
6737 level->num_android_clone_elements = 1;
6738 level->android_clone_element[0] = EL_EMPTY;
6742 int map_direction_RND_to_EM(int direction)
6744 return (direction == MV_UP ? 0 :
6745 direction == MV_RIGHT ? 1 :
6746 direction == MV_DOWN ? 2 :
6747 direction == MV_LEFT ? 3 :
6751 int map_direction_EM_to_RND(int direction)
6753 return (direction == 0 ? MV_UP :
6754 direction == 1 ? MV_RIGHT :
6755 direction == 2 ? MV_DOWN :
6756 direction == 3 ? MV_LEFT :
6760 int map_element_RND_to_SP(int element_rnd)
6762 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6764 if (element_rnd >= EL_SP_START &&
6765 element_rnd <= EL_SP_END)
6766 element_sp = element_rnd - EL_SP_START;
6767 else if (element_rnd == EL_EMPTY_SPACE)
6769 else if (element_rnd == EL_INVISIBLE_WALL)
6775 int map_element_SP_to_RND(int element_sp)
6777 int element_rnd = EL_UNKNOWN;
6779 if (element_sp >= 0x00 &&
6781 element_rnd = EL_SP_START + element_sp;
6782 else if (element_sp == 0x28)
6783 element_rnd = EL_INVISIBLE_WALL;
6788 int map_action_SP_to_RND(int action_sp)
6792 case actActive: return ACTION_ACTIVE;
6793 case actImpact: return ACTION_IMPACT;
6794 case actExploding: return ACTION_EXPLODING;
6795 case actDigging: return ACTION_DIGGING;
6796 case actSnapping: return ACTION_SNAPPING;
6797 case actCollecting: return ACTION_COLLECTING;
6798 case actPassing: return ACTION_PASSING;
6799 case actPushing: return ACTION_PUSHING;
6800 case actDropping: return ACTION_DROPPING;
6802 default: return ACTION_DEFAULT;
6806 int get_next_element(int element)
6810 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6811 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6812 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6813 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6814 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6815 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6816 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6817 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6818 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6819 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6820 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6822 default: return element;
6826 int el_act_dir2img(int element, int action, int direction)
6828 element = GFX_ELEMENT(element);
6829 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6831 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6832 return element_info[element].direction_graphic[action][direction];
6835 static int el_act_dir2crm(int element, int action, int direction)
6837 element = GFX_ELEMENT(element);
6838 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6840 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6841 return element_info[element].direction_crumbled[action][direction];
6844 int el_act2img(int element, int action)
6846 element = GFX_ELEMENT(element);
6848 return element_info[element].graphic[action];
6851 int el_act2crm(int element, int action)
6853 element = GFX_ELEMENT(element);
6855 return element_info[element].crumbled[action];
6858 int el_dir2img(int element, int direction)
6860 element = GFX_ELEMENT(element);
6862 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6865 int el2baseimg(int element)
6867 return element_info[element].graphic[ACTION_DEFAULT];
6870 int el2img(int element)
6872 element = GFX_ELEMENT(element);
6874 return element_info[element].graphic[ACTION_DEFAULT];
6877 int el2edimg(int element)
6879 element = GFX_ELEMENT(element);
6881 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6884 int el2preimg(int element)
6886 element = GFX_ELEMENT(element);
6888 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6891 int el2panelimg(int element)
6893 element = GFX_ELEMENT(element);
6895 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6898 int font2baseimg(int font_nr)
6900 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6903 int getBeltNrFromBeltElement(int element)
6905 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6906 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6907 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6910 int getBeltNrFromBeltActiveElement(int element)
6912 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6913 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6914 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6917 int getBeltNrFromBeltSwitchElement(int element)
6919 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6920 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6921 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6924 int getBeltDirNrFromBeltElement(int element)
6926 static int belt_base_element[4] =
6928 EL_CONVEYOR_BELT_1_LEFT,
6929 EL_CONVEYOR_BELT_2_LEFT,
6930 EL_CONVEYOR_BELT_3_LEFT,
6931 EL_CONVEYOR_BELT_4_LEFT
6934 int belt_nr = getBeltNrFromBeltElement(element);
6935 int belt_dir_nr = element - belt_base_element[belt_nr];
6937 return (belt_dir_nr % 3);
6940 int getBeltDirNrFromBeltSwitchElement(int element)
6942 static int belt_base_element[4] =
6944 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6945 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6946 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6947 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6950 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6951 int belt_dir_nr = element - belt_base_element[belt_nr];
6953 return (belt_dir_nr % 3);
6956 int getBeltDirFromBeltElement(int element)
6958 static int belt_move_dir[3] =
6965 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6967 return belt_move_dir[belt_dir_nr];
6970 int getBeltDirFromBeltSwitchElement(int element)
6972 static int belt_move_dir[3] =
6979 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6981 return belt_move_dir[belt_dir_nr];
6984 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6986 static int belt_base_element[4] =
6988 EL_CONVEYOR_BELT_1_LEFT,
6989 EL_CONVEYOR_BELT_2_LEFT,
6990 EL_CONVEYOR_BELT_3_LEFT,
6991 EL_CONVEYOR_BELT_4_LEFT
6994 return belt_base_element[belt_nr] + belt_dir_nr;
6997 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6999 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7001 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7004 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7006 static int belt_base_element[4] =
7008 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7009 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7010 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7011 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7014 return belt_base_element[belt_nr] + belt_dir_nr;
7017 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7019 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7021 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7024 boolean getTeamMode_EM()
7026 return game.team_mode;
7029 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7031 int game_frame_delay_value;
7033 game_frame_delay_value =
7034 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7035 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7038 if (tape.playing && tape.warp_forward && !tape.pausing)
7039 game_frame_delay_value = 0;
7041 return game_frame_delay_value;
7044 unsigned int InitRND(int seed)
7046 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7047 return InitEngineRandom_EM(seed);
7048 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7049 return InitEngineRandom_SP(seed);
7051 return InitEngineRandom_RND(seed);
7054 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7055 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7057 inline static int get_effective_element_EM(int tile, int frame_em)
7059 int element = object_mapping[tile].element_rnd;
7060 int action = object_mapping[tile].action;
7061 boolean is_backside = object_mapping[tile].is_backside;
7062 boolean action_removing = (action == ACTION_DIGGING ||
7063 action == ACTION_SNAPPING ||
7064 action == ACTION_COLLECTING);
7070 case Yacid_splash_eB:
7071 case Yacid_splash_wB:
7072 return (frame_em > 5 ? EL_EMPTY : element);
7078 else /* frame_em == 7 */
7082 case Yacid_splash_eB:
7083 case Yacid_splash_wB:
7086 case Yemerald_stone:
7089 case Ydiamond_stone:
7093 case Xdrip_stretchB:
7112 case Xsand_stonein_1:
7113 case Xsand_stonein_2:
7114 case Xsand_stonein_3:
7115 case Xsand_stonein_4:
7119 return (is_backside || action_removing ? EL_EMPTY : element);
7124 inline static boolean check_linear_animation_EM(int tile)
7128 case Xsand_stonesand_1:
7129 case Xsand_stonesand_quickout_1:
7130 case Xsand_sandstone_1:
7131 case Xsand_stonein_1:
7132 case Xsand_stoneout_1:
7151 case Yacid_splash_eB:
7152 case Yacid_splash_wB:
7153 case Yemerald_stone:
7160 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7161 boolean has_crumbled_graphics,
7162 int crumbled, int sync_frame)
7164 /* if element can be crumbled, but certain action graphics are just empty
7165 space (like instantly snapping sand to empty space in 1 frame), do not
7166 treat these empty space graphics as crumbled graphics in EMC engine */
7167 if (crumbled == IMG_EMPTY_SPACE)
7168 has_crumbled_graphics = FALSE;
7170 if (has_crumbled_graphics)
7172 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7173 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7174 g_crumbled->anim_delay,
7175 g_crumbled->anim_mode,
7176 g_crumbled->anim_start_frame,
7179 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7180 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7182 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7184 g_em->has_crumbled_graphics = TRUE;
7188 g_em->crumbled_bitmap = NULL;
7189 g_em->crumbled_src_x = 0;
7190 g_em->crumbled_src_y = 0;
7191 g_em->crumbled_border_size = 0;
7193 g_em->has_crumbled_graphics = FALSE;
7197 void ResetGfxAnimation_EM(int x, int y, int tile)
7202 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7203 int tile, int frame_em, int x, int y)
7205 int action = object_mapping[tile].action;
7206 int direction = object_mapping[tile].direction;
7207 int effective_element = get_effective_element_EM(tile, frame_em);
7208 int graphic = (direction == MV_NONE ?
7209 el_act2img(effective_element, action) :
7210 el_act_dir2img(effective_element, action, direction));
7211 struct GraphicInfo *g = &graphic_info[graphic];
7213 boolean action_removing = (action == ACTION_DIGGING ||
7214 action == ACTION_SNAPPING ||
7215 action == ACTION_COLLECTING);
7216 boolean action_moving = (action == ACTION_FALLING ||
7217 action == ACTION_MOVING ||
7218 action == ACTION_PUSHING ||
7219 action == ACTION_EATING ||
7220 action == ACTION_FILLING ||
7221 action == ACTION_EMPTYING);
7222 boolean action_falling = (action == ACTION_FALLING ||
7223 action == ACTION_FILLING ||
7224 action == ACTION_EMPTYING);
7226 /* special case: graphic uses "2nd movement tile" and has defined
7227 7 frames for movement animation (or less) => use default graphic
7228 for last (8th) frame which ends the movement animation */
7229 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7231 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7232 graphic = (direction == MV_NONE ?
7233 el_act2img(effective_element, action) :
7234 el_act_dir2img(effective_element, action, direction));
7236 g = &graphic_info[graphic];
7239 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7243 else if (action_moving)
7245 boolean is_backside = object_mapping[tile].is_backside;
7249 int direction = object_mapping[tile].direction;
7250 int move_dir = (action_falling ? MV_DOWN : direction);
7255 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7256 if (g->double_movement && frame_em == 0)
7260 if (move_dir == MV_LEFT)
7261 GfxFrame[x - 1][y] = GfxFrame[x][y];
7262 else if (move_dir == MV_RIGHT)
7263 GfxFrame[x + 1][y] = GfxFrame[x][y];
7264 else if (move_dir == MV_UP)
7265 GfxFrame[x][y - 1] = GfxFrame[x][y];
7266 else if (move_dir == MV_DOWN)
7267 GfxFrame[x][y + 1] = GfxFrame[x][y];
7274 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7275 if (tile == Xsand_stonesand_quickout_1 ||
7276 tile == Xsand_stonesand_quickout_2)
7280 if (graphic_info[graphic].anim_global_sync)
7281 sync_frame = FrameCounter;
7282 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7283 sync_frame = GfxFrame[x][y];
7285 sync_frame = 0; /* playfield border (pseudo steel) */
7287 SetRandomAnimationValue(x, y);
7289 int frame = getAnimationFrame(g->anim_frames,
7292 g->anim_start_frame,
7295 g_em->unique_identifier =
7296 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7299 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7300 int tile, int frame_em, int x, int y)
7302 int action = object_mapping[tile].action;
7303 int direction = object_mapping[tile].direction;
7304 boolean is_backside = object_mapping[tile].is_backside;
7305 int effective_element = get_effective_element_EM(tile, frame_em);
7306 int effective_action = action;
7307 int graphic = (direction == MV_NONE ?
7308 el_act2img(effective_element, effective_action) :
7309 el_act_dir2img(effective_element, effective_action,
7311 int crumbled = (direction == MV_NONE ?
7312 el_act2crm(effective_element, effective_action) :
7313 el_act_dir2crm(effective_element, effective_action,
7315 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7316 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7317 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7318 struct GraphicInfo *g = &graphic_info[graphic];
7321 /* special case: graphic uses "2nd movement tile" and has defined
7322 7 frames for movement animation (or less) => use default graphic
7323 for last (8th) frame which ends the movement animation */
7324 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7326 effective_action = ACTION_DEFAULT;
7327 graphic = (direction == MV_NONE ?
7328 el_act2img(effective_element, effective_action) :
7329 el_act_dir2img(effective_element, effective_action,
7331 crumbled = (direction == MV_NONE ?
7332 el_act2crm(effective_element, effective_action) :
7333 el_act_dir2crm(effective_element, effective_action,
7336 g = &graphic_info[graphic];
7339 if (graphic_info[graphic].anim_global_sync)
7340 sync_frame = FrameCounter;
7341 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7342 sync_frame = GfxFrame[x][y];
7344 sync_frame = 0; /* playfield border (pseudo steel) */
7346 SetRandomAnimationValue(x, y);
7348 int frame = getAnimationFrame(g->anim_frames,
7351 g->anim_start_frame,
7354 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7355 g->double_movement && is_backside);
7357 /* (updating the "crumbled" graphic definitions is probably not really needed,
7358 as animations for crumbled graphics can't be longer than one EMC cycle) */
7359 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7363 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7364 int player_nr, int anim, int frame_em)
7366 int element = player_mapping[player_nr][anim].element_rnd;
7367 int action = player_mapping[player_nr][anim].action;
7368 int direction = player_mapping[player_nr][anim].direction;
7369 int graphic = (direction == MV_NONE ?
7370 el_act2img(element, action) :
7371 el_act_dir2img(element, action, direction));
7372 struct GraphicInfo *g = &graphic_info[graphic];
7375 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7377 stored_player[player_nr].StepFrame = frame_em;
7379 sync_frame = stored_player[player_nr].Frame;
7381 int frame = getAnimationFrame(g->anim_frames,
7384 g->anim_start_frame,
7387 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7388 &g_em->src_x, &g_em->src_y, FALSE);
7391 void InitGraphicInfo_EM(void)
7396 int num_em_gfx_errors = 0;
7398 if (graphic_info_em_object[0][0].bitmap == NULL)
7400 /* EM graphics not yet initialized in em_open_all() */
7405 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7408 /* always start with reliable default values */
7409 for (i = 0; i < TILE_MAX; i++)
7411 object_mapping[i].element_rnd = EL_UNKNOWN;
7412 object_mapping[i].is_backside = FALSE;
7413 object_mapping[i].action = ACTION_DEFAULT;
7414 object_mapping[i].direction = MV_NONE;
7417 /* always start with reliable default values */
7418 for (p = 0; p < MAX_PLAYERS; p++)
7420 for (i = 0; i < SPR_MAX; i++)
7422 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7423 player_mapping[p][i].action = ACTION_DEFAULT;
7424 player_mapping[p][i].direction = MV_NONE;
7428 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7430 int e = em_object_mapping_list[i].element_em;
7432 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7433 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7435 if (em_object_mapping_list[i].action != -1)
7436 object_mapping[e].action = em_object_mapping_list[i].action;
7438 if (em_object_mapping_list[i].direction != -1)
7439 object_mapping[e].direction =
7440 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7443 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7445 int a = em_player_mapping_list[i].action_em;
7446 int p = em_player_mapping_list[i].player_nr;
7448 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7450 if (em_player_mapping_list[i].action != -1)
7451 player_mapping[p][a].action = em_player_mapping_list[i].action;
7453 if (em_player_mapping_list[i].direction != -1)
7454 player_mapping[p][a].direction =
7455 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7458 for (i = 0; i < TILE_MAX; i++)
7460 int element = object_mapping[i].element_rnd;
7461 int action = object_mapping[i].action;
7462 int direction = object_mapping[i].direction;
7463 boolean is_backside = object_mapping[i].is_backside;
7464 boolean action_exploding = ((action == ACTION_EXPLODING ||
7465 action == ACTION_SMASHED_BY_ROCK ||
7466 action == ACTION_SMASHED_BY_SPRING) &&
7467 element != EL_DIAMOND);
7468 boolean action_active = (action == ACTION_ACTIVE);
7469 boolean action_other = (action == ACTION_OTHER);
7471 for (j = 0; j < 8; j++)
7473 int effective_element = get_effective_element_EM(i, j);
7474 int effective_action = (j < 7 ? action :
7475 i == Xdrip_stretch ? action :
7476 i == Xdrip_stretchB ? action :
7477 i == Ydrip_s1 ? action :
7478 i == Ydrip_s1B ? action :
7479 i == Xball_1B ? action :
7480 i == Xball_2 ? action :
7481 i == Xball_2B ? action :
7482 i == Yball_eat ? action :
7483 i == Ykey_1_eat ? action :
7484 i == Ykey_2_eat ? action :
7485 i == Ykey_3_eat ? action :
7486 i == Ykey_4_eat ? action :
7487 i == Ykey_5_eat ? action :
7488 i == Ykey_6_eat ? action :
7489 i == Ykey_7_eat ? action :
7490 i == Ykey_8_eat ? action :
7491 i == Ylenses_eat ? action :
7492 i == Ymagnify_eat ? action :
7493 i == Ygrass_eat ? action :
7494 i == Ydirt_eat ? action :
7495 i == Xsand_stonein_1 ? action :
7496 i == Xsand_stonein_2 ? action :
7497 i == Xsand_stonein_3 ? action :
7498 i == Xsand_stonein_4 ? action :
7499 i == Xsand_stoneout_1 ? action :
7500 i == Xsand_stoneout_2 ? action :
7501 i == Xboom_android ? ACTION_EXPLODING :
7502 action_exploding ? ACTION_EXPLODING :
7503 action_active ? action :
7504 action_other ? action :
7506 int graphic = (el_act_dir2img(effective_element, effective_action,
7508 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7510 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7511 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7512 boolean has_action_graphics = (graphic != base_graphic);
7513 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7514 struct GraphicInfo *g = &graphic_info[graphic];
7515 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7518 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7519 boolean special_animation = (action != ACTION_DEFAULT &&
7520 g->anim_frames == 3 &&
7521 g->anim_delay == 2 &&
7522 g->anim_mode & ANIM_LINEAR);
7523 int sync_frame = (i == Xdrip_stretch ? 7 :
7524 i == Xdrip_stretchB ? 7 :
7525 i == Ydrip_s2 ? j + 8 :
7526 i == Ydrip_s2B ? j + 8 :
7535 i == Xfake_acid_1 ? 0 :
7536 i == Xfake_acid_2 ? 10 :
7537 i == Xfake_acid_3 ? 20 :
7538 i == Xfake_acid_4 ? 30 :
7539 i == Xfake_acid_5 ? 40 :
7540 i == Xfake_acid_6 ? 50 :
7541 i == Xfake_acid_7 ? 60 :
7542 i == Xfake_acid_8 ? 70 :
7544 i == Xball_2B ? j + 8 :
7545 i == Yball_eat ? j + 1 :
7546 i == Ykey_1_eat ? j + 1 :
7547 i == Ykey_2_eat ? j + 1 :
7548 i == Ykey_3_eat ? j + 1 :
7549 i == Ykey_4_eat ? j + 1 :
7550 i == Ykey_5_eat ? j + 1 :
7551 i == Ykey_6_eat ? j + 1 :
7552 i == Ykey_7_eat ? j + 1 :
7553 i == Ykey_8_eat ? j + 1 :
7554 i == Ylenses_eat ? j + 1 :
7555 i == Ymagnify_eat ? j + 1 :
7556 i == Ygrass_eat ? j + 1 :
7557 i == Ydirt_eat ? j + 1 :
7558 i == Xamoeba_1 ? 0 :
7559 i == Xamoeba_2 ? 1 :
7560 i == Xamoeba_3 ? 2 :
7561 i == Xamoeba_4 ? 3 :
7562 i == Xamoeba_5 ? 0 :
7563 i == Xamoeba_6 ? 1 :
7564 i == Xamoeba_7 ? 2 :
7565 i == Xamoeba_8 ? 3 :
7566 i == Xexit_2 ? j + 8 :
7567 i == Xexit_3 ? j + 16 :
7568 i == Xdynamite_1 ? 0 :
7569 i == Xdynamite_2 ? 8 :
7570 i == Xdynamite_3 ? 16 :
7571 i == Xdynamite_4 ? 24 :
7572 i == Xsand_stonein_1 ? j + 1 :
7573 i == Xsand_stonein_2 ? j + 9 :
7574 i == Xsand_stonein_3 ? j + 17 :
7575 i == Xsand_stonein_4 ? j + 25 :
7576 i == Xsand_stoneout_1 && j == 0 ? 0 :
7577 i == Xsand_stoneout_1 && j == 1 ? 0 :
7578 i == Xsand_stoneout_1 && j == 2 ? 1 :
7579 i == Xsand_stoneout_1 && j == 3 ? 2 :
7580 i == Xsand_stoneout_1 && j == 4 ? 2 :
7581 i == Xsand_stoneout_1 && j == 5 ? 3 :
7582 i == Xsand_stoneout_1 && j == 6 ? 4 :
7583 i == Xsand_stoneout_1 && j == 7 ? 4 :
7584 i == Xsand_stoneout_2 && j == 0 ? 5 :
7585 i == Xsand_stoneout_2 && j == 1 ? 6 :
7586 i == Xsand_stoneout_2 && j == 2 ? 7 :
7587 i == Xsand_stoneout_2 && j == 3 ? 8 :
7588 i == Xsand_stoneout_2 && j == 4 ? 9 :
7589 i == Xsand_stoneout_2 && j == 5 ? 11 :
7590 i == Xsand_stoneout_2 && j == 6 ? 13 :
7591 i == Xsand_stoneout_2 && j == 7 ? 15 :
7592 i == Xboom_bug && j == 1 ? 2 :
7593 i == Xboom_bug && j == 2 ? 2 :
7594 i == Xboom_bug && j == 3 ? 4 :
7595 i == Xboom_bug && j == 4 ? 4 :
7596 i == Xboom_bug && j == 5 ? 2 :
7597 i == Xboom_bug && j == 6 ? 2 :
7598 i == Xboom_bug && j == 7 ? 0 :
7599 i == Xboom_bomb && j == 1 ? 2 :
7600 i == Xboom_bomb && j == 2 ? 2 :
7601 i == Xboom_bomb && j == 3 ? 4 :
7602 i == Xboom_bomb && j == 4 ? 4 :
7603 i == Xboom_bomb && j == 5 ? 2 :
7604 i == Xboom_bomb && j == 6 ? 2 :
7605 i == Xboom_bomb && j == 7 ? 0 :
7606 i == Xboom_android && j == 7 ? 6 :
7607 i == Xboom_1 && j == 1 ? 2 :
7608 i == Xboom_1 && j == 2 ? 2 :
7609 i == Xboom_1 && j == 3 ? 4 :
7610 i == Xboom_1 && j == 4 ? 4 :
7611 i == Xboom_1 && j == 5 ? 6 :
7612 i == Xboom_1 && j == 6 ? 6 :
7613 i == Xboom_1 && j == 7 ? 8 :
7614 i == Xboom_2 && j == 0 ? 8 :
7615 i == Xboom_2 && j == 1 ? 8 :
7616 i == Xboom_2 && j == 2 ? 10 :
7617 i == Xboom_2 && j == 3 ? 10 :
7618 i == Xboom_2 && j == 4 ? 10 :
7619 i == Xboom_2 && j == 5 ? 12 :
7620 i == Xboom_2 && j == 6 ? 12 :
7621 i == Xboom_2 && j == 7 ? 12 :
7622 special_animation && j == 4 ? 3 :
7623 effective_action != action ? 0 :
7627 Bitmap *debug_bitmap = g_em->bitmap;
7628 int debug_src_x = g_em->src_x;
7629 int debug_src_y = g_em->src_y;
7632 int frame = getAnimationFrame(g->anim_frames,
7635 g->anim_start_frame,
7638 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7639 g->double_movement && is_backside);
7641 g_em->bitmap = src_bitmap;
7642 g_em->src_x = src_x;
7643 g_em->src_y = src_y;
7644 g_em->src_offset_x = 0;
7645 g_em->src_offset_y = 0;
7646 g_em->dst_offset_x = 0;
7647 g_em->dst_offset_y = 0;
7648 g_em->width = TILEX;
7649 g_em->height = TILEY;
7651 g_em->preserve_background = FALSE;
7653 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7656 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7657 effective_action == ACTION_MOVING ||
7658 effective_action == ACTION_PUSHING ||
7659 effective_action == ACTION_EATING)) ||
7660 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7661 effective_action == ACTION_EMPTYING)))
7664 (effective_action == ACTION_FALLING ||
7665 effective_action == ACTION_FILLING ||
7666 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7667 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7668 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7669 int num_steps = (i == Ydrip_s1 ? 16 :
7670 i == Ydrip_s1B ? 16 :
7671 i == Ydrip_s2 ? 16 :
7672 i == Ydrip_s2B ? 16 :
7673 i == Xsand_stonein_1 ? 32 :
7674 i == Xsand_stonein_2 ? 32 :
7675 i == Xsand_stonein_3 ? 32 :
7676 i == Xsand_stonein_4 ? 32 :
7677 i == Xsand_stoneout_1 ? 16 :
7678 i == Xsand_stoneout_2 ? 16 : 8);
7679 int cx = ABS(dx) * (TILEX / num_steps);
7680 int cy = ABS(dy) * (TILEY / num_steps);
7681 int step_frame = (i == Ydrip_s2 ? j + 8 :
7682 i == Ydrip_s2B ? j + 8 :
7683 i == Xsand_stonein_2 ? j + 8 :
7684 i == Xsand_stonein_3 ? j + 16 :
7685 i == Xsand_stonein_4 ? j + 24 :
7686 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7687 int step = (is_backside ? step_frame : num_steps - step_frame);
7689 if (is_backside) /* tile where movement starts */
7691 if (dx < 0 || dy < 0)
7693 g_em->src_offset_x = cx * step;
7694 g_em->src_offset_y = cy * step;
7698 g_em->dst_offset_x = cx * step;
7699 g_em->dst_offset_y = cy * step;
7702 else /* tile where movement ends */
7704 if (dx < 0 || dy < 0)
7706 g_em->dst_offset_x = cx * step;
7707 g_em->dst_offset_y = cy * step;
7711 g_em->src_offset_x = cx * step;
7712 g_em->src_offset_y = cy * step;
7716 g_em->width = TILEX - cx * step;
7717 g_em->height = TILEY - cy * step;
7720 /* create unique graphic identifier to decide if tile must be redrawn */
7721 /* bit 31 - 16 (16 bit): EM style graphic
7722 bit 15 - 12 ( 4 bit): EM style frame
7723 bit 11 - 6 ( 6 bit): graphic width
7724 bit 5 - 0 ( 6 bit): graphic height */
7725 g_em->unique_identifier =
7726 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7730 /* skip check for EMC elements not contained in original EMC artwork */
7731 if (element == EL_EMC_FAKE_ACID)
7734 if (g_em->bitmap != debug_bitmap ||
7735 g_em->src_x != debug_src_x ||
7736 g_em->src_y != debug_src_y ||
7737 g_em->src_offset_x != 0 ||
7738 g_em->src_offset_y != 0 ||
7739 g_em->dst_offset_x != 0 ||
7740 g_em->dst_offset_y != 0 ||
7741 g_em->width != TILEX ||
7742 g_em->height != TILEY)
7744 static int last_i = -1;
7752 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7753 i, element, element_info[element].token_name,
7754 element_action_info[effective_action].suffix, direction);
7756 if (element != effective_element)
7757 printf(" [%d ('%s')]",
7759 element_info[effective_element].token_name);
7763 if (g_em->bitmap != debug_bitmap)
7764 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7765 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7767 if (g_em->src_x != debug_src_x ||
7768 g_em->src_y != debug_src_y)
7769 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7770 j, (is_backside ? 'B' : 'F'),
7771 g_em->src_x, g_em->src_y,
7772 g_em->src_x / 32, g_em->src_y / 32,
7773 debug_src_x, debug_src_y,
7774 debug_src_x / 32, debug_src_y / 32);
7776 if (g_em->src_offset_x != 0 ||
7777 g_em->src_offset_y != 0 ||
7778 g_em->dst_offset_x != 0 ||
7779 g_em->dst_offset_y != 0)
7780 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7782 g_em->src_offset_x, g_em->src_offset_y,
7783 g_em->dst_offset_x, g_em->dst_offset_y);
7785 if (g_em->width != TILEX ||
7786 g_em->height != TILEY)
7787 printf(" %d (%d): size %d,%d should be %d,%d\n",
7789 g_em->width, g_em->height, TILEX, TILEY);
7791 num_em_gfx_errors++;
7798 for (i = 0; i < TILE_MAX; i++)
7800 for (j = 0; j < 8; j++)
7802 int element = object_mapping[i].element_rnd;
7803 int action = object_mapping[i].action;
7804 int direction = object_mapping[i].direction;
7805 boolean is_backside = object_mapping[i].is_backside;
7806 int graphic_action = el_act_dir2img(element, action, direction);
7807 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7809 if ((action == ACTION_SMASHED_BY_ROCK ||
7810 action == ACTION_SMASHED_BY_SPRING ||
7811 action == ACTION_EATING) &&
7812 graphic_action == graphic_default)
7814 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7815 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7816 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7817 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7820 /* no separate animation for "smashed by rock" -- use rock instead */
7821 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7822 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7824 g_em->bitmap = g_xx->bitmap;
7825 g_em->src_x = g_xx->src_x;
7826 g_em->src_y = g_xx->src_y;
7827 g_em->src_offset_x = g_xx->src_offset_x;
7828 g_em->src_offset_y = g_xx->src_offset_y;
7829 g_em->dst_offset_x = g_xx->dst_offset_x;
7830 g_em->dst_offset_y = g_xx->dst_offset_y;
7831 g_em->width = g_xx->width;
7832 g_em->height = g_xx->height;
7833 g_em->unique_identifier = g_xx->unique_identifier;
7836 g_em->preserve_background = TRUE;
7841 for (p = 0; p < MAX_PLAYERS; p++)
7843 for (i = 0; i < SPR_MAX; i++)
7845 int element = player_mapping[p][i].element_rnd;
7846 int action = player_mapping[p][i].action;
7847 int direction = player_mapping[p][i].direction;
7849 for (j = 0; j < 8; j++)
7851 int effective_element = element;
7852 int effective_action = action;
7853 int graphic = (direction == MV_NONE ?
7854 el_act2img(effective_element, effective_action) :
7855 el_act_dir2img(effective_element, effective_action,
7857 struct GraphicInfo *g = &graphic_info[graphic];
7858 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7864 Bitmap *debug_bitmap = g_em->bitmap;
7865 int debug_src_x = g_em->src_x;
7866 int debug_src_y = g_em->src_y;
7869 int frame = getAnimationFrame(g->anim_frames,
7872 g->anim_start_frame,
7875 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7877 g_em->bitmap = src_bitmap;
7878 g_em->src_x = src_x;
7879 g_em->src_y = src_y;
7880 g_em->src_offset_x = 0;
7881 g_em->src_offset_y = 0;
7882 g_em->dst_offset_x = 0;
7883 g_em->dst_offset_y = 0;
7884 g_em->width = TILEX;
7885 g_em->height = TILEY;
7889 /* skip check for EMC elements not contained in original EMC artwork */
7890 if (element == EL_PLAYER_3 ||
7891 element == EL_PLAYER_4)
7894 if (g_em->bitmap != debug_bitmap ||
7895 g_em->src_x != debug_src_x ||
7896 g_em->src_y != debug_src_y)
7898 static int last_i = -1;
7906 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7907 p, i, element, element_info[element].token_name,
7908 element_action_info[effective_action].suffix, direction);
7910 if (element != effective_element)
7911 printf(" [%d ('%s')]",
7913 element_info[effective_element].token_name);
7917 if (g_em->bitmap != debug_bitmap)
7918 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7919 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7921 if (g_em->src_x != debug_src_x ||
7922 g_em->src_y != debug_src_y)
7923 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7925 g_em->src_x, g_em->src_y,
7926 g_em->src_x / 32, g_em->src_y / 32,
7927 debug_src_x, debug_src_y,
7928 debug_src_x / 32, debug_src_y / 32);
7930 num_em_gfx_errors++;
7940 printf("::: [%d errors found]\n", num_em_gfx_errors);
7946 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7947 boolean any_player_moving,
7948 boolean any_player_snapping,
7949 boolean any_player_dropping)
7951 static boolean player_was_waiting = TRUE;
7953 if (frame == 0 && !any_player_dropping)
7955 if (!player_was_waiting)
7957 if (!SaveEngineSnapshotToList())
7960 player_was_waiting = TRUE;
7963 else if (any_player_moving || any_player_snapping || any_player_dropping)
7965 player_was_waiting = FALSE;
7969 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7970 boolean murphy_is_dropping)
7972 static boolean player_was_waiting = TRUE;
7974 if (murphy_is_waiting)
7976 if (!player_was_waiting)
7978 if (!SaveEngineSnapshotToList())
7981 player_was_waiting = TRUE;
7986 player_was_waiting = FALSE;
7990 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7991 boolean any_player_moving,
7992 boolean any_player_snapping,
7993 boolean any_player_dropping)
7995 if (tape.single_step && tape.recording && !tape.pausing)
7996 if (frame == 0 && !any_player_dropping)
7997 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7999 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8000 any_player_snapping, any_player_dropping);
8003 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8004 boolean murphy_is_dropping)
8006 if (tape.single_step && tape.recording && !tape.pausing)
8007 if (murphy_is_waiting)
8008 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8010 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8013 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8014 int graphic, int sync_frame, int x, int y)
8016 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8018 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8021 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8023 return (IS_NEXT_FRAME(sync_frame, graphic));
8026 int getGraphicInfo_Delay(int graphic)
8028 return graphic_info[graphic].anim_delay;
8031 void PlayMenuSoundExt(int sound)
8033 if (sound == SND_UNDEFINED)
8036 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8037 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8040 if (IS_LOOP_SOUND(sound))
8041 PlaySoundLoop(sound);
8046 void PlayMenuSound()
8048 PlayMenuSoundExt(menu.sound[game_status]);
8051 void PlayMenuSoundStereo(int sound, int stereo_position)
8053 if (sound == SND_UNDEFINED)
8056 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8057 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8060 if (IS_LOOP_SOUND(sound))
8061 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8063 PlaySoundStereo(sound, stereo_position);
8066 void PlayMenuSoundIfLoopExt(int sound)
8068 if (sound == SND_UNDEFINED)
8071 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8072 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8075 if (IS_LOOP_SOUND(sound))
8076 PlaySoundLoop(sound);
8079 void PlayMenuSoundIfLoop()
8081 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8084 void PlayMenuMusicExt(int music)
8086 if (music == MUS_UNDEFINED)
8089 if (!setup.sound_music)
8095 void PlayMenuMusic()
8097 PlayMenuMusicExt(menu.music[game_status]);
8100 void PlaySoundActivating()
8103 PlaySound(SND_MENU_ITEM_ACTIVATING);
8107 void PlaySoundSelecting()
8110 PlaySound(SND_MENU_ITEM_SELECTING);
8114 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8116 boolean change_fullscreen = (setup.fullscreen !=
8117 video.fullscreen_enabled);
8118 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8119 !strEqual(setup.fullscreen_mode,
8120 video.fullscreen_mode_current));
8121 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8122 setup.window_scaling_percent !=
8123 video.window_scaling_percent);
8125 if (change_window_scaling_percent && video.fullscreen_enabled)
8128 if (!change_window_scaling_percent && !video.fullscreen_available)
8131 #if defined(TARGET_SDL2)
8132 if (change_window_scaling_percent)
8134 SDLSetWindowScaling(setup.window_scaling_percent);
8138 else if (change_fullscreen)
8140 SDLSetWindowFullscreen(setup.fullscreen);
8142 /* set setup value according to successfully changed fullscreen mode */
8143 setup.fullscreen = video.fullscreen_enabled;
8149 if (change_fullscreen ||
8150 change_fullscreen_mode ||
8151 change_window_scaling_percent)
8153 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8155 /* save backbuffer content which gets lost when toggling fullscreen mode */
8156 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8158 if (change_fullscreen_mode)
8160 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8161 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8164 if (change_window_scaling_percent)
8166 /* keep window mode, but change window scaling */
8167 video.fullscreen_enabled = TRUE; /* force new window scaling */
8170 /* toggle fullscreen */
8171 ChangeVideoModeIfNeeded(setup.fullscreen);
8173 /* set setup value according to successfully changed fullscreen mode */
8174 setup.fullscreen = video.fullscreen_enabled;
8176 /* restore backbuffer content from temporary backbuffer backup bitmap */
8177 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8179 FreeBitmap(tmp_backbuffer);
8181 /* update visible window/screen */
8182 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8186 void JoinRectangles(int *x, int *y, int *width, int *height,
8187 int x2, int y2, int width2, int height2)
8189 // do not join with "off-screen" rectangle
8190 if (x2 == -1 || y2 == -1)
8195 *width = MAX(*width, width2);
8196 *height = MAX(*height, height2);
8199 void SetGameStatus(int game_status_new)
8201 game_status = game_status_new;
8203 global.anim_status_next = game_status;
8206 void ChangeViewportPropertiesIfNeeded()
8208 int gfx_game_mode = game_status;
8209 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8211 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8212 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8213 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8214 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8215 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8216 int new_win_xsize = vp_window->width;
8217 int new_win_ysize = vp_window->height;
8218 int border_size = vp_playfield->border_size;
8219 int new_sx = vp_playfield->x + border_size;
8220 int new_sy = vp_playfield->y + border_size;
8221 int new_sxsize = vp_playfield->width - 2 * border_size;
8222 int new_sysize = vp_playfield->height - 2 * border_size;
8223 int new_real_sx = vp_playfield->x;
8224 int new_real_sy = vp_playfield->y;
8225 int new_full_sxsize = vp_playfield->width;
8226 int new_full_sysize = vp_playfield->height;
8227 int new_dx = vp_door_1->x;
8228 int new_dy = vp_door_1->y;
8229 int new_dxsize = vp_door_1->width;
8230 int new_dysize = vp_door_1->height;
8231 int new_vx = vp_door_2->x;
8232 int new_vy = vp_door_2->y;
8233 int new_vxsize = vp_door_2->width;
8234 int new_vysize = vp_door_2->height;
8235 int new_ex = vp_door_3->x;
8236 int new_ey = vp_door_3->y;
8237 int new_exsize = vp_door_3->width;
8238 int new_eysize = vp_door_3->height;
8239 int new_tilesize_var =
8240 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8242 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8243 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8244 int new_scr_fieldx = new_sxsize / tilesize;
8245 int new_scr_fieldy = new_sysize / tilesize;
8246 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8247 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8248 boolean init_gfx_buffers = FALSE;
8249 boolean init_video_buffer = FALSE;
8250 boolean init_gadgets_and_toons = FALSE;
8251 boolean init_em_graphics = FALSE;
8253 if (new_win_xsize != WIN_XSIZE ||
8254 new_win_ysize != WIN_YSIZE)
8256 WIN_XSIZE = new_win_xsize;
8257 WIN_YSIZE = new_win_ysize;
8259 init_video_buffer = TRUE;
8260 init_gfx_buffers = TRUE;
8261 init_gadgets_and_toons = TRUE;
8263 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8266 if (new_scr_fieldx != SCR_FIELDX ||
8267 new_scr_fieldy != SCR_FIELDY)
8269 /* this always toggles between MAIN and GAME when using small tile size */
8271 SCR_FIELDX = new_scr_fieldx;
8272 SCR_FIELDY = new_scr_fieldy;
8274 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8285 new_sxsize != SXSIZE ||
8286 new_sysize != SYSIZE ||
8287 new_dxsize != DXSIZE ||
8288 new_dysize != DYSIZE ||
8289 new_vxsize != VXSIZE ||
8290 new_vysize != VYSIZE ||
8291 new_exsize != EXSIZE ||
8292 new_eysize != EYSIZE ||
8293 new_real_sx != REAL_SX ||
8294 new_real_sy != REAL_SY ||
8295 new_full_sxsize != FULL_SXSIZE ||
8296 new_full_sysize != FULL_SYSIZE ||
8297 new_tilesize_var != TILESIZE_VAR
8300 // ------------------------------------------------------------------------
8301 // determine next fading area for changed viewport definitions
8302 // ------------------------------------------------------------------------
8304 // start with current playfield area (default fading area)
8307 FADE_SXSIZE = FULL_SXSIZE;
8308 FADE_SYSIZE = FULL_SYSIZE;
8310 // add new playfield area if position or size has changed
8311 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8312 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8314 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8315 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8318 // add current and new door 1 area if position or size has changed
8319 if (new_dx != DX || new_dy != DY ||
8320 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8322 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8323 DX, DY, DXSIZE, DYSIZE);
8324 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8325 new_dx, new_dy, new_dxsize, new_dysize);
8328 // add current and new door 2 area if position or size has changed
8329 if (new_dx != VX || new_dy != VY ||
8330 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8332 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8333 VX, VY, VXSIZE, VYSIZE);
8334 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8335 new_vx, new_vy, new_vxsize, new_vysize);
8338 // ------------------------------------------------------------------------
8339 // handle changed tile size
8340 // ------------------------------------------------------------------------
8342 if (new_tilesize_var != TILESIZE_VAR)
8344 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8346 // changing tile size invalidates scroll values of engine snapshots
8347 FreeEngineSnapshotSingle();
8349 // changing tile size requires update of graphic mapping for EM engine
8350 init_em_graphics = TRUE;
8361 SXSIZE = new_sxsize;
8362 SYSIZE = new_sysize;
8363 DXSIZE = new_dxsize;
8364 DYSIZE = new_dysize;
8365 VXSIZE = new_vxsize;
8366 VYSIZE = new_vysize;
8367 EXSIZE = new_exsize;
8368 EYSIZE = new_eysize;
8369 REAL_SX = new_real_sx;
8370 REAL_SY = new_real_sy;
8371 FULL_SXSIZE = new_full_sxsize;
8372 FULL_SYSIZE = new_full_sysize;
8373 TILESIZE_VAR = new_tilesize_var;
8375 init_gfx_buffers = TRUE;
8376 init_gadgets_and_toons = TRUE;
8378 // printf("::: viewports: init_gfx_buffers\n");
8379 // printf("::: viewports: init_gadgets_and_toons\n");
8382 if (init_gfx_buffers)
8384 // printf("::: init_gfx_buffers\n");
8386 SCR_FIELDX = new_scr_fieldx_buffers;
8387 SCR_FIELDY = new_scr_fieldy_buffers;
8391 SCR_FIELDX = new_scr_fieldx;
8392 SCR_FIELDY = new_scr_fieldy;
8394 SetDrawDeactivationMask(REDRAW_NONE);
8395 SetDrawBackgroundMask(REDRAW_FIELD);
8398 if (init_video_buffer)
8400 // printf("::: init_video_buffer\n");
8402 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8403 InitImageTextures();
8406 if (init_gadgets_and_toons)
8408 // printf("::: init_gadgets_and_toons\n");
8412 InitGlobalAnimations();
8415 if (init_em_graphics)
8417 InitGraphicInfo_EM();