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 char *print_if_not_empty(int element)
178 static char *s = NULL;
179 char *token_name = element_info[element].token_name;
184 s = checked_malloc(strlen(token_name) + 10 + 1);
186 if (element != EL_EMPTY)
187 sprintf(s, "%d\t['%s']", element, token_name);
189 sprintf(s, "%d", element);
194 void DumpTile(int x, int y)
199 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
205 printf_line("-", 79);
206 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
207 printf_line("-", 79);
209 if (!IN_LEV_FIELD(x, y))
211 printf("(not in level field)\n");
217 printf(" Feld: %d\t['%s']\n", Feld[x][y],
218 element_info[Feld[x][y]].token_name);
219 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
220 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
221 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
222 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
223 printf(" MovPos: %d\n", MovPos[x][y]);
224 printf(" MovDir: %d\n", MovDir[x][y]);
225 printf(" MovDelay: %d\n", MovDelay[x][y]);
226 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
227 printf(" CustomValue: %d\n", CustomValue[x][y]);
228 printf(" GfxElement: %d\n", GfxElement[x][y]);
229 printf(" GfxAction: %d\n", GfxAction[x][y]);
230 printf(" GfxFrame: %d\n", GfxFrame[x][y]);
234 void SetDrawtoField(int mode)
236 if (mode == DRAW_FIELDBUFFER)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
245 drawto_field = fieldbuffer;
247 else /* DRAW_BACKBUFFER */
253 BX2 = SCR_FIELDX - 1;
254 BY2 = SCR_FIELDY - 1;
256 drawto_field = backbuffer;
260 static void RedrawPlayfield_RND()
262 if (game.envelope_active)
265 DrawLevel(REDRAW_ALL);
269 void RedrawPlayfield()
271 if (game_status != GAME_MODE_PLAYING)
274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
275 RedrawPlayfield_EM(TRUE);
276 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
277 RedrawPlayfield_SP(TRUE);
278 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
279 RedrawPlayfield_RND();
281 BlitScreenToBitmap(backbuffer);
283 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
287 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
289 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
291 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
294 void DrawMaskedBorder_FIELD()
296 if (global.border_status >= GAME_MODE_TITLE &&
297 global.border_status <= GAME_MODE_PLAYING &&
298 border.draw_masked[global.border_status])
299 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
302 void DrawMaskedBorder_DOOR_1()
304 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
305 (global.border_status != GAME_MODE_EDITOR ||
306 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
307 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
310 void DrawMaskedBorder_DOOR_2()
312 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
313 global.border_status != GAME_MODE_EDITOR)
314 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
317 void DrawMaskedBorder_DOOR_3()
319 /* currently not available */
322 void DrawMaskedBorder_ALL()
324 DrawMaskedBorder_FIELD();
325 DrawMaskedBorder_DOOR_1();
326 DrawMaskedBorder_DOOR_2();
327 DrawMaskedBorder_DOOR_3();
330 void DrawMaskedBorder(int redraw_mask)
332 /* never draw masked screen borders on borderless screens */
333 if (effectiveGameStatus() == GAME_MODE_LOADING ||
334 effectiveGameStatus() == GAME_MODE_TITLE)
337 if (redraw_mask & REDRAW_ALL)
338 DrawMaskedBorder_ALL();
341 if (redraw_mask & REDRAW_FIELD)
342 DrawMaskedBorder_FIELD();
343 if (redraw_mask & REDRAW_DOOR_1)
344 DrawMaskedBorder_DOOR_1();
345 if (redraw_mask & REDRAW_DOOR_2)
346 DrawMaskedBorder_DOOR_2();
347 if (redraw_mask & REDRAW_DOOR_3)
348 DrawMaskedBorder_DOOR_3();
352 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
354 int fx = FX, fy = FY;
355 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
356 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
358 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
359 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
360 int dx_var = dx * TILESIZE_VAR / TILESIZE;
361 int dy_var = dy * TILESIZE_VAR / TILESIZE;
364 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
365 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
367 if (EVEN(SCR_FIELDX))
369 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
370 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
372 fx += (dx_var > 0 ? TILEX_VAR : 0);
379 if (EVEN(SCR_FIELDY))
381 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
382 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
384 fy += (dy_var > 0 ? TILEY_VAR : 0);
391 if (full_lev_fieldx <= SCR_FIELDX)
393 if (EVEN(SCR_FIELDX))
394 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
396 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
399 if (full_lev_fieldy <= SCR_FIELDY)
401 if (EVEN(SCR_FIELDY))
402 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
404 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
407 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
410 void BlitScreenToBitmap(Bitmap *target_bitmap)
412 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
413 BlitScreenToBitmap_EM(target_bitmap);
414 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
415 BlitScreenToBitmap_SP(target_bitmap);
416 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
417 BlitScreenToBitmap_RND(target_bitmap);
419 redraw_mask |= REDRAW_FIELD;
422 void DrawFramesPerSecond()
425 int font_nr = FONT_TEXT_2;
426 int font_width = getFontWidth(font_nr);
428 sprintf(text, "%04.1f fps", global.frames_per_second);
430 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
431 font_nr, BLIT_OPAQUE);
436 if (redraw_mask == REDRAW_NONE)
439 // draw masked border to all viewports, if defined
440 DrawMaskedBorder(redraw_mask);
442 // draw frames per second (only if debug mode is enabled)
443 if (redraw_mask & REDRAW_FPS)
444 DrawFramesPerSecond();
446 // redraw complete window if both playfield and (some) doors need redraw
447 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
448 redraw_mask = REDRAW_ALL;
450 /* although redrawing the whole window would be fine for normal gameplay,
451 being able to only redraw the playfield is required for deactivating
452 certain drawing areas (mainly playfield) to work, which is needed for
453 warp-forward to be fast enough (by skipping redraw of most frames) */
455 if (redraw_mask & REDRAW_ALL)
457 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
459 else if (redraw_mask & REDRAW_FIELD)
461 BlitBitmap(backbuffer, window,
462 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
464 else if (redraw_mask & REDRAW_DOORS)
466 if (redraw_mask & REDRAW_DOOR_1)
467 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
469 if (redraw_mask & REDRAW_DOOR_2)
470 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
472 if (redraw_mask & REDRAW_DOOR_3)
473 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
476 redraw_mask = REDRAW_NONE;
479 static void FadeCrossSaveBackbuffer()
481 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
484 static void FadeCrossRestoreBackbuffer()
486 int redraw_mask_last = redraw_mask;
488 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
490 // do not change redraw mask when restoring backbuffer after cross-fading
491 redraw_mask = redraw_mask_last;
494 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
496 static int fade_type_skip = FADE_TYPE_NONE;
497 void (*draw_border_function)(void) = NULL;
498 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
499 int x, y, width, height;
500 int fade_delay, post_delay;
502 if (fade_type == FADE_TYPE_FADE_OUT)
504 if (fade_type_skip != FADE_TYPE_NONE)
506 /* skip all fade operations until specified fade operation */
507 if (fade_type & fade_type_skip)
508 fade_type_skip = FADE_TYPE_NONE;
514 FadeCrossSaveBackbuffer();
517 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
520 FadeCrossSaveBackbuffer();
527 redraw_mask |= fade_mask;
529 if (fade_type == FADE_TYPE_SKIP)
531 fade_type_skip = fade_mode;
536 fade_delay = fading.fade_delay;
537 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
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;
548 if (global.autoplay_leveldir)
553 if (fade_mask == REDRAW_FIELD)
558 height = FADE_SYSIZE;
560 if (border.draw_masked_when_fading)
561 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
563 DrawMaskedBorder_FIELD(); /* draw once */
565 else /* REDRAW_ALL */
573 if (!setup.fade_screens ||
575 fading.fade_mode == FADE_MODE_NONE)
577 if (fade_mode == FADE_MODE_FADE_OUT)
580 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
582 redraw_mask &= ~fade_mask;
587 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
588 draw_border_function);
590 if (fade_type == FADE_TYPE_FADE_OUT)
591 FadeCrossRestoreBackbuffer();
593 redraw_mask &= ~fade_mask;
596 void FadeIn(int fade_mask)
598 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
599 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
601 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
605 FADE_SXSIZE = FULL_SXSIZE;
606 FADE_SYSIZE = FULL_SYSIZE;
609 void FadeOut(int fade_mask)
611 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
612 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
614 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
616 global.border_status = game_status;
619 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
621 static struct TitleFadingInfo fading_leave_stored;
624 fading_leave_stored = fading_leave;
626 fading = fading_leave_stored;
629 void FadeSetEnterMenu()
631 fading = menu.enter_menu;
633 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
636 void FadeSetLeaveMenu()
638 fading = menu.leave_menu;
640 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
643 void FadeSetEnterScreen()
645 fading = menu.enter_screen[game_status];
647 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
650 void FadeSetNextScreen()
652 fading = menu.next_screen;
654 // (do not overwrite fade mode set by FadeSetEnterScreen)
655 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
658 void FadeSetLeaveScreen()
660 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
663 void FadeSetFromType(int type)
665 if (type & TYPE_ENTER_SCREEN)
666 FadeSetEnterScreen();
667 else if (type & TYPE_ENTER)
669 else if (type & TYPE_LEAVE)
673 void FadeSetDisabled()
675 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
677 fading = fading_none;
680 void FadeSkipNextFadeIn()
682 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
685 void FadeSkipNextFadeOut()
687 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
690 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
692 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
694 return (graphic == IMG_UNDEFINED ? NULL :
695 graphic_info[graphic].bitmap != NULL || redefined ?
696 graphic_info[graphic].bitmap :
697 graphic_info[default_graphic].bitmap);
700 Bitmap *getBackgroundBitmap(int graphic)
702 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
705 Bitmap *getGlobalBorderBitmap(int graphic)
707 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
710 Bitmap *getGlobalBorderBitmapFromGameStatus()
713 (game_status == GAME_MODE_MAIN ||
714 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
715 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
716 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
717 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
720 return getGlobalBorderBitmap(graphic);
723 void SetWindowBackgroundImageIfDefined(int graphic)
725 if (graphic_info[graphic].bitmap)
726 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
729 void SetMainBackgroundImageIfDefined(int graphic)
731 if (graphic_info[graphic].bitmap)
732 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
735 void SetDoorBackgroundImageIfDefined(int graphic)
737 if (graphic_info[graphic].bitmap)
738 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
741 void SetWindowBackgroundImage(int graphic)
743 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
746 void SetMainBackgroundImage(int graphic)
748 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
751 void SetDoorBackgroundImage(int graphic)
753 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
756 void SetPanelBackground()
758 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
760 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
761 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
763 SetDoorBackgroundBitmap(bitmap_db_panel);
766 void DrawBackground(int x, int y, int width, int height)
768 /* "drawto" might still point to playfield buffer here (hall of fame) */
769 ClearRectangleOnBackground(backbuffer, x, y, width, height);
771 if (IN_GFX_FIELD_FULL(x, y))
772 redraw_mask |= REDRAW_FIELD;
773 else if (IN_GFX_DOOR_1(x, y))
774 redraw_mask |= REDRAW_DOOR_1;
775 else if (IN_GFX_DOOR_2(x, y))
776 redraw_mask |= REDRAW_DOOR_2;
777 else if (IN_GFX_DOOR_3(x, y))
778 redraw_mask |= REDRAW_DOOR_3;
781 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
783 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
785 if (font->bitmap == NULL)
788 DrawBackground(x, y, width, height);
791 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
793 struct GraphicInfo *g = &graphic_info[graphic];
795 if (g->bitmap == NULL)
798 DrawBackground(x, y, width, height);
801 static int game_status_last = -1;
802 static Bitmap *global_border_bitmap_last = NULL;
803 static Bitmap *global_border_bitmap = NULL;
804 static int real_sx_last = -1, real_sy_last = -1;
805 static int full_sxsize_last = -1, full_sysize_last = -1;
806 static int dx_last = -1, dy_last = -1;
807 static int dxsize_last = -1, dysize_last = -1;
808 static int vx_last = -1, vy_last = -1;
809 static int vxsize_last = -1, vysize_last = -1;
811 boolean CheckIfGlobalBorderHasChanged()
813 // if game status has not changed, global border has not changed either
814 if (game_status == game_status_last)
817 // determine and store new global border bitmap for current game status
818 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
820 return (global_border_bitmap_last != global_border_bitmap);
823 boolean CheckIfGlobalBorderRedrawIsNeeded()
825 // if game status has not changed, nothing has to be redrawn
826 if (game_status == game_status_last)
829 // redraw if global screen border has changed
830 if (CheckIfGlobalBorderHasChanged())
833 // redraw if position or size of playfield area has changed
834 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
835 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
838 // redraw if position or size of door area has changed
839 if (dx_last != DX || dy_last != DY ||
840 dxsize_last != DXSIZE || dysize_last != DYSIZE)
843 // redraw if position or size of tape area has changed
844 if (vx_last != VX || vy_last != VY ||
845 vxsize_last != VXSIZE || vysize_last != VYSIZE)
851 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
854 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
856 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
859 void RedrawGlobalBorder()
861 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
863 RedrawGlobalBorderFromBitmap(bitmap);
865 redraw_mask = REDRAW_ALL;
868 static void RedrawGlobalBorderIfNeeded()
870 if (game_status == game_status_last)
873 // copy current draw buffer to later copy back areas that have not changed
874 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
876 if (CheckIfGlobalBorderRedrawIsNeeded())
878 // redraw global screen border (or clear, if defined to be empty)
879 RedrawGlobalBorderFromBitmap(global_border_bitmap);
881 // copy previous playfield and door areas, if they are defined on both
882 // previous and current screen and if they still have the same size
884 if (real_sx_last != -1 && real_sy_last != -1 &&
885 REAL_SX != -1 && REAL_SY != -1 &&
886 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
887 BlitBitmap(bitmap_db_store, backbuffer,
888 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
891 if (dx_last != -1 && dy_last != -1 &&
892 DX != -1 && DY != -1 &&
893 dxsize_last == DXSIZE && dysize_last == DYSIZE)
894 BlitBitmap(bitmap_db_store, backbuffer,
895 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
897 if (vx_last != -1 && vy_last != -1 &&
898 VX != -1 && VY != -1 &&
899 vxsize_last == VXSIZE && vysize_last == VYSIZE)
900 BlitBitmap(bitmap_db_store, backbuffer,
901 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
903 redraw_mask = REDRAW_ALL;
906 game_status_last = game_status;
908 global_border_bitmap_last = global_border_bitmap;
910 real_sx_last = REAL_SX;
911 real_sy_last = REAL_SY;
912 full_sxsize_last = FULL_SXSIZE;
913 full_sysize_last = FULL_SYSIZE;
916 dxsize_last = DXSIZE;
917 dysize_last = DYSIZE;
920 vxsize_last = VXSIZE;
921 vysize_last = VYSIZE;
926 RedrawGlobalBorderIfNeeded();
928 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
929 /* (when entering hall of fame after playing) */
930 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
932 /* !!! maybe this should be done before clearing the background !!! */
933 if (game_status == GAME_MODE_PLAYING)
935 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
936 SetDrawtoField(DRAW_FIELDBUFFER);
940 SetDrawtoField(DRAW_BACKBUFFER);
944 void MarkTileDirty(int x, int y)
946 redraw_mask |= REDRAW_FIELD;
949 void SetBorderElement()
953 BorderElement = EL_EMPTY;
955 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
957 for (x = 0; x < lev_fieldx; x++)
959 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
960 BorderElement = EL_STEELWALL;
962 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
968 void FloodFillLevel(int from_x, int from_y, int fill_element,
969 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
970 int max_fieldx, int max_fieldy)
974 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
975 static int safety = 0;
977 /* check if starting field still has the desired content */
978 if (field[from_x][from_y] == fill_element)
983 if (safety > max_fieldx * max_fieldy)
984 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
986 old_element = field[from_x][from_y];
987 field[from_x][from_y] = fill_element;
989 for (i = 0; i < 4; i++)
991 x = from_x + check[i][0];
992 y = from_y + check[i][1];
994 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
995 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1001 void SetRandomAnimationValue(int x, int y)
1003 gfx.anim_random_frame = GfxRandom[x][y];
1006 int getGraphicAnimationFrame(int graphic, int sync_frame)
1008 /* animation synchronized with global frame counter, not move position */
1009 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1010 sync_frame = FrameCounter;
1012 return getAnimationFrame(graphic_info[graphic].anim_frames,
1013 graphic_info[graphic].anim_delay,
1014 graphic_info[graphic].anim_mode,
1015 graphic_info[graphic].anim_start_frame,
1019 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1020 Bitmap **bitmap, int *x, int *y,
1021 boolean get_backside)
1023 struct GraphicInfo *g = &graphic_info[graphic];
1024 Bitmap *src_bitmap = g->bitmap;
1025 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1026 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1027 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1029 // if no in-game graphics defined, always use standard graphic size
1030 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1031 tilesize = TILESIZE;
1033 if (tilesize == gfx.standard_tile_size)
1034 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1035 else if (tilesize == game.tile_size)
1036 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1038 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1040 if (g->offset_y == 0) /* frames are ordered horizontally */
1042 int max_width = g->anim_frames_per_line * g->width;
1043 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1045 src_x = pos % max_width;
1046 src_y = src_y % g->height + pos / max_width * g->height;
1048 else if (g->offset_x == 0) /* frames are ordered vertically */
1050 int max_height = g->anim_frames_per_line * g->height;
1051 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1053 src_x = src_x % g->width + pos / max_height * g->width;
1054 src_y = pos % max_height;
1056 else /* frames are ordered diagonally */
1058 src_x = src_x + frame * g->offset_x;
1059 src_y = src_y + frame * g->offset_y;
1062 *bitmap = src_bitmap;
1063 *x = src_x * tilesize / TILESIZE;
1064 *y = src_y * tilesize / TILESIZE;
1067 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1068 int *x, int *y, boolean get_backside)
1070 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1074 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1075 Bitmap **bitmap, int *x, int *y)
1077 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1080 void getFixedGraphicSource(int graphic, int frame,
1081 Bitmap **bitmap, int *x, int *y)
1083 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1086 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1088 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1091 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1092 int *x, int *y, boolean get_backside)
1094 struct GraphicInfo *g = &graphic_info[graphic];
1095 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1096 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1098 if (TILESIZE_VAR != TILESIZE)
1099 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1102 *bitmap = g->bitmap;
1104 if (g->offset_y == 0) /* frames are ordered horizontally */
1106 int max_width = g->anim_frames_per_line * g->width;
1107 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1109 *x = pos % max_width;
1110 *y = src_y % g->height + pos / max_width * g->height;
1112 else if (g->offset_x == 0) /* frames are ordered vertically */
1114 int max_height = g->anim_frames_per_line * g->height;
1115 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1117 *x = src_x % g->width + pos / max_height * g->width;
1118 *y = pos % max_height;
1120 else /* frames are ordered diagonally */
1122 *x = src_x + frame * g->offset_x;
1123 *y = src_y + frame * g->offset_y;
1127 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1129 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1132 void DrawGraphic(int x, int y, int graphic, int frame)
1135 if (!IN_SCR_FIELD(x, y))
1137 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1138 printf("DrawGraphic(): This should never happen!\n");
1143 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1146 MarkTileDirty(x, y);
1149 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1152 if (!IN_SCR_FIELD(x, y))
1154 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1155 printf("DrawGraphic(): This should never happen!\n");
1160 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1162 MarkTileDirty(x, y);
1165 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1171 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1173 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1176 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1182 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1183 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1186 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1189 if (!IN_SCR_FIELD(x, y))
1191 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1192 printf("DrawGraphicThruMask(): This should never happen!\n");
1197 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1200 MarkTileDirty(x, y);
1203 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1206 if (!IN_SCR_FIELD(x, y))
1208 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1209 printf("DrawGraphicThruMask(): This should never happen!\n");
1214 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1216 MarkTileDirty(x, y);
1219 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1225 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1227 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1231 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1232 int graphic, int frame)
1234 struct GraphicInfo *g = &graphic_info[graphic];
1238 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1240 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1244 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1246 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1248 MarkTileDirty(x / tilesize, y / tilesize);
1251 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1257 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1258 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1261 void DrawMiniGraphic(int x, int y, int graphic)
1263 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1264 MarkTileDirty(x / 2, y / 2);
1267 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1272 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1273 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1276 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1277 int graphic, int frame,
1278 int cut_mode, int mask_mode)
1283 int width = TILEX, height = TILEY;
1286 if (dx || dy) /* shifted graphic */
1288 if (x < BX1) /* object enters playfield from the left */
1295 else if (x > BX2) /* object enters playfield from the right */
1301 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1307 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1309 else if (dx) /* general horizontal movement */
1310 MarkTileDirty(x + SIGN(dx), y);
1312 if (y < BY1) /* object enters playfield from the top */
1314 if (cut_mode==CUT_BELOW) /* object completely above top border */
1322 else if (y > BY2) /* object enters playfield from the bottom */
1328 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1334 else if (dy > 0 && cut_mode == CUT_ABOVE)
1336 if (y == BY2) /* object completely above bottom border */
1342 MarkTileDirty(x, y + 1);
1343 } /* object leaves playfield to the bottom */
1344 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1346 else if (dy) /* general vertical movement */
1347 MarkTileDirty(x, y + SIGN(dy));
1351 if (!IN_SCR_FIELD(x, y))
1353 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1354 printf("DrawGraphicShifted(): This should never happen!\n");
1359 width = width * TILESIZE_VAR / TILESIZE;
1360 height = height * TILESIZE_VAR / TILESIZE;
1361 cx = cx * TILESIZE_VAR / TILESIZE;
1362 cy = cy * TILESIZE_VAR / TILESIZE;
1363 dx = dx * TILESIZE_VAR / TILESIZE;
1364 dy = dy * TILESIZE_VAR / TILESIZE;
1366 if (width > 0 && height > 0)
1368 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1373 dst_x = FX + x * TILEX_VAR + dx;
1374 dst_y = FY + y * TILEY_VAR + dy;
1376 if (mask_mode == USE_MASKING)
1377 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1380 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1383 MarkTileDirty(x, y);
1387 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1388 int graphic, int frame,
1389 int cut_mode, int mask_mode)
1394 int width = TILEX_VAR, height = TILEY_VAR;
1397 int x2 = x + SIGN(dx);
1398 int y2 = y + SIGN(dy);
1400 /* movement with two-tile animations must be sync'ed with movement position,
1401 not with current GfxFrame (which can be higher when using slow movement) */
1402 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1403 int anim_frames = graphic_info[graphic].anim_frames;
1405 /* (we also need anim_delay here for movement animations with less frames) */
1406 int anim_delay = graphic_info[graphic].anim_delay;
1407 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1409 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1410 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1412 /* re-calculate animation frame for two-tile movement animation */
1413 frame = getGraphicAnimationFrame(graphic, sync_frame);
1415 /* check if movement start graphic inside screen area and should be drawn */
1416 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1418 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1420 dst_x = FX + x1 * TILEX_VAR;
1421 dst_y = FY + y1 * TILEY_VAR;
1423 if (mask_mode == USE_MASKING)
1424 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1427 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1430 MarkTileDirty(x1, y1);
1433 /* check if movement end graphic inside screen area and should be drawn */
1434 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1436 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1438 dst_x = FX + x2 * TILEX_VAR;
1439 dst_y = FY + y2 * TILEY_VAR;
1441 if (mask_mode == USE_MASKING)
1442 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1445 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1448 MarkTileDirty(x2, y2);
1452 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1453 int graphic, int frame,
1454 int cut_mode, int mask_mode)
1458 DrawGraphic(x, y, graphic, frame);
1463 if (graphic_info[graphic].double_movement) /* EM style movement images */
1464 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1466 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1469 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1470 int frame, int cut_mode)
1472 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1475 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1476 int cut_mode, int mask_mode)
1478 int lx = LEVELX(x), ly = LEVELY(y);
1482 if (IN_LEV_FIELD(lx, ly))
1484 SetRandomAnimationValue(lx, ly);
1486 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1487 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1489 /* do not use double (EM style) movement graphic when not moving */
1490 if (graphic_info[graphic].double_movement && !dx && !dy)
1492 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1493 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1496 else /* border element */
1498 graphic = el2img(element);
1499 frame = getGraphicAnimationFrame(graphic, -1);
1502 if (element == EL_EXPANDABLE_WALL)
1504 boolean left_stopped = FALSE, right_stopped = FALSE;
1506 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1507 left_stopped = TRUE;
1508 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1509 right_stopped = TRUE;
1511 if (left_stopped && right_stopped)
1513 else if (left_stopped)
1515 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1516 frame = graphic_info[graphic].anim_frames - 1;
1518 else if (right_stopped)
1520 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1521 frame = graphic_info[graphic].anim_frames - 1;
1526 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1527 else if (mask_mode == USE_MASKING)
1528 DrawGraphicThruMask(x, y, graphic, frame);
1530 DrawGraphic(x, y, graphic, frame);
1533 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1534 int cut_mode, int mask_mode)
1536 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1537 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1538 cut_mode, mask_mode);
1541 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1544 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1547 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1550 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1553 void DrawLevelElementThruMask(int x, int y, int element)
1555 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1558 void DrawLevelFieldThruMask(int x, int y)
1560 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1563 /* !!! implementation of quicksand is totally broken !!! */
1564 #define IS_CRUMBLED_TILE(x, y, e) \
1565 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1566 !IS_MOVING(x, y) || \
1567 (e) == EL_QUICKSAND_EMPTYING || \
1568 (e) == EL_QUICKSAND_FAST_EMPTYING))
1570 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1575 int width, height, cx, cy;
1576 int sx = SCREENX(x), sy = SCREENY(y);
1577 int crumbled_border_size = graphic_info[graphic].border_size;
1580 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1582 for (i = 1; i < 4; i++)
1584 int dxx = (i & 1 ? dx : 0);
1585 int dyy = (i & 2 ? dy : 0);
1588 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1591 /* check if neighbour field is of same crumble type */
1592 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1593 graphic_info[graphic].class ==
1594 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1596 /* return if check prevents inner corner */
1597 if (same == (dxx == dx && dyy == dy))
1601 /* if we reach this point, we have an inner corner */
1603 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1605 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1606 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1607 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1608 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1610 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1611 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1614 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1619 int width, height, bx, by, cx, cy;
1620 int sx = SCREENX(x), sy = SCREENY(y);
1621 int crumbled_border_size = graphic_info[graphic].border_size;
1622 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1623 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1626 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1628 /* draw simple, sloppy, non-corner-accurate crumbled border */
1630 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1631 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1632 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1633 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1635 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1636 FX + sx * TILEX_VAR + cx,
1637 FY + sy * TILEY_VAR + cy);
1639 /* (remaining middle border part must be at least as big as corner part) */
1640 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1641 crumbled_border_size >= TILESIZE / 3)
1644 /* correct corners of crumbled border, if needed */
1646 for (i = -1; i <= 1; i += 2)
1648 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1649 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1650 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1653 /* check if neighbour field is of same crumble type */
1654 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1655 graphic_info[graphic].class ==
1656 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1658 /* no crumbled corner, but continued crumbled border */
1660 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1661 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1662 int b1 = (i == 1 ? crumbled_border_size_var :
1663 TILESIZE_VAR - 2 * crumbled_border_size_var);
1665 width = crumbled_border_size_var;
1666 height = crumbled_border_size_var;
1668 if (dir == 1 || dir == 2)
1683 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1685 FX + sx * TILEX_VAR + cx,
1686 FY + sy * TILEY_VAR + cy);
1691 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1693 int sx = SCREENX(x), sy = SCREENY(y);
1696 static int xy[4][2] =
1704 if (!IN_LEV_FIELD(x, y))
1707 element = TILE_GFX_ELEMENT(x, y);
1709 /* crumble field itself */
1710 if (IS_CRUMBLED_TILE(x, y, element))
1712 if (!IN_SCR_FIELD(sx, sy))
1715 for (i = 0; i < 4; i++)
1717 int xx = x + xy[i][0];
1718 int yy = y + xy[i][1];
1720 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1723 /* check if neighbour field is of same crumble type */
1724 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1725 graphic_info[graphic].class ==
1726 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1729 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1732 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1733 graphic_info[graphic].anim_frames == 2)
1735 for (i = 0; i < 4; i++)
1737 int dx = (i & 1 ? +1 : -1);
1738 int dy = (i & 2 ? +1 : -1);
1740 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1744 MarkTileDirty(sx, sy);
1746 else /* center field not crumbled -- crumble neighbour fields */
1748 for (i = 0; i < 4; i++)
1750 int xx = x + xy[i][0];
1751 int yy = y + xy[i][1];
1752 int sxx = sx + xy[i][0];
1753 int syy = sy + xy[i][1];
1755 if (!IN_LEV_FIELD(xx, yy) ||
1756 !IN_SCR_FIELD(sxx, syy))
1759 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1762 element = TILE_GFX_ELEMENT(xx, yy);
1764 if (!IS_CRUMBLED_TILE(xx, yy, element))
1767 graphic = el_act2crm(element, ACTION_DEFAULT);
1769 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1771 MarkTileDirty(sxx, syy);
1776 void DrawLevelFieldCrumbled(int x, int y)
1780 if (!IN_LEV_FIELD(x, y))
1783 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1784 GfxElement[x][y] != EL_UNDEFINED &&
1785 GFX_CRUMBLED(GfxElement[x][y]))
1787 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1792 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1794 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1797 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1800 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1801 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1802 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1803 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1804 int sx = SCREENX(x), sy = SCREENY(y);
1806 DrawGraphic(sx, sy, graphic1, frame1);
1807 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1810 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1812 int sx = SCREENX(x), sy = SCREENY(y);
1813 static int xy[4][2] =
1822 for (i = 0; i < 4; i++)
1824 int xx = x + xy[i][0];
1825 int yy = y + xy[i][1];
1826 int sxx = sx + xy[i][0];
1827 int syy = sy + xy[i][1];
1829 if (!IN_LEV_FIELD(xx, yy) ||
1830 !IN_SCR_FIELD(sxx, syy) ||
1831 !GFX_CRUMBLED(Feld[xx][yy]) ||
1835 DrawLevelField(xx, yy);
1839 static int getBorderElement(int x, int y)
1843 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1844 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1845 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1846 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1847 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1848 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1849 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1851 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1852 int steel_position = (x == -1 && y == -1 ? 0 :
1853 x == lev_fieldx && y == -1 ? 1 :
1854 x == -1 && y == lev_fieldy ? 2 :
1855 x == lev_fieldx && y == lev_fieldy ? 3 :
1856 x == -1 || x == lev_fieldx ? 4 :
1857 y == -1 || y == lev_fieldy ? 5 : 6);
1859 return border[steel_position][steel_type];
1862 void DrawScreenElement(int x, int y, int element)
1864 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1865 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1868 void DrawLevelElement(int x, int y, int element)
1870 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1871 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1874 void DrawScreenField(int x, int y)
1876 int lx = LEVELX(x), ly = LEVELY(y);
1877 int element, content;
1879 if (!IN_LEV_FIELD(lx, ly))
1881 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1884 element = getBorderElement(lx, ly);
1886 DrawScreenElement(x, y, element);
1891 element = Feld[lx][ly];
1892 content = Store[lx][ly];
1894 if (IS_MOVING(lx, ly))
1896 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1897 boolean cut_mode = NO_CUTTING;
1899 if (element == EL_QUICKSAND_EMPTYING ||
1900 element == EL_QUICKSAND_FAST_EMPTYING ||
1901 element == EL_MAGIC_WALL_EMPTYING ||
1902 element == EL_BD_MAGIC_WALL_EMPTYING ||
1903 element == EL_DC_MAGIC_WALL_EMPTYING ||
1904 element == EL_AMOEBA_DROPPING)
1905 cut_mode = CUT_ABOVE;
1906 else if (element == EL_QUICKSAND_FILLING ||
1907 element == EL_QUICKSAND_FAST_FILLING ||
1908 element == EL_MAGIC_WALL_FILLING ||
1909 element == EL_BD_MAGIC_WALL_FILLING ||
1910 element == EL_DC_MAGIC_WALL_FILLING)
1911 cut_mode = CUT_BELOW;
1913 if (cut_mode == CUT_ABOVE)
1914 DrawScreenElement(x, y, element);
1916 DrawScreenElement(x, y, EL_EMPTY);
1919 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1920 else if (cut_mode == NO_CUTTING)
1921 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1924 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1926 if (cut_mode == CUT_BELOW &&
1927 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1928 DrawLevelElement(lx, ly + 1, element);
1931 if (content == EL_ACID)
1933 int dir = MovDir[lx][ly];
1934 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1935 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1937 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1940 else if (IS_BLOCKED(lx, ly))
1945 boolean cut_mode = NO_CUTTING;
1946 int element_old, content_old;
1948 Blocked2Moving(lx, ly, &oldx, &oldy);
1951 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1952 MovDir[oldx][oldy] == MV_RIGHT);
1954 element_old = Feld[oldx][oldy];
1955 content_old = Store[oldx][oldy];
1957 if (element_old == EL_QUICKSAND_EMPTYING ||
1958 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1959 element_old == EL_MAGIC_WALL_EMPTYING ||
1960 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1961 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1962 element_old == EL_AMOEBA_DROPPING)
1963 cut_mode = CUT_ABOVE;
1965 DrawScreenElement(x, y, EL_EMPTY);
1968 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1970 else if (cut_mode == NO_CUTTING)
1971 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1974 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1977 else if (IS_DRAWABLE(element))
1978 DrawScreenElement(x, y, element);
1980 DrawScreenElement(x, y, EL_EMPTY);
1983 void DrawLevelField(int x, int y)
1985 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1986 DrawScreenField(SCREENX(x), SCREENY(y));
1987 else if (IS_MOVING(x, y))
1991 Moving2Blocked(x, y, &newx, &newy);
1992 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1993 DrawScreenField(SCREENX(newx), SCREENY(newy));
1995 else if (IS_BLOCKED(x, y))
1999 Blocked2Moving(x, y, &oldx, &oldy);
2000 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2001 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2005 void DrawSizedElement(int x, int y, int element, int tilesize)
2009 graphic = el2edimg(element);
2010 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2013 void DrawMiniElement(int x, int y, int element)
2017 graphic = el2edimg(element);
2018 DrawMiniGraphic(x, y, graphic);
2021 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2024 int x = sx + scroll_x, y = sy + scroll_y;
2026 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2027 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2028 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2029 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2031 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2034 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2036 int x = sx + scroll_x, y = sy + scroll_y;
2038 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2039 DrawMiniElement(sx, sy, EL_EMPTY);
2040 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2041 DrawMiniElement(sx, sy, Feld[x][y]);
2043 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2046 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2047 int x, int y, int xsize, int ysize,
2048 int tile_width, int tile_height)
2052 int dst_x = startx + x * tile_width;
2053 int dst_y = starty + y * tile_height;
2054 int width = graphic_info[graphic].width;
2055 int height = graphic_info[graphic].height;
2056 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2057 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2058 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2059 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2060 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2061 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2062 boolean draw_masked = graphic_info[graphic].draw_masked;
2064 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2066 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2068 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2072 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2073 inner_sx + (x - 1) * tile_width % inner_width);
2074 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2075 inner_sy + (y - 1) * tile_height % inner_height);
2078 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2081 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2085 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2086 int x, int y, int xsize, int ysize, int font_nr)
2088 int font_width = getFontWidth(font_nr);
2089 int font_height = getFontHeight(font_nr);
2091 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2092 font_width, font_height);
2095 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2097 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2098 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2099 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2100 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2101 boolean no_delay = (tape.warp_forward);
2102 unsigned int anim_delay = 0;
2103 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2104 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2105 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2106 int font_width = getFontWidth(font_nr);
2107 int font_height = getFontHeight(font_nr);
2108 int max_xsize = level.envelope[envelope_nr].xsize;
2109 int max_ysize = level.envelope[envelope_nr].ysize;
2110 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2111 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2112 int xend = max_xsize;
2113 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2114 int xstep = (xstart < xend ? 1 : 0);
2115 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2117 int end = MAX(xend - xstart, yend - ystart);
2120 for (i = start; i <= end; i++)
2122 int last_frame = end; // last frame of this "for" loop
2123 int x = xstart + i * xstep;
2124 int y = ystart + i * ystep;
2125 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2126 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2127 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2128 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2131 SetDrawtoField(DRAW_FIELDBUFFER);
2133 BlitScreenToBitmap(backbuffer);
2135 SetDrawtoField(DRAW_BACKBUFFER);
2137 for (yy = 0; yy < ysize; yy++)
2138 for (xx = 0; xx < xsize; xx++)
2139 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2141 DrawTextBuffer(sx + font_width, sy + font_height,
2142 level.envelope[envelope_nr].text, font_nr, max_xsize,
2143 xsize - 2, ysize - 2, 0, mask_mode,
2144 level.envelope[envelope_nr].autowrap,
2145 level.envelope[envelope_nr].centered, FALSE);
2147 redraw_mask |= REDRAW_FIELD;
2150 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2154 void ShowEnvelope(int envelope_nr)
2156 int element = EL_ENVELOPE_1 + envelope_nr;
2157 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2158 int sound_opening = element_info[element].sound[ACTION_OPENING];
2159 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2160 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2161 boolean no_delay = (tape.warp_forward);
2162 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2163 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2164 int anim_mode = graphic_info[graphic].anim_mode;
2165 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2166 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2168 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2170 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2172 if (anim_mode == ANIM_DEFAULT)
2173 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2175 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2178 Delay(wait_delay_value);
2180 WaitForEventToContinue();
2182 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2184 if (anim_mode != ANIM_NONE)
2185 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2187 if (anim_mode == ANIM_DEFAULT)
2188 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2190 game.envelope_active = FALSE;
2192 SetDrawtoField(DRAW_FIELDBUFFER);
2194 redraw_mask |= REDRAW_FIELD;
2198 static void setRequestBasePosition(int *x, int *y)
2200 int sx_base, sy_base;
2202 if (request.x != -1)
2203 sx_base = request.x;
2204 else if (request.align == ALIGN_LEFT)
2206 else if (request.align == ALIGN_RIGHT)
2207 sx_base = SX + SXSIZE;
2209 sx_base = SX + SXSIZE / 2;
2211 if (request.y != -1)
2212 sy_base = request.y;
2213 else if (request.valign == VALIGN_TOP)
2215 else if (request.valign == VALIGN_BOTTOM)
2216 sy_base = SY + SYSIZE;
2218 sy_base = SY + SYSIZE / 2;
2224 static void setRequestPositionExt(int *x, int *y, int width, int height,
2225 boolean add_border_size)
2227 int border_size = request.border_size;
2228 int sx_base, sy_base;
2231 setRequestBasePosition(&sx_base, &sy_base);
2233 if (request.align == ALIGN_LEFT)
2235 else if (request.align == ALIGN_RIGHT)
2236 sx = sx_base - width;
2238 sx = sx_base - width / 2;
2240 if (request.valign == VALIGN_TOP)
2242 else if (request.valign == VALIGN_BOTTOM)
2243 sy = sy_base - height;
2245 sy = sy_base - height / 2;
2247 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2248 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2250 if (add_border_size)
2260 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2262 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2265 void DrawEnvelopeRequest(char *text)
2267 int last_game_status = game_status; /* save current game status */
2268 char *text_final = text;
2269 char *text_door_style = NULL;
2270 int graphic = IMG_BACKGROUND_REQUEST;
2271 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2272 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2273 int font_nr = FONT_REQUEST;
2274 int font_width = getFontWidth(font_nr);
2275 int font_height = getFontHeight(font_nr);
2276 int border_size = request.border_size;
2277 int line_spacing = request.line_spacing;
2278 int line_height = font_height + line_spacing;
2279 int max_text_width = request.width - 2 * border_size;
2280 int max_text_height = request.height - 2 * border_size;
2281 int line_length = max_text_width / font_width;
2282 int max_lines = max_text_height / line_height;
2283 int text_width = line_length * font_width;
2284 int width = request.width;
2285 int height = request.height;
2286 int tile_size = MAX(request.step_offset, 1);
2287 int x_steps = width / tile_size;
2288 int y_steps = height / tile_size;
2289 int sx_offset = border_size;
2290 int sy_offset = border_size;
2294 if (request.centered)
2295 sx_offset = (request.width - text_width) / 2;
2297 if (request.wrap_single_words)
2299 char *src_text_ptr, *dst_text_ptr;
2301 text_door_style = checked_malloc(2 * strlen(text) + 1);
2303 src_text_ptr = text;
2304 dst_text_ptr = text_door_style;
2306 while (*src_text_ptr)
2308 if (*src_text_ptr == ' ' ||
2309 *src_text_ptr == '?' ||
2310 *src_text_ptr == '!')
2311 *dst_text_ptr++ = '\n';
2313 if (*src_text_ptr != ' ')
2314 *dst_text_ptr++ = *src_text_ptr;
2319 *dst_text_ptr = '\0';
2321 text_final = text_door_style;
2324 setRequestPosition(&sx, &sy, FALSE);
2326 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2328 for (y = 0; y < y_steps; y++)
2329 for (x = 0; x < x_steps; x++)
2330 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2331 x, y, x_steps, y_steps,
2332 tile_size, tile_size);
2334 /* force DOOR font inside door area */
2335 game_status = GAME_MODE_PSEUDO_DOOR;
2337 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2338 line_length, -1, max_lines, line_spacing, mask_mode,
2339 request.autowrap, request.centered, FALSE);
2341 game_status = last_game_status; /* restore current game status */
2343 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2344 RedrawGadget(tool_gadget[i]);
2346 // store readily prepared envelope request for later use when animating
2347 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2349 if (text_door_style)
2350 free(text_door_style);
2353 void AnimateEnvelopeRequest(int anim_mode, int action)
2355 int graphic = IMG_BACKGROUND_REQUEST;
2356 boolean draw_masked = graphic_info[graphic].draw_masked;
2357 int delay_value_normal = request.step_delay;
2358 int delay_value_fast = delay_value_normal / 2;
2359 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2360 boolean no_delay = (tape.warp_forward);
2361 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2362 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2363 unsigned int anim_delay = 0;
2365 int tile_size = MAX(request.step_offset, 1);
2366 int max_xsize = request.width / tile_size;
2367 int max_ysize = request.height / tile_size;
2368 int max_xsize_inner = max_xsize - 2;
2369 int max_ysize_inner = max_ysize - 2;
2371 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2372 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2373 int xend = max_xsize_inner;
2374 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2375 int xstep = (xstart < xend ? 1 : 0);
2376 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2378 int end = MAX(xend - xstart, yend - ystart);
2381 if (setup.quick_doors)
2389 if (action == ACTION_OPENING)
2390 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2391 else if (action == ACTION_CLOSING)
2392 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2395 for (i = start; i <= end; i++)
2397 int last_frame = end; // last frame of this "for" loop
2398 int x = xstart + i * xstep;
2399 int y = ystart + i * ystep;
2400 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2401 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2402 int xsize_size_left = (xsize - 1) * tile_size;
2403 int ysize_size_top = (ysize - 1) * tile_size;
2404 int max_xsize_pos = (max_xsize - 1) * tile_size;
2405 int max_ysize_pos = (max_ysize - 1) * tile_size;
2406 int width = xsize * tile_size;
2407 int height = ysize * tile_size;
2412 setRequestPosition(&src_x, &src_y, FALSE);
2413 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2415 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2417 for (yy = 0; yy < 2; yy++)
2419 for (xx = 0; xx < 2; xx++)
2421 int src_xx = src_x + xx * max_xsize_pos;
2422 int src_yy = src_y + yy * max_ysize_pos;
2423 int dst_xx = dst_x + xx * xsize_size_left;
2424 int dst_yy = dst_y + yy * ysize_size_top;
2425 int xx_size = (xx ? tile_size : xsize_size_left);
2426 int yy_size = (yy ? tile_size : ysize_size_top);
2429 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2430 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2432 BlitBitmap(bitmap_db_cross, backbuffer,
2433 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2437 redraw_mask |= REDRAW_FIELD;
2442 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2446 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2448 int graphic = IMG_BACKGROUND_REQUEST;
2449 int sound_opening = SND_REQUEST_OPENING;
2450 int sound_closing = SND_REQUEST_CLOSING;
2451 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2452 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2453 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2454 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2455 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2457 if (game_status == GAME_MODE_PLAYING)
2458 BlitScreenToBitmap(backbuffer);
2460 SetDrawtoField(DRAW_BACKBUFFER);
2462 // SetDrawBackgroundMask(REDRAW_NONE);
2464 if (action == ACTION_OPENING)
2466 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2468 if (req_state & REQ_ASK)
2470 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2471 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2473 else if (req_state & REQ_CONFIRM)
2475 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2477 else if (req_state & REQ_PLAYER)
2479 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2480 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2481 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2482 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2485 DrawEnvelopeRequest(text);
2487 if (game_status != GAME_MODE_MAIN)
2491 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2493 if (action == ACTION_OPENING)
2495 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2497 if (anim_mode == ANIM_DEFAULT)
2498 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2500 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2504 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2506 if (anim_mode != ANIM_NONE)
2507 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2509 if (anim_mode == ANIM_DEFAULT)
2510 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2513 game.envelope_active = FALSE;
2515 if (action == ACTION_CLOSING)
2517 if (game_status != GAME_MODE_MAIN)
2520 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2523 // SetDrawBackgroundMask(last_draw_background_mask);
2525 redraw_mask |= REDRAW_FIELD;
2527 if (game_status == GAME_MODE_MAIN)
2532 if (action == ACTION_CLOSING &&
2533 game_status == GAME_MODE_PLAYING &&
2534 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2535 SetDrawtoField(DRAW_FIELDBUFFER);
2538 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2542 int graphic = el2preimg(element);
2544 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2545 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2548 void DrawLevel(int draw_background_mask)
2552 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2553 SetDrawBackgroundMask(draw_background_mask);
2557 for (x = BX1; x <= BX2; x++)
2558 for (y = BY1; y <= BY2; y++)
2559 DrawScreenField(x, y);
2561 redraw_mask |= REDRAW_FIELD;
2564 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2569 for (x = 0; x < size_x; x++)
2570 for (y = 0; y < size_y; y++)
2571 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2573 redraw_mask |= REDRAW_FIELD;
2576 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2580 for (x = 0; x < size_x; x++)
2581 for (y = 0; y < size_y; y++)
2582 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2584 redraw_mask |= REDRAW_FIELD;
2587 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2589 boolean show_level_border = (BorderElement != EL_EMPTY);
2590 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2591 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2592 int tile_size = preview.tile_size;
2593 int preview_width = preview.xsize * tile_size;
2594 int preview_height = preview.ysize * tile_size;
2595 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2596 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2597 int real_preview_width = real_preview_xsize * tile_size;
2598 int real_preview_height = real_preview_ysize * tile_size;
2599 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2600 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2603 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2606 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2608 dst_x += (preview_width - real_preview_width) / 2;
2609 dst_y += (preview_height - real_preview_height) / 2;
2611 for (x = 0; x < real_preview_xsize; x++)
2613 for (y = 0; y < real_preview_ysize; y++)
2615 int lx = from_x + x + (show_level_border ? -1 : 0);
2616 int ly = from_y + y + (show_level_border ? -1 : 0);
2617 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2618 getBorderElement(lx, ly));
2620 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2621 element, tile_size);
2625 redraw_mask |= REDRAW_FIELD;
2628 #define MICROLABEL_EMPTY 0
2629 #define MICROLABEL_LEVEL_NAME 1
2630 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2631 #define MICROLABEL_LEVEL_AUTHOR 3
2632 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2633 #define MICROLABEL_IMPORTED_FROM 5
2634 #define MICROLABEL_IMPORTED_BY_HEAD 6
2635 #define MICROLABEL_IMPORTED_BY 7
2637 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2639 int max_text_width = SXSIZE;
2640 int font_width = getFontWidth(font_nr);
2642 if (pos->align == ALIGN_CENTER)
2643 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2644 else if (pos->align == ALIGN_RIGHT)
2645 max_text_width = pos->x;
2647 max_text_width = SXSIZE - pos->x;
2649 return max_text_width / font_width;
2652 static void DrawPreviewLevelLabelExt(int mode)
2654 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2655 char label_text[MAX_OUTPUT_LINESIZE + 1];
2656 int max_len_label_text;
2657 int font_nr = pos->font;
2660 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2663 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2664 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2665 mode == MICROLABEL_IMPORTED_BY_HEAD)
2666 font_nr = pos->font_alt;
2668 max_len_label_text = getMaxTextLength(pos, font_nr);
2670 if (pos->size != -1)
2671 max_len_label_text = pos->size;
2673 for (i = 0; i < max_len_label_text; i++)
2674 label_text[i] = ' ';
2675 label_text[max_len_label_text] = '\0';
2677 if (strlen(label_text) > 0)
2678 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2681 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2682 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2683 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2684 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2685 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2686 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2687 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2688 max_len_label_text);
2689 label_text[max_len_label_text] = '\0';
2691 if (strlen(label_text) > 0)
2692 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2694 redraw_mask |= REDRAW_FIELD;
2697 static void DrawPreviewLevelExt(boolean restart)
2699 static unsigned int scroll_delay = 0;
2700 static unsigned int label_delay = 0;
2701 static int from_x, from_y, scroll_direction;
2702 static int label_state, label_counter;
2703 unsigned int scroll_delay_value = preview.step_delay;
2704 boolean show_level_border = (BorderElement != EL_EMPTY);
2705 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2706 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2707 int last_game_status = game_status; /* save current game status */
2714 if (preview.anim_mode == ANIM_CENTERED)
2716 if (level_xsize > preview.xsize)
2717 from_x = (level_xsize - preview.xsize) / 2;
2718 if (level_ysize > preview.ysize)
2719 from_y = (level_ysize - preview.ysize) / 2;
2722 from_x += preview.xoffset;
2723 from_y += preview.yoffset;
2725 scroll_direction = MV_RIGHT;
2729 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2730 DrawPreviewLevelLabelExt(label_state);
2732 /* initialize delay counters */
2733 DelayReached(&scroll_delay, 0);
2734 DelayReached(&label_delay, 0);
2736 if (leveldir_current->name)
2738 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2739 char label_text[MAX_OUTPUT_LINESIZE + 1];
2740 int font_nr = pos->font;
2741 int max_len_label_text = getMaxTextLength(pos, font_nr);
2743 if (pos->size != -1)
2744 max_len_label_text = pos->size;
2746 strncpy(label_text, leveldir_current->name, max_len_label_text);
2747 label_text[max_len_label_text] = '\0';
2749 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2750 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2753 game_status = last_game_status; /* restore current game status */
2758 /* scroll preview level, if needed */
2759 if (preview.anim_mode != ANIM_NONE &&
2760 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2761 DelayReached(&scroll_delay, scroll_delay_value))
2763 switch (scroll_direction)
2768 from_x -= preview.step_offset;
2769 from_x = (from_x < 0 ? 0 : from_x);
2772 scroll_direction = MV_UP;
2776 if (from_x < level_xsize - preview.xsize)
2778 from_x += preview.step_offset;
2779 from_x = (from_x > level_xsize - preview.xsize ?
2780 level_xsize - preview.xsize : from_x);
2783 scroll_direction = MV_DOWN;
2789 from_y -= preview.step_offset;
2790 from_y = (from_y < 0 ? 0 : from_y);
2793 scroll_direction = MV_RIGHT;
2797 if (from_y < level_ysize - preview.ysize)
2799 from_y += preview.step_offset;
2800 from_y = (from_y > level_ysize - preview.ysize ?
2801 level_ysize - preview.ysize : from_y);
2804 scroll_direction = MV_LEFT;
2811 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2814 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2815 /* redraw micro level label, if needed */
2816 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2817 !strEqual(level.author, ANONYMOUS_NAME) &&
2818 !strEqual(level.author, leveldir_current->name) &&
2819 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2821 int max_label_counter = 23;
2823 if (leveldir_current->imported_from != NULL &&
2824 strlen(leveldir_current->imported_from) > 0)
2825 max_label_counter += 14;
2826 if (leveldir_current->imported_by != NULL &&
2827 strlen(leveldir_current->imported_by) > 0)
2828 max_label_counter += 14;
2830 label_counter = (label_counter + 1) % max_label_counter;
2831 label_state = (label_counter >= 0 && label_counter <= 7 ?
2832 MICROLABEL_LEVEL_NAME :
2833 label_counter >= 9 && label_counter <= 12 ?
2834 MICROLABEL_LEVEL_AUTHOR_HEAD :
2835 label_counter >= 14 && label_counter <= 21 ?
2836 MICROLABEL_LEVEL_AUTHOR :
2837 label_counter >= 23 && label_counter <= 26 ?
2838 MICROLABEL_IMPORTED_FROM_HEAD :
2839 label_counter >= 28 && label_counter <= 35 ?
2840 MICROLABEL_IMPORTED_FROM :
2841 label_counter >= 37 && label_counter <= 40 ?
2842 MICROLABEL_IMPORTED_BY_HEAD :
2843 label_counter >= 42 && label_counter <= 49 ?
2844 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2846 if (leveldir_current->imported_from == NULL &&
2847 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2848 label_state == MICROLABEL_IMPORTED_FROM))
2849 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2850 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2852 DrawPreviewLevelLabelExt(label_state);
2855 game_status = last_game_status; /* restore current game status */
2858 void DrawPreviewLevelInitial()
2860 DrawPreviewLevelExt(TRUE);
2863 void DrawPreviewLevelAnimation()
2865 DrawPreviewLevelExt(FALSE);
2868 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2869 int graphic, int sync_frame,
2872 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2874 if (mask_mode == USE_MASKING)
2875 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2877 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2880 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2881 int graphic, int sync_frame, int mask_mode)
2883 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2885 if (mask_mode == USE_MASKING)
2886 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2888 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2891 inline static void DrawGraphicAnimation(int x, int y, int graphic)
2893 int lx = LEVELX(x), ly = LEVELY(y);
2895 if (!IN_SCR_FIELD(x, y))
2898 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2899 graphic, GfxFrame[lx][ly], NO_MASKING);
2901 MarkTileDirty(x, y);
2904 void DrawFixedGraphicAnimation(int x, int y, int graphic)
2906 int lx = LEVELX(x), ly = LEVELY(y);
2908 if (!IN_SCR_FIELD(x, y))
2911 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2912 graphic, GfxFrame[lx][ly], NO_MASKING);
2913 MarkTileDirty(x, y);
2916 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2918 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2921 void DrawLevelElementAnimation(int x, int y, int element)
2923 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2925 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2928 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2930 int sx = SCREENX(x), sy = SCREENY(y);
2932 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2935 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2938 DrawGraphicAnimation(sx, sy, graphic);
2941 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2942 DrawLevelFieldCrumbled(x, y);
2944 if (GFX_CRUMBLED(Feld[x][y]))
2945 DrawLevelFieldCrumbled(x, y);
2949 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2951 int sx = SCREENX(x), sy = SCREENY(y);
2954 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2957 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2959 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2962 DrawGraphicAnimation(sx, sy, graphic);
2964 if (GFX_CRUMBLED(element))
2965 DrawLevelFieldCrumbled(x, y);
2968 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2970 if (player->use_murphy)
2972 /* this works only because currently only one player can be "murphy" ... */
2973 static int last_horizontal_dir = MV_LEFT;
2974 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2976 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2977 last_horizontal_dir = move_dir;
2979 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2981 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2983 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2989 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2992 static boolean equalGraphics(int graphic1, int graphic2)
2994 struct GraphicInfo *g1 = &graphic_info[graphic1];
2995 struct GraphicInfo *g2 = &graphic_info[graphic2];
2997 return (g1->bitmap == g2->bitmap &&
2998 g1->src_x == g2->src_x &&
2999 g1->src_y == g2->src_y &&
3000 g1->anim_frames == g2->anim_frames &&
3001 g1->anim_delay == g2->anim_delay &&
3002 g1->anim_mode == g2->anim_mode);
3005 void DrawAllPlayers()
3009 for (i = 0; i < MAX_PLAYERS; i++)
3010 if (stored_player[i].active)
3011 DrawPlayer(&stored_player[i]);
3014 void DrawPlayerField(int x, int y)
3016 if (!IS_PLAYER(x, y))
3019 DrawPlayer(PLAYERINFO(x, y));
3022 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3024 void DrawPlayer(struct PlayerInfo *player)
3026 int jx = player->jx;
3027 int jy = player->jy;
3028 int move_dir = player->MovDir;
3029 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3030 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3031 int last_jx = (player->is_moving ? jx - dx : jx);
3032 int last_jy = (player->is_moving ? jy - dy : jy);
3033 int next_jx = jx + dx;
3034 int next_jy = jy + dy;
3035 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3036 boolean player_is_opaque = FALSE;
3037 int sx = SCREENX(jx), sy = SCREENY(jy);
3038 int sxx = 0, syy = 0;
3039 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3041 int action = ACTION_DEFAULT;
3042 int last_player_graphic = getPlayerGraphic(player, move_dir);
3043 int last_player_frame = player->Frame;
3046 /* GfxElement[][] is set to the element the player is digging or collecting;
3047 remove also for off-screen player if the player is not moving anymore */
3048 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3049 GfxElement[jx][jy] = EL_UNDEFINED;
3051 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3055 if (!IN_LEV_FIELD(jx, jy))
3057 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3058 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3059 printf("DrawPlayerField(): This should never happen!\n");
3064 if (element == EL_EXPLOSION)
3067 action = (player->is_pushing ? ACTION_PUSHING :
3068 player->is_digging ? ACTION_DIGGING :
3069 player->is_collecting ? ACTION_COLLECTING :
3070 player->is_moving ? ACTION_MOVING :
3071 player->is_snapping ? ACTION_SNAPPING :
3072 player->is_dropping ? ACTION_DROPPING :
3073 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3075 if (player->is_waiting)
3076 move_dir = player->dir_waiting;
3078 InitPlayerGfxAnimation(player, action, move_dir);
3080 /* ----------------------------------------------------------------------- */
3081 /* draw things in the field the player is leaving, if needed */
3082 /* ----------------------------------------------------------------------- */
3084 if (player->is_moving)
3086 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3088 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3090 if (last_element == EL_DYNAMITE_ACTIVE ||
3091 last_element == EL_EM_DYNAMITE_ACTIVE ||
3092 last_element == EL_SP_DISK_RED_ACTIVE)
3093 DrawDynamite(last_jx, last_jy);
3095 DrawLevelFieldThruMask(last_jx, last_jy);
3097 else if (last_element == EL_DYNAMITE_ACTIVE ||
3098 last_element == EL_EM_DYNAMITE_ACTIVE ||
3099 last_element == EL_SP_DISK_RED_ACTIVE)
3100 DrawDynamite(last_jx, last_jy);
3102 /* !!! this is not enough to prevent flickering of players which are
3103 moving next to each others without a free tile between them -- this
3104 can only be solved by drawing all players layer by layer (first the
3105 background, then the foreground etc.) !!! => TODO */
3106 else if (!IS_PLAYER(last_jx, last_jy))
3107 DrawLevelField(last_jx, last_jy);
3110 DrawLevelField(last_jx, last_jy);
3113 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3114 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3117 if (!IN_SCR_FIELD(sx, sy))
3120 /* ----------------------------------------------------------------------- */
3121 /* draw things behind the player, if needed */
3122 /* ----------------------------------------------------------------------- */
3125 DrawLevelElement(jx, jy, Back[jx][jy]);
3126 else if (IS_ACTIVE_BOMB(element))
3127 DrawLevelElement(jx, jy, EL_EMPTY);
3130 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3132 int old_element = GfxElement[jx][jy];
3133 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3134 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3136 if (GFX_CRUMBLED(old_element))
3137 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3139 DrawGraphic(sx, sy, old_graphic, frame);
3141 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3142 player_is_opaque = TRUE;
3146 GfxElement[jx][jy] = EL_UNDEFINED;
3148 /* make sure that pushed elements are drawn with correct frame rate */
3149 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3151 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3152 GfxFrame[jx][jy] = player->StepFrame;
3154 DrawLevelField(jx, jy);
3158 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3159 /* ----------------------------------------------------------------------- */
3160 /* draw player himself */
3161 /* ----------------------------------------------------------------------- */
3163 graphic = getPlayerGraphic(player, move_dir);
3165 /* in the case of changed player action or direction, prevent the current
3166 animation frame from being restarted for identical animations */
3167 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3168 player->Frame = last_player_frame;
3170 frame = getGraphicAnimationFrame(graphic, player->Frame);
3174 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3175 sxx = player->GfxPos;
3177 syy = player->GfxPos;
3180 if (player_is_opaque)
3181 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3183 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3185 if (SHIELD_ON(player))
3187 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3188 IMG_SHIELD_NORMAL_ACTIVE);
3189 int frame = getGraphicAnimationFrame(graphic, -1);
3191 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3195 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3198 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3199 sxx = player->GfxPos;
3201 syy = player->GfxPos;
3205 /* ----------------------------------------------------------------------- */
3206 /* draw things the player is pushing, if needed */
3207 /* ----------------------------------------------------------------------- */
3209 if (player->is_pushing && player->is_moving)
3211 int px = SCREENX(jx), py = SCREENY(jy);
3212 int pxx = (TILEX - ABS(sxx)) * dx;
3213 int pyy = (TILEY - ABS(syy)) * dy;
3214 int gfx_frame = GfxFrame[jx][jy];
3220 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3222 element = Feld[next_jx][next_jy];
3223 gfx_frame = GfxFrame[next_jx][next_jy];
3226 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3228 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3229 frame = getGraphicAnimationFrame(graphic, sync_frame);
3231 /* draw background element under pushed element (like the Sokoban field) */
3232 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3234 /* this allows transparent pushing animation over non-black background */
3237 DrawLevelElement(jx, jy, Back[jx][jy]);
3239 DrawLevelElement(jx, jy, EL_EMPTY);
3241 if (Back[next_jx][next_jy])
3242 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3244 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3246 else if (Back[next_jx][next_jy])
3247 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3250 /* do not draw (EM style) pushing animation when pushing is finished */
3251 /* (two-tile animations usually do not contain start and end frame) */
3252 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3253 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3255 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3257 /* masked drawing is needed for EMC style (double) movement graphics */
3258 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3259 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3263 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3264 /* ----------------------------------------------------------------------- */
3265 /* draw player himself */
3266 /* ----------------------------------------------------------------------- */
3268 graphic = getPlayerGraphic(player, move_dir);
3270 /* in the case of changed player action or direction, prevent the current
3271 animation frame from being restarted for identical animations */
3272 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3273 player->Frame = last_player_frame;
3275 frame = getGraphicAnimationFrame(graphic, player->Frame);
3279 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3280 sxx = player->GfxPos;
3282 syy = player->GfxPos;
3285 if (player_is_opaque)
3286 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3288 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3290 if (SHIELD_ON(player))
3292 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3293 IMG_SHIELD_NORMAL_ACTIVE);
3294 int frame = getGraphicAnimationFrame(graphic, -1);
3296 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3300 /* ----------------------------------------------------------------------- */
3301 /* draw things in front of player (active dynamite or dynabombs) */
3302 /* ----------------------------------------------------------------------- */
3304 if (IS_ACTIVE_BOMB(element))
3306 graphic = el2img(element);
3307 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3309 if (game.emulation == EMU_SUPAPLEX)
3310 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3312 DrawGraphicThruMask(sx, sy, graphic, frame);
3315 if (player_is_moving && last_element == EL_EXPLOSION)
3317 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3318 GfxElement[last_jx][last_jy] : EL_EMPTY);
3319 int graphic = el_act2img(element, ACTION_EXPLODING);
3320 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3321 int phase = ExplodePhase[last_jx][last_jy] - 1;
3322 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3325 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3328 /* ----------------------------------------------------------------------- */
3329 /* draw elements the player is just walking/passing through/under */
3330 /* ----------------------------------------------------------------------- */
3332 if (player_is_moving)
3334 /* handle the field the player is leaving ... */
3335 if (IS_ACCESSIBLE_INSIDE(last_element))
3336 DrawLevelField(last_jx, last_jy);
3337 else if (IS_ACCESSIBLE_UNDER(last_element))
3338 DrawLevelFieldThruMask(last_jx, last_jy);
3341 /* do not redraw accessible elements if the player is just pushing them */
3342 if (!player_is_moving || !player->is_pushing)
3344 /* ... and the field the player is entering */
3345 if (IS_ACCESSIBLE_INSIDE(element))
3346 DrawLevelField(jx, jy);
3347 else if (IS_ACCESSIBLE_UNDER(element))
3348 DrawLevelFieldThruMask(jx, jy);
3351 MarkTileDirty(sx, sy);
3354 /* ------------------------------------------------------------------------- */
3356 void WaitForEventToContinue()
3358 boolean still_wait = TRUE;
3360 /* simulate releasing mouse button over last gadget, if still pressed */
3362 HandleGadgets(-1, -1, 0);
3364 button_status = MB_RELEASED;
3378 case EVENT_BUTTONPRESS:
3379 case EVENT_KEYPRESS:
3383 case EVENT_KEYRELEASE:
3384 ClearPlayerAction();
3388 HandleOtherEvents(&event);
3392 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3399 /* don't eat all CPU time */
3404 #define MAX_REQUEST_LINES 13
3405 #define MAX_REQUEST_LINE_FONT1_LEN 7
3406 #define MAX_REQUEST_LINE_FONT2_LEN 10
3408 static int RequestHandleEvents(unsigned int req_state)
3410 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3411 local_player->LevelSolved_GameEnd);
3412 int width = request.width;
3413 int height = request.height;
3417 setRequestPosition(&sx, &sy, FALSE);
3419 button_status = MB_RELEASED;
3421 request_gadget_id = -1;
3428 SetDrawtoField(DRAW_FIELDBUFFER);
3430 HandleGameActions();
3432 SetDrawtoField(DRAW_BACKBUFFER);
3434 if (global.use_envelope_request)
3436 /* copy current state of request area to middle of playfield area */
3437 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3445 while (NextValidEvent(&event))
3449 case EVENT_BUTTONPRESS:
3450 case EVENT_BUTTONRELEASE:
3451 case EVENT_MOTIONNOTIFY:
3455 if (event.type == EVENT_MOTIONNOTIFY)
3460 motion_status = TRUE;
3461 mx = ((MotionEvent *) &event)->x;
3462 my = ((MotionEvent *) &event)->y;
3466 motion_status = FALSE;
3467 mx = ((ButtonEvent *) &event)->x;
3468 my = ((ButtonEvent *) &event)->y;
3469 if (event.type == EVENT_BUTTONPRESS)
3470 button_status = ((ButtonEvent *) &event)->button;
3472 button_status = MB_RELEASED;
3475 /* this sets 'request_gadget_id' */
3476 HandleGadgets(mx, my, button_status);
3478 switch (request_gadget_id)
3480 case TOOL_CTRL_ID_YES:
3483 case TOOL_CTRL_ID_NO:
3486 case TOOL_CTRL_ID_CONFIRM:
3487 result = TRUE | FALSE;
3490 case TOOL_CTRL_ID_PLAYER_1:
3493 case TOOL_CTRL_ID_PLAYER_2:
3496 case TOOL_CTRL_ID_PLAYER_3:
3499 case TOOL_CTRL_ID_PLAYER_4:
3510 case EVENT_KEYPRESS:
3511 switch (GetEventKey((KeyEvent *)&event, TRUE))
3514 if (req_state & REQ_CONFIRM)
3519 #if defined(TARGET_SDL2)
3526 #if defined(TARGET_SDL2)
3536 if (req_state & REQ_PLAYER)
3540 case EVENT_KEYRELEASE:
3541 ClearPlayerAction();
3545 HandleOtherEvents(&event);
3550 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3552 int joy = AnyJoystick();
3554 if (joy & JOY_BUTTON_1)
3556 else if (joy & JOY_BUTTON_2)
3562 if (global.use_envelope_request)
3564 /* copy back current state of pressed buttons inside request area */
3565 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3572 if (!PendingEvent()) /* delay only if no pending events */
3582 static boolean RequestDoor(char *text, unsigned int req_state)
3584 unsigned int old_door_state;
3585 int last_game_status = game_status; /* save current game status */
3586 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3587 int font_nr = FONT_TEXT_2;
3592 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3594 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3595 font_nr = FONT_TEXT_1;
3598 if (game_status == GAME_MODE_PLAYING)
3599 BlitScreenToBitmap(backbuffer);
3601 /* disable deactivated drawing when quick-loading level tape recording */
3602 if (tape.playing && tape.deactivate_display)
3603 TapeDeactivateDisplayOff(TRUE);
3605 SetMouseCursor(CURSOR_DEFAULT);
3607 #if defined(NETWORK_AVALIABLE)
3608 /* pause network game while waiting for request to answer */
3609 if (options.network &&
3610 game_status == GAME_MODE_PLAYING &&
3611 req_state & REQUEST_WAIT_FOR_INPUT)
3612 SendToServer_PausePlaying();
3615 old_door_state = GetDoorState();
3617 /* simulate releasing mouse button over last gadget, if still pressed */
3619 HandleGadgets(-1, -1, 0);
3623 /* draw released gadget before proceeding */
3626 if (old_door_state & DOOR_OPEN_1)
3628 CloseDoor(DOOR_CLOSE_1);
3630 /* save old door content */
3631 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3632 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3635 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3636 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3638 /* clear door drawing field */
3639 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3641 /* force DOOR font inside door area */
3642 game_status = GAME_MODE_PSEUDO_DOOR;
3644 /* write text for request */
3645 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3647 char text_line[max_request_line_len + 1];
3653 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3655 tc = *(text_ptr + tx);
3656 // if (!tc || tc == ' ')
3657 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3661 if ((tc == '?' || tc == '!') && tl == 0)
3671 strncpy(text_line, text_ptr, tl);
3674 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3675 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3676 text_line, font_nr);
3678 text_ptr += tl + (tc == ' ' ? 1 : 0);
3679 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3682 game_status = last_game_status; /* restore current game status */
3684 if (req_state & REQ_ASK)
3686 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3687 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3689 else if (req_state & REQ_CONFIRM)
3691 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3693 else if (req_state & REQ_PLAYER)
3695 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3696 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3697 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3698 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3701 /* copy request gadgets to door backbuffer */
3702 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3704 OpenDoor(DOOR_OPEN_1);
3706 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3708 if (game_status == GAME_MODE_PLAYING)
3710 SetPanelBackground();
3711 SetDrawBackgroundMask(REDRAW_DOOR_1);
3715 SetDrawBackgroundMask(REDRAW_FIELD);
3721 if (game_status != GAME_MODE_MAIN)
3724 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3726 // ---------- handle request buttons ----------
3727 result = RequestHandleEvents(req_state);
3729 if (game_status != GAME_MODE_MAIN)
3734 if (!(req_state & REQ_STAY_OPEN))
3736 CloseDoor(DOOR_CLOSE_1);
3738 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3739 (req_state & REQ_REOPEN))
3740 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3745 if (game_status == GAME_MODE_PLAYING)
3747 SetPanelBackground();
3748 SetDrawBackgroundMask(REDRAW_DOOR_1);
3752 SetDrawBackgroundMask(REDRAW_FIELD);
3755 #if defined(NETWORK_AVALIABLE)
3756 /* continue network game after request */
3757 if (options.network &&
3758 game_status == GAME_MODE_PLAYING &&
3759 req_state & REQUEST_WAIT_FOR_INPUT)
3760 SendToServer_ContinuePlaying();
3763 /* restore deactivated drawing when quick-loading level tape recording */
3764 if (tape.playing && tape.deactivate_display)
3765 TapeDeactivateDisplayOn();
3770 static boolean RequestEnvelope(char *text, unsigned int req_state)
3774 if (game_status == GAME_MODE_PLAYING)
3775 BlitScreenToBitmap(backbuffer);
3777 /* disable deactivated drawing when quick-loading level tape recording */
3778 if (tape.playing && tape.deactivate_display)
3779 TapeDeactivateDisplayOff(TRUE);
3781 SetMouseCursor(CURSOR_DEFAULT);
3783 #if defined(NETWORK_AVALIABLE)
3784 /* pause network game while waiting for request to answer */
3785 if (options.network &&
3786 game_status == GAME_MODE_PLAYING &&
3787 req_state & REQUEST_WAIT_FOR_INPUT)
3788 SendToServer_PausePlaying();
3791 /* simulate releasing mouse button over last gadget, if still pressed */
3793 HandleGadgets(-1, -1, 0);
3797 // (replace with setting corresponding request background)
3798 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3799 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3801 /* clear door drawing field */
3802 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3804 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3806 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3808 if (game_status == GAME_MODE_PLAYING)
3810 SetPanelBackground();
3811 SetDrawBackgroundMask(REDRAW_DOOR_1);
3815 SetDrawBackgroundMask(REDRAW_FIELD);
3821 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3823 // ---------- handle request buttons ----------
3824 result = RequestHandleEvents(req_state);
3826 if (game_status != GAME_MODE_MAIN)
3831 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3835 if (game_status == GAME_MODE_PLAYING)
3837 SetPanelBackground();
3838 SetDrawBackgroundMask(REDRAW_DOOR_1);
3842 SetDrawBackgroundMask(REDRAW_FIELD);
3845 #if defined(NETWORK_AVALIABLE)
3846 /* continue network game after request */
3847 if (options.network &&
3848 game_status == GAME_MODE_PLAYING &&
3849 req_state & REQUEST_WAIT_FOR_INPUT)
3850 SendToServer_ContinuePlaying();
3853 /* restore deactivated drawing when quick-loading level tape recording */
3854 if (tape.playing && tape.deactivate_display)
3855 TapeDeactivateDisplayOn();
3860 boolean Request(char *text, unsigned int req_state)
3862 if (global.use_envelope_request)
3863 return RequestEnvelope(text, req_state);
3865 return RequestDoor(text, req_state);
3868 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3870 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3871 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3874 if (dpo1->sort_priority != dpo2->sort_priority)
3875 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3877 compare_result = dpo1->nr - dpo2->nr;
3879 return compare_result;
3882 void InitGraphicCompatibilityInfo_Doors()
3888 struct DoorInfo *door;
3892 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3893 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3895 { -1, -1, -1, NULL }
3897 struct Rect door_rect_list[] =
3899 { DX, DY, DXSIZE, DYSIZE },
3900 { VX, VY, VXSIZE, VYSIZE }
3904 for (i = 0; doors[i].door_token != -1; i++)
3906 int door_token = doors[i].door_token;
3907 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3908 int part_1 = doors[i].part_1;
3909 int part_8 = doors[i].part_8;
3910 int part_2 = part_1 + 1;
3911 int part_3 = part_1 + 2;
3912 struct DoorInfo *door = doors[i].door;
3913 struct Rect *door_rect = &door_rect_list[door_index];
3914 boolean door_gfx_redefined = FALSE;
3916 /* check if any door part graphic definitions have been redefined */
3918 for (j = 0; door_part_controls[j].door_token != -1; j++)
3920 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3921 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3923 if (dpc->door_token == door_token && fi->redefined)
3924 door_gfx_redefined = TRUE;
3927 /* check for old-style door graphic/animation modifications */
3929 if (!door_gfx_redefined)
3931 if (door->anim_mode & ANIM_STATIC_PANEL)
3933 door->panel.step_xoffset = 0;
3934 door->panel.step_yoffset = 0;
3937 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3939 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3940 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3941 int num_door_steps, num_panel_steps;
3943 /* remove door part graphics other than the two default wings */
3945 for (j = 0; door_part_controls[j].door_token != -1; j++)
3947 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3948 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3950 if (dpc->graphic >= part_3 &&
3951 dpc->graphic <= part_8)
3955 /* set graphics and screen positions of the default wings */
3957 g_part_1->width = door_rect->width;
3958 g_part_1->height = door_rect->height;
3959 g_part_2->width = door_rect->width;
3960 g_part_2->height = door_rect->height;
3961 g_part_2->src_x = door_rect->width;
3962 g_part_2->src_y = g_part_1->src_y;
3964 door->part_2.x = door->part_1.x;
3965 door->part_2.y = door->part_1.y;
3967 if (door->width != -1)
3969 g_part_1->width = door->width;
3970 g_part_2->width = door->width;
3972 // special treatment for graphics and screen position of right wing
3973 g_part_2->src_x += door_rect->width - door->width;
3974 door->part_2.x += door_rect->width - door->width;
3977 if (door->height != -1)
3979 g_part_1->height = door->height;
3980 g_part_2->height = door->height;
3982 // special treatment for graphics and screen position of bottom wing
3983 g_part_2->src_y += door_rect->height - door->height;
3984 door->part_2.y += door_rect->height - door->height;
3987 /* set animation delays for the default wings and panels */
3989 door->part_1.step_delay = door->step_delay;
3990 door->part_2.step_delay = door->step_delay;
3991 door->panel.step_delay = door->step_delay;
3993 /* set animation draw order for the default wings */
3995 door->part_1.sort_priority = 2; /* draw left wing over ... */
3996 door->part_2.sort_priority = 1; /* ... right wing */
3998 /* set animation draw offset for the default wings */
4000 if (door->anim_mode & ANIM_HORIZONTAL)
4002 door->part_1.step_xoffset = door->step_offset;
4003 door->part_1.step_yoffset = 0;
4004 door->part_2.step_xoffset = door->step_offset * -1;
4005 door->part_2.step_yoffset = 0;
4007 num_door_steps = g_part_1->width / door->step_offset;
4009 else // ANIM_VERTICAL
4011 door->part_1.step_xoffset = 0;
4012 door->part_1.step_yoffset = door->step_offset;
4013 door->part_2.step_xoffset = 0;
4014 door->part_2.step_yoffset = door->step_offset * -1;
4016 num_door_steps = g_part_1->height / door->step_offset;
4019 /* set animation draw offset for the default panels */
4021 if (door->step_offset > 1)
4023 num_panel_steps = 2 * door_rect->height / door->step_offset;
4024 door->panel.start_step = num_panel_steps - num_door_steps;
4025 door->panel.start_step_closing = door->panel.start_step;
4029 num_panel_steps = door_rect->height / door->step_offset;
4030 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4031 door->panel.start_step_closing = door->panel.start_step;
4032 door->panel.step_delay *= 2;
4043 for (i = 0; door_part_controls[i].door_token != -1; i++)
4045 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4046 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4048 /* initialize "start_step_opening" and "start_step_closing", if needed */
4049 if (dpc->pos->start_step_opening == 0 &&
4050 dpc->pos->start_step_closing == 0)
4052 // dpc->pos->start_step_opening = dpc->pos->start_step;
4053 dpc->pos->start_step_closing = dpc->pos->start_step;
4056 /* fill structure for door part draw order (sorted below) */
4058 dpo->sort_priority = dpc->pos->sort_priority;
4061 /* sort door part controls according to sort_priority and graphic number */
4062 qsort(door_part_order, MAX_DOOR_PARTS,
4063 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4066 unsigned int OpenDoor(unsigned int door_state)
4068 if (door_state & DOOR_COPY_BACK)
4070 if (door_state & DOOR_OPEN_1)
4071 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4072 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4074 if (door_state & DOOR_OPEN_2)
4075 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4076 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4078 door_state &= ~DOOR_COPY_BACK;
4081 return MoveDoor(door_state);
4084 unsigned int CloseDoor(unsigned int door_state)
4086 unsigned int old_door_state = GetDoorState();
4088 if (!(door_state & DOOR_NO_COPY_BACK))
4090 if (old_door_state & DOOR_OPEN_1)
4091 BlitBitmap(backbuffer, bitmap_db_door_1,
4092 DX, DY, DXSIZE, DYSIZE, 0, 0);
4094 if (old_door_state & DOOR_OPEN_2)
4095 BlitBitmap(backbuffer, bitmap_db_door_2,
4096 VX, VY, VXSIZE, VYSIZE, 0, 0);
4098 door_state &= ~DOOR_NO_COPY_BACK;
4101 return MoveDoor(door_state);
4104 unsigned int GetDoorState()
4106 return MoveDoor(DOOR_GET_STATE);
4109 unsigned int SetDoorState(unsigned int door_state)
4111 return MoveDoor(door_state | DOOR_SET_STATE);
4114 int euclid(int a, int b)
4116 return (b ? euclid(b, a % b) : a);
4119 unsigned int MoveDoor(unsigned int door_state)
4121 struct Rect door_rect_list[] =
4123 { DX, DY, DXSIZE, DYSIZE },
4124 { VX, VY, VXSIZE, VYSIZE }
4126 static int door1 = DOOR_OPEN_1;
4127 static int door2 = DOOR_CLOSE_2;
4128 unsigned int door_delay = 0;
4129 unsigned int door_delay_value;
4132 if (door_state == DOOR_GET_STATE)
4133 return (door1 | door2);
4135 if (door_state & DOOR_SET_STATE)
4137 if (door_state & DOOR_ACTION_1)
4138 door1 = door_state & DOOR_ACTION_1;
4139 if (door_state & DOOR_ACTION_2)
4140 door2 = door_state & DOOR_ACTION_2;
4142 return (door1 | door2);
4145 if (!(door_state & DOOR_FORCE_REDRAW))
4147 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4148 door_state &= ~DOOR_OPEN_1;
4149 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4150 door_state &= ~DOOR_CLOSE_1;
4151 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4152 door_state &= ~DOOR_OPEN_2;
4153 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4154 door_state &= ~DOOR_CLOSE_2;
4157 if (global.autoplay_leveldir)
4159 door_state |= DOOR_NO_DELAY;
4160 door_state &= ~DOOR_CLOSE_ALL;
4163 if (game_status == GAME_MODE_EDITOR)
4164 door_state |= DOOR_NO_DELAY;
4166 if (door_state & DOOR_ACTION)
4168 boolean door_panel_drawn[NUM_DOORS];
4169 boolean panel_has_doors[NUM_DOORS];
4170 boolean door_part_skip[MAX_DOOR_PARTS];
4171 boolean door_part_done[MAX_DOOR_PARTS];
4172 boolean door_part_done_all;
4173 int num_steps[MAX_DOOR_PARTS];
4174 int max_move_delay = 0; // delay for complete animations of all doors
4175 int max_step_delay = 0; // delay (ms) between two animation frames
4176 int num_move_steps = 0; // number of animation steps for all doors
4177 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4178 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4179 int current_move_delay = 0;
4183 for (i = 0; i < NUM_DOORS; i++)
4184 panel_has_doors[i] = FALSE;
4186 for (i = 0; i < MAX_DOOR_PARTS; i++)
4188 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4189 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4190 int door_token = dpc->door_token;
4192 door_part_done[i] = FALSE;
4193 door_part_skip[i] = (!(door_state & door_token) ||
4197 for (i = 0; i < MAX_DOOR_PARTS; i++)
4199 int nr = door_part_order[i].nr;
4200 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4201 struct DoorPartPosInfo *pos = dpc->pos;
4202 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4203 int door_token = dpc->door_token;
4204 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4205 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4206 int step_xoffset = ABS(pos->step_xoffset);
4207 int step_yoffset = ABS(pos->step_yoffset);
4208 int step_delay = pos->step_delay;
4209 int current_door_state = door_state & door_token;
4210 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4211 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4212 boolean part_opening = (is_panel ? door_closing : door_opening);
4213 int start_step = (part_opening ? pos->start_step_opening :
4214 pos->start_step_closing);
4215 float move_xsize = (step_xoffset ? g->width : 0);
4216 float move_ysize = (step_yoffset ? g->height : 0);
4217 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4218 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4219 int move_steps = (move_xsteps && move_ysteps ?
4220 MIN(move_xsteps, move_ysteps) :
4221 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4222 int move_delay = move_steps * step_delay;
4224 if (door_part_skip[nr])
4227 max_move_delay = MAX(max_move_delay, move_delay);
4228 max_step_delay = (max_step_delay == 0 ? step_delay :
4229 euclid(max_step_delay, step_delay));
4230 num_steps[nr] = move_steps;
4234 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4236 panel_has_doors[door_index] = TRUE;
4240 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4242 num_move_steps = max_move_delay / max_step_delay;
4243 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4245 door_delay_value = max_step_delay;
4247 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4249 start = num_move_steps - 1;
4253 /* opening door sound has priority over simultaneously closing door */
4254 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4255 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4256 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4257 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4260 for (k = start; k < num_move_steps; k++)
4262 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4264 door_part_done_all = TRUE;
4266 for (i = 0; i < NUM_DOORS; i++)
4267 door_panel_drawn[i] = FALSE;
4269 for (i = 0; i < MAX_DOOR_PARTS; i++)
4271 int nr = door_part_order[i].nr;
4272 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4273 struct DoorPartPosInfo *pos = dpc->pos;
4274 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4275 int door_token = dpc->door_token;
4276 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4277 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4278 boolean is_panel_and_door_has_closed = FALSE;
4279 struct Rect *door_rect = &door_rect_list[door_index];
4280 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4282 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4283 int current_door_state = door_state & door_token;
4284 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4285 boolean door_closing = !door_opening;
4286 boolean part_opening = (is_panel ? door_closing : door_opening);
4287 boolean part_closing = !part_opening;
4288 int start_step = (part_opening ? pos->start_step_opening :
4289 pos->start_step_closing);
4290 int step_delay = pos->step_delay;
4291 int step_factor = step_delay / max_step_delay;
4292 int k1 = (step_factor ? k / step_factor + 1 : k);
4293 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4294 int kk = MAX(0, k2);
4297 int src_x, src_y, src_xx, src_yy;
4298 int dst_x, dst_y, dst_xx, dst_yy;
4301 if (door_part_skip[nr])
4304 if (!(door_state & door_token))
4312 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4313 int kk_door = MAX(0, k2_door);
4314 int sync_frame = kk_door * door_delay_value;
4315 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4317 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4322 if (!door_panel_drawn[door_index])
4324 ClearRectangle(drawto, door_rect->x, door_rect->y,
4325 door_rect->width, door_rect->height);
4327 door_panel_drawn[door_index] = TRUE;
4330 // draw opening or closing door parts
4332 if (pos->step_xoffset < 0) // door part on right side
4335 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4338 if (dst_xx + width > door_rect->width)
4339 width = door_rect->width - dst_xx;
4341 else // door part on left side
4344 dst_xx = pos->x - kk * pos->step_xoffset;
4348 src_xx = ABS(dst_xx);
4352 width = g->width - src_xx;
4354 if (width > door_rect->width)
4355 width = door_rect->width;
4357 // printf("::: k == %d [%d] \n", k, start_step);
4360 if (pos->step_yoffset < 0) // door part on bottom side
4363 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4366 if (dst_yy + height > door_rect->height)
4367 height = door_rect->height - dst_yy;
4369 else // door part on top side
4372 dst_yy = pos->y - kk * pos->step_yoffset;
4376 src_yy = ABS(dst_yy);
4380 height = g->height - src_yy;
4383 src_x = g_src_x + src_xx;
4384 src_y = g_src_y + src_yy;
4386 dst_x = door_rect->x + dst_xx;
4387 dst_y = door_rect->y + dst_yy;
4389 is_panel_and_door_has_closed =
4392 panel_has_doors[door_index] &&
4393 k >= num_move_steps_doors_only - 1);
4395 if (width >= 0 && width <= g->width &&
4396 height >= 0 && height <= g->height &&
4397 !is_panel_and_door_has_closed)
4399 if (is_panel || !pos->draw_masked)
4400 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4403 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4407 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4409 if ((part_opening && (width < 0 || height < 0)) ||
4410 (part_closing && (width >= g->width && height >= g->height)))
4411 door_part_done[nr] = TRUE;
4413 // continue door part animations, but not panel after door has closed
4414 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4415 door_part_done_all = FALSE;
4418 if (!(door_state & DOOR_NO_DELAY))
4422 if (game_status == GAME_MODE_MAIN)
4425 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4427 current_move_delay += max_step_delay;
4430 if (door_part_done_all)
4435 if (door_state & DOOR_ACTION_1)
4436 door1 = door_state & DOOR_ACTION_1;
4437 if (door_state & DOOR_ACTION_2)
4438 door2 = door_state & DOOR_ACTION_2;
4440 return (door1 | door2);
4443 static boolean useSpecialEditorDoor()
4445 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4446 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4448 // do not draw special editor door if editor border defined or redefined
4449 if (graphic_info[graphic].bitmap != NULL || redefined)
4452 // do not draw special editor door if global border defined to be empty
4453 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4456 // do not draw special editor door if viewport definitions do not match
4460 EY + EYSIZE != VY + VYSIZE)
4466 void DrawSpecialEditorDoor()
4468 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4469 int top_border_width = gfx1->width;
4470 int top_border_height = gfx1->height;
4471 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4472 int ex = EX - outer_border;
4473 int ey = EY - outer_border;
4474 int vy = VY - outer_border;
4475 int exsize = EXSIZE + 2 * outer_border;
4477 if (!useSpecialEditorDoor())
4480 /* draw bigger level editor toolbox window */
4481 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4482 top_border_width, top_border_height, ex, ey - top_border_height);
4483 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4484 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4486 redraw_mask |= REDRAW_ALL;
4489 void UndrawSpecialEditorDoor()
4491 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4492 int top_border_width = gfx1->width;
4493 int top_border_height = gfx1->height;
4494 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4495 int ex = EX - outer_border;
4496 int ey = EY - outer_border;
4497 int ey_top = ey - top_border_height;
4498 int exsize = EXSIZE + 2 * outer_border;
4499 int eysize = EYSIZE + 2 * outer_border;
4501 if (!useSpecialEditorDoor())
4504 /* draw normal tape recorder window */
4505 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4507 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4508 ex, ey_top, top_border_width, top_border_height,
4510 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4511 ex, ey, exsize, eysize, ex, ey);
4515 // if screen background is set to "[NONE]", clear editor toolbox window
4516 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4517 ClearRectangle(drawto, ex, ey, exsize, eysize);
4520 redraw_mask |= REDRAW_ALL;
4524 /* ---------- new tool button stuff ---------------------------------------- */
4529 struct TextPosInfo *pos;
4532 } toolbutton_info[NUM_TOOL_BUTTONS] =
4535 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4536 TOOL_CTRL_ID_YES, "yes"
4539 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4540 TOOL_CTRL_ID_NO, "no"
4543 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4544 TOOL_CTRL_ID_CONFIRM, "confirm"
4547 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4548 TOOL_CTRL_ID_PLAYER_1, "player 1"
4551 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4552 TOOL_CTRL_ID_PLAYER_2, "player 2"
4555 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4556 TOOL_CTRL_ID_PLAYER_3, "player 3"
4559 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4560 TOOL_CTRL_ID_PLAYER_4, "player 4"
4564 void CreateToolButtons()
4568 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4570 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4571 struct TextPosInfo *pos = toolbutton_info[i].pos;
4572 struct GadgetInfo *gi;
4573 Bitmap *deco_bitmap = None;
4574 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4575 unsigned int event_mask = GD_EVENT_RELEASED;
4578 int gd_x = gfx->src_x;
4579 int gd_y = gfx->src_y;
4580 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4581 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4584 if (global.use_envelope_request)
4585 setRequestPosition(&dx, &dy, TRUE);
4587 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4589 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4591 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4592 pos->size, &deco_bitmap, &deco_x, &deco_y);
4593 deco_xpos = (gfx->width - pos->size) / 2;
4594 deco_ypos = (gfx->height - pos->size) / 2;
4597 gi = CreateGadget(GDI_CUSTOM_ID, id,
4598 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4599 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4600 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4601 GDI_WIDTH, gfx->width,
4602 GDI_HEIGHT, gfx->height,
4603 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4604 GDI_STATE, GD_BUTTON_UNPRESSED,
4605 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4606 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4607 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4608 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4609 GDI_DECORATION_SIZE, pos->size, pos->size,
4610 GDI_DECORATION_SHIFTING, 1, 1,
4611 GDI_DIRECT_DRAW, FALSE,
4612 GDI_EVENT_MASK, event_mask,
4613 GDI_CALLBACK_ACTION, HandleToolButtons,
4617 Error(ERR_EXIT, "cannot create gadget");
4619 tool_gadget[id] = gi;
4623 void FreeToolButtons()
4627 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4628 FreeGadget(tool_gadget[i]);
4631 static void UnmapToolButtons()
4635 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4636 UnmapGadget(tool_gadget[i]);
4639 static void HandleToolButtons(struct GadgetInfo *gi)
4641 request_gadget_id = gi->custom_id;
4644 static struct Mapping_EM_to_RND_object
4647 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4648 boolean is_backside; /* backside of moving element */
4654 em_object_mapping_list[] =
4657 Xblank, TRUE, FALSE,
4661 Yacid_splash_eB, FALSE, FALSE,
4662 EL_ACID_SPLASH_RIGHT, -1, -1
4665 Yacid_splash_wB, FALSE, FALSE,
4666 EL_ACID_SPLASH_LEFT, -1, -1
4669 #ifdef EM_ENGINE_BAD_ROLL
4671 Xstone_force_e, FALSE, FALSE,
4672 EL_ROCK, -1, MV_BIT_RIGHT
4675 Xstone_force_w, FALSE, FALSE,
4676 EL_ROCK, -1, MV_BIT_LEFT
4679 Xnut_force_e, FALSE, FALSE,
4680 EL_NUT, -1, MV_BIT_RIGHT
4683 Xnut_force_w, FALSE, FALSE,
4684 EL_NUT, -1, MV_BIT_LEFT
4687 Xspring_force_e, FALSE, FALSE,
4688 EL_SPRING, -1, MV_BIT_RIGHT
4691 Xspring_force_w, FALSE, FALSE,
4692 EL_SPRING, -1, MV_BIT_LEFT
4695 Xemerald_force_e, FALSE, FALSE,
4696 EL_EMERALD, -1, MV_BIT_RIGHT
4699 Xemerald_force_w, FALSE, FALSE,
4700 EL_EMERALD, -1, MV_BIT_LEFT
4703 Xdiamond_force_e, FALSE, FALSE,
4704 EL_DIAMOND, -1, MV_BIT_RIGHT
4707 Xdiamond_force_w, FALSE, FALSE,
4708 EL_DIAMOND, -1, MV_BIT_LEFT
4711 Xbomb_force_e, FALSE, FALSE,
4712 EL_BOMB, -1, MV_BIT_RIGHT
4715 Xbomb_force_w, FALSE, FALSE,
4716 EL_BOMB, -1, MV_BIT_LEFT
4718 #endif /* EM_ENGINE_BAD_ROLL */
4721 Xstone, TRUE, FALSE,
4725 Xstone_pause, FALSE, FALSE,
4729 Xstone_fall, FALSE, FALSE,
4733 Ystone_s, FALSE, FALSE,
4734 EL_ROCK, ACTION_FALLING, -1
4737 Ystone_sB, FALSE, TRUE,
4738 EL_ROCK, ACTION_FALLING, -1
4741 Ystone_e, FALSE, FALSE,
4742 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4745 Ystone_eB, FALSE, TRUE,
4746 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4749 Ystone_w, FALSE, FALSE,
4750 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4753 Ystone_wB, FALSE, TRUE,
4754 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4761 Xnut_pause, FALSE, FALSE,
4765 Xnut_fall, FALSE, FALSE,
4769 Ynut_s, FALSE, FALSE,
4770 EL_NUT, ACTION_FALLING, -1
4773 Ynut_sB, FALSE, TRUE,
4774 EL_NUT, ACTION_FALLING, -1
4777 Ynut_e, FALSE, FALSE,
4778 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4781 Ynut_eB, FALSE, TRUE,
4782 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4785 Ynut_w, FALSE, FALSE,
4786 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4789 Ynut_wB, FALSE, TRUE,
4790 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4793 Xbug_n, TRUE, FALSE,
4797 Xbug_e, TRUE, FALSE,
4798 EL_BUG_RIGHT, -1, -1
4801 Xbug_s, TRUE, FALSE,
4805 Xbug_w, TRUE, FALSE,
4809 Xbug_gon, FALSE, FALSE,
4813 Xbug_goe, FALSE, FALSE,
4814 EL_BUG_RIGHT, -1, -1
4817 Xbug_gos, FALSE, FALSE,
4821 Xbug_gow, FALSE, FALSE,
4825 Ybug_n, FALSE, FALSE,
4826 EL_BUG, ACTION_MOVING, MV_BIT_UP
4829 Ybug_nB, FALSE, TRUE,
4830 EL_BUG, ACTION_MOVING, MV_BIT_UP
4833 Ybug_e, FALSE, FALSE,
4834 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4837 Ybug_eB, FALSE, TRUE,
4838 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4841 Ybug_s, FALSE, FALSE,
4842 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4845 Ybug_sB, FALSE, TRUE,
4846 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4849 Ybug_w, FALSE, FALSE,
4850 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4853 Ybug_wB, FALSE, TRUE,
4854 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4857 Ybug_w_n, FALSE, FALSE,
4858 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4861 Ybug_n_e, FALSE, FALSE,
4862 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4865 Ybug_e_s, FALSE, FALSE,
4866 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4869 Ybug_s_w, FALSE, FALSE,
4870 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4873 Ybug_e_n, FALSE, FALSE,
4874 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4877 Ybug_s_e, FALSE, FALSE,
4878 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4881 Ybug_w_s, FALSE, FALSE,
4882 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4885 Ybug_n_w, FALSE, FALSE,
4886 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4889 Ybug_stone, FALSE, FALSE,
4890 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4893 Ybug_spring, FALSE, FALSE,
4894 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4897 Xtank_n, TRUE, FALSE,
4898 EL_SPACESHIP_UP, -1, -1
4901 Xtank_e, TRUE, FALSE,
4902 EL_SPACESHIP_RIGHT, -1, -1
4905 Xtank_s, TRUE, FALSE,
4906 EL_SPACESHIP_DOWN, -1, -1
4909 Xtank_w, TRUE, FALSE,
4910 EL_SPACESHIP_LEFT, -1, -1
4913 Xtank_gon, FALSE, FALSE,
4914 EL_SPACESHIP_UP, -1, -1
4917 Xtank_goe, FALSE, FALSE,
4918 EL_SPACESHIP_RIGHT, -1, -1
4921 Xtank_gos, FALSE, FALSE,
4922 EL_SPACESHIP_DOWN, -1, -1
4925 Xtank_gow, FALSE, FALSE,
4926 EL_SPACESHIP_LEFT, -1, -1
4929 Ytank_n, FALSE, FALSE,
4930 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4933 Ytank_nB, FALSE, TRUE,
4934 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4937 Ytank_e, FALSE, FALSE,
4938 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4941 Ytank_eB, FALSE, TRUE,
4942 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4945 Ytank_s, FALSE, FALSE,
4946 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4949 Ytank_sB, FALSE, TRUE,
4950 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4953 Ytank_w, FALSE, FALSE,
4954 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4957 Ytank_wB, FALSE, TRUE,
4958 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4961 Ytank_w_n, FALSE, FALSE,
4962 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4965 Ytank_n_e, FALSE, FALSE,
4966 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4969 Ytank_e_s, FALSE, FALSE,
4970 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4973 Ytank_s_w, FALSE, FALSE,
4974 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4977 Ytank_e_n, FALSE, FALSE,
4978 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4981 Ytank_s_e, FALSE, FALSE,
4982 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4985 Ytank_w_s, FALSE, FALSE,
4986 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4989 Ytank_n_w, FALSE, FALSE,
4990 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4993 Ytank_stone, FALSE, FALSE,
4994 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4997 Ytank_spring, FALSE, FALSE,
4998 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5001 Xandroid, TRUE, FALSE,
5002 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5005 Xandroid_1_n, FALSE, FALSE,
5006 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5009 Xandroid_2_n, FALSE, FALSE,
5010 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5013 Xandroid_1_e, FALSE, FALSE,
5014 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5017 Xandroid_2_e, FALSE, FALSE,
5018 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5021 Xandroid_1_w, FALSE, FALSE,
5022 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5025 Xandroid_2_w, FALSE, FALSE,
5026 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5029 Xandroid_1_s, FALSE, FALSE,
5030 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5033 Xandroid_2_s, FALSE, FALSE,
5034 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5037 Yandroid_n, FALSE, FALSE,
5038 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5041 Yandroid_nB, FALSE, TRUE,
5042 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5045 Yandroid_ne, FALSE, FALSE,
5046 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5049 Yandroid_neB, FALSE, TRUE,
5050 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5053 Yandroid_e, FALSE, FALSE,
5054 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5057 Yandroid_eB, FALSE, TRUE,
5058 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5061 Yandroid_se, FALSE, FALSE,
5062 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5065 Yandroid_seB, FALSE, TRUE,
5066 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5069 Yandroid_s, FALSE, FALSE,
5070 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5073 Yandroid_sB, FALSE, TRUE,
5074 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5077 Yandroid_sw, FALSE, FALSE,
5078 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5081 Yandroid_swB, FALSE, TRUE,
5082 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5085 Yandroid_w, FALSE, FALSE,
5086 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5089 Yandroid_wB, FALSE, TRUE,
5090 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5093 Yandroid_nw, FALSE, FALSE,
5094 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5097 Yandroid_nwB, FALSE, TRUE,
5098 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5101 Xspring, TRUE, FALSE,
5105 Xspring_pause, FALSE, FALSE,
5109 Xspring_e, FALSE, FALSE,
5113 Xspring_w, FALSE, FALSE,
5117 Xspring_fall, FALSE, FALSE,
5121 Yspring_s, FALSE, FALSE,
5122 EL_SPRING, ACTION_FALLING, -1
5125 Yspring_sB, FALSE, TRUE,
5126 EL_SPRING, ACTION_FALLING, -1
5129 Yspring_e, FALSE, FALSE,
5130 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5133 Yspring_eB, FALSE, TRUE,
5134 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5137 Yspring_w, FALSE, FALSE,
5138 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5141 Yspring_wB, FALSE, TRUE,
5142 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5145 Yspring_kill_e, FALSE, FALSE,
5146 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5149 Yspring_kill_eB, FALSE, TRUE,
5150 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5153 Yspring_kill_w, FALSE, FALSE,
5154 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5157 Yspring_kill_wB, FALSE, TRUE,
5158 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5161 Xeater_n, TRUE, FALSE,
5162 EL_YAMYAM_UP, -1, -1
5165 Xeater_e, TRUE, FALSE,
5166 EL_YAMYAM_RIGHT, -1, -1
5169 Xeater_w, TRUE, FALSE,
5170 EL_YAMYAM_LEFT, -1, -1
5173 Xeater_s, TRUE, FALSE,
5174 EL_YAMYAM_DOWN, -1, -1
5177 Yeater_n, FALSE, FALSE,
5178 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5181 Yeater_nB, FALSE, TRUE,
5182 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5185 Yeater_e, FALSE, FALSE,
5186 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5189 Yeater_eB, FALSE, TRUE,
5190 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5193 Yeater_s, FALSE, FALSE,
5194 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5197 Yeater_sB, FALSE, TRUE,
5198 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5201 Yeater_w, FALSE, FALSE,
5202 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5205 Yeater_wB, FALSE, TRUE,
5206 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5209 Yeater_stone, FALSE, FALSE,
5210 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5213 Yeater_spring, FALSE, FALSE,
5214 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5217 Xalien, TRUE, FALSE,
5221 Xalien_pause, FALSE, FALSE,
5225 Yalien_n, FALSE, FALSE,
5226 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5229 Yalien_nB, FALSE, TRUE,
5230 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5233 Yalien_e, FALSE, FALSE,
5234 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5237 Yalien_eB, FALSE, TRUE,
5238 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5241 Yalien_s, FALSE, FALSE,
5242 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5245 Yalien_sB, FALSE, TRUE,
5246 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5249 Yalien_w, FALSE, FALSE,
5250 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5253 Yalien_wB, FALSE, TRUE,
5254 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5257 Yalien_stone, FALSE, FALSE,
5258 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5261 Yalien_spring, FALSE, FALSE,
5262 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5265 Xemerald, TRUE, FALSE,
5269 Xemerald_pause, FALSE, FALSE,
5273 Xemerald_fall, FALSE, FALSE,
5277 Xemerald_shine, FALSE, FALSE,
5278 EL_EMERALD, ACTION_TWINKLING, -1
5281 Yemerald_s, FALSE, FALSE,
5282 EL_EMERALD, ACTION_FALLING, -1
5285 Yemerald_sB, FALSE, TRUE,
5286 EL_EMERALD, ACTION_FALLING, -1
5289 Yemerald_e, FALSE, FALSE,
5290 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5293 Yemerald_eB, FALSE, TRUE,
5294 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5297 Yemerald_w, FALSE, FALSE,
5298 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5301 Yemerald_wB, FALSE, TRUE,
5302 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5305 Yemerald_eat, FALSE, FALSE,
5306 EL_EMERALD, ACTION_COLLECTING, -1
5309 Yemerald_stone, FALSE, FALSE,
5310 EL_NUT, ACTION_BREAKING, -1
5313 Xdiamond, TRUE, FALSE,
5317 Xdiamond_pause, FALSE, FALSE,
5321 Xdiamond_fall, FALSE, FALSE,
5325 Xdiamond_shine, FALSE, FALSE,
5326 EL_DIAMOND, ACTION_TWINKLING, -1
5329 Ydiamond_s, FALSE, FALSE,
5330 EL_DIAMOND, ACTION_FALLING, -1
5333 Ydiamond_sB, FALSE, TRUE,
5334 EL_DIAMOND, ACTION_FALLING, -1
5337 Ydiamond_e, FALSE, FALSE,
5338 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5341 Ydiamond_eB, FALSE, TRUE,
5342 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5345 Ydiamond_w, FALSE, FALSE,
5346 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5349 Ydiamond_wB, FALSE, TRUE,
5350 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5353 Ydiamond_eat, FALSE, FALSE,
5354 EL_DIAMOND, ACTION_COLLECTING, -1
5357 Ydiamond_stone, FALSE, FALSE,
5358 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5361 Xdrip_fall, TRUE, FALSE,
5362 EL_AMOEBA_DROP, -1, -1
5365 Xdrip_stretch, FALSE, FALSE,
5366 EL_AMOEBA_DROP, ACTION_FALLING, -1
5369 Xdrip_stretchB, FALSE, TRUE,
5370 EL_AMOEBA_DROP, ACTION_FALLING, -1
5373 Xdrip_eat, FALSE, FALSE,
5374 EL_AMOEBA_DROP, ACTION_GROWING, -1
5377 Ydrip_s1, FALSE, FALSE,
5378 EL_AMOEBA_DROP, ACTION_FALLING, -1
5381 Ydrip_s1B, FALSE, TRUE,
5382 EL_AMOEBA_DROP, ACTION_FALLING, -1
5385 Ydrip_s2, FALSE, FALSE,
5386 EL_AMOEBA_DROP, ACTION_FALLING, -1
5389 Ydrip_s2B, FALSE, TRUE,
5390 EL_AMOEBA_DROP, ACTION_FALLING, -1
5397 Xbomb_pause, FALSE, FALSE,
5401 Xbomb_fall, FALSE, FALSE,
5405 Ybomb_s, FALSE, FALSE,
5406 EL_BOMB, ACTION_FALLING, -1
5409 Ybomb_sB, FALSE, TRUE,
5410 EL_BOMB, ACTION_FALLING, -1
5413 Ybomb_e, FALSE, FALSE,
5414 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5417 Ybomb_eB, FALSE, TRUE,
5418 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5421 Ybomb_w, FALSE, FALSE,
5422 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5425 Ybomb_wB, FALSE, TRUE,
5426 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5429 Ybomb_eat, FALSE, FALSE,
5430 EL_BOMB, ACTION_ACTIVATING, -1
5433 Xballoon, TRUE, FALSE,
5437 Yballoon_n, FALSE, FALSE,
5438 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5441 Yballoon_nB, FALSE, TRUE,
5442 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5445 Yballoon_e, FALSE, FALSE,
5446 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5449 Yballoon_eB, FALSE, TRUE,
5450 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5453 Yballoon_s, FALSE, FALSE,
5454 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5457 Yballoon_sB, FALSE, TRUE,
5458 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5461 Yballoon_w, FALSE, FALSE,
5462 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5465 Yballoon_wB, FALSE, TRUE,
5466 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5469 Xgrass, TRUE, FALSE,
5470 EL_EMC_GRASS, -1, -1
5473 Ygrass_nB, FALSE, FALSE,
5474 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5477 Ygrass_eB, FALSE, FALSE,
5478 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5481 Ygrass_sB, FALSE, FALSE,
5482 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5485 Ygrass_wB, FALSE, FALSE,
5486 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5493 Ydirt_nB, FALSE, FALSE,
5494 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5497 Ydirt_eB, FALSE, FALSE,
5498 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5501 Ydirt_sB, FALSE, FALSE,
5502 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5505 Ydirt_wB, FALSE, FALSE,
5506 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5509 Xacid_ne, TRUE, FALSE,
5510 EL_ACID_POOL_TOPRIGHT, -1, -1
5513 Xacid_se, TRUE, FALSE,
5514 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5517 Xacid_s, TRUE, FALSE,
5518 EL_ACID_POOL_BOTTOM, -1, -1
5521 Xacid_sw, TRUE, FALSE,
5522 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5525 Xacid_nw, TRUE, FALSE,
5526 EL_ACID_POOL_TOPLEFT, -1, -1
5529 Xacid_1, TRUE, FALSE,
5533 Xacid_2, FALSE, FALSE,
5537 Xacid_3, FALSE, FALSE,
5541 Xacid_4, FALSE, FALSE,
5545 Xacid_5, FALSE, FALSE,
5549 Xacid_6, FALSE, FALSE,
5553 Xacid_7, FALSE, FALSE,
5557 Xacid_8, FALSE, FALSE,
5561 Xball_1, TRUE, FALSE,
5562 EL_EMC_MAGIC_BALL, -1, -1
5565 Xball_1B, FALSE, FALSE,
5566 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5569 Xball_2, FALSE, FALSE,
5570 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5573 Xball_2B, FALSE, FALSE,
5574 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5577 Yball_eat, FALSE, FALSE,
5578 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5581 Ykey_1_eat, FALSE, FALSE,
5582 EL_EM_KEY_1, ACTION_COLLECTING, -1
5585 Ykey_2_eat, FALSE, FALSE,
5586 EL_EM_KEY_2, ACTION_COLLECTING, -1
5589 Ykey_3_eat, FALSE, FALSE,
5590 EL_EM_KEY_3, ACTION_COLLECTING, -1
5593 Ykey_4_eat, FALSE, FALSE,
5594 EL_EM_KEY_4, ACTION_COLLECTING, -1
5597 Ykey_5_eat, FALSE, FALSE,
5598 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5601 Ykey_6_eat, FALSE, FALSE,
5602 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5605 Ykey_7_eat, FALSE, FALSE,
5606 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5609 Ykey_8_eat, FALSE, FALSE,
5610 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5613 Ylenses_eat, FALSE, FALSE,
5614 EL_EMC_LENSES, ACTION_COLLECTING, -1
5617 Ymagnify_eat, FALSE, FALSE,
5618 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5621 Ygrass_eat, FALSE, FALSE,
5622 EL_EMC_GRASS, ACTION_SNAPPING, -1
5625 Ydirt_eat, FALSE, FALSE,
5626 EL_SAND, ACTION_SNAPPING, -1
5629 Xgrow_ns, TRUE, FALSE,
5630 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5633 Ygrow_ns_eat, FALSE, FALSE,
5634 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5637 Xgrow_ew, TRUE, FALSE,
5638 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5641 Ygrow_ew_eat, FALSE, FALSE,
5642 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5645 Xwonderwall, TRUE, FALSE,
5646 EL_MAGIC_WALL, -1, -1
5649 XwonderwallB, FALSE, FALSE,
5650 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5653 Xamoeba_1, TRUE, FALSE,
5654 EL_AMOEBA_DRY, ACTION_OTHER, -1
5657 Xamoeba_2, FALSE, FALSE,
5658 EL_AMOEBA_DRY, ACTION_OTHER, -1
5661 Xamoeba_3, FALSE, FALSE,
5662 EL_AMOEBA_DRY, ACTION_OTHER, -1
5665 Xamoeba_4, FALSE, FALSE,
5666 EL_AMOEBA_DRY, ACTION_OTHER, -1
5669 Xamoeba_5, TRUE, FALSE,
5670 EL_AMOEBA_WET, ACTION_OTHER, -1
5673 Xamoeba_6, FALSE, FALSE,
5674 EL_AMOEBA_WET, ACTION_OTHER, -1
5677 Xamoeba_7, FALSE, FALSE,
5678 EL_AMOEBA_WET, ACTION_OTHER, -1
5681 Xamoeba_8, FALSE, FALSE,
5682 EL_AMOEBA_WET, ACTION_OTHER, -1
5685 Xdoor_1, TRUE, FALSE,
5686 EL_EM_GATE_1, -1, -1
5689 Xdoor_2, TRUE, FALSE,
5690 EL_EM_GATE_2, -1, -1
5693 Xdoor_3, TRUE, FALSE,
5694 EL_EM_GATE_3, -1, -1
5697 Xdoor_4, TRUE, FALSE,
5698 EL_EM_GATE_4, -1, -1
5701 Xdoor_5, TRUE, FALSE,
5702 EL_EMC_GATE_5, -1, -1
5705 Xdoor_6, TRUE, FALSE,
5706 EL_EMC_GATE_6, -1, -1
5709 Xdoor_7, TRUE, FALSE,
5710 EL_EMC_GATE_7, -1, -1
5713 Xdoor_8, TRUE, FALSE,
5714 EL_EMC_GATE_8, -1, -1
5717 Xkey_1, TRUE, FALSE,
5721 Xkey_2, TRUE, FALSE,
5725 Xkey_3, TRUE, FALSE,
5729 Xkey_4, TRUE, FALSE,
5733 Xkey_5, TRUE, FALSE,
5734 EL_EMC_KEY_5, -1, -1
5737 Xkey_6, TRUE, FALSE,
5738 EL_EMC_KEY_6, -1, -1
5741 Xkey_7, TRUE, FALSE,
5742 EL_EMC_KEY_7, -1, -1
5745 Xkey_8, TRUE, FALSE,
5746 EL_EMC_KEY_8, -1, -1
5749 Xwind_n, TRUE, FALSE,
5750 EL_BALLOON_SWITCH_UP, -1, -1
5753 Xwind_e, TRUE, FALSE,
5754 EL_BALLOON_SWITCH_RIGHT, -1, -1
5757 Xwind_s, TRUE, FALSE,
5758 EL_BALLOON_SWITCH_DOWN, -1, -1
5761 Xwind_w, TRUE, FALSE,
5762 EL_BALLOON_SWITCH_LEFT, -1, -1
5765 Xwind_nesw, TRUE, FALSE,
5766 EL_BALLOON_SWITCH_ANY, -1, -1
5769 Xwind_stop, TRUE, FALSE,
5770 EL_BALLOON_SWITCH_NONE, -1, -1
5774 EL_EM_EXIT_CLOSED, -1, -1
5777 Xexit_1, TRUE, FALSE,
5778 EL_EM_EXIT_OPEN, -1, -1
5781 Xexit_2, FALSE, FALSE,
5782 EL_EM_EXIT_OPEN, -1, -1
5785 Xexit_3, FALSE, FALSE,
5786 EL_EM_EXIT_OPEN, -1, -1
5789 Xdynamite, TRUE, FALSE,
5790 EL_EM_DYNAMITE, -1, -1
5793 Ydynamite_eat, FALSE, FALSE,
5794 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5797 Xdynamite_1, TRUE, FALSE,
5798 EL_EM_DYNAMITE_ACTIVE, -1, -1
5801 Xdynamite_2, FALSE, FALSE,
5802 EL_EM_DYNAMITE_ACTIVE, -1, -1
5805 Xdynamite_3, FALSE, FALSE,
5806 EL_EM_DYNAMITE_ACTIVE, -1, -1
5809 Xdynamite_4, FALSE, FALSE,
5810 EL_EM_DYNAMITE_ACTIVE, -1, -1
5813 Xbumper, TRUE, FALSE,
5814 EL_EMC_SPRING_BUMPER, -1, -1
5817 XbumperB, FALSE, FALSE,
5818 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5821 Xwheel, TRUE, FALSE,
5822 EL_ROBOT_WHEEL, -1, -1
5825 XwheelB, FALSE, FALSE,
5826 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5829 Xswitch, TRUE, FALSE,
5830 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5833 XswitchB, FALSE, FALSE,
5834 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5838 EL_QUICKSAND_EMPTY, -1, -1
5841 Xsand_stone, TRUE, FALSE,
5842 EL_QUICKSAND_FULL, -1, -1
5845 Xsand_stonein_1, FALSE, TRUE,
5846 EL_ROCK, ACTION_FILLING, -1
5849 Xsand_stonein_2, FALSE, TRUE,
5850 EL_ROCK, ACTION_FILLING, -1
5853 Xsand_stonein_3, FALSE, TRUE,
5854 EL_ROCK, ACTION_FILLING, -1
5857 Xsand_stonein_4, FALSE, TRUE,
5858 EL_ROCK, ACTION_FILLING, -1
5861 Xsand_stonesand_1, FALSE, FALSE,
5862 EL_QUICKSAND_EMPTYING, -1, -1
5865 Xsand_stonesand_2, FALSE, FALSE,
5866 EL_QUICKSAND_EMPTYING, -1, -1
5869 Xsand_stonesand_3, FALSE, FALSE,
5870 EL_QUICKSAND_EMPTYING, -1, -1
5873 Xsand_stonesand_4, FALSE, FALSE,
5874 EL_QUICKSAND_EMPTYING, -1, -1
5877 Xsand_stonesand_quickout_1, FALSE, FALSE,
5878 EL_QUICKSAND_EMPTYING, -1, -1
5881 Xsand_stonesand_quickout_2, FALSE, FALSE,
5882 EL_QUICKSAND_EMPTYING, -1, -1
5885 Xsand_stoneout_1, FALSE, FALSE,
5886 EL_ROCK, ACTION_EMPTYING, -1
5889 Xsand_stoneout_2, FALSE, FALSE,
5890 EL_ROCK, ACTION_EMPTYING, -1
5893 Xsand_sandstone_1, FALSE, FALSE,
5894 EL_QUICKSAND_FILLING, -1, -1
5897 Xsand_sandstone_2, FALSE, FALSE,
5898 EL_QUICKSAND_FILLING, -1, -1
5901 Xsand_sandstone_3, FALSE, FALSE,
5902 EL_QUICKSAND_FILLING, -1, -1
5905 Xsand_sandstone_4, FALSE, FALSE,
5906 EL_QUICKSAND_FILLING, -1, -1
5909 Xplant, TRUE, FALSE,
5910 EL_EMC_PLANT, -1, -1
5913 Yplant, FALSE, FALSE,
5914 EL_EMC_PLANT, -1, -1
5917 Xlenses, TRUE, FALSE,
5918 EL_EMC_LENSES, -1, -1
5921 Xmagnify, TRUE, FALSE,
5922 EL_EMC_MAGNIFIER, -1, -1
5925 Xdripper, TRUE, FALSE,
5926 EL_EMC_DRIPPER, -1, -1
5929 XdripperB, FALSE, FALSE,
5930 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5933 Xfake_blank, TRUE, FALSE,
5934 EL_INVISIBLE_WALL, -1, -1
5937 Xfake_blankB, FALSE, FALSE,
5938 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5941 Xfake_grass, TRUE, FALSE,
5942 EL_EMC_FAKE_GRASS, -1, -1
5945 Xfake_grassB, FALSE, FALSE,
5946 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5949 Xfake_door_1, TRUE, FALSE,
5950 EL_EM_GATE_1_GRAY, -1, -1
5953 Xfake_door_2, TRUE, FALSE,
5954 EL_EM_GATE_2_GRAY, -1, -1
5957 Xfake_door_3, TRUE, FALSE,
5958 EL_EM_GATE_3_GRAY, -1, -1
5961 Xfake_door_4, TRUE, FALSE,
5962 EL_EM_GATE_4_GRAY, -1, -1
5965 Xfake_door_5, TRUE, FALSE,
5966 EL_EMC_GATE_5_GRAY, -1, -1
5969 Xfake_door_6, TRUE, FALSE,
5970 EL_EMC_GATE_6_GRAY, -1, -1
5973 Xfake_door_7, TRUE, FALSE,
5974 EL_EMC_GATE_7_GRAY, -1, -1
5977 Xfake_door_8, TRUE, FALSE,
5978 EL_EMC_GATE_8_GRAY, -1, -1
5981 Xfake_acid_1, TRUE, FALSE,
5982 EL_EMC_FAKE_ACID, -1, -1
5985 Xfake_acid_2, FALSE, FALSE,
5986 EL_EMC_FAKE_ACID, -1, -1
5989 Xfake_acid_3, FALSE, FALSE,
5990 EL_EMC_FAKE_ACID, -1, -1
5993 Xfake_acid_4, FALSE, FALSE,
5994 EL_EMC_FAKE_ACID, -1, -1
5997 Xfake_acid_5, FALSE, FALSE,
5998 EL_EMC_FAKE_ACID, -1, -1
6001 Xfake_acid_6, FALSE, FALSE,
6002 EL_EMC_FAKE_ACID, -1, -1
6005 Xfake_acid_7, FALSE, FALSE,
6006 EL_EMC_FAKE_ACID, -1, -1
6009 Xfake_acid_8, FALSE, FALSE,
6010 EL_EMC_FAKE_ACID, -1, -1
6013 Xsteel_1, TRUE, FALSE,
6014 EL_STEELWALL, -1, -1
6017 Xsteel_2, TRUE, FALSE,
6018 EL_EMC_STEELWALL_2, -1, -1
6021 Xsteel_3, TRUE, FALSE,
6022 EL_EMC_STEELWALL_3, -1, -1
6025 Xsteel_4, TRUE, FALSE,
6026 EL_EMC_STEELWALL_4, -1, -1
6029 Xwall_1, TRUE, FALSE,
6033 Xwall_2, TRUE, FALSE,
6034 EL_EMC_WALL_14, -1, -1
6037 Xwall_3, TRUE, FALSE,
6038 EL_EMC_WALL_15, -1, -1
6041 Xwall_4, TRUE, FALSE,
6042 EL_EMC_WALL_16, -1, -1
6045 Xround_wall_1, TRUE, FALSE,
6046 EL_WALL_SLIPPERY, -1, -1
6049 Xround_wall_2, TRUE, FALSE,
6050 EL_EMC_WALL_SLIPPERY_2, -1, -1
6053 Xround_wall_3, TRUE, FALSE,
6054 EL_EMC_WALL_SLIPPERY_3, -1, -1
6057 Xround_wall_4, TRUE, FALSE,
6058 EL_EMC_WALL_SLIPPERY_4, -1, -1
6061 Xdecor_1, TRUE, FALSE,
6062 EL_EMC_WALL_8, -1, -1
6065 Xdecor_2, TRUE, FALSE,
6066 EL_EMC_WALL_6, -1, -1
6069 Xdecor_3, TRUE, FALSE,
6070 EL_EMC_WALL_4, -1, -1
6073 Xdecor_4, TRUE, FALSE,
6074 EL_EMC_WALL_7, -1, -1
6077 Xdecor_5, TRUE, FALSE,
6078 EL_EMC_WALL_5, -1, -1
6081 Xdecor_6, TRUE, FALSE,
6082 EL_EMC_WALL_9, -1, -1
6085 Xdecor_7, TRUE, FALSE,
6086 EL_EMC_WALL_10, -1, -1
6089 Xdecor_8, TRUE, FALSE,
6090 EL_EMC_WALL_1, -1, -1
6093 Xdecor_9, TRUE, FALSE,
6094 EL_EMC_WALL_2, -1, -1
6097 Xdecor_10, TRUE, FALSE,
6098 EL_EMC_WALL_3, -1, -1
6101 Xdecor_11, TRUE, FALSE,
6102 EL_EMC_WALL_11, -1, -1
6105 Xdecor_12, TRUE, FALSE,
6106 EL_EMC_WALL_12, -1, -1
6109 Xalpha_0, TRUE, FALSE,
6110 EL_CHAR('0'), -1, -1
6113 Xalpha_1, TRUE, FALSE,
6114 EL_CHAR('1'), -1, -1
6117 Xalpha_2, TRUE, FALSE,
6118 EL_CHAR('2'), -1, -1
6121 Xalpha_3, TRUE, FALSE,
6122 EL_CHAR('3'), -1, -1
6125 Xalpha_4, TRUE, FALSE,
6126 EL_CHAR('4'), -1, -1
6129 Xalpha_5, TRUE, FALSE,
6130 EL_CHAR('5'), -1, -1
6133 Xalpha_6, TRUE, FALSE,
6134 EL_CHAR('6'), -1, -1
6137 Xalpha_7, TRUE, FALSE,
6138 EL_CHAR('7'), -1, -1
6141 Xalpha_8, TRUE, FALSE,
6142 EL_CHAR('8'), -1, -1
6145 Xalpha_9, TRUE, FALSE,
6146 EL_CHAR('9'), -1, -1
6149 Xalpha_excla, TRUE, FALSE,
6150 EL_CHAR('!'), -1, -1
6153 Xalpha_quote, TRUE, FALSE,
6154 EL_CHAR('"'), -1, -1
6157 Xalpha_comma, TRUE, FALSE,
6158 EL_CHAR(','), -1, -1
6161 Xalpha_minus, TRUE, FALSE,
6162 EL_CHAR('-'), -1, -1
6165 Xalpha_perio, TRUE, FALSE,
6166 EL_CHAR('.'), -1, -1
6169 Xalpha_colon, TRUE, FALSE,
6170 EL_CHAR(':'), -1, -1
6173 Xalpha_quest, TRUE, FALSE,
6174 EL_CHAR('?'), -1, -1
6177 Xalpha_a, TRUE, FALSE,
6178 EL_CHAR('A'), -1, -1
6181 Xalpha_b, TRUE, FALSE,
6182 EL_CHAR('B'), -1, -1
6185 Xalpha_c, TRUE, FALSE,
6186 EL_CHAR('C'), -1, -1
6189 Xalpha_d, TRUE, FALSE,
6190 EL_CHAR('D'), -1, -1
6193 Xalpha_e, TRUE, FALSE,
6194 EL_CHAR('E'), -1, -1
6197 Xalpha_f, TRUE, FALSE,
6198 EL_CHAR('F'), -1, -1
6201 Xalpha_g, TRUE, FALSE,
6202 EL_CHAR('G'), -1, -1
6205 Xalpha_h, TRUE, FALSE,
6206 EL_CHAR('H'), -1, -1
6209 Xalpha_i, TRUE, FALSE,
6210 EL_CHAR('I'), -1, -1
6213 Xalpha_j, TRUE, FALSE,
6214 EL_CHAR('J'), -1, -1
6217 Xalpha_k, TRUE, FALSE,
6218 EL_CHAR('K'), -1, -1
6221 Xalpha_l, TRUE, FALSE,
6222 EL_CHAR('L'), -1, -1
6225 Xalpha_m, TRUE, FALSE,
6226 EL_CHAR('M'), -1, -1
6229 Xalpha_n, TRUE, FALSE,
6230 EL_CHAR('N'), -1, -1
6233 Xalpha_o, TRUE, FALSE,
6234 EL_CHAR('O'), -1, -1
6237 Xalpha_p, TRUE, FALSE,
6238 EL_CHAR('P'), -1, -1
6241 Xalpha_q, TRUE, FALSE,
6242 EL_CHAR('Q'), -1, -1
6245 Xalpha_r, TRUE, FALSE,
6246 EL_CHAR('R'), -1, -1
6249 Xalpha_s, TRUE, FALSE,
6250 EL_CHAR('S'), -1, -1
6253 Xalpha_t, TRUE, FALSE,
6254 EL_CHAR('T'), -1, -1
6257 Xalpha_u, TRUE, FALSE,
6258 EL_CHAR('U'), -1, -1
6261 Xalpha_v, TRUE, FALSE,
6262 EL_CHAR('V'), -1, -1
6265 Xalpha_w, TRUE, FALSE,
6266 EL_CHAR('W'), -1, -1
6269 Xalpha_x, TRUE, FALSE,
6270 EL_CHAR('X'), -1, -1
6273 Xalpha_y, TRUE, FALSE,
6274 EL_CHAR('Y'), -1, -1
6277 Xalpha_z, TRUE, FALSE,
6278 EL_CHAR('Z'), -1, -1
6281 Xalpha_arrow_e, TRUE, FALSE,
6282 EL_CHAR('>'), -1, -1
6285 Xalpha_arrow_w, TRUE, FALSE,
6286 EL_CHAR('<'), -1, -1
6289 Xalpha_copyr, TRUE, FALSE,
6290 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6294 Xboom_bug, FALSE, FALSE,
6295 EL_BUG, ACTION_EXPLODING, -1
6298 Xboom_bomb, FALSE, FALSE,
6299 EL_BOMB, ACTION_EXPLODING, -1
6302 Xboom_android, FALSE, FALSE,
6303 EL_EMC_ANDROID, ACTION_OTHER, -1
6306 Xboom_1, FALSE, FALSE,
6307 EL_DEFAULT, ACTION_EXPLODING, -1
6310 Xboom_2, FALSE, FALSE,
6311 EL_DEFAULT, ACTION_EXPLODING, -1
6314 Znormal, FALSE, FALSE,
6318 Zdynamite, FALSE, FALSE,
6322 Zplayer, FALSE, FALSE,
6326 ZBORDER, FALSE, FALSE,
6336 static struct Mapping_EM_to_RND_player
6345 em_player_mapping_list[] =
6349 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6353 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6357 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6361 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6365 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6369 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6373 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6377 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6381 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6385 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6389 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6393 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6397 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6401 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6405 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6409 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6413 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6417 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6421 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6425 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6429 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6433 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6437 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6441 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6445 EL_PLAYER_1, ACTION_DEFAULT, -1,
6449 EL_PLAYER_2, ACTION_DEFAULT, -1,
6453 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6457 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6461 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6465 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6469 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6473 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6477 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6481 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6485 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6489 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6493 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6497 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6501 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6505 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6509 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6513 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6517 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6521 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6525 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6529 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6533 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6537 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6541 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6545 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6549 EL_PLAYER_3, ACTION_DEFAULT, -1,
6553 EL_PLAYER_4, ACTION_DEFAULT, -1,
6562 int map_element_RND_to_EM(int element_rnd)
6564 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6565 static boolean mapping_initialized = FALSE;
6567 if (!mapping_initialized)
6571 /* return "Xalpha_quest" for all undefined elements in mapping array */
6572 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6573 mapping_RND_to_EM[i] = Xalpha_quest;
6575 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6576 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6577 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6578 em_object_mapping_list[i].element_em;
6580 mapping_initialized = TRUE;
6583 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6584 return mapping_RND_to_EM[element_rnd];
6586 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6591 int map_element_EM_to_RND(int element_em)
6593 static unsigned short mapping_EM_to_RND[TILE_MAX];
6594 static boolean mapping_initialized = FALSE;
6596 if (!mapping_initialized)
6600 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6601 for (i = 0; i < TILE_MAX; i++)
6602 mapping_EM_to_RND[i] = EL_UNKNOWN;
6604 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6605 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6606 em_object_mapping_list[i].element_rnd;
6608 mapping_initialized = TRUE;
6611 if (element_em >= 0 && element_em < TILE_MAX)
6612 return mapping_EM_to_RND[element_em];
6614 Error(ERR_WARN, "invalid EM level element %d", element_em);
6619 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6621 struct LevelInfo_EM *level_em = level->native_em_level;
6622 struct LEVEL *lev = level_em->lev;
6625 for (i = 0; i < TILE_MAX; i++)
6626 lev->android_array[i] = Xblank;
6628 for (i = 0; i < level->num_android_clone_elements; i++)
6630 int element_rnd = level->android_clone_element[i];
6631 int element_em = map_element_RND_to_EM(element_rnd);
6633 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6634 if (em_object_mapping_list[j].element_rnd == element_rnd)
6635 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6639 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6641 struct LevelInfo_EM *level_em = level->native_em_level;
6642 struct LEVEL *lev = level_em->lev;
6645 level->num_android_clone_elements = 0;
6647 for (i = 0; i < TILE_MAX; i++)
6649 int element_em = lev->android_array[i];
6651 boolean element_found = FALSE;
6653 if (element_em == Xblank)
6656 element_rnd = map_element_EM_to_RND(element_em);
6658 for (j = 0; j < level->num_android_clone_elements; j++)
6659 if (level->android_clone_element[j] == element_rnd)
6660 element_found = TRUE;
6664 level->android_clone_element[level->num_android_clone_elements++] =
6667 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6672 if (level->num_android_clone_elements == 0)
6674 level->num_android_clone_elements = 1;
6675 level->android_clone_element[0] = EL_EMPTY;
6679 int map_direction_RND_to_EM(int direction)
6681 return (direction == MV_UP ? 0 :
6682 direction == MV_RIGHT ? 1 :
6683 direction == MV_DOWN ? 2 :
6684 direction == MV_LEFT ? 3 :
6688 int map_direction_EM_to_RND(int direction)
6690 return (direction == 0 ? MV_UP :
6691 direction == 1 ? MV_RIGHT :
6692 direction == 2 ? MV_DOWN :
6693 direction == 3 ? MV_LEFT :
6697 int map_element_RND_to_SP(int element_rnd)
6699 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6701 if (element_rnd >= EL_SP_START &&
6702 element_rnd <= EL_SP_END)
6703 element_sp = element_rnd - EL_SP_START;
6704 else if (element_rnd == EL_EMPTY_SPACE)
6706 else if (element_rnd == EL_INVISIBLE_WALL)
6712 int map_element_SP_to_RND(int element_sp)
6714 int element_rnd = EL_UNKNOWN;
6716 if (element_sp >= 0x00 &&
6718 element_rnd = EL_SP_START + element_sp;
6719 else if (element_sp == 0x28)
6720 element_rnd = EL_INVISIBLE_WALL;
6725 int map_action_SP_to_RND(int action_sp)
6729 case actActive: return ACTION_ACTIVE;
6730 case actImpact: return ACTION_IMPACT;
6731 case actExploding: return ACTION_EXPLODING;
6732 case actDigging: return ACTION_DIGGING;
6733 case actSnapping: return ACTION_SNAPPING;
6734 case actCollecting: return ACTION_COLLECTING;
6735 case actPassing: return ACTION_PASSING;
6736 case actPushing: return ACTION_PUSHING;
6737 case actDropping: return ACTION_DROPPING;
6739 default: return ACTION_DEFAULT;
6743 int get_next_element(int element)
6747 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6748 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6749 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6750 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6751 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6752 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6753 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6754 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6755 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6756 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6757 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6759 default: return element;
6763 int el_act_dir2img(int element, int action, int direction)
6765 element = GFX_ELEMENT(element);
6766 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6768 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6769 return element_info[element].direction_graphic[action][direction];
6772 static int el_act_dir2crm(int element, int action, int direction)
6774 element = GFX_ELEMENT(element);
6775 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6777 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6778 return element_info[element].direction_crumbled[action][direction];
6781 int el_act2img(int element, int action)
6783 element = GFX_ELEMENT(element);
6785 return element_info[element].graphic[action];
6788 int el_act2crm(int element, int action)
6790 element = GFX_ELEMENT(element);
6792 return element_info[element].crumbled[action];
6795 int el_dir2img(int element, int direction)
6797 element = GFX_ELEMENT(element);
6799 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6802 int el2baseimg(int element)
6804 return element_info[element].graphic[ACTION_DEFAULT];
6807 int el2img(int element)
6809 element = GFX_ELEMENT(element);
6811 return element_info[element].graphic[ACTION_DEFAULT];
6814 int el2edimg(int element)
6816 element = GFX_ELEMENT(element);
6818 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6821 int el2preimg(int element)
6823 element = GFX_ELEMENT(element);
6825 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6828 int el2panelimg(int element)
6830 element = GFX_ELEMENT(element);
6832 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6835 int font2baseimg(int font_nr)
6837 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6840 int getBeltNrFromBeltElement(int element)
6842 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6843 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6844 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6847 int getBeltNrFromBeltActiveElement(int element)
6849 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6850 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6851 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6854 int getBeltNrFromBeltSwitchElement(int element)
6856 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6857 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6858 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6861 int getBeltDirNrFromBeltElement(int element)
6863 static int belt_base_element[4] =
6865 EL_CONVEYOR_BELT_1_LEFT,
6866 EL_CONVEYOR_BELT_2_LEFT,
6867 EL_CONVEYOR_BELT_3_LEFT,
6868 EL_CONVEYOR_BELT_4_LEFT
6871 int belt_nr = getBeltNrFromBeltElement(element);
6872 int belt_dir_nr = element - belt_base_element[belt_nr];
6874 return (belt_dir_nr % 3);
6877 int getBeltDirNrFromBeltSwitchElement(int element)
6879 static int belt_base_element[4] =
6881 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6882 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6883 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6884 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6887 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6888 int belt_dir_nr = element - belt_base_element[belt_nr];
6890 return (belt_dir_nr % 3);
6893 int getBeltDirFromBeltElement(int element)
6895 static int belt_move_dir[3] =
6902 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6904 return belt_move_dir[belt_dir_nr];
6907 int getBeltDirFromBeltSwitchElement(int element)
6909 static int belt_move_dir[3] =
6916 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6918 return belt_move_dir[belt_dir_nr];
6921 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6923 static int belt_base_element[4] =
6925 EL_CONVEYOR_BELT_1_LEFT,
6926 EL_CONVEYOR_BELT_2_LEFT,
6927 EL_CONVEYOR_BELT_3_LEFT,
6928 EL_CONVEYOR_BELT_4_LEFT
6931 return belt_base_element[belt_nr] + belt_dir_nr;
6934 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6936 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6938 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6941 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6943 static int belt_base_element[4] =
6945 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6946 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6947 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6948 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6951 return belt_base_element[belt_nr] + belt_dir_nr;
6954 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6956 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6958 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6961 boolean getTeamMode_EM()
6963 return game.team_mode;
6966 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6968 int game_frame_delay_value;
6970 game_frame_delay_value =
6971 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6972 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6975 if (tape.playing && tape.warp_forward && !tape.pausing)
6976 game_frame_delay_value = 0;
6978 return game_frame_delay_value;
6981 unsigned int InitRND(int seed)
6983 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6984 return InitEngineRandom_EM(seed);
6985 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6986 return InitEngineRandom_SP(seed);
6988 return InitEngineRandom_RND(seed);
6991 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6992 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6994 inline static int get_effective_element_EM(int tile, int frame_em)
6996 int element = object_mapping[tile].element_rnd;
6997 int action = object_mapping[tile].action;
6998 boolean is_backside = object_mapping[tile].is_backside;
6999 boolean action_removing = (action == ACTION_DIGGING ||
7000 action == ACTION_SNAPPING ||
7001 action == ACTION_COLLECTING);
7007 case Yacid_splash_eB:
7008 case Yacid_splash_wB:
7009 return (frame_em > 5 ? EL_EMPTY : element);
7015 else /* frame_em == 7 */
7019 case Yacid_splash_eB:
7020 case Yacid_splash_wB:
7023 case Yemerald_stone:
7026 case Ydiamond_stone:
7030 case Xdrip_stretchB:
7049 case Xsand_stonein_1:
7050 case Xsand_stonein_2:
7051 case Xsand_stonein_3:
7052 case Xsand_stonein_4:
7056 return (is_backside || action_removing ? EL_EMPTY : element);
7061 inline static boolean check_linear_animation_EM(int tile)
7065 case Xsand_stonesand_1:
7066 case Xsand_stonesand_quickout_1:
7067 case Xsand_sandstone_1:
7068 case Xsand_stonein_1:
7069 case Xsand_stoneout_1:
7088 case Yacid_splash_eB:
7089 case Yacid_splash_wB:
7090 case Yemerald_stone:
7097 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7098 boolean has_crumbled_graphics,
7099 int crumbled, int sync_frame)
7101 /* if element can be crumbled, but certain action graphics are just empty
7102 space (like instantly snapping sand to empty space in 1 frame), do not
7103 treat these empty space graphics as crumbled graphics in EMC engine */
7104 if (crumbled == IMG_EMPTY_SPACE)
7105 has_crumbled_graphics = FALSE;
7107 if (has_crumbled_graphics)
7109 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7110 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7111 g_crumbled->anim_delay,
7112 g_crumbled->anim_mode,
7113 g_crumbled->anim_start_frame,
7116 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7117 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7119 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7121 g_em->has_crumbled_graphics = TRUE;
7125 g_em->crumbled_bitmap = NULL;
7126 g_em->crumbled_src_x = 0;
7127 g_em->crumbled_src_y = 0;
7128 g_em->crumbled_border_size = 0;
7130 g_em->has_crumbled_graphics = FALSE;
7134 void ResetGfxAnimation_EM(int x, int y, int tile)
7139 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7140 int tile, int frame_em, int x, int y)
7142 int action = object_mapping[tile].action;
7143 int direction = object_mapping[tile].direction;
7144 int effective_element = get_effective_element_EM(tile, frame_em);
7145 int graphic = (direction == MV_NONE ?
7146 el_act2img(effective_element, action) :
7147 el_act_dir2img(effective_element, action, direction));
7148 struct GraphicInfo *g = &graphic_info[graphic];
7150 boolean action_removing = (action == ACTION_DIGGING ||
7151 action == ACTION_SNAPPING ||
7152 action == ACTION_COLLECTING);
7153 boolean action_moving = (action == ACTION_FALLING ||
7154 action == ACTION_MOVING ||
7155 action == ACTION_PUSHING ||
7156 action == ACTION_EATING ||
7157 action == ACTION_FILLING ||
7158 action == ACTION_EMPTYING);
7159 boolean action_falling = (action == ACTION_FALLING ||
7160 action == ACTION_FILLING ||
7161 action == ACTION_EMPTYING);
7163 /* special case: graphic uses "2nd movement tile" and has defined
7164 7 frames for movement animation (or less) => use default graphic
7165 for last (8th) frame which ends the movement animation */
7166 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7168 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7169 graphic = (direction == MV_NONE ?
7170 el_act2img(effective_element, action) :
7171 el_act_dir2img(effective_element, action, direction));
7173 g = &graphic_info[graphic];
7176 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7180 else if (action_moving)
7182 boolean is_backside = object_mapping[tile].is_backside;
7186 int direction = object_mapping[tile].direction;
7187 int move_dir = (action_falling ? MV_DOWN : direction);
7192 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7193 if (g->double_movement && frame_em == 0)
7197 if (move_dir == MV_LEFT)
7198 GfxFrame[x - 1][y] = GfxFrame[x][y];
7199 else if (move_dir == MV_RIGHT)
7200 GfxFrame[x + 1][y] = GfxFrame[x][y];
7201 else if (move_dir == MV_UP)
7202 GfxFrame[x][y - 1] = GfxFrame[x][y];
7203 else if (move_dir == MV_DOWN)
7204 GfxFrame[x][y + 1] = GfxFrame[x][y];
7211 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7212 if (tile == Xsand_stonesand_quickout_1 ||
7213 tile == Xsand_stonesand_quickout_2)
7217 if (graphic_info[graphic].anim_global_sync)
7218 sync_frame = FrameCounter;
7219 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7220 sync_frame = GfxFrame[x][y];
7222 sync_frame = 0; /* playfield border (pseudo steel) */
7224 SetRandomAnimationValue(x, y);
7226 int frame = getAnimationFrame(g->anim_frames,
7229 g->anim_start_frame,
7232 g_em->unique_identifier =
7233 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7236 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7237 int tile, int frame_em, int x, int y)
7239 int action = object_mapping[tile].action;
7240 int direction = object_mapping[tile].direction;
7241 boolean is_backside = object_mapping[tile].is_backside;
7242 int effective_element = get_effective_element_EM(tile, frame_em);
7243 int effective_action = action;
7244 int graphic = (direction == MV_NONE ?
7245 el_act2img(effective_element, effective_action) :
7246 el_act_dir2img(effective_element, effective_action,
7248 int crumbled = (direction == MV_NONE ?
7249 el_act2crm(effective_element, effective_action) :
7250 el_act_dir2crm(effective_element, effective_action,
7252 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7253 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7254 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7255 struct GraphicInfo *g = &graphic_info[graphic];
7258 /* special case: graphic uses "2nd movement tile" and has defined
7259 7 frames for movement animation (or less) => use default graphic
7260 for last (8th) frame which ends the movement animation */
7261 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7263 effective_action = ACTION_DEFAULT;
7264 graphic = (direction == MV_NONE ?
7265 el_act2img(effective_element, effective_action) :
7266 el_act_dir2img(effective_element, effective_action,
7268 crumbled = (direction == MV_NONE ?
7269 el_act2crm(effective_element, effective_action) :
7270 el_act_dir2crm(effective_element, effective_action,
7273 g = &graphic_info[graphic];
7276 if (graphic_info[graphic].anim_global_sync)
7277 sync_frame = FrameCounter;
7278 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7279 sync_frame = GfxFrame[x][y];
7281 sync_frame = 0; /* playfield border (pseudo steel) */
7283 SetRandomAnimationValue(x, y);
7285 int frame = getAnimationFrame(g->anim_frames,
7288 g->anim_start_frame,
7291 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7292 g->double_movement && is_backside);
7294 /* (updating the "crumbled" graphic definitions is probably not really needed,
7295 as animations for crumbled graphics can't be longer than one EMC cycle) */
7296 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7300 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7301 int player_nr, int anim, int frame_em)
7303 int element = player_mapping[player_nr][anim].element_rnd;
7304 int action = player_mapping[player_nr][anim].action;
7305 int direction = player_mapping[player_nr][anim].direction;
7306 int graphic = (direction == MV_NONE ?
7307 el_act2img(element, action) :
7308 el_act_dir2img(element, action, direction));
7309 struct GraphicInfo *g = &graphic_info[graphic];
7312 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7314 stored_player[player_nr].StepFrame = frame_em;
7316 sync_frame = stored_player[player_nr].Frame;
7318 int frame = getAnimationFrame(g->anim_frames,
7321 g->anim_start_frame,
7324 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7325 &g_em->src_x, &g_em->src_y, FALSE);
7328 void InitGraphicInfo_EM(void)
7333 int num_em_gfx_errors = 0;
7335 if (graphic_info_em_object[0][0].bitmap == NULL)
7337 /* EM graphics not yet initialized in em_open_all() */
7342 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7345 /* always start with reliable default values */
7346 for (i = 0; i < TILE_MAX; i++)
7348 object_mapping[i].element_rnd = EL_UNKNOWN;
7349 object_mapping[i].is_backside = FALSE;
7350 object_mapping[i].action = ACTION_DEFAULT;
7351 object_mapping[i].direction = MV_NONE;
7354 /* always start with reliable default values */
7355 for (p = 0; p < MAX_PLAYERS; p++)
7357 for (i = 0; i < SPR_MAX; i++)
7359 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7360 player_mapping[p][i].action = ACTION_DEFAULT;
7361 player_mapping[p][i].direction = MV_NONE;
7365 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7367 int e = em_object_mapping_list[i].element_em;
7369 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7370 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7372 if (em_object_mapping_list[i].action != -1)
7373 object_mapping[e].action = em_object_mapping_list[i].action;
7375 if (em_object_mapping_list[i].direction != -1)
7376 object_mapping[e].direction =
7377 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7380 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7382 int a = em_player_mapping_list[i].action_em;
7383 int p = em_player_mapping_list[i].player_nr;
7385 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7387 if (em_player_mapping_list[i].action != -1)
7388 player_mapping[p][a].action = em_player_mapping_list[i].action;
7390 if (em_player_mapping_list[i].direction != -1)
7391 player_mapping[p][a].direction =
7392 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7395 for (i = 0; i < TILE_MAX; i++)
7397 int element = object_mapping[i].element_rnd;
7398 int action = object_mapping[i].action;
7399 int direction = object_mapping[i].direction;
7400 boolean is_backside = object_mapping[i].is_backside;
7401 boolean action_exploding = ((action == ACTION_EXPLODING ||
7402 action == ACTION_SMASHED_BY_ROCK ||
7403 action == ACTION_SMASHED_BY_SPRING) &&
7404 element != EL_DIAMOND);
7405 boolean action_active = (action == ACTION_ACTIVE);
7406 boolean action_other = (action == ACTION_OTHER);
7408 for (j = 0; j < 8; j++)
7410 int effective_element = get_effective_element_EM(i, j);
7411 int effective_action = (j < 7 ? action :
7412 i == Xdrip_stretch ? action :
7413 i == Xdrip_stretchB ? action :
7414 i == Ydrip_s1 ? action :
7415 i == Ydrip_s1B ? action :
7416 i == Xball_1B ? action :
7417 i == Xball_2 ? action :
7418 i == Xball_2B ? action :
7419 i == Yball_eat ? action :
7420 i == Ykey_1_eat ? action :
7421 i == Ykey_2_eat ? action :
7422 i == Ykey_3_eat ? action :
7423 i == Ykey_4_eat ? action :
7424 i == Ykey_5_eat ? action :
7425 i == Ykey_6_eat ? action :
7426 i == Ykey_7_eat ? action :
7427 i == Ykey_8_eat ? action :
7428 i == Ylenses_eat ? action :
7429 i == Ymagnify_eat ? action :
7430 i == Ygrass_eat ? action :
7431 i == Ydirt_eat ? action :
7432 i == Xsand_stonein_1 ? action :
7433 i == Xsand_stonein_2 ? action :
7434 i == Xsand_stonein_3 ? action :
7435 i == Xsand_stonein_4 ? action :
7436 i == Xsand_stoneout_1 ? action :
7437 i == Xsand_stoneout_2 ? action :
7438 i == Xboom_android ? ACTION_EXPLODING :
7439 action_exploding ? ACTION_EXPLODING :
7440 action_active ? action :
7441 action_other ? action :
7443 int graphic = (el_act_dir2img(effective_element, effective_action,
7445 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7447 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7448 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7449 boolean has_action_graphics = (graphic != base_graphic);
7450 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7451 struct GraphicInfo *g = &graphic_info[graphic];
7452 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7455 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7456 boolean special_animation = (action != ACTION_DEFAULT &&
7457 g->anim_frames == 3 &&
7458 g->anim_delay == 2 &&
7459 g->anim_mode & ANIM_LINEAR);
7460 int sync_frame = (i == Xdrip_stretch ? 7 :
7461 i == Xdrip_stretchB ? 7 :
7462 i == Ydrip_s2 ? j + 8 :
7463 i == Ydrip_s2B ? j + 8 :
7472 i == Xfake_acid_1 ? 0 :
7473 i == Xfake_acid_2 ? 10 :
7474 i == Xfake_acid_3 ? 20 :
7475 i == Xfake_acid_4 ? 30 :
7476 i == Xfake_acid_5 ? 40 :
7477 i == Xfake_acid_6 ? 50 :
7478 i == Xfake_acid_7 ? 60 :
7479 i == Xfake_acid_8 ? 70 :
7481 i == Xball_2B ? j + 8 :
7482 i == Yball_eat ? j + 1 :
7483 i == Ykey_1_eat ? j + 1 :
7484 i == Ykey_2_eat ? j + 1 :
7485 i == Ykey_3_eat ? j + 1 :
7486 i == Ykey_4_eat ? j + 1 :
7487 i == Ykey_5_eat ? j + 1 :
7488 i == Ykey_6_eat ? j + 1 :
7489 i == Ykey_7_eat ? j + 1 :
7490 i == Ykey_8_eat ? j + 1 :
7491 i == Ylenses_eat ? j + 1 :
7492 i == Ymagnify_eat ? j + 1 :
7493 i == Ygrass_eat ? j + 1 :
7494 i == Ydirt_eat ? j + 1 :
7495 i == Xamoeba_1 ? 0 :
7496 i == Xamoeba_2 ? 1 :
7497 i == Xamoeba_3 ? 2 :
7498 i == Xamoeba_4 ? 3 :
7499 i == Xamoeba_5 ? 0 :
7500 i == Xamoeba_6 ? 1 :
7501 i == Xamoeba_7 ? 2 :
7502 i == Xamoeba_8 ? 3 :
7503 i == Xexit_2 ? j + 8 :
7504 i == Xexit_3 ? j + 16 :
7505 i == Xdynamite_1 ? 0 :
7506 i == Xdynamite_2 ? 8 :
7507 i == Xdynamite_3 ? 16 :
7508 i == Xdynamite_4 ? 24 :
7509 i == Xsand_stonein_1 ? j + 1 :
7510 i == Xsand_stonein_2 ? j + 9 :
7511 i == Xsand_stonein_3 ? j + 17 :
7512 i == Xsand_stonein_4 ? j + 25 :
7513 i == Xsand_stoneout_1 && j == 0 ? 0 :
7514 i == Xsand_stoneout_1 && j == 1 ? 0 :
7515 i == Xsand_stoneout_1 && j == 2 ? 1 :
7516 i == Xsand_stoneout_1 && j == 3 ? 2 :
7517 i == Xsand_stoneout_1 && j == 4 ? 2 :
7518 i == Xsand_stoneout_1 && j == 5 ? 3 :
7519 i == Xsand_stoneout_1 && j == 6 ? 4 :
7520 i == Xsand_stoneout_1 && j == 7 ? 4 :
7521 i == Xsand_stoneout_2 && j == 0 ? 5 :
7522 i == Xsand_stoneout_2 && j == 1 ? 6 :
7523 i == Xsand_stoneout_2 && j == 2 ? 7 :
7524 i == Xsand_stoneout_2 && j == 3 ? 8 :
7525 i == Xsand_stoneout_2 && j == 4 ? 9 :
7526 i == Xsand_stoneout_2 && j == 5 ? 11 :
7527 i == Xsand_stoneout_2 && j == 6 ? 13 :
7528 i == Xsand_stoneout_2 && j == 7 ? 15 :
7529 i == Xboom_bug && j == 1 ? 2 :
7530 i == Xboom_bug && j == 2 ? 2 :
7531 i == Xboom_bug && j == 3 ? 4 :
7532 i == Xboom_bug && j == 4 ? 4 :
7533 i == Xboom_bug && j == 5 ? 2 :
7534 i == Xboom_bug && j == 6 ? 2 :
7535 i == Xboom_bug && j == 7 ? 0 :
7536 i == Xboom_bomb && j == 1 ? 2 :
7537 i == Xboom_bomb && j == 2 ? 2 :
7538 i == Xboom_bomb && j == 3 ? 4 :
7539 i == Xboom_bomb && j == 4 ? 4 :
7540 i == Xboom_bomb && j == 5 ? 2 :
7541 i == Xboom_bomb && j == 6 ? 2 :
7542 i == Xboom_bomb && j == 7 ? 0 :
7543 i == Xboom_android && j == 7 ? 6 :
7544 i == Xboom_1 && j == 1 ? 2 :
7545 i == Xboom_1 && j == 2 ? 2 :
7546 i == Xboom_1 && j == 3 ? 4 :
7547 i == Xboom_1 && j == 4 ? 4 :
7548 i == Xboom_1 && j == 5 ? 6 :
7549 i == Xboom_1 && j == 6 ? 6 :
7550 i == Xboom_1 && j == 7 ? 8 :
7551 i == Xboom_2 && j == 0 ? 8 :
7552 i == Xboom_2 && j == 1 ? 8 :
7553 i == Xboom_2 && j == 2 ? 10 :
7554 i == Xboom_2 && j == 3 ? 10 :
7555 i == Xboom_2 && j == 4 ? 10 :
7556 i == Xboom_2 && j == 5 ? 12 :
7557 i == Xboom_2 && j == 6 ? 12 :
7558 i == Xboom_2 && j == 7 ? 12 :
7559 special_animation && j == 4 ? 3 :
7560 effective_action != action ? 0 :
7564 Bitmap *debug_bitmap = g_em->bitmap;
7565 int debug_src_x = g_em->src_x;
7566 int debug_src_y = g_em->src_y;
7569 int frame = getAnimationFrame(g->anim_frames,
7572 g->anim_start_frame,
7575 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7576 g->double_movement && is_backside);
7578 g_em->bitmap = src_bitmap;
7579 g_em->src_x = src_x;
7580 g_em->src_y = src_y;
7581 g_em->src_offset_x = 0;
7582 g_em->src_offset_y = 0;
7583 g_em->dst_offset_x = 0;
7584 g_em->dst_offset_y = 0;
7585 g_em->width = TILEX;
7586 g_em->height = TILEY;
7588 g_em->preserve_background = FALSE;
7590 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7593 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7594 effective_action == ACTION_MOVING ||
7595 effective_action == ACTION_PUSHING ||
7596 effective_action == ACTION_EATING)) ||
7597 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7598 effective_action == ACTION_EMPTYING)))
7601 (effective_action == ACTION_FALLING ||
7602 effective_action == ACTION_FILLING ||
7603 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7604 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7605 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7606 int num_steps = (i == Ydrip_s1 ? 16 :
7607 i == Ydrip_s1B ? 16 :
7608 i == Ydrip_s2 ? 16 :
7609 i == Ydrip_s2B ? 16 :
7610 i == Xsand_stonein_1 ? 32 :
7611 i == Xsand_stonein_2 ? 32 :
7612 i == Xsand_stonein_3 ? 32 :
7613 i == Xsand_stonein_4 ? 32 :
7614 i == Xsand_stoneout_1 ? 16 :
7615 i == Xsand_stoneout_2 ? 16 : 8);
7616 int cx = ABS(dx) * (TILEX / num_steps);
7617 int cy = ABS(dy) * (TILEY / num_steps);
7618 int step_frame = (i == Ydrip_s2 ? j + 8 :
7619 i == Ydrip_s2B ? j + 8 :
7620 i == Xsand_stonein_2 ? j + 8 :
7621 i == Xsand_stonein_3 ? j + 16 :
7622 i == Xsand_stonein_4 ? j + 24 :
7623 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7624 int step = (is_backside ? step_frame : num_steps - step_frame);
7626 if (is_backside) /* tile where movement starts */
7628 if (dx < 0 || dy < 0)
7630 g_em->src_offset_x = cx * step;
7631 g_em->src_offset_y = cy * step;
7635 g_em->dst_offset_x = cx * step;
7636 g_em->dst_offset_y = cy * step;
7639 else /* tile where movement ends */
7641 if (dx < 0 || dy < 0)
7643 g_em->dst_offset_x = cx * step;
7644 g_em->dst_offset_y = cy * step;
7648 g_em->src_offset_x = cx * step;
7649 g_em->src_offset_y = cy * step;
7653 g_em->width = TILEX - cx * step;
7654 g_em->height = TILEY - cy * step;
7657 /* create unique graphic identifier to decide if tile must be redrawn */
7658 /* bit 31 - 16 (16 bit): EM style graphic
7659 bit 15 - 12 ( 4 bit): EM style frame
7660 bit 11 - 6 ( 6 bit): graphic width
7661 bit 5 - 0 ( 6 bit): graphic height */
7662 g_em->unique_identifier =
7663 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7667 /* skip check for EMC elements not contained in original EMC artwork */
7668 if (element == EL_EMC_FAKE_ACID)
7671 if (g_em->bitmap != debug_bitmap ||
7672 g_em->src_x != debug_src_x ||
7673 g_em->src_y != debug_src_y ||
7674 g_em->src_offset_x != 0 ||
7675 g_em->src_offset_y != 0 ||
7676 g_em->dst_offset_x != 0 ||
7677 g_em->dst_offset_y != 0 ||
7678 g_em->width != TILEX ||
7679 g_em->height != TILEY)
7681 static int last_i = -1;
7689 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7690 i, element, element_info[element].token_name,
7691 element_action_info[effective_action].suffix, direction);
7693 if (element != effective_element)
7694 printf(" [%d ('%s')]",
7696 element_info[effective_element].token_name);
7700 if (g_em->bitmap != debug_bitmap)
7701 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7702 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7704 if (g_em->src_x != debug_src_x ||
7705 g_em->src_y != debug_src_y)
7706 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7707 j, (is_backside ? 'B' : 'F'),
7708 g_em->src_x, g_em->src_y,
7709 g_em->src_x / 32, g_em->src_y / 32,
7710 debug_src_x, debug_src_y,
7711 debug_src_x / 32, debug_src_y / 32);
7713 if (g_em->src_offset_x != 0 ||
7714 g_em->src_offset_y != 0 ||
7715 g_em->dst_offset_x != 0 ||
7716 g_em->dst_offset_y != 0)
7717 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7719 g_em->src_offset_x, g_em->src_offset_y,
7720 g_em->dst_offset_x, g_em->dst_offset_y);
7722 if (g_em->width != TILEX ||
7723 g_em->height != TILEY)
7724 printf(" %d (%d): size %d,%d should be %d,%d\n",
7726 g_em->width, g_em->height, TILEX, TILEY);
7728 num_em_gfx_errors++;
7735 for (i = 0; i < TILE_MAX; i++)
7737 for (j = 0; j < 8; j++)
7739 int element = object_mapping[i].element_rnd;
7740 int action = object_mapping[i].action;
7741 int direction = object_mapping[i].direction;
7742 boolean is_backside = object_mapping[i].is_backside;
7743 int graphic_action = el_act_dir2img(element, action, direction);
7744 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7746 if ((action == ACTION_SMASHED_BY_ROCK ||
7747 action == ACTION_SMASHED_BY_SPRING ||
7748 action == ACTION_EATING) &&
7749 graphic_action == graphic_default)
7751 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7752 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7753 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7754 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7757 /* no separate animation for "smashed by rock" -- use rock instead */
7758 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7759 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7761 g_em->bitmap = g_xx->bitmap;
7762 g_em->src_x = g_xx->src_x;
7763 g_em->src_y = g_xx->src_y;
7764 g_em->src_offset_x = g_xx->src_offset_x;
7765 g_em->src_offset_y = g_xx->src_offset_y;
7766 g_em->dst_offset_x = g_xx->dst_offset_x;
7767 g_em->dst_offset_y = g_xx->dst_offset_y;
7768 g_em->width = g_xx->width;
7769 g_em->height = g_xx->height;
7770 g_em->unique_identifier = g_xx->unique_identifier;
7773 g_em->preserve_background = TRUE;
7778 for (p = 0; p < MAX_PLAYERS; p++)
7780 for (i = 0; i < SPR_MAX; i++)
7782 int element = player_mapping[p][i].element_rnd;
7783 int action = player_mapping[p][i].action;
7784 int direction = player_mapping[p][i].direction;
7786 for (j = 0; j < 8; j++)
7788 int effective_element = element;
7789 int effective_action = action;
7790 int graphic = (direction == MV_NONE ?
7791 el_act2img(effective_element, effective_action) :
7792 el_act_dir2img(effective_element, effective_action,
7794 struct GraphicInfo *g = &graphic_info[graphic];
7795 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7801 Bitmap *debug_bitmap = g_em->bitmap;
7802 int debug_src_x = g_em->src_x;
7803 int debug_src_y = g_em->src_y;
7806 int frame = getAnimationFrame(g->anim_frames,
7809 g->anim_start_frame,
7812 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7814 g_em->bitmap = src_bitmap;
7815 g_em->src_x = src_x;
7816 g_em->src_y = src_y;
7817 g_em->src_offset_x = 0;
7818 g_em->src_offset_y = 0;
7819 g_em->dst_offset_x = 0;
7820 g_em->dst_offset_y = 0;
7821 g_em->width = TILEX;
7822 g_em->height = TILEY;
7826 /* skip check for EMC elements not contained in original EMC artwork */
7827 if (element == EL_PLAYER_3 ||
7828 element == EL_PLAYER_4)
7831 if (g_em->bitmap != debug_bitmap ||
7832 g_em->src_x != debug_src_x ||
7833 g_em->src_y != debug_src_y)
7835 static int last_i = -1;
7843 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7844 p, i, element, element_info[element].token_name,
7845 element_action_info[effective_action].suffix, direction);
7847 if (element != effective_element)
7848 printf(" [%d ('%s')]",
7850 element_info[effective_element].token_name);
7854 if (g_em->bitmap != debug_bitmap)
7855 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7856 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7858 if (g_em->src_x != debug_src_x ||
7859 g_em->src_y != debug_src_y)
7860 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7862 g_em->src_x, g_em->src_y,
7863 g_em->src_x / 32, g_em->src_y / 32,
7864 debug_src_x, debug_src_y,
7865 debug_src_x / 32, debug_src_y / 32);
7867 num_em_gfx_errors++;
7877 printf("::: [%d errors found]\n", num_em_gfx_errors);
7883 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7884 boolean any_player_moving,
7885 boolean any_player_snapping,
7886 boolean any_player_dropping)
7888 static boolean player_was_waiting = TRUE;
7890 if (frame == 0 && !any_player_dropping)
7892 if (!player_was_waiting)
7894 if (!SaveEngineSnapshotToList())
7897 player_was_waiting = TRUE;
7900 else if (any_player_moving || any_player_snapping || any_player_dropping)
7902 player_was_waiting = FALSE;
7906 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7907 boolean murphy_is_dropping)
7909 static boolean player_was_waiting = TRUE;
7911 if (murphy_is_waiting)
7913 if (!player_was_waiting)
7915 if (!SaveEngineSnapshotToList())
7918 player_was_waiting = TRUE;
7923 player_was_waiting = FALSE;
7927 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7928 boolean any_player_moving,
7929 boolean any_player_snapping,
7930 boolean any_player_dropping)
7932 if (tape.single_step && tape.recording && !tape.pausing)
7933 if (frame == 0 && !any_player_dropping)
7934 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7936 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7937 any_player_snapping, any_player_dropping);
7940 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7941 boolean murphy_is_dropping)
7943 if (tape.single_step && tape.recording && !tape.pausing)
7944 if (murphy_is_waiting)
7945 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7947 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7950 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7951 int graphic, int sync_frame, int x, int y)
7953 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7955 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7958 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7960 return (IS_NEXT_FRAME(sync_frame, graphic));
7963 int getGraphicInfo_Delay(int graphic)
7965 return graphic_info[graphic].anim_delay;
7968 void PlayMenuSoundExt(int sound)
7970 if (sound == SND_UNDEFINED)
7973 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7974 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7977 if (IS_LOOP_SOUND(sound))
7978 PlaySoundLoop(sound);
7983 void PlayMenuSound()
7985 PlayMenuSoundExt(menu.sound[game_status]);
7988 void PlayMenuSoundStereo(int sound, int stereo_position)
7990 if (sound == SND_UNDEFINED)
7993 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7994 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7997 if (IS_LOOP_SOUND(sound))
7998 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8000 PlaySoundStereo(sound, stereo_position);
8003 void PlayMenuSoundIfLoopExt(int sound)
8005 if (sound == SND_UNDEFINED)
8008 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8009 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8012 if (IS_LOOP_SOUND(sound))
8013 PlaySoundLoop(sound);
8016 void PlayMenuSoundIfLoop()
8018 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8021 void PlayMenuMusicExt(int music)
8023 if (music == MUS_UNDEFINED)
8026 if (!setup.sound_music)
8032 void PlayMenuMusic()
8034 PlayMenuMusicExt(menu.music[game_status]);
8037 void PlaySoundActivating()
8040 PlaySound(SND_MENU_ITEM_ACTIVATING);
8044 void PlaySoundSelecting()
8047 PlaySound(SND_MENU_ITEM_SELECTING);
8051 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8053 boolean change_fullscreen = (setup.fullscreen !=
8054 video.fullscreen_enabled);
8055 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8056 !strEqual(setup.fullscreen_mode,
8057 video.fullscreen_mode_current));
8058 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8059 setup.window_scaling_percent !=
8060 video.window_scaling_percent);
8062 if (change_window_scaling_percent && video.fullscreen_enabled)
8065 if (!change_window_scaling_percent && !video.fullscreen_available)
8068 #if defined(TARGET_SDL2)
8069 if (change_window_scaling_percent)
8071 SDLSetWindowScaling(setup.window_scaling_percent);
8075 else if (change_fullscreen)
8077 SDLSetWindowFullscreen(setup.fullscreen);
8079 /* set setup value according to successfully changed fullscreen mode */
8080 setup.fullscreen = video.fullscreen_enabled;
8086 if (change_fullscreen ||
8087 change_fullscreen_mode ||
8088 change_window_scaling_percent)
8090 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8092 /* save backbuffer content which gets lost when toggling fullscreen mode */
8093 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8095 if (change_fullscreen_mode)
8097 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8098 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8101 if (change_window_scaling_percent)
8103 /* keep window mode, but change window scaling */
8104 video.fullscreen_enabled = TRUE; /* force new window scaling */
8107 /* toggle fullscreen */
8108 ChangeVideoModeIfNeeded(setup.fullscreen);
8110 /* set setup value according to successfully changed fullscreen mode */
8111 setup.fullscreen = video.fullscreen_enabled;
8113 /* restore backbuffer content from temporary backbuffer backup bitmap */
8114 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8116 FreeBitmap(tmp_backbuffer);
8118 /* update visible window/screen */
8119 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8123 void JoinRectangles(int *x, int *y, int *width, int *height,
8124 int x2, int y2, int width2, int height2)
8126 // do not join with "off-screen" rectangle
8127 if (x2 == -1 || y2 == -1)
8132 *width = MAX(*width, width2);
8133 *height = MAX(*height, height2);
8136 void ChangeViewportPropertiesIfNeeded()
8138 int gfx_game_mode = game_status;
8139 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8141 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8142 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8143 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8144 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8145 int border_size = vp_playfield->border_size;
8146 int new_sx = vp_playfield->x + border_size;
8147 int new_sy = vp_playfield->y + border_size;
8148 int new_sxsize = vp_playfield->width - 2 * border_size;
8149 int new_sysize = vp_playfield->height - 2 * border_size;
8150 int new_real_sx = vp_playfield->x;
8151 int new_real_sy = vp_playfield->y;
8152 int new_full_sxsize = vp_playfield->width;
8153 int new_full_sysize = vp_playfield->height;
8154 int new_dx = vp_door_1->x;
8155 int new_dy = vp_door_1->y;
8156 int new_dxsize = vp_door_1->width;
8157 int new_dysize = vp_door_1->height;
8158 int new_vx = vp_door_2->x;
8159 int new_vy = vp_door_2->y;
8160 int new_vxsize = vp_door_2->width;
8161 int new_vysize = vp_door_2->height;
8162 int new_ex = vp_door_3->x;
8163 int new_ey = vp_door_3->y;
8164 int new_exsize = vp_door_3->width;
8165 int new_eysize = vp_door_3->height;
8166 int new_tilesize_var =
8167 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8169 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8170 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8171 int new_scr_fieldx = new_sxsize / tilesize;
8172 int new_scr_fieldy = new_sysize / tilesize;
8173 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8174 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8175 boolean init_gfx_buffers = FALSE;
8176 boolean init_video_buffer = FALSE;
8177 boolean init_gadgets_and_toons = FALSE;
8178 boolean init_em_graphics = FALSE;
8180 if (viewport.window.width != WIN_XSIZE ||
8181 viewport.window.height != WIN_YSIZE)
8183 WIN_XSIZE = viewport.window.width;
8184 WIN_YSIZE = viewport.window.height;
8186 init_video_buffer = TRUE;
8187 init_gfx_buffers = TRUE;
8189 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8192 if (new_scr_fieldx != SCR_FIELDX ||
8193 new_scr_fieldy != SCR_FIELDY)
8195 /* this always toggles between MAIN and GAME when using small tile size */
8197 SCR_FIELDX = new_scr_fieldx;
8198 SCR_FIELDY = new_scr_fieldy;
8200 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8211 new_sxsize != SXSIZE ||
8212 new_sysize != SYSIZE ||
8213 new_dxsize != DXSIZE ||
8214 new_dysize != DYSIZE ||
8215 new_vxsize != VXSIZE ||
8216 new_vysize != VYSIZE ||
8217 new_exsize != EXSIZE ||
8218 new_eysize != EYSIZE ||
8219 new_real_sx != REAL_SX ||
8220 new_real_sy != REAL_SY ||
8221 new_full_sxsize != FULL_SXSIZE ||
8222 new_full_sysize != FULL_SYSIZE ||
8223 new_tilesize_var != TILESIZE_VAR
8226 // ------------------------------------------------------------------------
8227 // determine next fading area for changed viewport definitions
8228 // ------------------------------------------------------------------------
8230 // start with current playfield area (default fading area)
8233 FADE_SXSIZE = FULL_SXSIZE;
8234 FADE_SYSIZE = FULL_SYSIZE;
8236 // add new playfield area if position or size has changed
8237 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8238 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8240 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8241 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8244 // add current and new door 1 area if position or size has changed
8245 if (new_dx != DX || new_dy != DY ||
8246 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8248 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8249 DX, DY, DXSIZE, DYSIZE);
8250 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8251 new_dx, new_dy, new_dxsize, new_dysize);
8254 // add current and new door 2 area if position or size has changed
8255 if (new_dx != VX || new_dy != VY ||
8256 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8258 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8259 VX, VY, VXSIZE, VYSIZE);
8260 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8261 new_vx, new_vy, new_vxsize, new_vysize);
8264 // ------------------------------------------------------------------------
8265 // handle changed tile size
8266 // ------------------------------------------------------------------------
8268 if (new_tilesize_var != TILESIZE_VAR)
8270 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8272 // changing tile size invalidates scroll values of engine snapshots
8273 FreeEngineSnapshotSingle();
8275 // changing tile size requires update of graphic mapping for EM engine
8276 init_em_graphics = TRUE;
8287 SXSIZE = new_sxsize;
8288 SYSIZE = new_sysize;
8289 DXSIZE = new_dxsize;
8290 DYSIZE = new_dysize;
8291 VXSIZE = new_vxsize;
8292 VYSIZE = new_vysize;
8293 EXSIZE = new_exsize;
8294 EYSIZE = new_eysize;
8295 REAL_SX = new_real_sx;
8296 REAL_SY = new_real_sy;
8297 FULL_SXSIZE = new_full_sxsize;
8298 FULL_SYSIZE = new_full_sysize;
8299 TILESIZE_VAR = new_tilesize_var;
8301 init_gfx_buffers = TRUE;
8302 init_gadgets_and_toons = TRUE;
8304 // printf("::: viewports: init_gfx_buffers\n");
8305 // printf("::: viewports: init_gadgets_and_toons\n");
8308 if (init_gfx_buffers)
8310 // printf("::: init_gfx_buffers\n");
8312 SCR_FIELDX = new_scr_fieldx_buffers;
8313 SCR_FIELDY = new_scr_fieldy_buffers;
8317 SCR_FIELDX = new_scr_fieldx;
8318 SCR_FIELDY = new_scr_fieldy;
8320 SetDrawDeactivationMask(REDRAW_NONE);
8321 SetDrawBackgroundMask(REDRAW_FIELD);
8324 if (init_video_buffer)
8326 // printf("::: init_video_buffer\n");
8328 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8331 if (init_gadgets_and_toons)
8333 // printf("::: init_gadgets_and_toons\n");
8339 if (init_em_graphics)
8341 InitGraphicInfo_EM();