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 [%d]\n", GfxFrame[x][y], FrameCounter);
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 (game_status == GAME_MODE_LOADING ||
334 game_status == 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[game_status];
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 last screen was title screen
830 if (game_status_last == GAME_MODE_TITLE)
833 // redraw if global screen border has changed
834 if (CheckIfGlobalBorderHasChanged())
837 // redraw if position or size of playfield area has changed
838 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
839 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
842 // redraw if position or size of door area has changed
843 if (dx_last != DX || dy_last != DY ||
844 dxsize_last != DXSIZE || dysize_last != DYSIZE)
847 // redraw if position or size of tape area has changed
848 if (vx_last != VX || vy_last != VY ||
849 vxsize_last != VXSIZE || vysize_last != VYSIZE)
855 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
858 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
860 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
863 void RedrawGlobalBorder()
865 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
867 RedrawGlobalBorderFromBitmap(bitmap);
869 redraw_mask = REDRAW_ALL;
872 static void RedrawGlobalBorderIfNeeded()
874 if (game_status == game_status_last)
877 // copy current draw buffer to later copy back areas that have not changed
878 if (game_status_last != GAME_MODE_TITLE)
879 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
881 if (CheckIfGlobalBorderRedrawIsNeeded())
883 // redraw global screen border (or clear, if defined to be empty)
884 RedrawGlobalBorderFromBitmap(global_border_bitmap);
886 // copy previous playfield and door areas, if they are defined on both
887 // previous and current screen and if they still have the same size
889 if (real_sx_last != -1 && real_sy_last != -1 &&
890 REAL_SX != -1 && REAL_SY != -1 &&
891 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
892 BlitBitmap(bitmap_db_store, backbuffer,
893 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
896 if (dx_last != -1 && dy_last != -1 &&
897 DX != -1 && DY != -1 &&
898 dxsize_last == DXSIZE && dysize_last == DYSIZE)
899 BlitBitmap(bitmap_db_store, backbuffer,
900 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
902 if (vx_last != -1 && vy_last != -1 &&
903 VX != -1 && VY != -1 &&
904 vxsize_last == VXSIZE && vysize_last == VYSIZE)
905 BlitBitmap(bitmap_db_store, backbuffer,
906 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
908 redraw_mask = REDRAW_ALL;
911 game_status_last = game_status;
913 global_border_bitmap_last = global_border_bitmap;
915 real_sx_last = REAL_SX;
916 real_sy_last = REAL_SY;
917 full_sxsize_last = FULL_SXSIZE;
918 full_sysize_last = FULL_SYSIZE;
921 dxsize_last = DXSIZE;
922 dysize_last = DYSIZE;
925 vxsize_last = VXSIZE;
926 vysize_last = VYSIZE;
931 RedrawGlobalBorderIfNeeded();
933 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
934 /* (when entering hall of fame after playing) */
935 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
937 /* !!! maybe this should be done before clearing the background !!! */
938 if (game_status == GAME_MODE_PLAYING)
940 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
941 SetDrawtoField(DRAW_FIELDBUFFER);
945 SetDrawtoField(DRAW_BACKBUFFER);
949 void MarkTileDirty(int x, int y)
951 redraw_mask |= REDRAW_FIELD;
954 void SetBorderElement()
958 BorderElement = EL_EMPTY;
960 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
962 for (x = 0; x < lev_fieldx; x++)
964 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
965 BorderElement = EL_STEELWALL;
967 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
973 void FloodFillLevel(int from_x, int from_y, int fill_element,
974 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
975 int max_fieldx, int max_fieldy)
979 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
980 static int safety = 0;
982 /* check if starting field still has the desired content */
983 if (field[from_x][from_y] == fill_element)
988 if (safety > max_fieldx * max_fieldy)
989 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
991 old_element = field[from_x][from_y];
992 field[from_x][from_y] = fill_element;
994 for (i = 0; i < 4; i++)
996 x = from_x + check[i][0];
997 y = from_y + check[i][1];
999 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1000 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1006 void SetRandomAnimationValue(int x, int y)
1008 gfx.anim_random_frame = GfxRandom[x][y];
1011 int getGraphicAnimationFrame(int graphic, int sync_frame)
1013 /* animation synchronized with global frame counter, not move position */
1014 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1015 sync_frame = FrameCounter;
1017 return getAnimationFrame(graphic_info[graphic].anim_frames,
1018 graphic_info[graphic].anim_delay,
1019 graphic_info[graphic].anim_mode,
1020 graphic_info[graphic].anim_start_frame,
1024 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1025 Bitmap **bitmap, int *x, int *y,
1026 boolean get_backside)
1028 struct GraphicInfo *g = &graphic_info[graphic];
1029 Bitmap *src_bitmap = g->bitmap;
1030 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1031 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1032 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1034 // if no in-game graphics defined, always use standard graphic size
1035 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1036 tilesize = TILESIZE;
1038 if (tilesize == gfx.standard_tile_size)
1039 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1040 else if (tilesize == game.tile_size)
1041 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1043 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1045 if (g->offset_y == 0) /* frames are ordered horizontally */
1047 int max_width = g->anim_frames_per_line * g->width;
1048 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1050 src_x = pos % max_width;
1051 src_y = src_y % g->height + pos / max_width * g->height;
1053 else if (g->offset_x == 0) /* frames are ordered vertically */
1055 int max_height = g->anim_frames_per_line * g->height;
1056 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1058 src_x = src_x % g->width + pos / max_height * g->width;
1059 src_y = pos % max_height;
1061 else /* frames are ordered diagonally */
1063 src_x = src_x + frame * g->offset_x;
1064 src_y = src_y + frame * g->offset_y;
1067 *bitmap = src_bitmap;
1068 *x = src_x * tilesize / g->tile_size;
1069 *y = src_y * tilesize / g->tile_size;
1072 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1073 int *x, int *y, boolean get_backside)
1075 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1079 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1080 Bitmap **bitmap, int *x, int *y)
1082 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1085 void getFixedGraphicSource(int graphic, int frame,
1086 Bitmap **bitmap, int *x, int *y)
1088 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1091 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1093 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1096 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1097 int *x, int *y, boolean get_backside)
1099 struct GraphicInfo *g = &graphic_info[graphic];
1100 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1101 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1103 if (TILESIZE_VAR != TILESIZE)
1104 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1107 *bitmap = g->bitmap;
1109 if (g->offset_y == 0) /* frames are ordered horizontally */
1111 int max_width = g->anim_frames_per_line * g->width;
1112 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1114 *x = pos % max_width;
1115 *y = src_y % g->height + pos / max_width * g->height;
1117 else if (g->offset_x == 0) /* frames are ordered vertically */
1119 int max_height = g->anim_frames_per_line * g->height;
1120 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1122 *x = src_x % g->width + pos / max_height * g->width;
1123 *y = pos % max_height;
1125 else /* frames are ordered diagonally */
1127 *x = src_x + frame * g->offset_x;
1128 *y = src_y + frame * g->offset_y;
1131 *x = *x * TILESIZE_VAR / g->tile_size;
1132 *y = *y * TILESIZE_VAR / g->tile_size;
1135 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1137 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1140 void DrawGraphic(int x, int y, int graphic, int frame)
1143 if (!IN_SCR_FIELD(x, y))
1145 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1146 printf("DrawGraphic(): This should never happen!\n");
1151 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1154 MarkTileDirty(x, y);
1157 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1160 if (!IN_SCR_FIELD(x, y))
1162 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1163 printf("DrawGraphic(): This should never happen!\n");
1168 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1170 MarkTileDirty(x, y);
1173 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1179 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1181 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1184 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1190 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1191 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1194 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1197 if (!IN_SCR_FIELD(x, y))
1199 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1200 printf("DrawGraphicThruMask(): This should never happen!\n");
1205 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1208 MarkTileDirty(x, y);
1211 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1214 if (!IN_SCR_FIELD(x, y))
1216 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1217 printf("DrawGraphicThruMask(): This should never happen!\n");
1222 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1224 MarkTileDirty(x, y);
1227 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1233 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1235 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1239 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1240 int graphic, int frame)
1245 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1247 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1251 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1253 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1255 MarkTileDirty(x / tilesize, y / tilesize);
1258 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1264 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1265 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1268 void DrawMiniGraphic(int x, int y, int graphic)
1270 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1271 MarkTileDirty(x / 2, y / 2);
1274 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1279 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1280 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1283 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1284 int graphic, int frame,
1285 int cut_mode, int mask_mode)
1290 int width = TILEX, height = TILEY;
1293 if (dx || dy) /* shifted graphic */
1295 if (x < BX1) /* object enters playfield from the left */
1302 else if (x > BX2) /* object enters playfield from the right */
1308 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1314 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1316 else if (dx) /* general horizontal movement */
1317 MarkTileDirty(x + SIGN(dx), y);
1319 if (y < BY1) /* object enters playfield from the top */
1321 if (cut_mode == CUT_BELOW) /* object completely above top border */
1329 else if (y > BY2) /* object enters playfield from the bottom */
1335 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1341 else if (dy > 0 && cut_mode == CUT_ABOVE)
1343 if (y == BY2) /* object completely above bottom border */
1349 MarkTileDirty(x, y + 1);
1350 } /* object leaves playfield to the bottom */
1351 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1353 else if (dy) /* general vertical movement */
1354 MarkTileDirty(x, y + SIGN(dy));
1358 if (!IN_SCR_FIELD(x, y))
1360 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1361 printf("DrawGraphicShifted(): This should never happen!\n");
1366 width = width * TILESIZE_VAR / TILESIZE;
1367 height = height * TILESIZE_VAR / TILESIZE;
1368 cx = cx * TILESIZE_VAR / TILESIZE;
1369 cy = cy * TILESIZE_VAR / TILESIZE;
1370 dx = dx * TILESIZE_VAR / TILESIZE;
1371 dy = dy * TILESIZE_VAR / TILESIZE;
1373 if (width > 0 && height > 0)
1375 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1380 dst_x = FX + x * TILEX_VAR + dx;
1381 dst_y = FY + y * TILEY_VAR + dy;
1383 if (mask_mode == USE_MASKING)
1384 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1387 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1390 MarkTileDirty(x, y);
1394 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1395 int graphic, int frame,
1396 int cut_mode, int mask_mode)
1401 int width = TILEX_VAR, height = TILEY_VAR;
1404 int x2 = x + SIGN(dx);
1405 int y2 = y + SIGN(dy);
1407 /* movement with two-tile animations must be sync'ed with movement position,
1408 not with current GfxFrame (which can be higher when using slow movement) */
1409 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1410 int anim_frames = graphic_info[graphic].anim_frames;
1412 /* (we also need anim_delay here for movement animations with less frames) */
1413 int anim_delay = graphic_info[graphic].anim_delay;
1414 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1416 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1417 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1419 /* re-calculate animation frame for two-tile movement animation */
1420 frame = getGraphicAnimationFrame(graphic, sync_frame);
1422 /* check if movement start graphic inside screen area and should be drawn */
1423 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1425 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1427 dst_x = FX + x1 * TILEX_VAR;
1428 dst_y = FY + y1 * TILEY_VAR;
1430 if (mask_mode == USE_MASKING)
1431 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1434 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1437 MarkTileDirty(x1, y1);
1440 /* check if movement end graphic inside screen area and should be drawn */
1441 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1443 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1445 dst_x = FX + x2 * TILEX_VAR;
1446 dst_y = FY + y2 * TILEY_VAR;
1448 if (mask_mode == USE_MASKING)
1449 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1452 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1455 MarkTileDirty(x2, y2);
1459 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1460 int graphic, int frame,
1461 int cut_mode, int mask_mode)
1465 DrawGraphic(x, y, graphic, frame);
1470 if (graphic_info[graphic].double_movement) /* EM style movement images */
1471 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1473 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1476 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1477 int frame, int cut_mode)
1479 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1482 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1483 int cut_mode, int mask_mode)
1485 int lx = LEVELX(x), ly = LEVELY(y);
1489 if (IN_LEV_FIELD(lx, ly))
1491 SetRandomAnimationValue(lx, ly);
1493 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1494 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1496 /* do not use double (EM style) movement graphic when not moving */
1497 if (graphic_info[graphic].double_movement && !dx && !dy)
1499 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1500 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1503 else /* border element */
1505 graphic = el2img(element);
1506 frame = getGraphicAnimationFrame(graphic, -1);
1509 if (element == EL_EXPANDABLE_WALL)
1511 boolean left_stopped = FALSE, right_stopped = FALSE;
1513 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1514 left_stopped = TRUE;
1515 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1516 right_stopped = TRUE;
1518 if (left_stopped && right_stopped)
1520 else if (left_stopped)
1522 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1523 frame = graphic_info[graphic].anim_frames - 1;
1525 else if (right_stopped)
1527 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1528 frame = graphic_info[graphic].anim_frames - 1;
1533 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1534 else if (mask_mode == USE_MASKING)
1535 DrawGraphicThruMask(x, y, graphic, frame);
1537 DrawGraphic(x, y, graphic, frame);
1540 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1541 int cut_mode, int mask_mode)
1543 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1544 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1545 cut_mode, mask_mode);
1548 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1551 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1554 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1557 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1560 void DrawLevelElementThruMask(int x, int y, int element)
1562 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1565 void DrawLevelFieldThruMask(int x, int y)
1567 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1570 /* !!! implementation of quicksand is totally broken !!! */
1571 #define IS_CRUMBLED_TILE(x, y, e) \
1572 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1573 !IS_MOVING(x, y) || \
1574 (e) == EL_QUICKSAND_EMPTYING || \
1575 (e) == EL_QUICKSAND_FAST_EMPTYING))
1577 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1582 int width, height, cx, cy;
1583 int sx = SCREENX(x), sy = SCREENY(y);
1584 int crumbled_border_size = graphic_info[graphic].border_size;
1587 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1589 for (i = 1; i < 4; i++)
1591 int dxx = (i & 1 ? dx : 0);
1592 int dyy = (i & 2 ? dy : 0);
1595 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1598 /* check if neighbour field is of same crumble type */
1599 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1600 graphic_info[graphic].class ==
1601 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1603 /* return if check prevents inner corner */
1604 if (same == (dxx == dx && dyy == dy))
1608 /* if we reach this point, we have an inner corner */
1610 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1612 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1613 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1614 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1615 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1617 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1618 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1621 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1626 int width, height, bx, by, cx, cy;
1627 int sx = SCREENX(x), sy = SCREENY(y);
1628 int crumbled_border_size = graphic_info[graphic].border_size;
1629 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1630 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1633 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1635 /* draw simple, sloppy, non-corner-accurate crumbled border */
1637 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1638 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1639 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1640 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1642 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1643 FX + sx * TILEX_VAR + cx,
1644 FY + sy * TILEY_VAR + cy);
1646 /* (remaining middle border part must be at least as big as corner part) */
1647 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1648 crumbled_border_size >= TILESIZE / 3)
1651 /* correct corners of crumbled border, if needed */
1653 for (i = -1; i <= 1; i += 2)
1655 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1656 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1657 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1660 /* check if neighbour field is of same crumble type */
1661 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1662 graphic_info[graphic].class ==
1663 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1665 /* no crumbled corner, but continued crumbled border */
1667 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1668 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1669 int b1 = (i == 1 ? crumbled_border_size_var :
1670 TILESIZE_VAR - 2 * crumbled_border_size_var);
1672 width = crumbled_border_size_var;
1673 height = crumbled_border_size_var;
1675 if (dir == 1 || dir == 2)
1690 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1692 FX + sx * TILEX_VAR + cx,
1693 FY + sy * TILEY_VAR + cy);
1698 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1700 int sx = SCREENX(x), sy = SCREENY(y);
1703 static int xy[4][2] =
1711 if (!IN_LEV_FIELD(x, y))
1714 element = TILE_GFX_ELEMENT(x, y);
1716 /* crumble field itself */
1717 if (IS_CRUMBLED_TILE(x, y, element))
1719 if (!IN_SCR_FIELD(sx, sy))
1722 for (i = 0; i < 4; i++)
1724 int xx = x + xy[i][0];
1725 int yy = y + xy[i][1];
1727 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1730 /* check if neighbour field is of same crumble type */
1731 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1732 graphic_info[graphic].class ==
1733 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1736 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1739 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1740 graphic_info[graphic].anim_frames == 2)
1742 for (i = 0; i < 4; i++)
1744 int dx = (i & 1 ? +1 : -1);
1745 int dy = (i & 2 ? +1 : -1);
1747 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1751 MarkTileDirty(sx, sy);
1753 else /* center field not crumbled -- crumble neighbour fields */
1755 for (i = 0; i < 4; i++)
1757 int xx = x + xy[i][0];
1758 int yy = y + xy[i][1];
1759 int sxx = sx + xy[i][0];
1760 int syy = sy + xy[i][1];
1762 if (!IN_LEV_FIELD(xx, yy) ||
1763 !IN_SCR_FIELD(sxx, syy))
1766 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1769 element = TILE_GFX_ELEMENT(xx, yy);
1771 if (!IS_CRUMBLED_TILE(xx, yy, element))
1774 graphic = el_act2crm(element, ACTION_DEFAULT);
1776 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1778 MarkTileDirty(sxx, syy);
1783 void DrawLevelFieldCrumbled(int x, int y)
1787 if (!IN_LEV_FIELD(x, y))
1790 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1791 GfxElement[x][y] != EL_UNDEFINED &&
1792 GFX_CRUMBLED(GfxElement[x][y]))
1794 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1799 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1801 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1804 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1807 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1808 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1809 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1810 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1811 int sx = SCREENX(x), sy = SCREENY(y);
1813 DrawGraphic(sx, sy, graphic1, frame1);
1814 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1817 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1819 int sx = SCREENX(x), sy = SCREENY(y);
1820 static int xy[4][2] =
1829 for (i = 0; i < 4; i++)
1831 int xx = x + xy[i][0];
1832 int yy = y + xy[i][1];
1833 int sxx = sx + xy[i][0];
1834 int syy = sy + xy[i][1];
1836 if (!IN_LEV_FIELD(xx, yy) ||
1837 !IN_SCR_FIELD(sxx, syy) ||
1838 !GFX_CRUMBLED(Feld[xx][yy]) ||
1842 DrawLevelField(xx, yy);
1846 static int getBorderElement(int x, int y)
1850 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1851 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1852 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1853 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1854 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1855 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1856 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1858 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1859 int steel_position = (x == -1 && y == -1 ? 0 :
1860 x == lev_fieldx && y == -1 ? 1 :
1861 x == -1 && y == lev_fieldy ? 2 :
1862 x == lev_fieldx && y == lev_fieldy ? 3 :
1863 x == -1 || x == lev_fieldx ? 4 :
1864 y == -1 || y == lev_fieldy ? 5 : 6);
1866 return border[steel_position][steel_type];
1869 void DrawScreenElement(int x, int y, int element)
1871 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1872 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1875 void DrawLevelElement(int x, int y, int element)
1877 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1878 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1881 void DrawScreenField(int x, int y)
1883 int lx = LEVELX(x), ly = LEVELY(y);
1884 int element, content;
1886 if (!IN_LEV_FIELD(lx, ly))
1888 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1891 element = getBorderElement(lx, ly);
1893 DrawScreenElement(x, y, element);
1898 element = Feld[lx][ly];
1899 content = Store[lx][ly];
1901 if (IS_MOVING(lx, ly))
1903 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1904 boolean cut_mode = NO_CUTTING;
1906 if (element == EL_QUICKSAND_EMPTYING ||
1907 element == EL_QUICKSAND_FAST_EMPTYING ||
1908 element == EL_MAGIC_WALL_EMPTYING ||
1909 element == EL_BD_MAGIC_WALL_EMPTYING ||
1910 element == EL_DC_MAGIC_WALL_EMPTYING ||
1911 element == EL_AMOEBA_DROPPING)
1912 cut_mode = CUT_ABOVE;
1913 else if (element == EL_QUICKSAND_FILLING ||
1914 element == EL_QUICKSAND_FAST_FILLING ||
1915 element == EL_MAGIC_WALL_FILLING ||
1916 element == EL_BD_MAGIC_WALL_FILLING ||
1917 element == EL_DC_MAGIC_WALL_FILLING)
1918 cut_mode = CUT_BELOW;
1920 if (cut_mode == CUT_ABOVE)
1921 DrawScreenElement(x, y, element);
1923 DrawScreenElement(x, y, EL_EMPTY);
1926 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1927 else if (cut_mode == NO_CUTTING)
1928 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1931 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1933 if (cut_mode == CUT_BELOW &&
1934 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1935 DrawLevelElement(lx, ly + 1, element);
1938 if (content == EL_ACID)
1940 int dir = MovDir[lx][ly];
1941 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1942 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1944 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1947 else if (IS_BLOCKED(lx, ly))
1952 boolean cut_mode = NO_CUTTING;
1953 int element_old, content_old;
1955 Blocked2Moving(lx, ly, &oldx, &oldy);
1958 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1959 MovDir[oldx][oldy] == MV_RIGHT);
1961 element_old = Feld[oldx][oldy];
1962 content_old = Store[oldx][oldy];
1964 if (element_old == EL_QUICKSAND_EMPTYING ||
1965 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1966 element_old == EL_MAGIC_WALL_EMPTYING ||
1967 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1968 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1969 element_old == EL_AMOEBA_DROPPING)
1970 cut_mode = CUT_ABOVE;
1972 DrawScreenElement(x, y, EL_EMPTY);
1975 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1977 else if (cut_mode == NO_CUTTING)
1978 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1981 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1984 else if (IS_DRAWABLE(element))
1985 DrawScreenElement(x, y, element);
1987 DrawScreenElement(x, y, EL_EMPTY);
1990 void DrawLevelField(int x, int y)
1992 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1993 DrawScreenField(SCREENX(x), SCREENY(y));
1994 else if (IS_MOVING(x, y))
1998 Moving2Blocked(x, y, &newx, &newy);
1999 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2000 DrawScreenField(SCREENX(newx), SCREENY(newy));
2002 else if (IS_BLOCKED(x, y))
2006 Blocked2Moving(x, y, &oldx, &oldy);
2007 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2008 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2012 void DrawSizedElement(int x, int y, int element, int tilesize)
2016 graphic = el2edimg(element);
2017 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2020 void DrawMiniElement(int x, int y, int element)
2024 graphic = el2edimg(element);
2025 DrawMiniGraphic(x, y, graphic);
2028 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2031 int x = sx + scroll_x, y = sy + scroll_y;
2033 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2034 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2035 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2036 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2038 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2041 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2043 int x = sx + scroll_x, y = sy + scroll_y;
2045 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2046 DrawMiniElement(sx, sy, EL_EMPTY);
2047 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2048 DrawMiniElement(sx, sy, Feld[x][y]);
2050 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2053 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2054 int x, int y, int xsize, int ysize,
2055 int tile_width, int tile_height)
2059 int dst_x = startx + x * tile_width;
2060 int dst_y = starty + y * tile_height;
2061 int width = graphic_info[graphic].width;
2062 int height = graphic_info[graphic].height;
2063 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2064 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2065 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2066 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2067 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2068 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2069 boolean draw_masked = graphic_info[graphic].draw_masked;
2071 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2073 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2075 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2079 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2080 inner_sx + (x - 1) * tile_width % inner_width);
2081 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2082 inner_sy + (y - 1) * tile_height % inner_height);
2085 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2088 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2092 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2093 int x, int y, int xsize, int ysize, int font_nr)
2095 int font_width = getFontWidth(font_nr);
2096 int font_height = getFontHeight(font_nr);
2098 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2099 font_width, font_height);
2102 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2104 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2105 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2106 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2107 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2108 boolean no_delay = (tape.warp_forward);
2109 unsigned int anim_delay = 0;
2110 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2111 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2112 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2113 int font_width = getFontWidth(font_nr);
2114 int font_height = getFontHeight(font_nr);
2115 int max_xsize = level.envelope[envelope_nr].xsize;
2116 int max_ysize = level.envelope[envelope_nr].ysize;
2117 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2118 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2119 int xend = max_xsize;
2120 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2121 int xstep = (xstart < xend ? 1 : 0);
2122 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2124 int end = MAX(xend - xstart, yend - ystart);
2127 for (i = start; i <= end; i++)
2129 int last_frame = end; // last frame of this "for" loop
2130 int x = xstart + i * xstep;
2131 int y = ystart + i * ystep;
2132 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2133 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2134 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2135 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2138 SetDrawtoField(DRAW_FIELDBUFFER);
2140 BlitScreenToBitmap(backbuffer);
2142 SetDrawtoField(DRAW_BACKBUFFER);
2144 for (yy = 0; yy < ysize; yy++)
2145 for (xx = 0; xx < xsize; xx++)
2146 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2148 DrawTextBuffer(sx + font_width, sy + font_height,
2149 level.envelope[envelope_nr].text, font_nr, max_xsize,
2150 xsize - 2, ysize - 2, 0, mask_mode,
2151 level.envelope[envelope_nr].autowrap,
2152 level.envelope[envelope_nr].centered, FALSE);
2154 redraw_mask |= REDRAW_FIELD;
2157 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2161 void ShowEnvelope(int envelope_nr)
2163 int element = EL_ENVELOPE_1 + envelope_nr;
2164 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2165 int sound_opening = element_info[element].sound[ACTION_OPENING];
2166 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2167 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2168 boolean no_delay = (tape.warp_forward);
2169 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2170 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2171 int anim_mode = graphic_info[graphic].anim_mode;
2172 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2173 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2175 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2177 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2179 if (anim_mode == ANIM_DEFAULT)
2180 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2182 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2185 Delay(wait_delay_value);
2187 WaitForEventToContinue();
2189 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2191 if (anim_mode != ANIM_NONE)
2192 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2194 if (anim_mode == ANIM_DEFAULT)
2195 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2197 game.envelope_active = FALSE;
2199 SetDrawtoField(DRAW_FIELDBUFFER);
2201 redraw_mask |= REDRAW_FIELD;
2205 static void setRequestBasePosition(int *x, int *y)
2207 int sx_base, sy_base;
2209 if (request.x != -1)
2210 sx_base = request.x;
2211 else if (request.align == ALIGN_LEFT)
2213 else if (request.align == ALIGN_RIGHT)
2214 sx_base = SX + SXSIZE;
2216 sx_base = SX + SXSIZE / 2;
2218 if (request.y != -1)
2219 sy_base = request.y;
2220 else if (request.valign == VALIGN_TOP)
2222 else if (request.valign == VALIGN_BOTTOM)
2223 sy_base = SY + SYSIZE;
2225 sy_base = SY + SYSIZE / 2;
2231 static void setRequestPositionExt(int *x, int *y, int width, int height,
2232 boolean add_border_size)
2234 int border_size = request.border_size;
2235 int sx_base, sy_base;
2238 setRequestBasePosition(&sx_base, &sy_base);
2240 if (request.align == ALIGN_LEFT)
2242 else if (request.align == ALIGN_RIGHT)
2243 sx = sx_base - width;
2245 sx = sx_base - width / 2;
2247 if (request.valign == VALIGN_TOP)
2249 else if (request.valign == VALIGN_BOTTOM)
2250 sy = sy_base - height;
2252 sy = sy_base - height / 2;
2254 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2255 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2257 if (add_border_size)
2267 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2269 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2272 void DrawEnvelopeRequest(char *text)
2274 int last_game_status = game_status; /* save current game status */
2275 char *text_final = text;
2276 char *text_door_style = NULL;
2277 int graphic = IMG_BACKGROUND_REQUEST;
2278 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2279 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2280 int font_nr = FONT_REQUEST;
2281 int font_width = getFontWidth(font_nr);
2282 int font_height = getFontHeight(font_nr);
2283 int border_size = request.border_size;
2284 int line_spacing = request.line_spacing;
2285 int line_height = font_height + line_spacing;
2286 int max_text_width = request.width - 2 * border_size;
2287 int max_text_height = request.height - 2 * border_size;
2288 int line_length = max_text_width / font_width;
2289 int max_lines = max_text_height / line_height;
2290 int text_width = line_length * font_width;
2291 int width = request.width;
2292 int height = request.height;
2293 int tile_size = MAX(request.step_offset, 1);
2294 int x_steps = width / tile_size;
2295 int y_steps = height / tile_size;
2296 int sx_offset = border_size;
2297 int sy_offset = border_size;
2301 if (request.centered)
2302 sx_offset = (request.width - text_width) / 2;
2304 if (request.wrap_single_words && !request.autowrap)
2306 char *src_text_ptr, *dst_text_ptr;
2308 text_door_style = checked_malloc(2 * strlen(text) + 1);
2310 src_text_ptr = text;
2311 dst_text_ptr = text_door_style;
2313 while (*src_text_ptr)
2315 if (*src_text_ptr == ' ' ||
2316 *src_text_ptr == '?' ||
2317 *src_text_ptr == '!')
2318 *dst_text_ptr++ = '\n';
2320 if (*src_text_ptr != ' ')
2321 *dst_text_ptr++ = *src_text_ptr;
2326 *dst_text_ptr = '\0';
2328 text_final = text_door_style;
2331 setRequestPosition(&sx, &sy, FALSE);
2333 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2335 for (y = 0; y < y_steps; y++)
2336 for (x = 0; x < x_steps; x++)
2337 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2338 x, y, x_steps, y_steps,
2339 tile_size, tile_size);
2341 /* force DOOR font inside door area */
2342 game_status = GAME_MODE_PSEUDO_DOOR;
2344 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2345 line_length, -1, max_lines, line_spacing, mask_mode,
2346 request.autowrap, request.centered, FALSE);
2348 game_status = last_game_status; /* restore current game status */
2350 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2351 RedrawGadget(tool_gadget[i]);
2353 // store readily prepared envelope request for later use when animating
2354 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2356 if (text_door_style)
2357 free(text_door_style);
2360 void AnimateEnvelopeRequest(int anim_mode, int action)
2362 int graphic = IMG_BACKGROUND_REQUEST;
2363 boolean draw_masked = graphic_info[graphic].draw_masked;
2364 int delay_value_normal = request.step_delay;
2365 int delay_value_fast = delay_value_normal / 2;
2366 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2367 boolean no_delay = (tape.warp_forward);
2368 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2369 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2370 unsigned int anim_delay = 0;
2372 int tile_size = MAX(request.step_offset, 1);
2373 int max_xsize = request.width / tile_size;
2374 int max_ysize = request.height / tile_size;
2375 int max_xsize_inner = max_xsize - 2;
2376 int max_ysize_inner = max_ysize - 2;
2378 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2379 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2380 int xend = max_xsize_inner;
2381 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2382 int xstep = (xstart < xend ? 1 : 0);
2383 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2385 int end = MAX(xend - xstart, yend - ystart);
2388 if (setup.quick_doors)
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_window = &viewport.window[gfx_game_mode];
8142 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8143 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8144 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8145 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8146 int new_win_xsize = vp_window->width;
8147 int new_win_ysize = vp_window->height;
8148 int border_size = vp_playfield->border_size;
8149 int new_sx = vp_playfield->x + border_size;
8150 int new_sy = vp_playfield->y + border_size;
8151 int new_sxsize = vp_playfield->width - 2 * border_size;
8152 int new_sysize = vp_playfield->height - 2 * border_size;
8153 int new_real_sx = vp_playfield->x;
8154 int new_real_sy = vp_playfield->y;
8155 int new_full_sxsize = vp_playfield->width;
8156 int new_full_sysize = vp_playfield->height;
8157 int new_dx = vp_door_1->x;
8158 int new_dy = vp_door_1->y;
8159 int new_dxsize = vp_door_1->width;
8160 int new_dysize = vp_door_1->height;
8161 int new_vx = vp_door_2->x;
8162 int new_vy = vp_door_2->y;
8163 int new_vxsize = vp_door_2->width;
8164 int new_vysize = vp_door_2->height;
8165 int new_ex = vp_door_3->x;
8166 int new_ey = vp_door_3->y;
8167 int new_exsize = vp_door_3->width;
8168 int new_eysize = vp_door_3->height;
8169 int new_tilesize_var =
8170 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8172 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8173 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8174 int new_scr_fieldx = new_sxsize / tilesize;
8175 int new_scr_fieldy = new_sysize / tilesize;
8176 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8177 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8178 boolean init_gfx_buffers = FALSE;
8179 boolean init_video_buffer = FALSE;
8180 boolean init_gadgets_and_toons = FALSE;
8181 boolean init_em_graphics = FALSE;
8183 if (new_win_xsize != WIN_XSIZE ||
8184 new_win_ysize != WIN_YSIZE)
8186 WIN_XSIZE = new_win_xsize;
8187 WIN_YSIZE = new_win_ysize;
8189 init_video_buffer = TRUE;
8190 init_gfx_buffers = TRUE;
8192 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8195 if (new_scr_fieldx != SCR_FIELDX ||
8196 new_scr_fieldy != SCR_FIELDY)
8198 /* this always toggles between MAIN and GAME when using small tile size */
8200 SCR_FIELDX = new_scr_fieldx;
8201 SCR_FIELDY = new_scr_fieldy;
8203 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8214 new_sxsize != SXSIZE ||
8215 new_sysize != SYSIZE ||
8216 new_dxsize != DXSIZE ||
8217 new_dysize != DYSIZE ||
8218 new_vxsize != VXSIZE ||
8219 new_vysize != VYSIZE ||
8220 new_exsize != EXSIZE ||
8221 new_eysize != EYSIZE ||
8222 new_real_sx != REAL_SX ||
8223 new_real_sy != REAL_SY ||
8224 new_full_sxsize != FULL_SXSIZE ||
8225 new_full_sysize != FULL_SYSIZE ||
8226 new_tilesize_var != TILESIZE_VAR
8229 // ------------------------------------------------------------------------
8230 // determine next fading area for changed viewport definitions
8231 // ------------------------------------------------------------------------
8233 // start with current playfield area (default fading area)
8236 FADE_SXSIZE = FULL_SXSIZE;
8237 FADE_SYSIZE = FULL_SYSIZE;
8239 // add new playfield area if position or size has changed
8240 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8241 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8243 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8244 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8247 // add current and new door 1 area if position or size has changed
8248 if (new_dx != DX || new_dy != DY ||
8249 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8251 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8252 DX, DY, DXSIZE, DYSIZE);
8253 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8254 new_dx, new_dy, new_dxsize, new_dysize);
8257 // add current and new door 2 area if position or size has changed
8258 if (new_dx != VX || new_dy != VY ||
8259 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8261 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8262 VX, VY, VXSIZE, VYSIZE);
8263 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8264 new_vx, new_vy, new_vxsize, new_vysize);
8267 // ------------------------------------------------------------------------
8268 // handle changed tile size
8269 // ------------------------------------------------------------------------
8271 if (new_tilesize_var != TILESIZE_VAR)
8273 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8275 // changing tile size invalidates scroll values of engine snapshots
8276 FreeEngineSnapshotSingle();
8278 // changing tile size requires update of graphic mapping for EM engine
8279 init_em_graphics = TRUE;
8290 SXSIZE = new_sxsize;
8291 SYSIZE = new_sysize;
8292 DXSIZE = new_dxsize;
8293 DYSIZE = new_dysize;
8294 VXSIZE = new_vxsize;
8295 VYSIZE = new_vysize;
8296 EXSIZE = new_exsize;
8297 EYSIZE = new_eysize;
8298 REAL_SX = new_real_sx;
8299 REAL_SY = new_real_sy;
8300 FULL_SXSIZE = new_full_sxsize;
8301 FULL_SYSIZE = new_full_sysize;
8302 TILESIZE_VAR = new_tilesize_var;
8304 init_gfx_buffers = TRUE;
8305 init_gadgets_and_toons = TRUE;
8307 // printf("::: viewports: init_gfx_buffers\n");
8308 // printf("::: viewports: init_gadgets_and_toons\n");
8311 if (init_gfx_buffers)
8313 // printf("::: init_gfx_buffers\n");
8315 SCR_FIELDX = new_scr_fieldx_buffers;
8316 SCR_FIELDY = new_scr_fieldy_buffers;
8320 SCR_FIELDX = new_scr_fieldx;
8321 SCR_FIELDY = new_scr_fieldy;
8323 SetDrawDeactivationMask(REDRAW_NONE);
8324 SetDrawBackgroundMask(REDRAW_FIELD);
8327 if (init_video_buffer)
8329 // printf("::: init_video_buffer\n");
8331 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8334 if (init_gadgets_and_toons)
8336 // printf("::: init_gadgets_and_toons\n");
8342 if (init_em_graphics)
8344 InitGraphicInfo_EM();