1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static char *print_if_not_empty(int element)
178 static char *s = NULL;
179 char *token_name = element_info[element].token_name;
184 s = checked_malloc(strlen(token_name) + 10 + 1);
186 if (element != EL_EMPTY)
187 sprintf(s, "%d\t['%s']", element, token_name);
189 sprintf(s, "%d", element);
194 void DumpTile(int x, int y)
199 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
205 printf_line("-", 79);
206 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
207 printf_line("-", 79);
209 if (!IN_LEV_FIELD(x, y))
211 printf("(not in level field)\n");
217 printf(" Feld: %d\t['%s']\n", Feld[x][y],
218 element_info[Feld[x][y]].token_name);
219 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
220 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
221 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
222 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
223 printf(" MovPos: %d\n", MovPos[x][y]);
224 printf(" MovDir: %d\n", MovDir[x][y]);
225 printf(" MovDelay: %d\n", MovDelay[x][y]);
226 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
227 printf(" CustomValue: %d\n", CustomValue[x][y]);
228 printf(" GfxElement: %d\n", GfxElement[x][y]);
229 printf(" GfxAction: %d\n", GfxAction[x][y]);
230 printf(" GfxFrame: %d\n", GfxFrame[x][y]);
234 void SetDrawtoField(int mode)
236 if (mode == DRAW_FIELDBUFFER)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
245 drawto_field = fieldbuffer;
247 else /* DRAW_BACKBUFFER */
253 BX2 = SCR_FIELDX - 1;
254 BY2 = SCR_FIELDY - 1;
256 drawto_field = backbuffer;
260 static void RedrawPlayfield_RND()
262 if (game.envelope_active)
265 DrawLevel(REDRAW_ALL);
269 void RedrawPlayfield()
271 if (game_status != GAME_MODE_PLAYING)
274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
275 RedrawPlayfield_EM(TRUE);
276 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
277 RedrawPlayfield_SP(TRUE);
278 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
279 RedrawPlayfield_RND();
281 BlitScreenToBitmap(backbuffer);
283 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
287 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
289 Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
291 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
294 void DrawMaskedBorder_FIELD()
296 if (global.border_status >= GAME_MODE_TITLE &&
297 global.border_status <= GAME_MODE_PLAYING &&
298 border.draw_masked[global.border_status])
299 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
302 void DrawMaskedBorder_DOOR_1()
304 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
305 (global.border_status != GAME_MODE_EDITOR ||
306 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
307 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
310 void DrawMaskedBorder_DOOR_2()
312 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
313 global.border_status != GAME_MODE_EDITOR)
314 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
317 void DrawMaskedBorder_DOOR_3()
319 /* currently not available */
322 void DrawMaskedBorder_ALL()
324 DrawMaskedBorder_FIELD();
325 DrawMaskedBorder_DOOR_1();
326 DrawMaskedBorder_DOOR_2();
327 DrawMaskedBorder_DOOR_3();
330 void DrawMaskedBorder(int redraw_mask)
332 /* never draw masked screen borders on borderless screens */
333 if (effectiveGameStatus() == GAME_MODE_LOADING ||
334 effectiveGameStatus() == GAME_MODE_TITLE)
337 if (redraw_mask & REDRAW_ALL)
338 DrawMaskedBorder_ALL();
341 if (redraw_mask & REDRAW_FIELD)
342 DrawMaskedBorder_FIELD();
343 if (redraw_mask & REDRAW_DOOR_1)
344 DrawMaskedBorder_DOOR_1();
345 if (redraw_mask & REDRAW_DOOR_2)
346 DrawMaskedBorder_DOOR_2();
347 if (redraw_mask & REDRAW_DOOR_3)
348 DrawMaskedBorder_DOOR_3();
352 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
354 int fx = FX, fy = FY;
355 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
356 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
358 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
359 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
360 int dx_var = dx * TILESIZE_VAR / TILESIZE;
361 int dy_var = dy * TILESIZE_VAR / TILESIZE;
364 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
365 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
367 if (EVEN(SCR_FIELDX))
369 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
370 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
372 fx += (dx_var > 0 ? TILEX_VAR : 0);
379 if (EVEN(SCR_FIELDY))
381 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
382 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
384 fy += (dy_var > 0 ? TILEY_VAR : 0);
391 if (full_lev_fieldx <= SCR_FIELDX)
393 if (EVEN(SCR_FIELDX))
394 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
396 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
399 if (full_lev_fieldy <= SCR_FIELDY)
401 if (EVEN(SCR_FIELDY))
402 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
404 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
407 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
410 void BlitScreenToBitmap(Bitmap *target_bitmap)
412 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
413 BlitScreenToBitmap_EM(target_bitmap);
414 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
415 BlitScreenToBitmap_SP(target_bitmap);
416 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
417 BlitScreenToBitmap_RND(target_bitmap);
419 redraw_mask |= REDRAW_FIELD;
422 void DrawFramesPerSecond()
425 int font_nr = FONT_TEXT_2;
426 int font_width = getFontWidth(font_nr);
428 sprintf(text, "%04.1f fps", global.frames_per_second);
430 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
431 font_nr, BLIT_OPAQUE);
436 if (redraw_mask == REDRAW_NONE)
439 // draw masked border to all viewports, if defined
440 DrawMaskedBorder(redraw_mask);
442 // draw frames per second (only if debug mode is enabled)
443 if (redraw_mask & REDRAW_FPS)
444 DrawFramesPerSecond();
446 // redraw complete window if both playfield and (some) doors need redraw
447 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
448 redraw_mask = REDRAW_ALL;
450 /* although redrawing the whole window would be fine for normal gameplay,
451 being able to only redraw the playfield is required for deactivating
452 certain drawing areas (mainly playfield) to work, which is needed for
453 warp-forward to be fast enough (by skipping redraw of most frames) */
455 if (redraw_mask & REDRAW_ALL)
457 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
459 else if (redraw_mask & REDRAW_FIELD)
461 BlitBitmap(backbuffer, window,
462 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
464 else if (redraw_mask & REDRAW_DOORS)
466 if (redraw_mask & REDRAW_DOOR_1)
467 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
469 if (redraw_mask & REDRAW_DOOR_2)
470 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
472 if (redraw_mask & REDRAW_DOOR_3)
473 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
476 redraw_mask = REDRAW_NONE;
479 static void FadeCrossSaveBackbuffer()
481 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
484 static void FadeCrossRestoreBackbuffer()
486 int redraw_mask_last = redraw_mask;
488 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
490 // do not change redraw mask when restoring backbuffer after cross-fading
491 redraw_mask = redraw_mask_last;
494 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
496 static int fade_type_skip = FADE_TYPE_NONE;
497 void (*draw_border_function)(void) = NULL;
498 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
499 int x, y, width, height;
500 int fade_delay, post_delay;
502 if (fade_type == FADE_TYPE_FADE_OUT)
504 if (fade_type_skip != FADE_TYPE_NONE)
506 /* skip all fade operations until specified fade operation */
507 if (fade_type & fade_type_skip)
508 fade_type_skip = FADE_TYPE_NONE;
514 FadeCrossSaveBackbuffer();
517 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
520 FadeCrossSaveBackbuffer();
527 redraw_mask |= fade_mask;
529 if (fade_type == FADE_TYPE_SKIP)
531 fade_type_skip = fade_mode;
536 fade_delay = fading.fade_delay;
537 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
539 if (fade_type_skip != FADE_TYPE_NONE)
541 /* skip all fade operations until specified fade operation */
542 if (fade_type & fade_type_skip)
543 fade_type_skip = FADE_TYPE_NONE;
548 if (global.autoplay_leveldir)
553 if (fade_mask == REDRAW_FIELD)
558 height = FADE_SYSIZE;
560 if (border.draw_masked_when_fading)
561 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
563 DrawMaskedBorder_FIELD(); /* draw once */
565 else /* REDRAW_ALL */
573 if (!setup.fade_screens ||
575 fading.fade_mode == FADE_MODE_NONE)
577 if (fade_mode == FADE_MODE_FADE_OUT)
580 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
582 redraw_mask &= ~fade_mask;
587 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
588 draw_border_function);
590 if (fade_type == FADE_TYPE_FADE_OUT)
591 FadeCrossRestoreBackbuffer();
593 redraw_mask &= ~fade_mask;
596 void FadeIn(int fade_mask)
598 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
599 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
601 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
605 FADE_SXSIZE = FULL_SXSIZE;
606 FADE_SYSIZE = FULL_SYSIZE;
609 void FadeOut(int fade_mask)
611 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
612 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
614 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
616 global.border_status = game_status;
619 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
621 static struct TitleFadingInfo fading_leave_stored;
624 fading_leave_stored = fading_leave;
626 fading = fading_leave_stored;
629 void FadeSetEnterMenu()
631 fading = menu.enter_menu;
633 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
636 void FadeSetLeaveMenu()
638 fading = menu.leave_menu;
640 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
643 void FadeSetEnterScreen()
645 fading = menu.enter_screen[game_status];
647 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
650 void FadeSetNextScreen()
652 fading = menu.next_screen;
654 // (do not overwrite fade mode set by FadeSetEnterScreen)
655 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
658 void FadeSetLeaveScreen()
660 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
663 void FadeSetFromType(int type)
665 if (type & TYPE_ENTER_SCREEN)
666 FadeSetEnterScreen();
667 else if (type & TYPE_ENTER)
669 else if (type & TYPE_LEAVE)
673 void FadeSetDisabled()
675 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
677 fading = fading_none;
680 void FadeSkipNextFadeIn()
682 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
685 void FadeSkipNextFadeOut()
687 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
690 void SetWindowBackgroundImageIfDefined(int graphic)
692 if (graphic_info[graphic].bitmap)
693 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
696 void SetMainBackgroundImageIfDefined(int graphic)
698 if (graphic_info[graphic].bitmap)
699 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
702 void SetDoorBackgroundImageIfDefined(int graphic)
704 if (graphic_info[graphic].bitmap)
705 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
708 void SetWindowBackgroundImage(int graphic)
710 SetWindowBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
711 graphic_info[graphic].bitmap ?
712 graphic_info[graphic].bitmap :
713 graphic_info[IMG_BACKGROUND].bitmap);
716 void SetMainBackgroundImage(int graphic)
718 SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
719 graphic_info[graphic].bitmap ?
720 graphic_info[graphic].bitmap :
721 graphic_info[IMG_BACKGROUND].bitmap);
724 void SetDoorBackgroundImage(int graphic)
726 SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
727 graphic_info[graphic].bitmap ?
728 graphic_info[graphic].bitmap :
729 graphic_info[IMG_BACKGROUND].bitmap);
732 void SetPanelBackground()
734 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
736 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
737 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
739 SetDoorBackgroundBitmap(bitmap_db_panel);
742 void DrawBackground(int x, int y, int width, int height)
744 /* "drawto" might still point to playfield buffer here (hall of fame) */
745 ClearRectangleOnBackground(backbuffer, x, y, width, height);
747 if (IN_GFX_FIELD_FULL(x, y))
748 redraw_mask |= REDRAW_FIELD;
749 else if (IN_GFX_DOOR_1(x, y))
750 redraw_mask |= REDRAW_DOOR_1;
751 else if (IN_GFX_DOOR_2(x, y))
752 redraw_mask |= REDRAW_DOOR_2;
753 else if (IN_GFX_DOOR_3(x, y))
754 redraw_mask |= REDRAW_DOOR_3;
757 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
759 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
761 if (font->bitmap == NULL)
764 DrawBackground(x, y, width, height);
767 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
769 struct GraphicInfo *g = &graphic_info[graphic];
771 if (g->bitmap == NULL)
774 DrawBackground(x, y, width, height);
777 static int game_status_last = -1;
778 static Bitmap *global_border_bitmap_last = NULL;
779 static Bitmap *global_border_bitmap = NULL;
780 static int real_sx_last = -1, real_sy_last = -1;
781 static int full_sxsize_last = -1, full_sysize_last = -1;
782 static int dx_last = -1, dy_last = -1;
783 static int dxsize_last = -1, dysize_last = -1;
784 static int vx_last = -1, vy_last = -1;
785 static int vxsize_last = -1, vysize_last = -1;
787 boolean CheckIfGlobalBorderHasChanged()
789 int global_border_graphic;
791 // if game status has not changed, global border has not changed either
792 if (game_status == game_status_last)
795 global_border_graphic =
796 (game_status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
797 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
798 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
799 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
802 global_border_bitmap =
803 (graphic_info[global_border_graphic].bitmap ?
804 graphic_info[global_border_graphic].bitmap :
805 graphic_info[IMG_GLOBAL_BORDER].bitmap);
807 return (global_border_bitmap_last != global_border_bitmap);
810 boolean CheckIfGlobalBorderRedrawIsNeeded()
812 // if game status has not changed, nothing has to be redrawn
813 if (game_status == game_status_last)
816 // redraw if global screen border has changed
817 if (CheckIfGlobalBorderHasChanged())
820 // redraw if position or size of playfield area has changed
821 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
822 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
825 // redraw if position or size of door area has changed
826 if (dx_last != DX || dy_last != DY ||
827 dxsize_last != DXSIZE || dysize_last != DYSIZE)
830 // redraw if position or size of tape area has changed
831 if (vx_last != VX || vy_last != VY ||
832 vxsize_last != VXSIZE || vysize_last != VYSIZE)
838 static void RedrawGlobalBorderIfNeeded()
840 if (game_status == game_status_last)
843 // copy current draw buffer to later copy back areas that have not changed
844 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
846 if (CheckIfGlobalBorderRedrawIsNeeded())
848 // redraw global screen border (or clear, if defined to be empty)
850 if (global_border_bitmap)
851 BlitBitmap(global_border_bitmap, backbuffer,
852 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
854 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
856 // copy previous playfield and door areas, if they are defined on both
857 // previous and current screen and if they still have the same size
859 if (real_sx_last != -1 && real_sy_last != -1 &&
860 REAL_SX != -1 && REAL_SY != -1 &&
861 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
862 BlitBitmap(bitmap_db_store, backbuffer,
863 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
866 if (dx_last != -1 && dy_last != -1 &&
867 DX != -1 && DY != -1 &&
868 dxsize_last == DXSIZE && dysize_last == DYSIZE)
869 BlitBitmap(bitmap_db_store, backbuffer,
870 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
872 if (vx_last != -1 && vy_last != -1 &&
873 VX != -1 && VY != -1 &&
874 vxsize_last == VXSIZE && vysize_last == VYSIZE)
875 BlitBitmap(bitmap_db_store, backbuffer,
876 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
878 redraw_mask = REDRAW_ALL;
881 game_status_last = game_status;
883 global_border_bitmap_last = global_border_bitmap;
885 real_sx_last = REAL_SX;
886 real_sy_last = REAL_SY;
887 full_sxsize_last = FULL_SXSIZE;
888 full_sysize_last = FULL_SYSIZE;
891 dxsize_last = DXSIZE;
892 dysize_last = DYSIZE;
895 vxsize_last = VXSIZE;
896 vysize_last = VYSIZE;
901 RedrawGlobalBorderIfNeeded();
903 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
904 /* (when entering hall of fame after playing) */
905 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
907 /* !!! maybe this should be done before clearing the background !!! */
908 if (game_status == GAME_MODE_PLAYING)
910 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
911 SetDrawtoField(DRAW_FIELDBUFFER);
915 SetDrawtoField(DRAW_BACKBUFFER);
919 void MarkTileDirty(int x, int y)
921 redraw_mask |= REDRAW_FIELD;
924 void SetBorderElement()
928 BorderElement = EL_EMPTY;
930 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
932 for (x = 0; x < lev_fieldx; x++)
934 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
935 BorderElement = EL_STEELWALL;
937 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
943 void FloodFillLevel(int from_x, int from_y, int fill_element,
944 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
945 int max_fieldx, int max_fieldy)
949 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
950 static int safety = 0;
952 /* check if starting field still has the desired content */
953 if (field[from_x][from_y] == fill_element)
958 if (safety > max_fieldx * max_fieldy)
959 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
961 old_element = field[from_x][from_y];
962 field[from_x][from_y] = fill_element;
964 for (i = 0; i < 4; i++)
966 x = from_x + check[i][0];
967 y = from_y + check[i][1];
969 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
970 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
976 void SetRandomAnimationValue(int x, int y)
978 gfx.anim_random_frame = GfxRandom[x][y];
981 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
983 /* animation synchronized with global frame counter, not move position */
984 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
985 sync_frame = FrameCounter;
987 return getAnimationFrame(graphic_info[graphic].anim_frames,
988 graphic_info[graphic].anim_delay,
989 graphic_info[graphic].anim_mode,
990 graphic_info[graphic].anim_start_frame,
994 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
995 Bitmap **bitmap, int *x, int *y,
996 boolean get_backside)
998 struct GraphicInfo *g = &graphic_info[graphic];
999 Bitmap *src_bitmap = g->bitmap;
1000 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1001 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1002 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1004 // if no in-game graphics defined, always use standard graphic size
1005 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1006 tilesize = TILESIZE;
1008 if (tilesize == gfx.standard_tile_size)
1009 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1010 else if (tilesize == game.tile_size)
1011 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1013 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1015 if (g->offset_y == 0) /* frames are ordered horizontally */
1017 int max_width = g->anim_frames_per_line * g->width;
1018 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1020 src_x = pos % max_width;
1021 src_y = src_y % g->height + pos / max_width * g->height;
1023 else if (g->offset_x == 0) /* frames are ordered vertically */
1025 int max_height = g->anim_frames_per_line * g->height;
1026 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1028 src_x = src_x % g->width + pos / max_height * g->width;
1029 src_y = pos % max_height;
1031 else /* frames are ordered diagonally */
1033 src_x = src_x + frame * g->offset_x;
1034 src_y = src_y + frame * g->offset_y;
1037 *bitmap = src_bitmap;
1038 *x = src_x * tilesize / TILESIZE;
1039 *y = src_y * tilesize / TILESIZE;
1042 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1043 int *x, int *y, boolean get_backside)
1045 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1049 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1050 Bitmap **bitmap, int *x, int *y)
1052 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1055 void getFixedGraphicSource(int graphic, int frame,
1056 Bitmap **bitmap, int *x, int *y)
1058 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1061 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1063 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1066 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1067 int *x, int *y, boolean get_backside)
1069 struct GraphicInfo *g = &graphic_info[graphic];
1070 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1071 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1073 if (TILESIZE_VAR != TILESIZE)
1074 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1077 *bitmap = g->bitmap;
1079 if (g->offset_y == 0) /* frames are ordered horizontally */
1081 int max_width = g->anim_frames_per_line * g->width;
1082 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1084 *x = pos % max_width;
1085 *y = src_y % g->height + pos / max_width * g->height;
1087 else if (g->offset_x == 0) /* frames are ordered vertically */
1089 int max_height = g->anim_frames_per_line * g->height;
1090 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1092 *x = src_x % g->width + pos / max_height * g->width;
1093 *y = pos % max_height;
1095 else /* frames are ordered diagonally */
1097 *x = src_x + frame * g->offset_x;
1098 *y = src_y + frame * g->offset_y;
1102 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1104 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1107 void DrawGraphic(int x, int y, int graphic, int frame)
1110 if (!IN_SCR_FIELD(x, y))
1112 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1113 printf("DrawGraphic(): This should never happen!\n");
1118 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1121 MarkTileDirty(x, y);
1124 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1127 if (!IN_SCR_FIELD(x, y))
1129 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1130 printf("DrawGraphic(): This should never happen!\n");
1135 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1137 MarkTileDirty(x, y);
1140 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1146 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1148 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1151 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1157 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1158 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1161 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1164 if (!IN_SCR_FIELD(x, y))
1166 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1167 printf("DrawGraphicThruMask(): This should never happen!\n");
1172 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1175 MarkTileDirty(x, y);
1178 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1181 if (!IN_SCR_FIELD(x, y))
1183 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1184 printf("DrawGraphicThruMask(): This should never happen!\n");
1189 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1191 MarkTileDirty(x, y);
1194 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1200 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1202 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1206 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1207 int graphic, int frame)
1209 struct GraphicInfo *g = &graphic_info[graphic];
1213 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1215 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1219 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1221 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1223 MarkTileDirty(x / tilesize, y / tilesize);
1226 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1232 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1233 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1236 void DrawMiniGraphic(int x, int y, int graphic)
1238 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1239 MarkTileDirty(x / 2, y / 2);
1242 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1247 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1248 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1251 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1252 int graphic, int frame,
1253 int cut_mode, int mask_mode)
1258 int width = TILEX, height = TILEY;
1261 if (dx || dy) /* shifted graphic */
1263 if (x < BX1) /* object enters playfield from the left */
1270 else if (x > BX2) /* object enters playfield from the right */
1276 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1282 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1284 else if (dx) /* general horizontal movement */
1285 MarkTileDirty(x + SIGN(dx), y);
1287 if (y < BY1) /* object enters playfield from the top */
1289 if (cut_mode==CUT_BELOW) /* object completely above top border */
1297 else if (y > BY2) /* object enters playfield from the bottom */
1303 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1309 else if (dy > 0 && cut_mode == CUT_ABOVE)
1311 if (y == BY2) /* object completely above bottom border */
1317 MarkTileDirty(x, y + 1);
1318 } /* object leaves playfield to the bottom */
1319 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1321 else if (dy) /* general vertical movement */
1322 MarkTileDirty(x, y + SIGN(dy));
1326 if (!IN_SCR_FIELD(x, y))
1328 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1329 printf("DrawGraphicShifted(): This should never happen!\n");
1334 width = width * TILESIZE_VAR / TILESIZE;
1335 height = height * TILESIZE_VAR / TILESIZE;
1336 cx = cx * TILESIZE_VAR / TILESIZE;
1337 cy = cy * TILESIZE_VAR / TILESIZE;
1338 dx = dx * TILESIZE_VAR / TILESIZE;
1339 dy = dy * TILESIZE_VAR / TILESIZE;
1341 if (width > 0 && height > 0)
1343 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1348 dst_x = FX + x * TILEX_VAR + dx;
1349 dst_y = FY + y * TILEY_VAR + dy;
1351 if (mask_mode == USE_MASKING)
1352 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1355 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1358 MarkTileDirty(x, y);
1362 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1363 int graphic, int frame,
1364 int cut_mode, int mask_mode)
1369 int width = TILEX_VAR, height = TILEY_VAR;
1372 int x2 = x + SIGN(dx);
1373 int y2 = y + SIGN(dy);
1375 /* movement with two-tile animations must be sync'ed with movement position,
1376 not with current GfxFrame (which can be higher when using slow movement) */
1377 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1378 int anim_frames = graphic_info[graphic].anim_frames;
1380 /* (we also need anim_delay here for movement animations with less frames) */
1381 int anim_delay = graphic_info[graphic].anim_delay;
1382 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1384 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1385 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1387 /* re-calculate animation frame for two-tile movement animation */
1388 frame = getGraphicAnimationFrame(graphic, sync_frame);
1390 /* check if movement start graphic inside screen area and should be drawn */
1391 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1393 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1395 dst_x = FX + x1 * TILEX_VAR;
1396 dst_y = FY + y1 * TILEY_VAR;
1398 if (mask_mode == USE_MASKING)
1399 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1402 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1405 MarkTileDirty(x1, y1);
1408 /* check if movement end graphic inside screen area and should be drawn */
1409 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1411 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1413 dst_x = FX + x2 * TILEX_VAR;
1414 dst_y = FY + y2 * TILEY_VAR;
1416 if (mask_mode == USE_MASKING)
1417 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1420 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1423 MarkTileDirty(x2, y2);
1427 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1428 int graphic, int frame,
1429 int cut_mode, int mask_mode)
1433 DrawGraphic(x, y, graphic, frame);
1438 if (graphic_info[graphic].double_movement) /* EM style movement images */
1439 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1441 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1444 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1445 int frame, int cut_mode)
1447 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1450 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1451 int cut_mode, int mask_mode)
1453 int lx = LEVELX(x), ly = LEVELY(y);
1457 if (IN_LEV_FIELD(lx, ly))
1459 SetRandomAnimationValue(lx, ly);
1461 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1462 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1464 /* do not use double (EM style) movement graphic when not moving */
1465 if (graphic_info[graphic].double_movement && !dx && !dy)
1467 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1468 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1471 else /* border element */
1473 graphic = el2img(element);
1474 frame = getGraphicAnimationFrame(graphic, -1);
1477 if (element == EL_EXPANDABLE_WALL)
1479 boolean left_stopped = FALSE, right_stopped = FALSE;
1481 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1482 left_stopped = TRUE;
1483 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1484 right_stopped = TRUE;
1486 if (left_stopped && right_stopped)
1488 else if (left_stopped)
1490 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1491 frame = graphic_info[graphic].anim_frames - 1;
1493 else if (right_stopped)
1495 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1496 frame = graphic_info[graphic].anim_frames - 1;
1501 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1502 else if (mask_mode == USE_MASKING)
1503 DrawGraphicThruMask(x, y, graphic, frame);
1505 DrawGraphic(x, y, graphic, frame);
1508 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1509 int cut_mode, int mask_mode)
1511 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1512 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1513 cut_mode, mask_mode);
1516 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1519 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1522 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1525 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1528 void DrawLevelElementThruMask(int x, int y, int element)
1530 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1533 void DrawLevelFieldThruMask(int x, int y)
1535 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1538 /* !!! implementation of quicksand is totally broken !!! */
1539 #define IS_CRUMBLED_TILE(x, y, e) \
1540 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1541 !IS_MOVING(x, y) || \
1542 (e) == EL_QUICKSAND_EMPTYING || \
1543 (e) == EL_QUICKSAND_FAST_EMPTYING))
1545 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1550 int width, height, cx, cy;
1551 int sx = SCREENX(x), sy = SCREENY(y);
1552 int crumbled_border_size = graphic_info[graphic].border_size;
1555 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1557 for (i = 1; i < 4; i++)
1559 int dxx = (i & 1 ? dx : 0);
1560 int dyy = (i & 2 ? dy : 0);
1563 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1566 /* check if neighbour field is of same crumble type */
1567 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1568 graphic_info[graphic].class ==
1569 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1571 /* return if check prevents inner corner */
1572 if (same == (dxx == dx && dyy == dy))
1576 /* if we reach this point, we have an inner corner */
1578 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1580 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1581 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1582 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1583 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1585 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1586 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1589 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1594 int width, height, bx, by, cx, cy;
1595 int sx = SCREENX(x), sy = SCREENY(y);
1596 int crumbled_border_size = graphic_info[graphic].border_size;
1597 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1598 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1601 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1603 /* draw simple, sloppy, non-corner-accurate crumbled border */
1605 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1606 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1607 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1608 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1610 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1611 FX + sx * TILEX_VAR + cx,
1612 FY + sy * TILEY_VAR + cy);
1614 /* (remaining middle border part must be at least as big as corner part) */
1615 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1616 crumbled_border_size >= TILESIZE / 3)
1619 /* correct corners of crumbled border, if needed */
1621 for (i = -1; i <= 1; i += 2)
1623 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1624 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1625 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1628 /* check if neighbour field is of same crumble type */
1629 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1630 graphic_info[graphic].class ==
1631 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1633 /* no crumbled corner, but continued crumbled border */
1635 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1636 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1637 int b1 = (i == 1 ? crumbled_border_size_var :
1638 TILESIZE_VAR - 2 * crumbled_border_size_var);
1640 width = crumbled_border_size_var;
1641 height = crumbled_border_size_var;
1643 if (dir == 1 || dir == 2)
1658 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1660 FX + sx * TILEX_VAR + cx,
1661 FY + sy * TILEY_VAR + cy);
1666 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1668 int sx = SCREENX(x), sy = SCREENY(y);
1671 static int xy[4][2] =
1679 if (!IN_LEV_FIELD(x, y))
1682 element = TILE_GFX_ELEMENT(x, y);
1684 /* crumble field itself */
1685 if (IS_CRUMBLED_TILE(x, y, element))
1687 if (!IN_SCR_FIELD(sx, sy))
1690 for (i = 0; i < 4; i++)
1692 int xx = x + xy[i][0];
1693 int yy = y + xy[i][1];
1695 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1698 /* check if neighbour field is of same crumble type */
1699 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1700 graphic_info[graphic].class ==
1701 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1704 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1707 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1708 graphic_info[graphic].anim_frames == 2)
1710 for (i = 0; i < 4; i++)
1712 int dx = (i & 1 ? +1 : -1);
1713 int dy = (i & 2 ? +1 : -1);
1715 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1719 MarkTileDirty(sx, sy);
1721 else /* center field not crumbled -- crumble neighbour fields */
1723 for (i = 0; i < 4; i++)
1725 int xx = x + xy[i][0];
1726 int yy = y + xy[i][1];
1727 int sxx = sx + xy[i][0];
1728 int syy = sy + xy[i][1];
1730 if (!IN_LEV_FIELD(xx, yy) ||
1731 !IN_SCR_FIELD(sxx, syy))
1734 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1737 element = TILE_GFX_ELEMENT(xx, yy);
1739 if (!IS_CRUMBLED_TILE(xx, yy, element))
1742 graphic = el_act2crm(element, ACTION_DEFAULT);
1744 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1746 MarkTileDirty(sxx, syy);
1751 void DrawLevelFieldCrumbled(int x, int y)
1755 if (!IN_LEV_FIELD(x, y))
1758 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1759 GfxElement[x][y] != EL_UNDEFINED &&
1760 GFX_CRUMBLED(GfxElement[x][y]))
1762 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1767 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1769 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1772 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1775 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1776 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1777 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1778 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1779 int sx = SCREENX(x), sy = SCREENY(y);
1781 DrawGraphic(sx, sy, graphic1, frame1);
1782 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1785 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1787 int sx = SCREENX(x), sy = SCREENY(y);
1788 static int xy[4][2] =
1797 for (i = 0; i < 4; i++)
1799 int xx = x + xy[i][0];
1800 int yy = y + xy[i][1];
1801 int sxx = sx + xy[i][0];
1802 int syy = sy + xy[i][1];
1804 if (!IN_LEV_FIELD(xx, yy) ||
1805 !IN_SCR_FIELD(sxx, syy) ||
1806 !GFX_CRUMBLED(Feld[xx][yy]) ||
1810 DrawLevelField(xx, yy);
1814 static int getBorderElement(int x, int y)
1818 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1819 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1820 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1821 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1822 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1823 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1824 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1826 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1827 int steel_position = (x == -1 && y == -1 ? 0 :
1828 x == lev_fieldx && y == -1 ? 1 :
1829 x == -1 && y == lev_fieldy ? 2 :
1830 x == lev_fieldx && y == lev_fieldy ? 3 :
1831 x == -1 || x == lev_fieldx ? 4 :
1832 y == -1 || y == lev_fieldy ? 5 : 6);
1834 return border[steel_position][steel_type];
1837 void DrawScreenElement(int x, int y, int element)
1839 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1840 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1843 void DrawLevelElement(int x, int y, int element)
1845 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1846 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1849 void DrawScreenField(int x, int y)
1851 int lx = LEVELX(x), ly = LEVELY(y);
1852 int element, content;
1854 if (!IN_LEV_FIELD(lx, ly))
1856 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1859 element = getBorderElement(lx, ly);
1861 DrawScreenElement(x, y, element);
1866 element = Feld[lx][ly];
1867 content = Store[lx][ly];
1869 if (IS_MOVING(lx, ly))
1871 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1872 boolean cut_mode = NO_CUTTING;
1874 if (element == EL_QUICKSAND_EMPTYING ||
1875 element == EL_QUICKSAND_FAST_EMPTYING ||
1876 element == EL_MAGIC_WALL_EMPTYING ||
1877 element == EL_BD_MAGIC_WALL_EMPTYING ||
1878 element == EL_DC_MAGIC_WALL_EMPTYING ||
1879 element == EL_AMOEBA_DROPPING)
1880 cut_mode = CUT_ABOVE;
1881 else if (element == EL_QUICKSAND_FILLING ||
1882 element == EL_QUICKSAND_FAST_FILLING ||
1883 element == EL_MAGIC_WALL_FILLING ||
1884 element == EL_BD_MAGIC_WALL_FILLING ||
1885 element == EL_DC_MAGIC_WALL_FILLING)
1886 cut_mode = CUT_BELOW;
1888 if (cut_mode == CUT_ABOVE)
1889 DrawScreenElement(x, y, element);
1891 DrawScreenElement(x, y, EL_EMPTY);
1894 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1895 else if (cut_mode == NO_CUTTING)
1896 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1899 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1901 if (cut_mode == CUT_BELOW &&
1902 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1903 DrawLevelElement(lx, ly + 1, element);
1906 if (content == EL_ACID)
1908 int dir = MovDir[lx][ly];
1909 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1910 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1912 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1915 else if (IS_BLOCKED(lx, ly))
1920 boolean cut_mode = NO_CUTTING;
1921 int element_old, content_old;
1923 Blocked2Moving(lx, ly, &oldx, &oldy);
1926 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1927 MovDir[oldx][oldy] == MV_RIGHT);
1929 element_old = Feld[oldx][oldy];
1930 content_old = Store[oldx][oldy];
1932 if (element_old == EL_QUICKSAND_EMPTYING ||
1933 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1934 element_old == EL_MAGIC_WALL_EMPTYING ||
1935 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1936 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1937 element_old == EL_AMOEBA_DROPPING)
1938 cut_mode = CUT_ABOVE;
1940 DrawScreenElement(x, y, EL_EMPTY);
1943 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1945 else if (cut_mode == NO_CUTTING)
1946 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1949 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1952 else if (IS_DRAWABLE(element))
1953 DrawScreenElement(x, y, element);
1955 DrawScreenElement(x, y, EL_EMPTY);
1958 void DrawLevelField(int x, int y)
1960 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1961 DrawScreenField(SCREENX(x), SCREENY(y));
1962 else if (IS_MOVING(x, y))
1966 Moving2Blocked(x, y, &newx, &newy);
1967 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1968 DrawScreenField(SCREENX(newx), SCREENY(newy));
1970 else if (IS_BLOCKED(x, y))
1974 Blocked2Moving(x, y, &oldx, &oldy);
1975 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
1976 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
1980 void DrawSizedElement(int x, int y, int element, int tilesize)
1984 graphic = el2edimg(element);
1985 DrawSizedGraphic(x, y, graphic, 0, tilesize);
1988 void DrawMiniElement(int x, int y, int element)
1992 graphic = el2edimg(element);
1993 DrawMiniGraphic(x, y, graphic);
1996 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
1999 int x = sx + scroll_x, y = sy + scroll_y;
2001 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2002 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2003 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2004 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2006 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2009 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2011 int x = sx + scroll_x, y = sy + scroll_y;
2013 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2014 DrawMiniElement(sx, sy, EL_EMPTY);
2015 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2016 DrawMiniElement(sx, sy, Feld[x][y]);
2018 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2021 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2022 int x, int y, int xsize, int ysize,
2023 int tile_width, int tile_height)
2027 int dst_x = startx + x * tile_width;
2028 int dst_y = starty + y * tile_height;
2029 int width = graphic_info[graphic].width;
2030 int height = graphic_info[graphic].height;
2031 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2032 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2033 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2034 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2035 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2036 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2037 boolean draw_masked = graphic_info[graphic].draw_masked;
2039 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2041 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2043 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2047 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2048 inner_sx + (x - 1) * tile_width % inner_width);
2049 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2050 inner_sy + (y - 1) * tile_height % inner_height);
2053 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2056 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2060 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2061 int x, int y, int xsize, int ysize, int font_nr)
2063 int font_width = getFontWidth(font_nr);
2064 int font_height = getFontHeight(font_nr);
2066 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2067 font_width, font_height);
2070 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2072 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2073 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2074 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2075 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2076 boolean no_delay = (tape.warp_forward);
2077 unsigned int anim_delay = 0;
2078 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2079 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2080 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2081 int font_width = getFontWidth(font_nr);
2082 int font_height = getFontHeight(font_nr);
2083 int max_xsize = level.envelope[envelope_nr].xsize;
2084 int max_ysize = level.envelope[envelope_nr].ysize;
2085 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2086 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2087 int xend = max_xsize;
2088 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2089 int xstep = (xstart < xend ? 1 : 0);
2090 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2092 int end = MAX(xend - xstart, yend - ystart);
2095 for (i = start; i <= end; i++)
2097 int last_frame = end; // last frame of this "for" loop
2098 int x = xstart + i * xstep;
2099 int y = ystart + i * ystep;
2100 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2101 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2102 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2103 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2106 SetDrawtoField(DRAW_FIELDBUFFER);
2108 BlitScreenToBitmap(backbuffer);
2110 SetDrawtoField(DRAW_BACKBUFFER);
2112 for (yy = 0; yy < ysize; yy++)
2113 for (xx = 0; xx < xsize; xx++)
2114 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2116 DrawTextBuffer(sx + font_width, sy + font_height,
2117 level.envelope[envelope_nr].text, font_nr, max_xsize,
2118 xsize - 2, ysize - 2, 0, mask_mode,
2119 level.envelope[envelope_nr].autowrap,
2120 level.envelope[envelope_nr].centered, FALSE);
2122 redraw_mask |= REDRAW_FIELD;
2125 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2129 void ShowEnvelope(int envelope_nr)
2131 int element = EL_ENVELOPE_1 + envelope_nr;
2132 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2133 int sound_opening = element_info[element].sound[ACTION_OPENING];
2134 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2135 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2136 boolean no_delay = (tape.warp_forward);
2137 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2138 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2139 int anim_mode = graphic_info[graphic].anim_mode;
2140 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2141 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2143 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2145 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2147 if (anim_mode == ANIM_DEFAULT)
2148 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2150 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2153 Delay(wait_delay_value);
2155 WaitForEventToContinue();
2157 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2159 if (anim_mode != ANIM_NONE)
2160 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2162 if (anim_mode == ANIM_DEFAULT)
2163 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2165 game.envelope_active = FALSE;
2167 SetDrawtoField(DRAW_FIELDBUFFER);
2169 redraw_mask |= REDRAW_FIELD;
2173 static void setRequestBasePosition(int *x, int *y)
2175 int sx_base, sy_base;
2177 if (request.x != -1)
2178 sx_base = request.x;
2179 else if (request.align == ALIGN_LEFT)
2181 else if (request.align == ALIGN_RIGHT)
2182 sx_base = SX + SXSIZE;
2184 sx_base = SX + SXSIZE / 2;
2186 if (request.y != -1)
2187 sy_base = request.y;
2188 else if (request.valign == VALIGN_TOP)
2190 else if (request.valign == VALIGN_BOTTOM)
2191 sy_base = SY + SYSIZE;
2193 sy_base = SY + SYSIZE / 2;
2199 static void setRequestPositionExt(int *x, int *y, int width, int height,
2200 boolean add_border_size)
2202 int border_size = request.border_size;
2203 int sx_base, sy_base;
2206 setRequestBasePosition(&sx_base, &sy_base);
2208 if (request.align == ALIGN_LEFT)
2210 else if (request.align == ALIGN_RIGHT)
2211 sx = sx_base - width;
2213 sx = sx_base - width / 2;
2215 if (request.valign == VALIGN_TOP)
2217 else if (request.valign == VALIGN_BOTTOM)
2218 sy = sy_base - height;
2220 sy = sy_base - height / 2;
2222 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2223 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2225 if (add_border_size)
2235 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2237 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2240 void DrawEnvelopeRequest(char *text)
2242 char *text_final = text;
2243 char *text_door_style = NULL;
2244 int graphic = IMG_BACKGROUND_REQUEST;
2245 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2246 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2247 int font_nr = FONT_REQUEST;
2248 int font_width = getFontWidth(font_nr);
2249 int font_height = getFontHeight(font_nr);
2250 int border_size = request.border_size;
2251 int line_spacing = request.line_spacing;
2252 int line_height = font_height + line_spacing;
2253 int text_width = request.width - 2 * border_size;
2254 int text_height = request.height - 2 * border_size;
2255 int line_length = text_width / font_width;
2256 int max_lines = text_height / line_height;
2257 int width = request.width;
2258 int height = request.height;
2259 int tile_size = request.step_offset;
2260 int x_steps = width / tile_size;
2261 int y_steps = height / tile_size;
2265 if (request.wrap_single_words)
2267 char *src_text_ptr, *dst_text_ptr;
2269 text_door_style = checked_malloc(2 * strlen(text) + 1);
2271 src_text_ptr = text;
2272 dst_text_ptr = text_door_style;
2274 while (*src_text_ptr)
2276 if (*src_text_ptr == ' ' ||
2277 *src_text_ptr == '?' ||
2278 *src_text_ptr == '!')
2279 *dst_text_ptr++ = '\n';
2281 if (*src_text_ptr != ' ')
2282 *dst_text_ptr++ = *src_text_ptr;
2287 *dst_text_ptr = '\0';
2289 text_final = text_door_style;
2292 setRequestPosition(&sx, &sy, FALSE);
2294 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2296 for (y = 0; y < y_steps; y++)
2297 for (x = 0; x < x_steps; x++)
2298 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2299 x, y, x_steps, y_steps,
2300 tile_size, tile_size);
2302 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2303 line_length, -1, max_lines, line_spacing, mask_mode,
2304 request.autowrap, request.centered, FALSE);
2306 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2307 RedrawGadget(tool_gadget[i]);
2309 // store readily prepared envelope request for later use when animating
2310 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2312 if (text_door_style)
2313 free(text_door_style);
2316 void AnimateEnvelopeRequest(int anim_mode, int action)
2318 int graphic = IMG_BACKGROUND_REQUEST;
2319 boolean draw_masked = graphic_info[graphic].draw_masked;
2320 int delay_value_normal = request.step_delay;
2321 int delay_value_fast = delay_value_normal / 2;
2322 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2323 boolean no_delay = (tape.warp_forward);
2324 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2325 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2326 unsigned int anim_delay = 0;
2328 int tile_size = request.step_offset;
2329 int max_xsize = request.width / tile_size;
2330 int max_ysize = request.height / tile_size;
2331 int max_xsize_inner = max_xsize - 2;
2332 int max_ysize_inner = max_ysize - 2;
2334 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2335 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2336 int xend = max_xsize_inner;
2337 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2338 int xstep = (xstart < xend ? 1 : 0);
2339 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2341 int end = MAX(xend - xstart, yend - ystart);
2344 if (setup.quick_doors)
2352 if (action == ACTION_OPENING)
2353 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2354 else if (action == ACTION_CLOSING)
2355 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2358 for (i = start; i <= end; i++)
2360 int last_frame = end; // last frame of this "for" loop
2361 int x = xstart + i * xstep;
2362 int y = ystart + i * ystep;
2363 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2364 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2365 int xsize_size_left = (xsize - 1) * tile_size;
2366 int ysize_size_top = (ysize - 1) * tile_size;
2367 int max_xsize_pos = (max_xsize - 1) * tile_size;
2368 int max_ysize_pos = (max_ysize - 1) * tile_size;
2369 int width = xsize * tile_size;
2370 int height = ysize * tile_size;
2375 setRequestPosition(&src_x, &src_y, FALSE);
2376 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2378 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2380 for (yy = 0; yy < 2; yy++)
2382 for (xx = 0; xx < 2; xx++)
2384 int src_xx = src_x + xx * max_xsize_pos;
2385 int src_yy = src_y + yy * max_ysize_pos;
2386 int dst_xx = dst_x + xx * xsize_size_left;
2387 int dst_yy = dst_y + yy * ysize_size_top;
2388 int xx_size = (xx ? tile_size : xsize_size_left);
2389 int yy_size = (yy ? tile_size : ysize_size_top);
2392 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2393 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2395 BlitBitmap(bitmap_db_cross, backbuffer,
2396 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2400 redraw_mask |= REDRAW_FIELD;
2405 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2409 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2411 int last_game_status = game_status; /* save current game status */
2412 int graphic = IMG_BACKGROUND_REQUEST;
2413 int sound_opening = SND_REQUEST_OPENING;
2414 int sound_closing = SND_REQUEST_CLOSING;
2415 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2416 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2417 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2418 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2419 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2421 if (game_status == GAME_MODE_PLAYING)
2422 BlitScreenToBitmap(backbuffer);
2424 SetDrawtoField(DRAW_BACKBUFFER);
2426 // SetDrawBackgroundMask(REDRAW_NONE);
2428 if (action == ACTION_OPENING)
2430 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2432 if (req_state & REQ_ASK)
2434 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2435 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2437 else if (req_state & REQ_CONFIRM)
2439 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2441 else if (req_state & REQ_PLAYER)
2443 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2444 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2445 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2446 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2449 DrawEnvelopeRequest(text);
2451 if (game_status != GAME_MODE_MAIN)
2455 /* force DOOR font inside door area */
2456 game_status = GAME_MODE_PSEUDO_DOOR;
2458 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2460 if (action == ACTION_OPENING)
2462 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2464 if (anim_mode == ANIM_DEFAULT)
2465 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2467 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2471 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2473 if (anim_mode != ANIM_NONE)
2474 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2476 if (anim_mode == ANIM_DEFAULT)
2477 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2480 game.envelope_active = FALSE;
2482 game_status = last_game_status; /* restore current game status */
2484 if (action == ACTION_CLOSING)
2486 if (game_status != GAME_MODE_MAIN)
2489 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2492 // SetDrawBackgroundMask(last_draw_background_mask);
2494 redraw_mask |= REDRAW_FIELD;
2496 if (game_status == GAME_MODE_MAIN)
2501 if (action == ACTION_CLOSING &&
2502 game_status == GAME_MODE_PLAYING &&
2503 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2504 SetDrawtoField(DRAW_FIELDBUFFER);
2507 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2511 int graphic = el2preimg(element);
2513 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2514 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2517 void DrawLevel(int draw_background_mask)
2521 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2522 SetDrawBackgroundMask(draw_background_mask);
2526 for (x = BX1; x <= BX2; x++)
2527 for (y = BY1; y <= BY2; y++)
2528 DrawScreenField(x, y);
2530 redraw_mask |= REDRAW_FIELD;
2533 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2538 for (x = 0; x < size_x; x++)
2539 for (y = 0; y < size_y; y++)
2540 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2542 redraw_mask |= REDRAW_FIELD;
2545 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2549 for (x = 0; x < size_x; x++)
2550 for (y = 0; y < size_y; y++)
2551 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2553 redraw_mask |= REDRAW_FIELD;
2556 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2558 boolean show_level_border = (BorderElement != EL_EMPTY);
2559 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2560 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2561 int tile_size = preview.tile_size;
2562 int preview_width = preview.xsize * tile_size;
2563 int preview_height = preview.ysize * tile_size;
2564 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2565 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2566 int real_preview_width = real_preview_xsize * tile_size;
2567 int real_preview_height = real_preview_ysize * tile_size;
2568 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2569 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2572 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2575 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2577 dst_x += (preview_width - real_preview_width) / 2;
2578 dst_y += (preview_height - real_preview_height) / 2;
2580 for (x = 0; x < real_preview_xsize; x++)
2582 for (y = 0; y < real_preview_ysize; y++)
2584 int lx = from_x + x + (show_level_border ? -1 : 0);
2585 int ly = from_y + y + (show_level_border ? -1 : 0);
2586 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2587 getBorderElement(lx, ly));
2589 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2590 element, tile_size);
2594 redraw_mask |= REDRAW_FIELD;
2597 #define MICROLABEL_EMPTY 0
2598 #define MICROLABEL_LEVEL_NAME 1
2599 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2600 #define MICROLABEL_LEVEL_AUTHOR 3
2601 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2602 #define MICROLABEL_IMPORTED_FROM 5
2603 #define MICROLABEL_IMPORTED_BY_HEAD 6
2604 #define MICROLABEL_IMPORTED_BY 7
2606 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2608 int max_text_width = SXSIZE;
2609 int font_width = getFontWidth(font_nr);
2611 if (pos->align == ALIGN_CENTER)
2612 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2613 else if (pos->align == ALIGN_RIGHT)
2614 max_text_width = pos->x;
2616 max_text_width = SXSIZE - pos->x;
2618 return max_text_width / font_width;
2621 static void DrawPreviewLevelLabelExt(int mode)
2623 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2624 char label_text[MAX_OUTPUT_LINESIZE + 1];
2625 int max_len_label_text;
2626 int font_nr = pos->font;
2629 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2632 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2633 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2634 mode == MICROLABEL_IMPORTED_BY_HEAD)
2635 font_nr = pos->font_alt;
2637 max_len_label_text = getMaxTextLength(pos, font_nr);
2639 if (pos->size != -1)
2640 max_len_label_text = pos->size;
2642 for (i = 0; i < max_len_label_text; i++)
2643 label_text[i] = ' ';
2644 label_text[max_len_label_text] = '\0';
2646 if (strlen(label_text) > 0)
2647 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2650 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2651 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2652 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2653 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2654 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2655 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2656 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2657 max_len_label_text);
2658 label_text[max_len_label_text] = '\0';
2660 if (strlen(label_text) > 0)
2661 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2663 redraw_mask |= REDRAW_FIELD;
2666 static void DrawPreviewLevelExt(boolean restart)
2668 static unsigned int scroll_delay = 0;
2669 static unsigned int label_delay = 0;
2670 static int from_x, from_y, scroll_direction;
2671 static int label_state, label_counter;
2672 unsigned int scroll_delay_value = preview.step_delay;
2673 boolean show_level_border = (BorderElement != EL_EMPTY);
2674 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2675 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2676 int last_game_status = game_status; /* save current game status */
2683 if (preview.anim_mode == ANIM_CENTERED)
2685 if (level_xsize > preview.xsize)
2686 from_x = (level_xsize - preview.xsize) / 2;
2687 if (level_ysize > preview.ysize)
2688 from_y = (level_ysize - preview.ysize) / 2;
2691 from_x += preview.xoffset;
2692 from_y += preview.yoffset;
2694 scroll_direction = MV_RIGHT;
2698 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2699 DrawPreviewLevelLabelExt(label_state);
2701 /* initialize delay counters */
2702 DelayReached(&scroll_delay, 0);
2703 DelayReached(&label_delay, 0);
2705 if (leveldir_current->name)
2707 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2708 char label_text[MAX_OUTPUT_LINESIZE + 1];
2709 int font_nr = pos->font;
2710 int max_len_label_text = getMaxTextLength(pos, font_nr);
2712 if (pos->size != -1)
2713 max_len_label_text = pos->size;
2715 strncpy(label_text, leveldir_current->name, max_len_label_text);
2716 label_text[max_len_label_text] = '\0';
2718 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2719 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2722 game_status = last_game_status; /* restore current game status */
2727 /* scroll preview level, if needed */
2728 if (preview.anim_mode != ANIM_NONE &&
2729 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2730 DelayReached(&scroll_delay, scroll_delay_value))
2732 switch (scroll_direction)
2737 from_x -= preview.step_offset;
2738 from_x = (from_x < 0 ? 0 : from_x);
2741 scroll_direction = MV_UP;
2745 if (from_x < level_xsize - preview.xsize)
2747 from_x += preview.step_offset;
2748 from_x = (from_x > level_xsize - preview.xsize ?
2749 level_xsize - preview.xsize : from_x);
2752 scroll_direction = MV_DOWN;
2758 from_y -= preview.step_offset;
2759 from_y = (from_y < 0 ? 0 : from_y);
2762 scroll_direction = MV_RIGHT;
2766 if (from_y < level_ysize - preview.ysize)
2768 from_y += preview.step_offset;
2769 from_y = (from_y > level_ysize - preview.ysize ?
2770 level_ysize - preview.ysize : from_y);
2773 scroll_direction = MV_LEFT;
2780 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2783 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2784 /* redraw micro level label, if needed */
2785 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2786 !strEqual(level.author, ANONYMOUS_NAME) &&
2787 !strEqual(level.author, leveldir_current->name) &&
2788 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2790 int max_label_counter = 23;
2792 if (leveldir_current->imported_from != NULL &&
2793 strlen(leveldir_current->imported_from) > 0)
2794 max_label_counter += 14;
2795 if (leveldir_current->imported_by != NULL &&
2796 strlen(leveldir_current->imported_by) > 0)
2797 max_label_counter += 14;
2799 label_counter = (label_counter + 1) % max_label_counter;
2800 label_state = (label_counter >= 0 && label_counter <= 7 ?
2801 MICROLABEL_LEVEL_NAME :
2802 label_counter >= 9 && label_counter <= 12 ?
2803 MICROLABEL_LEVEL_AUTHOR_HEAD :
2804 label_counter >= 14 && label_counter <= 21 ?
2805 MICROLABEL_LEVEL_AUTHOR :
2806 label_counter >= 23 && label_counter <= 26 ?
2807 MICROLABEL_IMPORTED_FROM_HEAD :
2808 label_counter >= 28 && label_counter <= 35 ?
2809 MICROLABEL_IMPORTED_FROM :
2810 label_counter >= 37 && label_counter <= 40 ?
2811 MICROLABEL_IMPORTED_BY_HEAD :
2812 label_counter >= 42 && label_counter <= 49 ?
2813 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2815 if (leveldir_current->imported_from == NULL &&
2816 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2817 label_state == MICROLABEL_IMPORTED_FROM))
2818 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2819 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2821 DrawPreviewLevelLabelExt(label_state);
2824 game_status = last_game_status; /* restore current game status */
2827 void DrawPreviewLevelInitial()
2829 DrawPreviewLevelExt(TRUE);
2832 void DrawPreviewLevelAnimation()
2834 DrawPreviewLevelExt(FALSE);
2837 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2838 int graphic, int sync_frame, int mask_mode)
2840 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2842 if (mask_mode == USE_MASKING)
2843 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2845 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2848 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2849 int graphic, int sync_frame,
2852 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2854 if (mask_mode == USE_MASKING)
2855 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2857 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2860 inline void DrawGraphicAnimation(int x, int y, int graphic)
2862 int lx = LEVELX(x), ly = LEVELY(y);
2864 if (!IN_SCR_FIELD(x, y))
2867 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2868 graphic, GfxFrame[lx][ly], NO_MASKING);
2870 MarkTileDirty(x, y);
2873 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2875 int lx = LEVELX(x), ly = LEVELY(y);
2877 if (!IN_SCR_FIELD(x, y))
2880 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2881 graphic, GfxFrame[lx][ly], NO_MASKING);
2882 MarkTileDirty(x, y);
2885 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2887 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2890 void DrawLevelElementAnimation(int x, int y, int element)
2892 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2894 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2897 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2899 int sx = SCREENX(x), sy = SCREENY(y);
2901 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2904 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2907 DrawGraphicAnimation(sx, sy, graphic);
2910 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2911 DrawLevelFieldCrumbled(x, y);
2913 if (GFX_CRUMBLED(Feld[x][y]))
2914 DrawLevelFieldCrumbled(x, y);
2918 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2920 int sx = SCREENX(x), sy = SCREENY(y);
2923 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2926 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2928 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2931 DrawGraphicAnimation(sx, sy, graphic);
2933 if (GFX_CRUMBLED(element))
2934 DrawLevelFieldCrumbled(x, y);
2937 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2939 if (player->use_murphy)
2941 /* this works only because currently only one player can be "murphy" ... */
2942 static int last_horizontal_dir = MV_LEFT;
2943 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2945 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2946 last_horizontal_dir = move_dir;
2948 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2950 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2952 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2958 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2961 static boolean equalGraphics(int graphic1, int graphic2)
2963 struct GraphicInfo *g1 = &graphic_info[graphic1];
2964 struct GraphicInfo *g2 = &graphic_info[graphic2];
2966 return (g1->bitmap == g2->bitmap &&
2967 g1->src_x == g2->src_x &&
2968 g1->src_y == g2->src_y &&
2969 g1->anim_frames == g2->anim_frames &&
2970 g1->anim_delay == g2->anim_delay &&
2971 g1->anim_mode == g2->anim_mode);
2974 void DrawAllPlayers()
2978 for (i = 0; i < MAX_PLAYERS; i++)
2979 if (stored_player[i].active)
2980 DrawPlayer(&stored_player[i]);
2983 void DrawPlayerField(int x, int y)
2985 if (!IS_PLAYER(x, y))
2988 DrawPlayer(PLAYERINFO(x, y));
2991 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
2993 void DrawPlayer(struct PlayerInfo *player)
2995 int jx = player->jx;
2996 int jy = player->jy;
2997 int move_dir = player->MovDir;
2998 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
2999 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3000 int last_jx = (player->is_moving ? jx - dx : jx);
3001 int last_jy = (player->is_moving ? jy - dy : jy);
3002 int next_jx = jx + dx;
3003 int next_jy = jy + dy;
3004 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3005 boolean player_is_opaque = FALSE;
3006 int sx = SCREENX(jx), sy = SCREENY(jy);
3007 int sxx = 0, syy = 0;
3008 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3010 int action = ACTION_DEFAULT;
3011 int last_player_graphic = getPlayerGraphic(player, move_dir);
3012 int last_player_frame = player->Frame;
3015 /* GfxElement[][] is set to the element the player is digging or collecting;
3016 remove also for off-screen player if the player is not moving anymore */
3017 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3018 GfxElement[jx][jy] = EL_UNDEFINED;
3020 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3024 if (!IN_LEV_FIELD(jx, jy))
3026 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3027 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3028 printf("DrawPlayerField(): This should never happen!\n");
3033 if (element == EL_EXPLOSION)
3036 action = (player->is_pushing ? ACTION_PUSHING :
3037 player->is_digging ? ACTION_DIGGING :
3038 player->is_collecting ? ACTION_COLLECTING :
3039 player->is_moving ? ACTION_MOVING :
3040 player->is_snapping ? ACTION_SNAPPING :
3041 player->is_dropping ? ACTION_DROPPING :
3042 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3044 if (player->is_waiting)
3045 move_dir = player->dir_waiting;
3047 InitPlayerGfxAnimation(player, action, move_dir);
3049 /* ----------------------------------------------------------------------- */
3050 /* draw things in the field the player is leaving, if needed */
3051 /* ----------------------------------------------------------------------- */
3053 if (player->is_moving)
3055 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3057 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3059 if (last_element == EL_DYNAMITE_ACTIVE ||
3060 last_element == EL_EM_DYNAMITE_ACTIVE ||
3061 last_element == EL_SP_DISK_RED_ACTIVE)
3062 DrawDynamite(last_jx, last_jy);
3064 DrawLevelFieldThruMask(last_jx, last_jy);
3066 else if (last_element == EL_DYNAMITE_ACTIVE ||
3067 last_element == EL_EM_DYNAMITE_ACTIVE ||
3068 last_element == EL_SP_DISK_RED_ACTIVE)
3069 DrawDynamite(last_jx, last_jy);
3071 /* !!! this is not enough to prevent flickering of players which are
3072 moving next to each others without a free tile between them -- this
3073 can only be solved by drawing all players layer by layer (first the
3074 background, then the foreground etc.) !!! => TODO */
3075 else if (!IS_PLAYER(last_jx, last_jy))
3076 DrawLevelField(last_jx, last_jy);
3079 DrawLevelField(last_jx, last_jy);
3082 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3083 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3086 if (!IN_SCR_FIELD(sx, sy))
3089 /* ----------------------------------------------------------------------- */
3090 /* draw things behind the player, if needed */
3091 /* ----------------------------------------------------------------------- */
3094 DrawLevelElement(jx, jy, Back[jx][jy]);
3095 else if (IS_ACTIVE_BOMB(element))
3096 DrawLevelElement(jx, jy, EL_EMPTY);
3099 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3101 int old_element = GfxElement[jx][jy];
3102 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3103 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3105 if (GFX_CRUMBLED(old_element))
3106 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3108 DrawGraphic(sx, sy, old_graphic, frame);
3110 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3111 player_is_opaque = TRUE;
3115 GfxElement[jx][jy] = EL_UNDEFINED;
3117 /* make sure that pushed elements are drawn with correct frame rate */
3118 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3120 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3121 GfxFrame[jx][jy] = player->StepFrame;
3123 DrawLevelField(jx, jy);
3127 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3128 /* ----------------------------------------------------------------------- */
3129 /* draw player himself */
3130 /* ----------------------------------------------------------------------- */
3132 graphic = getPlayerGraphic(player, move_dir);
3134 /* in the case of changed player action or direction, prevent the current
3135 animation frame from being restarted for identical animations */
3136 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3137 player->Frame = last_player_frame;
3139 frame = getGraphicAnimationFrame(graphic, player->Frame);
3143 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3144 sxx = player->GfxPos;
3146 syy = player->GfxPos;
3149 if (player_is_opaque)
3150 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3152 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3154 if (SHIELD_ON(player))
3156 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3157 IMG_SHIELD_NORMAL_ACTIVE);
3158 int frame = getGraphicAnimationFrame(graphic, -1);
3160 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3164 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3167 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3168 sxx = player->GfxPos;
3170 syy = player->GfxPos;
3174 /* ----------------------------------------------------------------------- */
3175 /* draw things the player is pushing, if needed */
3176 /* ----------------------------------------------------------------------- */
3178 if (player->is_pushing && player->is_moving)
3180 int px = SCREENX(jx), py = SCREENY(jy);
3181 int pxx = (TILEX - ABS(sxx)) * dx;
3182 int pyy = (TILEY - ABS(syy)) * dy;
3183 int gfx_frame = GfxFrame[jx][jy];
3189 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3191 element = Feld[next_jx][next_jy];
3192 gfx_frame = GfxFrame[next_jx][next_jy];
3195 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3197 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3198 frame = getGraphicAnimationFrame(graphic, sync_frame);
3200 /* draw background element under pushed element (like the Sokoban field) */
3201 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3203 /* this allows transparent pushing animation over non-black background */
3206 DrawLevelElement(jx, jy, Back[jx][jy]);
3208 DrawLevelElement(jx, jy, EL_EMPTY);
3210 if (Back[next_jx][next_jy])
3211 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3213 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3215 else if (Back[next_jx][next_jy])
3216 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3219 /* do not draw (EM style) pushing animation when pushing is finished */
3220 /* (two-tile animations usually do not contain start and end frame) */
3221 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3222 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3224 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3226 /* masked drawing is needed for EMC style (double) movement graphics */
3227 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3228 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3232 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3233 /* ----------------------------------------------------------------------- */
3234 /* draw player himself */
3235 /* ----------------------------------------------------------------------- */
3237 graphic = getPlayerGraphic(player, move_dir);
3239 /* in the case of changed player action or direction, prevent the current
3240 animation frame from being restarted for identical animations */
3241 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3242 player->Frame = last_player_frame;
3244 frame = getGraphicAnimationFrame(graphic, player->Frame);
3248 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3249 sxx = player->GfxPos;
3251 syy = player->GfxPos;
3254 if (player_is_opaque)
3255 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3257 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3259 if (SHIELD_ON(player))
3261 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3262 IMG_SHIELD_NORMAL_ACTIVE);
3263 int frame = getGraphicAnimationFrame(graphic, -1);
3265 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3269 /* ----------------------------------------------------------------------- */
3270 /* draw things in front of player (active dynamite or dynabombs) */
3271 /* ----------------------------------------------------------------------- */
3273 if (IS_ACTIVE_BOMB(element))
3275 graphic = el2img(element);
3276 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3278 if (game.emulation == EMU_SUPAPLEX)
3279 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3281 DrawGraphicThruMask(sx, sy, graphic, frame);
3284 if (player_is_moving && last_element == EL_EXPLOSION)
3286 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3287 GfxElement[last_jx][last_jy] : EL_EMPTY);
3288 int graphic = el_act2img(element, ACTION_EXPLODING);
3289 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3290 int phase = ExplodePhase[last_jx][last_jy] - 1;
3291 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3294 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3297 /* ----------------------------------------------------------------------- */
3298 /* draw elements the player is just walking/passing through/under */
3299 /* ----------------------------------------------------------------------- */
3301 if (player_is_moving)
3303 /* handle the field the player is leaving ... */
3304 if (IS_ACCESSIBLE_INSIDE(last_element))
3305 DrawLevelField(last_jx, last_jy);
3306 else if (IS_ACCESSIBLE_UNDER(last_element))
3307 DrawLevelFieldThruMask(last_jx, last_jy);
3310 /* do not redraw accessible elements if the player is just pushing them */
3311 if (!player_is_moving || !player->is_pushing)
3313 /* ... and the field the player is entering */
3314 if (IS_ACCESSIBLE_INSIDE(element))
3315 DrawLevelField(jx, jy);
3316 else if (IS_ACCESSIBLE_UNDER(element))
3317 DrawLevelFieldThruMask(jx, jy);
3320 MarkTileDirty(sx, sy);
3323 /* ------------------------------------------------------------------------- */
3325 void WaitForEventToContinue()
3327 boolean still_wait = TRUE;
3329 /* simulate releasing mouse button over last gadget, if still pressed */
3331 HandleGadgets(-1, -1, 0);
3333 button_status = MB_RELEASED;
3347 case EVENT_BUTTONPRESS:
3348 case EVENT_KEYPRESS:
3352 case EVENT_KEYRELEASE:
3353 ClearPlayerAction();
3357 HandleOtherEvents(&event);
3361 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3368 /* don't eat all CPU time */
3373 #define MAX_REQUEST_LINES 13
3374 #define MAX_REQUEST_LINE_FONT1_LEN 7
3375 #define MAX_REQUEST_LINE_FONT2_LEN 10
3377 static int RequestHandleEvents(unsigned int req_state)
3379 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3380 local_player->LevelSolved_GameEnd);
3381 int last_game_status = game_status; /* save current game status */
3382 int width = request.width;
3383 int height = request.height;
3387 setRequestPosition(&sx, &sy, FALSE);
3389 button_status = MB_RELEASED;
3391 request_gadget_id = -1;
3398 SetDrawtoField(DRAW_FIELDBUFFER);
3400 HandleGameActions();
3402 SetDrawtoField(DRAW_BACKBUFFER);
3404 if (global.use_envelope_request)
3406 /* copy current state of request area to middle of playfield area */
3407 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3415 while (NextValidEvent(&event))
3419 case EVENT_BUTTONPRESS:
3420 case EVENT_BUTTONRELEASE:
3421 case EVENT_MOTIONNOTIFY:
3425 if (event.type == EVENT_MOTIONNOTIFY)
3430 motion_status = TRUE;
3431 mx = ((MotionEvent *) &event)->x;
3432 my = ((MotionEvent *) &event)->y;
3436 motion_status = FALSE;
3437 mx = ((ButtonEvent *) &event)->x;
3438 my = ((ButtonEvent *) &event)->y;
3439 if (event.type == EVENT_BUTTONPRESS)
3440 button_status = ((ButtonEvent *) &event)->button;
3442 button_status = MB_RELEASED;
3445 /* this sets 'request_gadget_id' */
3446 HandleGadgets(mx, my, button_status);
3448 switch (request_gadget_id)
3450 case TOOL_CTRL_ID_YES:
3453 case TOOL_CTRL_ID_NO:
3456 case TOOL_CTRL_ID_CONFIRM:
3457 result = TRUE | FALSE;
3460 case TOOL_CTRL_ID_PLAYER_1:
3463 case TOOL_CTRL_ID_PLAYER_2:
3466 case TOOL_CTRL_ID_PLAYER_3:
3469 case TOOL_CTRL_ID_PLAYER_4:
3480 case EVENT_KEYPRESS:
3481 switch (GetEventKey((KeyEvent *)&event, TRUE))
3484 if (req_state & REQ_CONFIRM)
3489 #if defined(TARGET_SDL2)
3496 #if defined(TARGET_SDL2)
3506 if (req_state & REQ_PLAYER)
3510 case EVENT_KEYRELEASE:
3511 ClearPlayerAction();
3515 HandleOtherEvents(&event);
3520 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3522 int joy = AnyJoystick();
3524 if (joy & JOY_BUTTON_1)
3526 else if (joy & JOY_BUTTON_2)
3532 if (global.use_envelope_request)
3534 /* copy back current state of pressed buttons inside request area */
3535 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3542 if (!PendingEvent()) /* delay only if no pending events */
3546 game_status = GAME_MODE_PSEUDO_DOOR;
3550 game_status = last_game_status; /* restore current game status */
3556 static boolean RequestDoor(char *text, unsigned int req_state)
3558 unsigned int old_door_state;
3559 int last_game_status = game_status; /* save current game status */
3560 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3561 int font_nr = FONT_TEXT_2;
3566 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3568 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3569 font_nr = FONT_TEXT_1;
3572 if (game_status == GAME_MODE_PLAYING)
3573 BlitScreenToBitmap(backbuffer);
3575 /* disable deactivated drawing when quick-loading level tape recording */
3576 if (tape.playing && tape.deactivate_display)
3577 TapeDeactivateDisplayOff(TRUE);
3579 SetMouseCursor(CURSOR_DEFAULT);
3581 #if defined(NETWORK_AVALIABLE)
3582 /* pause network game while waiting for request to answer */
3583 if (options.network &&
3584 game_status == GAME_MODE_PLAYING &&
3585 req_state & REQUEST_WAIT_FOR_INPUT)
3586 SendToServer_PausePlaying();
3589 old_door_state = GetDoorState();
3591 /* simulate releasing mouse button over last gadget, if still pressed */
3593 HandleGadgets(-1, -1, 0);
3597 /* draw released gadget before proceeding */
3600 if (old_door_state & DOOR_OPEN_1)
3602 CloseDoor(DOOR_CLOSE_1);
3604 /* save old door content */
3605 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3606 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3609 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3610 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3612 /* clear door drawing field */
3613 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3615 /* force DOOR font inside door area */
3616 game_status = GAME_MODE_PSEUDO_DOOR;
3618 /* write text for request */
3619 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3621 char text_line[max_request_line_len + 1];
3627 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3629 tc = *(text_ptr + tx);
3630 // if (!tc || tc == ' ')
3631 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3635 if ((tc == '?' || tc == '!') && tl == 0)
3645 strncpy(text_line, text_ptr, tl);
3648 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3649 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3650 text_line, font_nr);
3652 text_ptr += tl + (tc == ' ' ? 1 : 0);
3653 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3656 game_status = last_game_status; /* restore current game status */
3658 if (req_state & REQ_ASK)
3660 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3661 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3663 else if (req_state & REQ_CONFIRM)
3665 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3667 else if (req_state & REQ_PLAYER)
3669 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3670 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3671 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3672 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3675 /* copy request gadgets to door backbuffer */
3676 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3678 OpenDoor(DOOR_OPEN_1);
3680 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3682 if (game_status == GAME_MODE_PLAYING)
3684 SetPanelBackground();
3685 SetDrawBackgroundMask(REDRAW_DOOR_1);
3689 SetDrawBackgroundMask(REDRAW_FIELD);
3695 if (game_status != GAME_MODE_MAIN)
3698 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3700 // ---------- handle request buttons ----------
3701 result = RequestHandleEvents(req_state);
3703 if (game_status != GAME_MODE_MAIN)
3708 if (!(req_state & REQ_STAY_OPEN))
3710 CloseDoor(DOOR_CLOSE_1);
3712 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3713 (req_state & REQ_REOPEN))
3714 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3719 if (game_status == GAME_MODE_PLAYING)
3721 SetPanelBackground();
3722 SetDrawBackgroundMask(REDRAW_DOOR_1);
3726 SetDrawBackgroundMask(REDRAW_FIELD);
3729 #if defined(NETWORK_AVALIABLE)
3730 /* continue network game after request */
3731 if (options.network &&
3732 game_status == GAME_MODE_PLAYING &&
3733 req_state & REQUEST_WAIT_FOR_INPUT)
3734 SendToServer_ContinuePlaying();
3737 /* restore deactivated drawing when quick-loading level tape recording */
3738 if (tape.playing && tape.deactivate_display)
3739 TapeDeactivateDisplayOn();
3744 static boolean RequestEnvelope(char *text, unsigned int req_state)
3748 if (game_status == GAME_MODE_PLAYING)
3749 BlitScreenToBitmap(backbuffer);
3751 /* disable deactivated drawing when quick-loading level tape recording */
3752 if (tape.playing && tape.deactivate_display)
3753 TapeDeactivateDisplayOff(TRUE);
3755 SetMouseCursor(CURSOR_DEFAULT);
3757 #if defined(NETWORK_AVALIABLE)
3758 /* pause network game while waiting for request to answer */
3759 if (options.network &&
3760 game_status == GAME_MODE_PLAYING &&
3761 req_state & REQUEST_WAIT_FOR_INPUT)
3762 SendToServer_PausePlaying();
3765 /* simulate releasing mouse button over last gadget, if still pressed */
3767 HandleGadgets(-1, -1, 0);
3771 // (replace with setting corresponding request background)
3772 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3773 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3775 /* clear door drawing field */
3776 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3778 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3780 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3782 if (game_status == GAME_MODE_PLAYING)
3784 SetPanelBackground();
3785 SetDrawBackgroundMask(REDRAW_DOOR_1);
3789 SetDrawBackgroundMask(REDRAW_FIELD);
3795 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3797 // ---------- handle request buttons ----------
3798 result = RequestHandleEvents(req_state);
3800 if (game_status != GAME_MODE_MAIN)
3805 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3809 if (game_status == GAME_MODE_PLAYING)
3811 SetPanelBackground();
3812 SetDrawBackgroundMask(REDRAW_DOOR_1);
3816 SetDrawBackgroundMask(REDRAW_FIELD);
3819 #if defined(NETWORK_AVALIABLE)
3820 /* continue network game after request */
3821 if (options.network &&
3822 game_status == GAME_MODE_PLAYING &&
3823 req_state & REQUEST_WAIT_FOR_INPUT)
3824 SendToServer_ContinuePlaying();
3827 /* restore deactivated drawing when quick-loading level tape recording */
3828 if (tape.playing && tape.deactivate_display)
3829 TapeDeactivateDisplayOn();
3834 boolean Request(char *text, unsigned int req_state)
3836 if (global.use_envelope_request)
3837 return RequestEnvelope(text, req_state);
3839 return RequestDoor(text, req_state);
3842 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3844 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3845 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3848 if (dpo1->sort_priority != dpo2->sort_priority)
3849 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3851 compare_result = dpo1->nr - dpo2->nr;
3853 return compare_result;
3856 void InitGraphicCompatibilityInfo_Doors()
3862 struct DoorInfo *door;
3866 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3867 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3869 { -1, -1, -1, NULL }
3871 struct Rect door_rect_list[] =
3873 { DX, DY, DXSIZE, DYSIZE },
3874 { VX, VY, VXSIZE, VYSIZE }
3878 for (i = 0; doors[i].door_token != -1; i++)
3880 int door_token = doors[i].door_token;
3881 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3882 int part_1 = doors[i].part_1;
3883 int part_8 = doors[i].part_8;
3884 int part_2 = part_1 + 1;
3885 int part_3 = part_1 + 2;
3886 struct DoorInfo *door = doors[i].door;
3887 struct Rect *door_rect = &door_rect_list[door_index];
3888 boolean door_gfx_redefined = FALSE;
3890 /* check if any door part graphic definitions have been redefined */
3892 for (j = 0; door_part_controls[j].door_token != -1; j++)
3894 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3895 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3897 if (dpc->door_token == door_token && fi->redefined)
3898 door_gfx_redefined = TRUE;
3901 /* check for old-style door graphic/animation modifications */
3903 if (!door_gfx_redefined)
3905 if (door->anim_mode & ANIM_STATIC_PANEL)
3907 door->panel.step_xoffset = 0;
3908 door->panel.step_yoffset = 0;
3911 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3913 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3914 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3915 int num_door_steps, num_panel_steps;
3917 /* remove door part graphics other than the two default wings */
3919 for (j = 0; door_part_controls[j].door_token != -1; j++)
3921 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3922 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3924 if (dpc->graphic >= part_3 &&
3925 dpc->graphic <= part_8)
3929 /* set graphics and screen positions of the default wings */
3931 g_part_1->width = door_rect->width;
3932 g_part_1->height = door_rect->height;
3933 g_part_2->width = door_rect->width;
3934 g_part_2->height = door_rect->height;
3935 g_part_2->src_x = door_rect->width;
3936 g_part_2->src_y = g_part_1->src_y;
3938 door->part_2.x = door->part_1.x;
3939 door->part_2.y = door->part_1.y;
3941 if (door->width != -1)
3943 g_part_1->width = door->width;
3944 g_part_2->width = door->width;
3946 // special treatment for graphics and screen position of right wing
3947 g_part_2->src_x += door_rect->width - door->width;
3948 door->part_2.x += door_rect->width - door->width;
3951 if (door->height != -1)
3953 g_part_1->height = door->height;
3954 g_part_2->height = door->height;
3956 // special treatment for graphics and screen position of bottom wing
3957 g_part_2->src_y += door_rect->height - door->height;
3958 door->part_2.y += door_rect->height - door->height;
3961 /* set animation delays for the default wings and panels */
3963 door->part_1.step_delay = door->step_delay;
3964 door->part_2.step_delay = door->step_delay;
3965 door->panel.step_delay = door->step_delay;
3967 /* set animation draw order for the default wings */
3969 door->part_1.sort_priority = 2; /* draw left wing over ... */
3970 door->part_2.sort_priority = 1; /* ... right wing */
3972 /* set animation draw offset for the default wings */
3974 if (door->anim_mode & ANIM_HORIZONTAL)
3976 door->part_1.step_xoffset = door->step_offset;
3977 door->part_1.step_yoffset = 0;
3978 door->part_2.step_xoffset = door->step_offset * -1;
3979 door->part_2.step_yoffset = 0;
3981 num_door_steps = g_part_1->width / door->step_offset;
3983 else // ANIM_VERTICAL
3985 door->part_1.step_xoffset = 0;
3986 door->part_1.step_yoffset = door->step_offset;
3987 door->part_2.step_xoffset = 0;
3988 door->part_2.step_yoffset = door->step_offset * -1;
3990 num_door_steps = g_part_1->height / door->step_offset;
3993 /* set animation draw offset for the default panels */
3995 if (door->step_offset > 1)
3997 num_panel_steps = 2 * door_rect->height / door->step_offset;
3998 door->panel.start_step = num_panel_steps - num_door_steps;
3999 door->panel.start_step_closing = door->panel.start_step;
4003 num_panel_steps = door_rect->height / door->step_offset;
4004 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4005 door->panel.start_step_closing = door->panel.start_step;
4006 door->panel.step_delay *= 2;
4017 for (i = 0; door_part_controls[i].door_token != -1; i++)
4019 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4020 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4022 /* initialize "start_step_opening" and "start_step_closing", if needed */
4023 if (dpc->pos->start_step_opening == 0 &&
4024 dpc->pos->start_step_closing == 0)
4026 // dpc->pos->start_step_opening = dpc->pos->start_step;
4027 dpc->pos->start_step_closing = dpc->pos->start_step;
4030 /* fill structure for door part draw order (sorted below) */
4032 dpo->sort_priority = dpc->pos->sort_priority;
4035 /* sort door part controls according to sort_priority and graphic number */
4036 qsort(door_part_order, MAX_DOOR_PARTS,
4037 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4040 unsigned int OpenDoor(unsigned int door_state)
4042 if (door_state & DOOR_COPY_BACK)
4044 if (door_state & DOOR_OPEN_1)
4045 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4046 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4048 if (door_state & DOOR_OPEN_2)
4049 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4050 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4052 door_state &= ~DOOR_COPY_BACK;
4055 return MoveDoor(door_state);
4058 unsigned int CloseDoor(unsigned int door_state)
4060 unsigned int old_door_state = GetDoorState();
4062 if (!(door_state & DOOR_NO_COPY_BACK))
4064 if (old_door_state & DOOR_OPEN_1)
4065 BlitBitmap(backbuffer, bitmap_db_door_1,
4066 DX, DY, DXSIZE, DYSIZE, 0, 0);
4068 if (old_door_state & DOOR_OPEN_2)
4069 BlitBitmap(backbuffer, bitmap_db_door_2,
4070 VX, VY, VXSIZE, VYSIZE, 0, 0);
4072 door_state &= ~DOOR_NO_COPY_BACK;
4075 return MoveDoor(door_state);
4078 unsigned int GetDoorState()
4080 return MoveDoor(DOOR_GET_STATE);
4083 unsigned int SetDoorState(unsigned int door_state)
4085 return MoveDoor(door_state | DOOR_SET_STATE);
4088 int euclid(int a, int b)
4090 return (b ? euclid(b, a % b) : a);
4093 unsigned int MoveDoor(unsigned int door_state)
4095 struct Rect door_rect_list[] =
4097 { DX, DY, DXSIZE, DYSIZE },
4098 { VX, VY, VXSIZE, VYSIZE }
4100 static int door1 = DOOR_OPEN_1;
4101 static int door2 = DOOR_CLOSE_2;
4102 unsigned int door_delay = 0;
4103 unsigned int door_delay_value;
4106 if (door_state == DOOR_GET_STATE)
4107 return (door1 | door2);
4109 if (door_state & DOOR_SET_STATE)
4111 if (door_state & DOOR_ACTION_1)
4112 door1 = door_state & DOOR_ACTION_1;
4113 if (door_state & DOOR_ACTION_2)
4114 door2 = door_state & DOOR_ACTION_2;
4116 return (door1 | door2);
4119 if (!(door_state & DOOR_FORCE_REDRAW))
4121 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4122 door_state &= ~DOOR_OPEN_1;
4123 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4124 door_state &= ~DOOR_CLOSE_1;
4125 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4126 door_state &= ~DOOR_OPEN_2;
4127 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4128 door_state &= ~DOOR_CLOSE_2;
4131 if (global.autoplay_leveldir)
4133 door_state |= DOOR_NO_DELAY;
4134 door_state &= ~DOOR_CLOSE_ALL;
4137 if (game_status == GAME_MODE_EDITOR)
4138 door_state |= DOOR_NO_DELAY;
4140 if (door_state & DOOR_ACTION)
4142 boolean door_panel_drawn[NUM_DOORS];
4143 boolean panel_has_doors[NUM_DOORS];
4144 boolean door_part_skip[MAX_DOOR_PARTS];
4145 boolean door_part_done[MAX_DOOR_PARTS];
4146 boolean door_part_done_all;
4147 int num_steps[MAX_DOOR_PARTS];
4148 int max_move_delay = 0; // delay for complete animations of all doors
4149 int max_step_delay = 0; // delay (ms) between two animation frames
4150 int num_move_steps = 0; // number of animation steps for all doors
4151 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4152 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4153 int current_move_delay = 0;
4157 for (i = 0; i < NUM_DOORS; i++)
4158 panel_has_doors[i] = FALSE;
4160 for (i = 0; i < MAX_DOOR_PARTS; i++)
4162 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4163 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4164 int door_token = dpc->door_token;
4166 door_part_done[i] = FALSE;
4167 door_part_skip[i] = (!(door_state & door_token) ||
4171 for (i = 0; i < MAX_DOOR_PARTS; i++)
4173 int nr = door_part_order[i].nr;
4174 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4175 struct DoorPartPosInfo *pos = dpc->pos;
4176 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4177 int door_token = dpc->door_token;
4178 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4179 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4180 int step_xoffset = ABS(pos->step_xoffset);
4181 int step_yoffset = ABS(pos->step_yoffset);
4182 int step_delay = pos->step_delay;
4183 int current_door_state = door_state & door_token;
4184 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4185 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4186 boolean part_opening = (is_panel ? door_closing : door_opening);
4187 int start_step = (part_opening ? pos->start_step_opening :
4188 pos->start_step_closing);
4189 float move_xsize = (step_xoffset ? g->width : 0);
4190 float move_ysize = (step_yoffset ? g->height : 0);
4191 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4192 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4193 int move_steps = (move_xsteps && move_ysteps ?
4194 MIN(move_xsteps, move_ysteps) :
4195 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4196 int move_delay = move_steps * step_delay;
4198 if (door_part_skip[nr])
4201 max_move_delay = MAX(max_move_delay, move_delay);
4202 max_step_delay = (max_step_delay == 0 ? step_delay :
4203 euclid(max_step_delay, step_delay));
4204 num_steps[nr] = move_steps;
4208 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4210 panel_has_doors[door_index] = TRUE;
4214 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4216 num_move_steps = max_move_delay / max_step_delay;
4217 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4219 door_delay_value = max_step_delay;
4221 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4223 start = num_move_steps - 1;
4227 /* opening door sound has priority over simultaneously closing door */
4228 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4229 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4230 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4231 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4234 for (k = start; k < num_move_steps; k++)
4236 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4238 door_part_done_all = TRUE;
4240 for (i = 0; i < NUM_DOORS; i++)
4241 door_panel_drawn[i] = FALSE;
4243 for (i = 0; i < MAX_DOOR_PARTS; i++)
4245 int nr = door_part_order[i].nr;
4246 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4247 struct DoorPartPosInfo *pos = dpc->pos;
4248 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4249 int door_token = dpc->door_token;
4250 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4251 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4252 boolean is_panel_and_door_has_closed = FALSE;
4253 struct Rect *door_rect = &door_rect_list[door_index];
4254 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4256 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4257 int current_door_state = door_state & door_token;
4258 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4259 boolean door_closing = !door_opening;
4260 boolean part_opening = (is_panel ? door_closing : door_opening);
4261 boolean part_closing = !part_opening;
4262 int start_step = (part_opening ? pos->start_step_opening :
4263 pos->start_step_closing);
4264 int step_delay = pos->step_delay;
4265 int step_factor = step_delay / max_step_delay;
4266 int k1 = (step_factor ? k / step_factor + 1 : k);
4267 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4268 int kk = MAX(0, k2);
4271 int src_x, src_y, src_xx, src_yy;
4272 int dst_x, dst_y, dst_xx, dst_yy;
4275 if (door_part_skip[nr])
4278 if (!(door_state & door_token))
4286 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4287 int kk_door = MAX(0, k2_door);
4288 int sync_frame = kk_door * door_delay_value;
4289 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4291 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4296 if (!door_panel_drawn[door_index])
4298 ClearRectangle(drawto, door_rect->x, door_rect->y,
4299 door_rect->width, door_rect->height);
4301 door_panel_drawn[door_index] = TRUE;
4304 // draw opening or closing door parts
4306 if (pos->step_xoffset < 0) // door part on right side
4309 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4312 if (dst_xx + width > door_rect->width)
4313 width = door_rect->width - dst_xx;
4315 else // door part on left side
4318 dst_xx = pos->x - kk * pos->step_xoffset;
4322 src_xx = ABS(dst_xx);
4326 width = g->width - src_xx;
4328 if (width > door_rect->width)
4329 width = door_rect->width;
4331 // printf("::: k == %d [%d] \n", k, start_step);
4334 if (pos->step_yoffset < 0) // door part on bottom side
4337 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4340 if (dst_yy + height > door_rect->height)
4341 height = door_rect->height - dst_yy;
4343 else // door part on top side
4346 dst_yy = pos->y - kk * pos->step_yoffset;
4350 src_yy = ABS(dst_yy);
4354 height = g->height - src_yy;
4357 src_x = g_src_x + src_xx;
4358 src_y = g_src_y + src_yy;
4360 dst_x = door_rect->x + dst_xx;
4361 dst_y = door_rect->y + dst_yy;
4363 is_panel_and_door_has_closed =
4366 panel_has_doors[door_index] &&
4367 k >= num_move_steps_doors_only - 1);
4369 if (width >= 0 && width <= g->width &&
4370 height >= 0 && height <= g->height &&
4371 !is_panel_and_door_has_closed)
4373 if (is_panel || !pos->draw_masked)
4374 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4377 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4381 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4383 if ((part_opening && (width < 0 || height < 0)) ||
4384 (part_closing && (width >= g->width && height >= g->height)))
4385 door_part_done[nr] = TRUE;
4387 // continue door part animations, but not panel after door has closed
4388 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4389 door_part_done_all = FALSE;
4392 if (!(door_state & DOOR_NO_DELAY))
4396 if (game_status == GAME_MODE_MAIN)
4399 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4401 current_move_delay += max_step_delay;
4404 if (door_part_done_all)
4409 if (door_state & DOOR_ACTION_1)
4410 door1 = door_state & DOOR_ACTION_1;
4411 if (door_state & DOOR_ACTION_2)
4412 door2 = door_state & DOOR_ACTION_2;
4414 return (door1 | door2);
4417 void DrawSpecialEditorDoor()
4419 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4420 int top_border_width = gfx1->width;
4421 int top_border_height = gfx1->height;
4422 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4423 int ex = EX - outer_border;
4424 int ey = EY - outer_border;
4425 int vy = VY - outer_border;
4426 int exsize = EXSIZE + 2 * outer_border;
4428 /* draw bigger level editor toolbox window */
4429 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4430 top_border_width, top_border_height, ex, ey - top_border_height);
4431 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4432 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4434 redraw_mask |= REDRAW_ALL;
4437 void UndrawSpecialEditorDoor()
4439 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4440 int top_border_width = gfx1->width;
4441 int top_border_height = gfx1->height;
4442 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4443 int ex = EX - outer_border;
4444 int ey = EY - outer_border;
4445 int ey_top = ey - top_border_height;
4446 int exsize = EXSIZE + 2 * outer_border;
4447 int eysize = EYSIZE + 2 * outer_border;
4449 /* draw normal tape recorder window */
4450 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4452 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4453 ex, ey_top, top_border_width, top_border_height,
4455 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4456 ex, ey, exsize, eysize, ex, ey);
4460 // if screen background is set to "[NONE]", clear editor toolbox window
4461 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4462 ClearRectangle(drawto, ex, ey, exsize, eysize);
4465 redraw_mask |= REDRAW_ALL;
4469 /* ---------- new tool button stuff ---------------------------------------- */
4474 struct TextPosInfo *pos;
4477 } toolbutton_info[NUM_TOOL_BUTTONS] =
4480 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4481 TOOL_CTRL_ID_YES, "yes"
4484 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4485 TOOL_CTRL_ID_NO, "no"
4488 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4489 TOOL_CTRL_ID_CONFIRM, "confirm"
4492 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4493 TOOL_CTRL_ID_PLAYER_1, "player 1"
4496 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4497 TOOL_CTRL_ID_PLAYER_2, "player 2"
4500 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4501 TOOL_CTRL_ID_PLAYER_3, "player 3"
4504 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4505 TOOL_CTRL_ID_PLAYER_4, "player 4"
4509 void CreateToolButtons()
4513 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4515 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4516 struct TextPosInfo *pos = toolbutton_info[i].pos;
4517 struct GadgetInfo *gi;
4518 Bitmap *deco_bitmap = None;
4519 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4520 unsigned int event_mask = GD_EVENT_RELEASED;
4523 int gd_x = gfx->src_x;
4524 int gd_y = gfx->src_y;
4525 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4526 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4529 if (global.use_envelope_request)
4530 setRequestPosition(&dx, &dy, TRUE);
4532 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4534 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4536 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4537 pos->size, &deco_bitmap, &deco_x, &deco_y);
4538 deco_xpos = (gfx->width - pos->size) / 2;
4539 deco_ypos = (gfx->height - pos->size) / 2;
4542 gi = CreateGadget(GDI_CUSTOM_ID, id,
4543 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4544 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4545 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4546 GDI_WIDTH, gfx->width,
4547 GDI_HEIGHT, gfx->height,
4548 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4549 GDI_STATE, GD_BUTTON_UNPRESSED,
4550 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4551 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4552 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4553 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4554 GDI_DECORATION_SIZE, pos->size, pos->size,
4555 GDI_DECORATION_SHIFTING, 1, 1,
4556 GDI_DIRECT_DRAW, FALSE,
4557 GDI_EVENT_MASK, event_mask,
4558 GDI_CALLBACK_ACTION, HandleToolButtons,
4562 Error(ERR_EXIT, "cannot create gadget");
4564 tool_gadget[id] = gi;
4568 void FreeToolButtons()
4572 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4573 FreeGadget(tool_gadget[i]);
4576 static void UnmapToolButtons()
4580 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4581 UnmapGadget(tool_gadget[i]);
4584 static void HandleToolButtons(struct GadgetInfo *gi)
4586 request_gadget_id = gi->custom_id;
4589 static struct Mapping_EM_to_RND_object
4592 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4593 boolean is_backside; /* backside of moving element */
4599 em_object_mapping_list[] =
4602 Xblank, TRUE, FALSE,
4606 Yacid_splash_eB, FALSE, FALSE,
4607 EL_ACID_SPLASH_RIGHT, -1, -1
4610 Yacid_splash_wB, FALSE, FALSE,
4611 EL_ACID_SPLASH_LEFT, -1, -1
4614 #ifdef EM_ENGINE_BAD_ROLL
4616 Xstone_force_e, FALSE, FALSE,
4617 EL_ROCK, -1, MV_BIT_RIGHT
4620 Xstone_force_w, FALSE, FALSE,
4621 EL_ROCK, -1, MV_BIT_LEFT
4624 Xnut_force_e, FALSE, FALSE,
4625 EL_NUT, -1, MV_BIT_RIGHT
4628 Xnut_force_w, FALSE, FALSE,
4629 EL_NUT, -1, MV_BIT_LEFT
4632 Xspring_force_e, FALSE, FALSE,
4633 EL_SPRING, -1, MV_BIT_RIGHT
4636 Xspring_force_w, FALSE, FALSE,
4637 EL_SPRING, -1, MV_BIT_LEFT
4640 Xemerald_force_e, FALSE, FALSE,
4641 EL_EMERALD, -1, MV_BIT_RIGHT
4644 Xemerald_force_w, FALSE, FALSE,
4645 EL_EMERALD, -1, MV_BIT_LEFT
4648 Xdiamond_force_e, FALSE, FALSE,
4649 EL_DIAMOND, -1, MV_BIT_RIGHT
4652 Xdiamond_force_w, FALSE, FALSE,
4653 EL_DIAMOND, -1, MV_BIT_LEFT
4656 Xbomb_force_e, FALSE, FALSE,
4657 EL_BOMB, -1, MV_BIT_RIGHT
4660 Xbomb_force_w, FALSE, FALSE,
4661 EL_BOMB, -1, MV_BIT_LEFT
4663 #endif /* EM_ENGINE_BAD_ROLL */
4666 Xstone, TRUE, FALSE,
4670 Xstone_pause, FALSE, FALSE,
4674 Xstone_fall, FALSE, FALSE,
4678 Ystone_s, FALSE, FALSE,
4679 EL_ROCK, ACTION_FALLING, -1
4682 Ystone_sB, FALSE, TRUE,
4683 EL_ROCK, ACTION_FALLING, -1
4686 Ystone_e, FALSE, FALSE,
4687 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4690 Ystone_eB, FALSE, TRUE,
4691 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4694 Ystone_w, FALSE, FALSE,
4695 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4698 Ystone_wB, FALSE, TRUE,
4699 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4706 Xnut_pause, FALSE, FALSE,
4710 Xnut_fall, FALSE, FALSE,
4714 Ynut_s, FALSE, FALSE,
4715 EL_NUT, ACTION_FALLING, -1
4718 Ynut_sB, FALSE, TRUE,
4719 EL_NUT, ACTION_FALLING, -1
4722 Ynut_e, FALSE, FALSE,
4723 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4726 Ynut_eB, FALSE, TRUE,
4727 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4730 Ynut_w, FALSE, FALSE,
4731 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4734 Ynut_wB, FALSE, TRUE,
4735 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4738 Xbug_n, TRUE, FALSE,
4742 Xbug_e, TRUE, FALSE,
4743 EL_BUG_RIGHT, -1, -1
4746 Xbug_s, TRUE, FALSE,
4750 Xbug_w, TRUE, FALSE,
4754 Xbug_gon, FALSE, FALSE,
4758 Xbug_goe, FALSE, FALSE,
4759 EL_BUG_RIGHT, -1, -1
4762 Xbug_gos, FALSE, FALSE,
4766 Xbug_gow, FALSE, FALSE,
4770 Ybug_n, FALSE, FALSE,
4771 EL_BUG, ACTION_MOVING, MV_BIT_UP
4774 Ybug_nB, FALSE, TRUE,
4775 EL_BUG, ACTION_MOVING, MV_BIT_UP
4778 Ybug_e, FALSE, FALSE,
4779 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4782 Ybug_eB, FALSE, TRUE,
4783 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4786 Ybug_s, FALSE, FALSE,
4787 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4790 Ybug_sB, FALSE, TRUE,
4791 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4794 Ybug_w, FALSE, FALSE,
4795 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4798 Ybug_wB, FALSE, TRUE,
4799 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4802 Ybug_w_n, FALSE, FALSE,
4803 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4806 Ybug_n_e, FALSE, FALSE,
4807 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4810 Ybug_e_s, FALSE, FALSE,
4811 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4814 Ybug_s_w, FALSE, FALSE,
4815 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4818 Ybug_e_n, FALSE, FALSE,
4819 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4822 Ybug_s_e, FALSE, FALSE,
4823 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4826 Ybug_w_s, FALSE, FALSE,
4827 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4830 Ybug_n_w, FALSE, FALSE,
4831 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4834 Ybug_stone, FALSE, FALSE,
4835 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4838 Ybug_spring, FALSE, FALSE,
4839 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4842 Xtank_n, TRUE, FALSE,
4843 EL_SPACESHIP_UP, -1, -1
4846 Xtank_e, TRUE, FALSE,
4847 EL_SPACESHIP_RIGHT, -1, -1
4850 Xtank_s, TRUE, FALSE,
4851 EL_SPACESHIP_DOWN, -1, -1
4854 Xtank_w, TRUE, FALSE,
4855 EL_SPACESHIP_LEFT, -1, -1
4858 Xtank_gon, FALSE, FALSE,
4859 EL_SPACESHIP_UP, -1, -1
4862 Xtank_goe, FALSE, FALSE,
4863 EL_SPACESHIP_RIGHT, -1, -1
4866 Xtank_gos, FALSE, FALSE,
4867 EL_SPACESHIP_DOWN, -1, -1
4870 Xtank_gow, FALSE, FALSE,
4871 EL_SPACESHIP_LEFT, -1, -1
4874 Ytank_n, FALSE, FALSE,
4875 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4878 Ytank_nB, FALSE, TRUE,
4879 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4882 Ytank_e, FALSE, FALSE,
4883 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4886 Ytank_eB, FALSE, TRUE,
4887 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4890 Ytank_s, FALSE, FALSE,
4891 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4894 Ytank_sB, FALSE, TRUE,
4895 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4898 Ytank_w, FALSE, FALSE,
4899 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4902 Ytank_wB, FALSE, TRUE,
4903 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4906 Ytank_w_n, FALSE, FALSE,
4907 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4910 Ytank_n_e, FALSE, FALSE,
4911 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4914 Ytank_e_s, FALSE, FALSE,
4915 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4918 Ytank_s_w, FALSE, FALSE,
4919 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4922 Ytank_e_n, FALSE, FALSE,
4923 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4926 Ytank_s_e, FALSE, FALSE,
4927 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4930 Ytank_w_s, FALSE, FALSE,
4931 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4934 Ytank_n_w, FALSE, FALSE,
4935 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4938 Ytank_stone, FALSE, FALSE,
4939 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4942 Ytank_spring, FALSE, FALSE,
4943 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4946 Xandroid, TRUE, FALSE,
4947 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4950 Xandroid_1_n, FALSE, FALSE,
4951 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4954 Xandroid_2_n, FALSE, FALSE,
4955 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4958 Xandroid_1_e, FALSE, FALSE,
4959 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4962 Xandroid_2_e, FALSE, FALSE,
4963 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4966 Xandroid_1_w, FALSE, FALSE,
4967 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4970 Xandroid_2_w, FALSE, FALSE,
4971 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4974 Xandroid_1_s, FALSE, FALSE,
4975 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4978 Xandroid_2_s, FALSE, FALSE,
4979 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4982 Yandroid_n, FALSE, FALSE,
4983 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4986 Yandroid_nB, FALSE, TRUE,
4987 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4990 Yandroid_ne, FALSE, FALSE,
4991 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4994 Yandroid_neB, FALSE, TRUE,
4995 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4998 Yandroid_e, FALSE, FALSE,
4999 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5002 Yandroid_eB, FALSE, TRUE,
5003 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5006 Yandroid_se, FALSE, FALSE,
5007 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5010 Yandroid_seB, FALSE, TRUE,
5011 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5014 Yandroid_s, FALSE, FALSE,
5015 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5018 Yandroid_sB, FALSE, TRUE,
5019 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5022 Yandroid_sw, FALSE, FALSE,
5023 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5026 Yandroid_swB, FALSE, TRUE,
5027 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5030 Yandroid_w, FALSE, FALSE,
5031 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5034 Yandroid_wB, FALSE, TRUE,
5035 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5038 Yandroid_nw, FALSE, FALSE,
5039 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5042 Yandroid_nwB, FALSE, TRUE,
5043 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5046 Xspring, TRUE, FALSE,
5050 Xspring_pause, FALSE, FALSE,
5054 Xspring_e, FALSE, FALSE,
5058 Xspring_w, FALSE, FALSE,
5062 Xspring_fall, FALSE, FALSE,
5066 Yspring_s, FALSE, FALSE,
5067 EL_SPRING, ACTION_FALLING, -1
5070 Yspring_sB, FALSE, TRUE,
5071 EL_SPRING, ACTION_FALLING, -1
5074 Yspring_e, FALSE, FALSE,
5075 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5078 Yspring_eB, FALSE, TRUE,
5079 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5082 Yspring_w, FALSE, FALSE,
5083 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5086 Yspring_wB, FALSE, TRUE,
5087 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5090 Yspring_kill_e, FALSE, FALSE,
5091 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5094 Yspring_kill_eB, FALSE, TRUE,
5095 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5098 Yspring_kill_w, FALSE, FALSE,
5099 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5102 Yspring_kill_wB, FALSE, TRUE,
5103 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5106 Xeater_n, TRUE, FALSE,
5107 EL_YAMYAM_UP, -1, -1
5110 Xeater_e, TRUE, FALSE,
5111 EL_YAMYAM_RIGHT, -1, -1
5114 Xeater_w, TRUE, FALSE,
5115 EL_YAMYAM_LEFT, -1, -1
5118 Xeater_s, TRUE, FALSE,
5119 EL_YAMYAM_DOWN, -1, -1
5122 Yeater_n, FALSE, FALSE,
5123 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5126 Yeater_nB, FALSE, TRUE,
5127 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5130 Yeater_e, FALSE, FALSE,
5131 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5134 Yeater_eB, FALSE, TRUE,
5135 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5138 Yeater_s, FALSE, FALSE,
5139 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5142 Yeater_sB, FALSE, TRUE,
5143 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5146 Yeater_w, FALSE, FALSE,
5147 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5150 Yeater_wB, FALSE, TRUE,
5151 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5154 Yeater_stone, FALSE, FALSE,
5155 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5158 Yeater_spring, FALSE, FALSE,
5159 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5162 Xalien, TRUE, FALSE,
5166 Xalien_pause, FALSE, FALSE,
5170 Yalien_n, FALSE, FALSE,
5171 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5174 Yalien_nB, FALSE, TRUE,
5175 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5178 Yalien_e, FALSE, FALSE,
5179 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5182 Yalien_eB, FALSE, TRUE,
5183 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5186 Yalien_s, FALSE, FALSE,
5187 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5190 Yalien_sB, FALSE, TRUE,
5191 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5194 Yalien_w, FALSE, FALSE,
5195 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5198 Yalien_wB, FALSE, TRUE,
5199 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5202 Yalien_stone, FALSE, FALSE,
5203 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5206 Yalien_spring, FALSE, FALSE,
5207 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5210 Xemerald, TRUE, FALSE,
5214 Xemerald_pause, FALSE, FALSE,
5218 Xemerald_fall, FALSE, FALSE,
5222 Xemerald_shine, FALSE, FALSE,
5223 EL_EMERALD, ACTION_TWINKLING, -1
5226 Yemerald_s, FALSE, FALSE,
5227 EL_EMERALD, ACTION_FALLING, -1
5230 Yemerald_sB, FALSE, TRUE,
5231 EL_EMERALD, ACTION_FALLING, -1
5234 Yemerald_e, FALSE, FALSE,
5235 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5238 Yemerald_eB, FALSE, TRUE,
5239 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5242 Yemerald_w, FALSE, FALSE,
5243 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5246 Yemerald_wB, FALSE, TRUE,
5247 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5250 Yemerald_eat, FALSE, FALSE,
5251 EL_EMERALD, ACTION_COLLECTING, -1
5254 Yemerald_stone, FALSE, FALSE,
5255 EL_NUT, ACTION_BREAKING, -1
5258 Xdiamond, TRUE, FALSE,
5262 Xdiamond_pause, FALSE, FALSE,
5266 Xdiamond_fall, FALSE, FALSE,
5270 Xdiamond_shine, FALSE, FALSE,
5271 EL_DIAMOND, ACTION_TWINKLING, -1
5274 Ydiamond_s, FALSE, FALSE,
5275 EL_DIAMOND, ACTION_FALLING, -1
5278 Ydiamond_sB, FALSE, TRUE,
5279 EL_DIAMOND, ACTION_FALLING, -1
5282 Ydiamond_e, FALSE, FALSE,
5283 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5286 Ydiamond_eB, FALSE, TRUE,
5287 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5290 Ydiamond_w, FALSE, FALSE,
5291 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5294 Ydiamond_wB, FALSE, TRUE,
5295 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5298 Ydiamond_eat, FALSE, FALSE,
5299 EL_DIAMOND, ACTION_COLLECTING, -1
5302 Ydiamond_stone, FALSE, FALSE,
5303 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5306 Xdrip_fall, TRUE, FALSE,
5307 EL_AMOEBA_DROP, -1, -1
5310 Xdrip_stretch, FALSE, FALSE,
5311 EL_AMOEBA_DROP, ACTION_FALLING, -1
5314 Xdrip_stretchB, FALSE, TRUE,
5315 EL_AMOEBA_DROP, ACTION_FALLING, -1
5318 Xdrip_eat, FALSE, FALSE,
5319 EL_AMOEBA_DROP, ACTION_GROWING, -1
5322 Ydrip_s1, FALSE, FALSE,
5323 EL_AMOEBA_DROP, ACTION_FALLING, -1
5326 Ydrip_s1B, FALSE, TRUE,
5327 EL_AMOEBA_DROP, ACTION_FALLING, -1
5330 Ydrip_s2, FALSE, FALSE,
5331 EL_AMOEBA_DROP, ACTION_FALLING, -1
5334 Ydrip_s2B, FALSE, TRUE,
5335 EL_AMOEBA_DROP, ACTION_FALLING, -1
5342 Xbomb_pause, FALSE, FALSE,
5346 Xbomb_fall, FALSE, FALSE,
5350 Ybomb_s, FALSE, FALSE,
5351 EL_BOMB, ACTION_FALLING, -1
5354 Ybomb_sB, FALSE, TRUE,
5355 EL_BOMB, ACTION_FALLING, -1
5358 Ybomb_e, FALSE, FALSE,
5359 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5362 Ybomb_eB, FALSE, TRUE,
5363 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5366 Ybomb_w, FALSE, FALSE,
5367 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5370 Ybomb_wB, FALSE, TRUE,
5371 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5374 Ybomb_eat, FALSE, FALSE,
5375 EL_BOMB, ACTION_ACTIVATING, -1
5378 Xballoon, TRUE, FALSE,
5382 Yballoon_n, FALSE, FALSE,
5383 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5386 Yballoon_nB, FALSE, TRUE,
5387 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5390 Yballoon_e, FALSE, FALSE,
5391 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5394 Yballoon_eB, FALSE, TRUE,
5395 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5398 Yballoon_s, FALSE, FALSE,
5399 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5402 Yballoon_sB, FALSE, TRUE,
5403 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5406 Yballoon_w, FALSE, FALSE,
5407 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5410 Yballoon_wB, FALSE, TRUE,
5411 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5414 Xgrass, TRUE, FALSE,
5415 EL_EMC_GRASS, -1, -1
5418 Ygrass_nB, FALSE, FALSE,
5419 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5422 Ygrass_eB, FALSE, FALSE,
5423 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5426 Ygrass_sB, FALSE, FALSE,
5427 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5430 Ygrass_wB, FALSE, FALSE,
5431 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5438 Ydirt_nB, FALSE, FALSE,
5439 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5442 Ydirt_eB, FALSE, FALSE,
5443 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5446 Ydirt_sB, FALSE, FALSE,
5447 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5450 Ydirt_wB, FALSE, FALSE,
5451 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5454 Xacid_ne, TRUE, FALSE,
5455 EL_ACID_POOL_TOPRIGHT, -1, -1
5458 Xacid_se, TRUE, FALSE,
5459 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5462 Xacid_s, TRUE, FALSE,
5463 EL_ACID_POOL_BOTTOM, -1, -1
5466 Xacid_sw, TRUE, FALSE,
5467 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5470 Xacid_nw, TRUE, FALSE,
5471 EL_ACID_POOL_TOPLEFT, -1, -1
5474 Xacid_1, TRUE, FALSE,
5478 Xacid_2, FALSE, FALSE,
5482 Xacid_3, FALSE, FALSE,
5486 Xacid_4, FALSE, FALSE,
5490 Xacid_5, FALSE, FALSE,
5494 Xacid_6, FALSE, FALSE,
5498 Xacid_7, FALSE, FALSE,
5502 Xacid_8, FALSE, FALSE,
5506 Xball_1, TRUE, FALSE,
5507 EL_EMC_MAGIC_BALL, -1, -1
5510 Xball_1B, FALSE, FALSE,
5511 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5514 Xball_2, FALSE, FALSE,
5515 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5518 Xball_2B, FALSE, FALSE,
5519 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5522 Yball_eat, FALSE, FALSE,
5523 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5526 Ykey_1_eat, FALSE, FALSE,
5527 EL_EM_KEY_1, ACTION_COLLECTING, -1
5530 Ykey_2_eat, FALSE, FALSE,
5531 EL_EM_KEY_2, ACTION_COLLECTING, -1
5534 Ykey_3_eat, FALSE, FALSE,
5535 EL_EM_KEY_3, ACTION_COLLECTING, -1
5538 Ykey_4_eat, FALSE, FALSE,
5539 EL_EM_KEY_4, ACTION_COLLECTING, -1
5542 Ykey_5_eat, FALSE, FALSE,
5543 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5546 Ykey_6_eat, FALSE, FALSE,
5547 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5550 Ykey_7_eat, FALSE, FALSE,
5551 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5554 Ykey_8_eat, FALSE, FALSE,
5555 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5558 Ylenses_eat, FALSE, FALSE,
5559 EL_EMC_LENSES, ACTION_COLLECTING, -1
5562 Ymagnify_eat, FALSE, FALSE,
5563 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5566 Ygrass_eat, FALSE, FALSE,
5567 EL_EMC_GRASS, ACTION_SNAPPING, -1
5570 Ydirt_eat, FALSE, FALSE,
5571 EL_SAND, ACTION_SNAPPING, -1
5574 Xgrow_ns, TRUE, FALSE,
5575 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5578 Ygrow_ns_eat, FALSE, FALSE,
5579 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5582 Xgrow_ew, TRUE, FALSE,
5583 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5586 Ygrow_ew_eat, FALSE, FALSE,
5587 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5590 Xwonderwall, TRUE, FALSE,
5591 EL_MAGIC_WALL, -1, -1
5594 XwonderwallB, FALSE, FALSE,
5595 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5598 Xamoeba_1, TRUE, FALSE,
5599 EL_AMOEBA_DRY, ACTION_OTHER, -1
5602 Xamoeba_2, FALSE, FALSE,
5603 EL_AMOEBA_DRY, ACTION_OTHER, -1
5606 Xamoeba_3, FALSE, FALSE,
5607 EL_AMOEBA_DRY, ACTION_OTHER, -1
5610 Xamoeba_4, FALSE, FALSE,
5611 EL_AMOEBA_DRY, ACTION_OTHER, -1
5614 Xamoeba_5, TRUE, FALSE,
5615 EL_AMOEBA_WET, ACTION_OTHER, -1
5618 Xamoeba_6, FALSE, FALSE,
5619 EL_AMOEBA_WET, ACTION_OTHER, -1
5622 Xamoeba_7, FALSE, FALSE,
5623 EL_AMOEBA_WET, ACTION_OTHER, -1
5626 Xamoeba_8, FALSE, FALSE,
5627 EL_AMOEBA_WET, ACTION_OTHER, -1
5630 Xdoor_1, TRUE, FALSE,
5631 EL_EM_GATE_1, -1, -1
5634 Xdoor_2, TRUE, FALSE,
5635 EL_EM_GATE_2, -1, -1
5638 Xdoor_3, TRUE, FALSE,
5639 EL_EM_GATE_3, -1, -1
5642 Xdoor_4, TRUE, FALSE,
5643 EL_EM_GATE_4, -1, -1
5646 Xdoor_5, TRUE, FALSE,
5647 EL_EMC_GATE_5, -1, -1
5650 Xdoor_6, TRUE, FALSE,
5651 EL_EMC_GATE_6, -1, -1
5654 Xdoor_7, TRUE, FALSE,
5655 EL_EMC_GATE_7, -1, -1
5658 Xdoor_8, TRUE, FALSE,
5659 EL_EMC_GATE_8, -1, -1
5662 Xkey_1, TRUE, FALSE,
5666 Xkey_2, TRUE, FALSE,
5670 Xkey_3, TRUE, FALSE,
5674 Xkey_4, TRUE, FALSE,
5678 Xkey_5, TRUE, FALSE,
5679 EL_EMC_KEY_5, -1, -1
5682 Xkey_6, TRUE, FALSE,
5683 EL_EMC_KEY_6, -1, -1
5686 Xkey_7, TRUE, FALSE,
5687 EL_EMC_KEY_7, -1, -1
5690 Xkey_8, TRUE, FALSE,
5691 EL_EMC_KEY_8, -1, -1
5694 Xwind_n, TRUE, FALSE,
5695 EL_BALLOON_SWITCH_UP, -1, -1
5698 Xwind_e, TRUE, FALSE,
5699 EL_BALLOON_SWITCH_RIGHT, -1, -1
5702 Xwind_s, TRUE, FALSE,
5703 EL_BALLOON_SWITCH_DOWN, -1, -1
5706 Xwind_w, TRUE, FALSE,
5707 EL_BALLOON_SWITCH_LEFT, -1, -1
5710 Xwind_nesw, TRUE, FALSE,
5711 EL_BALLOON_SWITCH_ANY, -1, -1
5714 Xwind_stop, TRUE, FALSE,
5715 EL_BALLOON_SWITCH_NONE, -1, -1
5719 EL_EM_EXIT_CLOSED, -1, -1
5722 Xexit_1, TRUE, FALSE,
5723 EL_EM_EXIT_OPEN, -1, -1
5726 Xexit_2, FALSE, FALSE,
5727 EL_EM_EXIT_OPEN, -1, -1
5730 Xexit_3, FALSE, FALSE,
5731 EL_EM_EXIT_OPEN, -1, -1
5734 Xdynamite, TRUE, FALSE,
5735 EL_EM_DYNAMITE, -1, -1
5738 Ydynamite_eat, FALSE, FALSE,
5739 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5742 Xdynamite_1, TRUE, FALSE,
5743 EL_EM_DYNAMITE_ACTIVE, -1, -1
5746 Xdynamite_2, FALSE, FALSE,
5747 EL_EM_DYNAMITE_ACTIVE, -1, -1
5750 Xdynamite_3, FALSE, FALSE,
5751 EL_EM_DYNAMITE_ACTIVE, -1, -1
5754 Xdynamite_4, FALSE, FALSE,
5755 EL_EM_DYNAMITE_ACTIVE, -1, -1
5758 Xbumper, TRUE, FALSE,
5759 EL_EMC_SPRING_BUMPER, -1, -1
5762 XbumperB, FALSE, FALSE,
5763 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5766 Xwheel, TRUE, FALSE,
5767 EL_ROBOT_WHEEL, -1, -1
5770 XwheelB, FALSE, FALSE,
5771 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5774 Xswitch, TRUE, FALSE,
5775 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5778 XswitchB, FALSE, FALSE,
5779 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5783 EL_QUICKSAND_EMPTY, -1, -1
5786 Xsand_stone, TRUE, FALSE,
5787 EL_QUICKSAND_FULL, -1, -1
5790 Xsand_stonein_1, FALSE, TRUE,
5791 EL_ROCK, ACTION_FILLING, -1
5794 Xsand_stonein_2, FALSE, TRUE,
5795 EL_ROCK, ACTION_FILLING, -1
5798 Xsand_stonein_3, FALSE, TRUE,
5799 EL_ROCK, ACTION_FILLING, -1
5802 Xsand_stonein_4, FALSE, TRUE,
5803 EL_ROCK, ACTION_FILLING, -1
5806 Xsand_stonesand_1, FALSE, FALSE,
5807 EL_QUICKSAND_EMPTYING, -1, -1
5810 Xsand_stonesand_2, FALSE, FALSE,
5811 EL_QUICKSAND_EMPTYING, -1, -1
5814 Xsand_stonesand_3, FALSE, FALSE,
5815 EL_QUICKSAND_EMPTYING, -1, -1
5818 Xsand_stonesand_4, FALSE, FALSE,
5819 EL_QUICKSAND_EMPTYING, -1, -1
5822 Xsand_stonesand_quickout_1, FALSE, FALSE,
5823 EL_QUICKSAND_EMPTYING, -1, -1
5826 Xsand_stonesand_quickout_2, FALSE, FALSE,
5827 EL_QUICKSAND_EMPTYING, -1, -1
5830 Xsand_stoneout_1, FALSE, FALSE,
5831 EL_ROCK, ACTION_EMPTYING, -1
5834 Xsand_stoneout_2, FALSE, FALSE,
5835 EL_ROCK, ACTION_EMPTYING, -1
5838 Xsand_sandstone_1, FALSE, FALSE,
5839 EL_QUICKSAND_FILLING, -1, -1
5842 Xsand_sandstone_2, FALSE, FALSE,
5843 EL_QUICKSAND_FILLING, -1, -1
5846 Xsand_sandstone_3, FALSE, FALSE,
5847 EL_QUICKSAND_FILLING, -1, -1
5850 Xsand_sandstone_4, FALSE, FALSE,
5851 EL_QUICKSAND_FILLING, -1, -1
5854 Xplant, TRUE, FALSE,
5855 EL_EMC_PLANT, -1, -1
5858 Yplant, FALSE, FALSE,
5859 EL_EMC_PLANT, -1, -1
5862 Xlenses, TRUE, FALSE,
5863 EL_EMC_LENSES, -1, -1
5866 Xmagnify, TRUE, FALSE,
5867 EL_EMC_MAGNIFIER, -1, -1
5870 Xdripper, TRUE, FALSE,
5871 EL_EMC_DRIPPER, -1, -1
5874 XdripperB, FALSE, FALSE,
5875 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5878 Xfake_blank, TRUE, FALSE,
5879 EL_INVISIBLE_WALL, -1, -1
5882 Xfake_blankB, FALSE, FALSE,
5883 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5886 Xfake_grass, TRUE, FALSE,
5887 EL_EMC_FAKE_GRASS, -1, -1
5890 Xfake_grassB, FALSE, FALSE,
5891 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5894 Xfake_door_1, TRUE, FALSE,
5895 EL_EM_GATE_1_GRAY, -1, -1
5898 Xfake_door_2, TRUE, FALSE,
5899 EL_EM_GATE_2_GRAY, -1, -1
5902 Xfake_door_3, TRUE, FALSE,
5903 EL_EM_GATE_3_GRAY, -1, -1
5906 Xfake_door_4, TRUE, FALSE,
5907 EL_EM_GATE_4_GRAY, -1, -1
5910 Xfake_door_5, TRUE, FALSE,
5911 EL_EMC_GATE_5_GRAY, -1, -1
5914 Xfake_door_6, TRUE, FALSE,
5915 EL_EMC_GATE_6_GRAY, -1, -1
5918 Xfake_door_7, TRUE, FALSE,
5919 EL_EMC_GATE_7_GRAY, -1, -1
5922 Xfake_door_8, TRUE, FALSE,
5923 EL_EMC_GATE_8_GRAY, -1, -1
5926 Xfake_acid_1, TRUE, FALSE,
5927 EL_EMC_FAKE_ACID, -1, -1
5930 Xfake_acid_2, FALSE, FALSE,
5931 EL_EMC_FAKE_ACID, -1, -1
5934 Xfake_acid_3, FALSE, FALSE,
5935 EL_EMC_FAKE_ACID, -1, -1
5938 Xfake_acid_4, FALSE, FALSE,
5939 EL_EMC_FAKE_ACID, -1, -1
5942 Xfake_acid_5, FALSE, FALSE,
5943 EL_EMC_FAKE_ACID, -1, -1
5946 Xfake_acid_6, FALSE, FALSE,
5947 EL_EMC_FAKE_ACID, -1, -1
5950 Xfake_acid_7, FALSE, FALSE,
5951 EL_EMC_FAKE_ACID, -1, -1
5954 Xfake_acid_8, FALSE, FALSE,
5955 EL_EMC_FAKE_ACID, -1, -1
5958 Xsteel_1, TRUE, FALSE,
5959 EL_STEELWALL, -1, -1
5962 Xsteel_2, TRUE, FALSE,
5963 EL_EMC_STEELWALL_2, -1, -1
5966 Xsteel_3, TRUE, FALSE,
5967 EL_EMC_STEELWALL_3, -1, -1
5970 Xsteel_4, TRUE, FALSE,
5971 EL_EMC_STEELWALL_4, -1, -1
5974 Xwall_1, TRUE, FALSE,
5978 Xwall_2, TRUE, FALSE,
5979 EL_EMC_WALL_14, -1, -1
5982 Xwall_3, TRUE, FALSE,
5983 EL_EMC_WALL_15, -1, -1
5986 Xwall_4, TRUE, FALSE,
5987 EL_EMC_WALL_16, -1, -1
5990 Xround_wall_1, TRUE, FALSE,
5991 EL_WALL_SLIPPERY, -1, -1
5994 Xround_wall_2, TRUE, FALSE,
5995 EL_EMC_WALL_SLIPPERY_2, -1, -1
5998 Xround_wall_3, TRUE, FALSE,
5999 EL_EMC_WALL_SLIPPERY_3, -1, -1
6002 Xround_wall_4, TRUE, FALSE,
6003 EL_EMC_WALL_SLIPPERY_4, -1, -1
6006 Xdecor_1, TRUE, FALSE,
6007 EL_EMC_WALL_8, -1, -1
6010 Xdecor_2, TRUE, FALSE,
6011 EL_EMC_WALL_6, -1, -1
6014 Xdecor_3, TRUE, FALSE,
6015 EL_EMC_WALL_4, -1, -1
6018 Xdecor_4, TRUE, FALSE,
6019 EL_EMC_WALL_7, -1, -1
6022 Xdecor_5, TRUE, FALSE,
6023 EL_EMC_WALL_5, -1, -1
6026 Xdecor_6, TRUE, FALSE,
6027 EL_EMC_WALL_9, -1, -1
6030 Xdecor_7, TRUE, FALSE,
6031 EL_EMC_WALL_10, -1, -1
6034 Xdecor_8, TRUE, FALSE,
6035 EL_EMC_WALL_1, -1, -1
6038 Xdecor_9, TRUE, FALSE,
6039 EL_EMC_WALL_2, -1, -1
6042 Xdecor_10, TRUE, FALSE,
6043 EL_EMC_WALL_3, -1, -1
6046 Xdecor_11, TRUE, FALSE,
6047 EL_EMC_WALL_11, -1, -1
6050 Xdecor_12, TRUE, FALSE,
6051 EL_EMC_WALL_12, -1, -1
6054 Xalpha_0, TRUE, FALSE,
6055 EL_CHAR('0'), -1, -1
6058 Xalpha_1, TRUE, FALSE,
6059 EL_CHAR('1'), -1, -1
6062 Xalpha_2, TRUE, FALSE,
6063 EL_CHAR('2'), -1, -1
6066 Xalpha_3, TRUE, FALSE,
6067 EL_CHAR('3'), -1, -1
6070 Xalpha_4, TRUE, FALSE,
6071 EL_CHAR('4'), -1, -1
6074 Xalpha_5, TRUE, FALSE,
6075 EL_CHAR('5'), -1, -1
6078 Xalpha_6, TRUE, FALSE,
6079 EL_CHAR('6'), -1, -1
6082 Xalpha_7, TRUE, FALSE,
6083 EL_CHAR('7'), -1, -1
6086 Xalpha_8, TRUE, FALSE,
6087 EL_CHAR('8'), -1, -1
6090 Xalpha_9, TRUE, FALSE,
6091 EL_CHAR('9'), -1, -1
6094 Xalpha_excla, TRUE, FALSE,
6095 EL_CHAR('!'), -1, -1
6098 Xalpha_quote, TRUE, FALSE,
6099 EL_CHAR('"'), -1, -1
6102 Xalpha_comma, TRUE, FALSE,
6103 EL_CHAR(','), -1, -1
6106 Xalpha_minus, TRUE, FALSE,
6107 EL_CHAR('-'), -1, -1
6110 Xalpha_perio, TRUE, FALSE,
6111 EL_CHAR('.'), -1, -1
6114 Xalpha_colon, TRUE, FALSE,
6115 EL_CHAR(':'), -1, -1
6118 Xalpha_quest, TRUE, FALSE,
6119 EL_CHAR('?'), -1, -1
6122 Xalpha_a, TRUE, FALSE,
6123 EL_CHAR('A'), -1, -1
6126 Xalpha_b, TRUE, FALSE,
6127 EL_CHAR('B'), -1, -1
6130 Xalpha_c, TRUE, FALSE,
6131 EL_CHAR('C'), -1, -1
6134 Xalpha_d, TRUE, FALSE,
6135 EL_CHAR('D'), -1, -1
6138 Xalpha_e, TRUE, FALSE,
6139 EL_CHAR('E'), -1, -1
6142 Xalpha_f, TRUE, FALSE,
6143 EL_CHAR('F'), -1, -1
6146 Xalpha_g, TRUE, FALSE,
6147 EL_CHAR('G'), -1, -1
6150 Xalpha_h, TRUE, FALSE,
6151 EL_CHAR('H'), -1, -1
6154 Xalpha_i, TRUE, FALSE,
6155 EL_CHAR('I'), -1, -1
6158 Xalpha_j, TRUE, FALSE,
6159 EL_CHAR('J'), -1, -1
6162 Xalpha_k, TRUE, FALSE,
6163 EL_CHAR('K'), -1, -1
6166 Xalpha_l, TRUE, FALSE,
6167 EL_CHAR('L'), -1, -1
6170 Xalpha_m, TRUE, FALSE,
6171 EL_CHAR('M'), -1, -1
6174 Xalpha_n, TRUE, FALSE,
6175 EL_CHAR('N'), -1, -1
6178 Xalpha_o, TRUE, FALSE,
6179 EL_CHAR('O'), -1, -1
6182 Xalpha_p, TRUE, FALSE,
6183 EL_CHAR('P'), -1, -1
6186 Xalpha_q, TRUE, FALSE,
6187 EL_CHAR('Q'), -1, -1
6190 Xalpha_r, TRUE, FALSE,
6191 EL_CHAR('R'), -1, -1
6194 Xalpha_s, TRUE, FALSE,
6195 EL_CHAR('S'), -1, -1
6198 Xalpha_t, TRUE, FALSE,
6199 EL_CHAR('T'), -1, -1
6202 Xalpha_u, TRUE, FALSE,
6203 EL_CHAR('U'), -1, -1
6206 Xalpha_v, TRUE, FALSE,
6207 EL_CHAR('V'), -1, -1
6210 Xalpha_w, TRUE, FALSE,
6211 EL_CHAR('W'), -1, -1
6214 Xalpha_x, TRUE, FALSE,
6215 EL_CHAR('X'), -1, -1
6218 Xalpha_y, TRUE, FALSE,
6219 EL_CHAR('Y'), -1, -1
6222 Xalpha_z, TRUE, FALSE,
6223 EL_CHAR('Z'), -1, -1
6226 Xalpha_arrow_e, TRUE, FALSE,
6227 EL_CHAR('>'), -1, -1
6230 Xalpha_arrow_w, TRUE, FALSE,
6231 EL_CHAR('<'), -1, -1
6234 Xalpha_copyr, TRUE, FALSE,
6235 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6239 Xboom_bug, FALSE, FALSE,
6240 EL_BUG, ACTION_EXPLODING, -1
6243 Xboom_bomb, FALSE, FALSE,
6244 EL_BOMB, ACTION_EXPLODING, -1
6247 Xboom_android, FALSE, FALSE,
6248 EL_EMC_ANDROID, ACTION_OTHER, -1
6251 Xboom_1, FALSE, FALSE,
6252 EL_DEFAULT, ACTION_EXPLODING, -1
6255 Xboom_2, FALSE, FALSE,
6256 EL_DEFAULT, ACTION_EXPLODING, -1
6259 Znormal, FALSE, FALSE,
6263 Zdynamite, FALSE, FALSE,
6267 Zplayer, FALSE, FALSE,
6271 ZBORDER, FALSE, FALSE,
6281 static struct Mapping_EM_to_RND_player
6290 em_player_mapping_list[] =
6294 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6298 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6302 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6306 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6310 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6314 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6318 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6322 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6326 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6330 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6334 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6338 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6342 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6346 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6350 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6354 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6358 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6362 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6366 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6370 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6374 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6378 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6382 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6386 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6390 EL_PLAYER_1, ACTION_DEFAULT, -1,
6394 EL_PLAYER_2, ACTION_DEFAULT, -1,
6398 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6402 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6406 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6410 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6414 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6418 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6422 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6426 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6430 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6434 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6438 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6442 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6446 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6450 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6454 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6458 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6462 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6466 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6470 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6474 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6478 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6482 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6486 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6490 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6494 EL_PLAYER_3, ACTION_DEFAULT, -1,
6498 EL_PLAYER_4, ACTION_DEFAULT, -1,
6507 int map_element_RND_to_EM(int element_rnd)
6509 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6510 static boolean mapping_initialized = FALSE;
6512 if (!mapping_initialized)
6516 /* return "Xalpha_quest" for all undefined elements in mapping array */
6517 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6518 mapping_RND_to_EM[i] = Xalpha_quest;
6520 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6521 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6522 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6523 em_object_mapping_list[i].element_em;
6525 mapping_initialized = TRUE;
6528 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6529 return mapping_RND_to_EM[element_rnd];
6531 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6536 int map_element_EM_to_RND(int element_em)
6538 static unsigned short mapping_EM_to_RND[TILE_MAX];
6539 static boolean mapping_initialized = FALSE;
6541 if (!mapping_initialized)
6545 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6546 for (i = 0; i < TILE_MAX; i++)
6547 mapping_EM_to_RND[i] = EL_UNKNOWN;
6549 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6550 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6551 em_object_mapping_list[i].element_rnd;
6553 mapping_initialized = TRUE;
6556 if (element_em >= 0 && element_em < TILE_MAX)
6557 return mapping_EM_to_RND[element_em];
6559 Error(ERR_WARN, "invalid EM level element %d", element_em);
6564 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6566 struct LevelInfo_EM *level_em = level->native_em_level;
6567 struct LEVEL *lev = level_em->lev;
6570 for (i = 0; i < TILE_MAX; i++)
6571 lev->android_array[i] = Xblank;
6573 for (i = 0; i < level->num_android_clone_elements; i++)
6575 int element_rnd = level->android_clone_element[i];
6576 int element_em = map_element_RND_to_EM(element_rnd);
6578 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6579 if (em_object_mapping_list[j].element_rnd == element_rnd)
6580 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6584 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6586 struct LevelInfo_EM *level_em = level->native_em_level;
6587 struct LEVEL *lev = level_em->lev;
6590 level->num_android_clone_elements = 0;
6592 for (i = 0; i < TILE_MAX; i++)
6594 int element_em = lev->android_array[i];
6596 boolean element_found = FALSE;
6598 if (element_em == Xblank)
6601 element_rnd = map_element_EM_to_RND(element_em);
6603 for (j = 0; j < level->num_android_clone_elements; j++)
6604 if (level->android_clone_element[j] == element_rnd)
6605 element_found = TRUE;
6609 level->android_clone_element[level->num_android_clone_elements++] =
6612 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6617 if (level->num_android_clone_elements == 0)
6619 level->num_android_clone_elements = 1;
6620 level->android_clone_element[0] = EL_EMPTY;
6624 int map_direction_RND_to_EM(int direction)
6626 return (direction == MV_UP ? 0 :
6627 direction == MV_RIGHT ? 1 :
6628 direction == MV_DOWN ? 2 :
6629 direction == MV_LEFT ? 3 :
6633 int map_direction_EM_to_RND(int direction)
6635 return (direction == 0 ? MV_UP :
6636 direction == 1 ? MV_RIGHT :
6637 direction == 2 ? MV_DOWN :
6638 direction == 3 ? MV_LEFT :
6642 int map_element_RND_to_SP(int element_rnd)
6644 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6646 if (element_rnd >= EL_SP_START &&
6647 element_rnd <= EL_SP_END)
6648 element_sp = element_rnd - EL_SP_START;
6649 else if (element_rnd == EL_EMPTY_SPACE)
6651 else if (element_rnd == EL_INVISIBLE_WALL)
6657 int map_element_SP_to_RND(int element_sp)
6659 int element_rnd = EL_UNKNOWN;
6661 if (element_sp >= 0x00 &&
6663 element_rnd = EL_SP_START + element_sp;
6664 else if (element_sp == 0x28)
6665 element_rnd = EL_INVISIBLE_WALL;
6670 int map_action_SP_to_RND(int action_sp)
6674 case actActive: return ACTION_ACTIVE;
6675 case actImpact: return ACTION_IMPACT;
6676 case actExploding: return ACTION_EXPLODING;
6677 case actDigging: return ACTION_DIGGING;
6678 case actSnapping: return ACTION_SNAPPING;
6679 case actCollecting: return ACTION_COLLECTING;
6680 case actPassing: return ACTION_PASSING;
6681 case actPushing: return ACTION_PUSHING;
6682 case actDropping: return ACTION_DROPPING;
6684 default: return ACTION_DEFAULT;
6688 int get_next_element(int element)
6692 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6693 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6694 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6695 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6696 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6697 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6698 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6699 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6700 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6701 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6702 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6704 default: return element;
6708 int el_act_dir2img(int element, int action, int direction)
6710 element = GFX_ELEMENT(element);
6711 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6713 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6714 return element_info[element].direction_graphic[action][direction];
6717 static int el_act_dir2crm(int element, int action, int direction)
6719 element = GFX_ELEMENT(element);
6720 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6722 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6723 return element_info[element].direction_crumbled[action][direction];
6726 int el_act2img(int element, int action)
6728 element = GFX_ELEMENT(element);
6730 return element_info[element].graphic[action];
6733 int el_act2crm(int element, int action)
6735 element = GFX_ELEMENT(element);
6737 return element_info[element].crumbled[action];
6740 int el_dir2img(int element, int direction)
6742 element = GFX_ELEMENT(element);
6744 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6747 int el2baseimg(int element)
6749 return element_info[element].graphic[ACTION_DEFAULT];
6752 int el2img(int element)
6754 element = GFX_ELEMENT(element);
6756 return element_info[element].graphic[ACTION_DEFAULT];
6759 int el2edimg(int element)
6761 element = GFX_ELEMENT(element);
6763 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6766 int el2preimg(int element)
6768 element = GFX_ELEMENT(element);
6770 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6773 int el2panelimg(int element)
6775 element = GFX_ELEMENT(element);
6777 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6780 int font2baseimg(int font_nr)
6782 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6785 int getBeltNrFromBeltElement(int element)
6787 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6788 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6789 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6792 int getBeltNrFromBeltActiveElement(int element)
6794 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6795 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6796 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6799 int getBeltNrFromBeltSwitchElement(int element)
6801 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6802 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6803 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6806 int getBeltDirNrFromBeltElement(int element)
6808 static int belt_base_element[4] =
6810 EL_CONVEYOR_BELT_1_LEFT,
6811 EL_CONVEYOR_BELT_2_LEFT,
6812 EL_CONVEYOR_BELT_3_LEFT,
6813 EL_CONVEYOR_BELT_4_LEFT
6816 int belt_nr = getBeltNrFromBeltElement(element);
6817 int belt_dir_nr = element - belt_base_element[belt_nr];
6819 return (belt_dir_nr % 3);
6822 int getBeltDirNrFromBeltSwitchElement(int element)
6824 static int belt_base_element[4] =
6826 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6827 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6828 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6829 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6832 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6833 int belt_dir_nr = element - belt_base_element[belt_nr];
6835 return (belt_dir_nr % 3);
6838 int getBeltDirFromBeltElement(int element)
6840 static int belt_move_dir[3] =
6847 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6849 return belt_move_dir[belt_dir_nr];
6852 int getBeltDirFromBeltSwitchElement(int element)
6854 static int belt_move_dir[3] =
6861 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6863 return belt_move_dir[belt_dir_nr];
6866 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6868 static int belt_base_element[4] =
6870 EL_CONVEYOR_BELT_1_LEFT,
6871 EL_CONVEYOR_BELT_2_LEFT,
6872 EL_CONVEYOR_BELT_3_LEFT,
6873 EL_CONVEYOR_BELT_4_LEFT
6876 return belt_base_element[belt_nr] + belt_dir_nr;
6879 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6881 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6883 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6886 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6888 static int belt_base_element[4] =
6890 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6891 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6892 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6893 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6896 return belt_base_element[belt_nr] + belt_dir_nr;
6899 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6901 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6903 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6906 boolean getTeamMode_EM()
6908 return game.team_mode;
6911 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6913 int game_frame_delay_value;
6915 game_frame_delay_value =
6916 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6917 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6920 if (tape.playing && tape.warp_forward && !tape.pausing)
6921 game_frame_delay_value = 0;
6923 return game_frame_delay_value;
6926 unsigned int InitRND(int seed)
6928 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6929 return InitEngineRandom_EM(seed);
6930 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6931 return InitEngineRandom_SP(seed);
6933 return InitEngineRandom_RND(seed);
6936 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6937 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6939 inline static int get_effective_element_EM(int tile, int frame_em)
6941 int element = object_mapping[tile].element_rnd;
6942 int action = object_mapping[tile].action;
6943 boolean is_backside = object_mapping[tile].is_backside;
6944 boolean action_removing = (action == ACTION_DIGGING ||
6945 action == ACTION_SNAPPING ||
6946 action == ACTION_COLLECTING);
6952 case Yacid_splash_eB:
6953 case Yacid_splash_wB:
6954 return (frame_em > 5 ? EL_EMPTY : element);
6960 else /* frame_em == 7 */
6964 case Yacid_splash_eB:
6965 case Yacid_splash_wB:
6968 case Yemerald_stone:
6971 case Ydiamond_stone:
6975 case Xdrip_stretchB:
6994 case Xsand_stonein_1:
6995 case Xsand_stonein_2:
6996 case Xsand_stonein_3:
6997 case Xsand_stonein_4:
7001 return (is_backside || action_removing ? EL_EMPTY : element);
7006 inline static boolean check_linear_animation_EM(int tile)
7010 case Xsand_stonesand_1:
7011 case Xsand_stonesand_quickout_1:
7012 case Xsand_sandstone_1:
7013 case Xsand_stonein_1:
7014 case Xsand_stoneout_1:
7033 case Yacid_splash_eB:
7034 case Yacid_splash_wB:
7035 case Yemerald_stone:
7042 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7043 boolean has_crumbled_graphics,
7044 int crumbled, int sync_frame)
7046 /* if element can be crumbled, but certain action graphics are just empty
7047 space (like instantly snapping sand to empty space in 1 frame), do not
7048 treat these empty space graphics as crumbled graphics in EMC engine */
7049 if (crumbled == IMG_EMPTY_SPACE)
7050 has_crumbled_graphics = FALSE;
7052 if (has_crumbled_graphics)
7054 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7055 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7056 g_crumbled->anim_delay,
7057 g_crumbled->anim_mode,
7058 g_crumbled->anim_start_frame,
7061 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7062 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7064 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7066 g_em->has_crumbled_graphics = TRUE;
7070 g_em->crumbled_bitmap = NULL;
7071 g_em->crumbled_src_x = 0;
7072 g_em->crumbled_src_y = 0;
7073 g_em->crumbled_border_size = 0;
7075 g_em->has_crumbled_graphics = FALSE;
7079 void ResetGfxAnimation_EM(int x, int y, int tile)
7084 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7085 int tile, int frame_em, int x, int y)
7087 int action = object_mapping[tile].action;
7088 int direction = object_mapping[tile].direction;
7089 int effective_element = get_effective_element_EM(tile, frame_em);
7090 int graphic = (direction == MV_NONE ?
7091 el_act2img(effective_element, action) :
7092 el_act_dir2img(effective_element, action, direction));
7093 struct GraphicInfo *g = &graphic_info[graphic];
7095 boolean action_removing = (action == ACTION_DIGGING ||
7096 action == ACTION_SNAPPING ||
7097 action == ACTION_COLLECTING);
7098 boolean action_moving = (action == ACTION_FALLING ||
7099 action == ACTION_MOVING ||
7100 action == ACTION_PUSHING ||
7101 action == ACTION_EATING ||
7102 action == ACTION_FILLING ||
7103 action == ACTION_EMPTYING);
7104 boolean action_falling = (action == ACTION_FALLING ||
7105 action == ACTION_FILLING ||
7106 action == ACTION_EMPTYING);
7108 /* special case: graphic uses "2nd movement tile" and has defined
7109 7 frames for movement animation (or less) => use default graphic
7110 for last (8th) frame which ends the movement animation */
7111 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7113 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7114 graphic = (direction == MV_NONE ?
7115 el_act2img(effective_element, action) :
7116 el_act_dir2img(effective_element, action, direction));
7118 g = &graphic_info[graphic];
7121 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7125 else if (action_moving)
7127 boolean is_backside = object_mapping[tile].is_backside;
7131 int direction = object_mapping[tile].direction;
7132 int move_dir = (action_falling ? MV_DOWN : direction);
7137 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7138 if (g->double_movement && frame_em == 0)
7142 if (move_dir == MV_LEFT)
7143 GfxFrame[x - 1][y] = GfxFrame[x][y];
7144 else if (move_dir == MV_RIGHT)
7145 GfxFrame[x + 1][y] = GfxFrame[x][y];
7146 else if (move_dir == MV_UP)
7147 GfxFrame[x][y - 1] = GfxFrame[x][y];
7148 else if (move_dir == MV_DOWN)
7149 GfxFrame[x][y + 1] = GfxFrame[x][y];
7156 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7157 if (tile == Xsand_stonesand_quickout_1 ||
7158 tile == Xsand_stonesand_quickout_2)
7162 if (graphic_info[graphic].anim_global_sync)
7163 sync_frame = FrameCounter;
7164 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7165 sync_frame = GfxFrame[x][y];
7167 sync_frame = 0; /* playfield border (pseudo steel) */
7169 SetRandomAnimationValue(x, y);
7171 int frame = getAnimationFrame(g->anim_frames,
7174 g->anim_start_frame,
7177 g_em->unique_identifier =
7178 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7181 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7182 int tile, int frame_em, int x, int y)
7184 int action = object_mapping[tile].action;
7185 int direction = object_mapping[tile].direction;
7186 boolean is_backside = object_mapping[tile].is_backside;
7187 int effective_element = get_effective_element_EM(tile, frame_em);
7188 int effective_action = action;
7189 int graphic = (direction == MV_NONE ?
7190 el_act2img(effective_element, effective_action) :
7191 el_act_dir2img(effective_element, effective_action,
7193 int crumbled = (direction == MV_NONE ?
7194 el_act2crm(effective_element, effective_action) :
7195 el_act_dir2crm(effective_element, effective_action,
7197 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7198 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7199 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7200 struct GraphicInfo *g = &graphic_info[graphic];
7203 /* special case: graphic uses "2nd movement tile" and has defined
7204 7 frames for movement animation (or less) => use default graphic
7205 for last (8th) frame which ends the movement animation */
7206 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7208 effective_action = ACTION_DEFAULT;
7209 graphic = (direction == MV_NONE ?
7210 el_act2img(effective_element, effective_action) :
7211 el_act_dir2img(effective_element, effective_action,
7213 crumbled = (direction == MV_NONE ?
7214 el_act2crm(effective_element, effective_action) :
7215 el_act_dir2crm(effective_element, effective_action,
7218 g = &graphic_info[graphic];
7221 if (graphic_info[graphic].anim_global_sync)
7222 sync_frame = FrameCounter;
7223 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7224 sync_frame = GfxFrame[x][y];
7226 sync_frame = 0; /* playfield border (pseudo steel) */
7228 SetRandomAnimationValue(x, y);
7230 int frame = getAnimationFrame(g->anim_frames,
7233 g->anim_start_frame,
7236 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7237 g->double_movement && is_backside);
7239 /* (updating the "crumbled" graphic definitions is probably not really needed,
7240 as animations for crumbled graphics can't be longer than one EMC cycle) */
7241 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7245 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7246 int player_nr, int anim, int frame_em)
7248 int element = player_mapping[player_nr][anim].element_rnd;
7249 int action = player_mapping[player_nr][anim].action;
7250 int direction = player_mapping[player_nr][anim].direction;
7251 int graphic = (direction == MV_NONE ?
7252 el_act2img(element, action) :
7253 el_act_dir2img(element, action, direction));
7254 struct GraphicInfo *g = &graphic_info[graphic];
7257 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7259 stored_player[player_nr].StepFrame = frame_em;
7261 sync_frame = stored_player[player_nr].Frame;
7263 int frame = getAnimationFrame(g->anim_frames,
7266 g->anim_start_frame,
7269 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7270 &g_em->src_x, &g_em->src_y, FALSE);
7273 void InitGraphicInfo_EM(void)
7278 int num_em_gfx_errors = 0;
7280 if (graphic_info_em_object[0][0].bitmap == NULL)
7282 /* EM graphics not yet initialized in em_open_all() */
7287 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7290 /* always start with reliable default values */
7291 for (i = 0; i < TILE_MAX; i++)
7293 object_mapping[i].element_rnd = EL_UNKNOWN;
7294 object_mapping[i].is_backside = FALSE;
7295 object_mapping[i].action = ACTION_DEFAULT;
7296 object_mapping[i].direction = MV_NONE;
7299 /* always start with reliable default values */
7300 for (p = 0; p < MAX_PLAYERS; p++)
7302 for (i = 0; i < SPR_MAX; i++)
7304 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7305 player_mapping[p][i].action = ACTION_DEFAULT;
7306 player_mapping[p][i].direction = MV_NONE;
7310 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7312 int e = em_object_mapping_list[i].element_em;
7314 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7315 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7317 if (em_object_mapping_list[i].action != -1)
7318 object_mapping[e].action = em_object_mapping_list[i].action;
7320 if (em_object_mapping_list[i].direction != -1)
7321 object_mapping[e].direction =
7322 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7325 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7327 int a = em_player_mapping_list[i].action_em;
7328 int p = em_player_mapping_list[i].player_nr;
7330 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7332 if (em_player_mapping_list[i].action != -1)
7333 player_mapping[p][a].action = em_player_mapping_list[i].action;
7335 if (em_player_mapping_list[i].direction != -1)
7336 player_mapping[p][a].direction =
7337 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7340 for (i = 0; i < TILE_MAX; i++)
7342 int element = object_mapping[i].element_rnd;
7343 int action = object_mapping[i].action;
7344 int direction = object_mapping[i].direction;
7345 boolean is_backside = object_mapping[i].is_backside;
7346 boolean action_exploding = ((action == ACTION_EXPLODING ||
7347 action == ACTION_SMASHED_BY_ROCK ||
7348 action == ACTION_SMASHED_BY_SPRING) &&
7349 element != EL_DIAMOND);
7350 boolean action_active = (action == ACTION_ACTIVE);
7351 boolean action_other = (action == ACTION_OTHER);
7353 for (j = 0; j < 8; j++)
7355 int effective_element = get_effective_element_EM(i, j);
7356 int effective_action = (j < 7 ? action :
7357 i == Xdrip_stretch ? action :
7358 i == Xdrip_stretchB ? action :
7359 i == Ydrip_s1 ? action :
7360 i == Ydrip_s1B ? action :
7361 i == Xball_1B ? action :
7362 i == Xball_2 ? action :
7363 i == Xball_2B ? action :
7364 i == Yball_eat ? action :
7365 i == Ykey_1_eat ? action :
7366 i == Ykey_2_eat ? action :
7367 i == Ykey_3_eat ? action :
7368 i == Ykey_4_eat ? action :
7369 i == Ykey_5_eat ? action :
7370 i == Ykey_6_eat ? action :
7371 i == Ykey_7_eat ? action :
7372 i == Ykey_8_eat ? action :
7373 i == Ylenses_eat ? action :
7374 i == Ymagnify_eat ? action :
7375 i == Ygrass_eat ? action :
7376 i == Ydirt_eat ? action :
7377 i == Xsand_stonein_1 ? action :
7378 i == Xsand_stonein_2 ? action :
7379 i == Xsand_stonein_3 ? action :
7380 i == Xsand_stonein_4 ? action :
7381 i == Xsand_stoneout_1 ? action :
7382 i == Xsand_stoneout_2 ? action :
7383 i == Xboom_android ? ACTION_EXPLODING :
7384 action_exploding ? ACTION_EXPLODING :
7385 action_active ? action :
7386 action_other ? action :
7388 int graphic = (el_act_dir2img(effective_element, effective_action,
7390 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7392 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7393 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7394 boolean has_action_graphics = (graphic != base_graphic);
7395 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7396 struct GraphicInfo *g = &graphic_info[graphic];
7397 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7400 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7401 boolean special_animation = (action != ACTION_DEFAULT &&
7402 g->anim_frames == 3 &&
7403 g->anim_delay == 2 &&
7404 g->anim_mode & ANIM_LINEAR);
7405 int sync_frame = (i == Xdrip_stretch ? 7 :
7406 i == Xdrip_stretchB ? 7 :
7407 i == Ydrip_s2 ? j + 8 :
7408 i == Ydrip_s2B ? j + 8 :
7417 i == Xfake_acid_1 ? 0 :
7418 i == Xfake_acid_2 ? 10 :
7419 i == Xfake_acid_3 ? 20 :
7420 i == Xfake_acid_4 ? 30 :
7421 i == Xfake_acid_5 ? 40 :
7422 i == Xfake_acid_6 ? 50 :
7423 i == Xfake_acid_7 ? 60 :
7424 i == Xfake_acid_8 ? 70 :
7426 i == Xball_2B ? j + 8 :
7427 i == Yball_eat ? j + 1 :
7428 i == Ykey_1_eat ? j + 1 :
7429 i == Ykey_2_eat ? j + 1 :
7430 i == Ykey_3_eat ? j + 1 :
7431 i == Ykey_4_eat ? j + 1 :
7432 i == Ykey_5_eat ? j + 1 :
7433 i == Ykey_6_eat ? j + 1 :
7434 i == Ykey_7_eat ? j + 1 :
7435 i == Ykey_8_eat ? j + 1 :
7436 i == Ylenses_eat ? j + 1 :
7437 i == Ymagnify_eat ? j + 1 :
7438 i == Ygrass_eat ? j + 1 :
7439 i == Ydirt_eat ? j + 1 :
7440 i == Xamoeba_1 ? 0 :
7441 i == Xamoeba_2 ? 1 :
7442 i == Xamoeba_3 ? 2 :
7443 i == Xamoeba_4 ? 3 :
7444 i == Xamoeba_5 ? 0 :
7445 i == Xamoeba_6 ? 1 :
7446 i == Xamoeba_7 ? 2 :
7447 i == Xamoeba_8 ? 3 :
7448 i == Xexit_2 ? j + 8 :
7449 i == Xexit_3 ? j + 16 :
7450 i == Xdynamite_1 ? 0 :
7451 i == Xdynamite_2 ? 8 :
7452 i == Xdynamite_3 ? 16 :
7453 i == Xdynamite_4 ? 24 :
7454 i == Xsand_stonein_1 ? j + 1 :
7455 i == Xsand_stonein_2 ? j + 9 :
7456 i == Xsand_stonein_3 ? j + 17 :
7457 i == Xsand_stonein_4 ? j + 25 :
7458 i == Xsand_stoneout_1 && j == 0 ? 0 :
7459 i == Xsand_stoneout_1 && j == 1 ? 0 :
7460 i == Xsand_stoneout_1 && j == 2 ? 1 :
7461 i == Xsand_stoneout_1 && j == 3 ? 2 :
7462 i == Xsand_stoneout_1 && j == 4 ? 2 :
7463 i == Xsand_stoneout_1 && j == 5 ? 3 :
7464 i == Xsand_stoneout_1 && j == 6 ? 4 :
7465 i == Xsand_stoneout_1 && j == 7 ? 4 :
7466 i == Xsand_stoneout_2 && j == 0 ? 5 :
7467 i == Xsand_stoneout_2 && j == 1 ? 6 :
7468 i == Xsand_stoneout_2 && j == 2 ? 7 :
7469 i == Xsand_stoneout_2 && j == 3 ? 8 :
7470 i == Xsand_stoneout_2 && j == 4 ? 9 :
7471 i == Xsand_stoneout_2 && j == 5 ? 11 :
7472 i == Xsand_stoneout_2 && j == 6 ? 13 :
7473 i == Xsand_stoneout_2 && j == 7 ? 15 :
7474 i == Xboom_bug && j == 1 ? 2 :
7475 i == Xboom_bug && j == 2 ? 2 :
7476 i == Xboom_bug && j == 3 ? 4 :
7477 i == Xboom_bug && j == 4 ? 4 :
7478 i == Xboom_bug && j == 5 ? 2 :
7479 i == Xboom_bug && j == 6 ? 2 :
7480 i == Xboom_bug && j == 7 ? 0 :
7481 i == Xboom_bomb && j == 1 ? 2 :
7482 i == Xboom_bomb && j == 2 ? 2 :
7483 i == Xboom_bomb && j == 3 ? 4 :
7484 i == Xboom_bomb && j == 4 ? 4 :
7485 i == Xboom_bomb && j == 5 ? 2 :
7486 i == Xboom_bomb && j == 6 ? 2 :
7487 i == Xboom_bomb && j == 7 ? 0 :
7488 i == Xboom_android && j == 7 ? 6 :
7489 i == Xboom_1 && j == 1 ? 2 :
7490 i == Xboom_1 && j == 2 ? 2 :
7491 i == Xboom_1 && j == 3 ? 4 :
7492 i == Xboom_1 && j == 4 ? 4 :
7493 i == Xboom_1 && j == 5 ? 6 :
7494 i == Xboom_1 && j == 6 ? 6 :
7495 i == Xboom_1 && j == 7 ? 8 :
7496 i == Xboom_2 && j == 0 ? 8 :
7497 i == Xboom_2 && j == 1 ? 8 :
7498 i == Xboom_2 && j == 2 ? 10 :
7499 i == Xboom_2 && j == 3 ? 10 :
7500 i == Xboom_2 && j == 4 ? 10 :
7501 i == Xboom_2 && j == 5 ? 12 :
7502 i == Xboom_2 && j == 6 ? 12 :
7503 i == Xboom_2 && j == 7 ? 12 :
7504 special_animation && j == 4 ? 3 :
7505 effective_action != action ? 0 :
7509 Bitmap *debug_bitmap = g_em->bitmap;
7510 int debug_src_x = g_em->src_x;
7511 int debug_src_y = g_em->src_y;
7514 int frame = getAnimationFrame(g->anim_frames,
7517 g->anim_start_frame,
7520 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7521 g->double_movement && is_backside);
7523 g_em->bitmap = src_bitmap;
7524 g_em->src_x = src_x;
7525 g_em->src_y = src_y;
7526 g_em->src_offset_x = 0;
7527 g_em->src_offset_y = 0;
7528 g_em->dst_offset_x = 0;
7529 g_em->dst_offset_y = 0;
7530 g_em->width = TILEX;
7531 g_em->height = TILEY;
7533 g_em->preserve_background = FALSE;
7535 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7538 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7539 effective_action == ACTION_MOVING ||
7540 effective_action == ACTION_PUSHING ||
7541 effective_action == ACTION_EATING)) ||
7542 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7543 effective_action == ACTION_EMPTYING)))
7546 (effective_action == ACTION_FALLING ||
7547 effective_action == ACTION_FILLING ||
7548 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7549 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7550 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7551 int num_steps = (i == Ydrip_s1 ? 16 :
7552 i == Ydrip_s1B ? 16 :
7553 i == Ydrip_s2 ? 16 :
7554 i == Ydrip_s2B ? 16 :
7555 i == Xsand_stonein_1 ? 32 :
7556 i == Xsand_stonein_2 ? 32 :
7557 i == Xsand_stonein_3 ? 32 :
7558 i == Xsand_stonein_4 ? 32 :
7559 i == Xsand_stoneout_1 ? 16 :
7560 i == Xsand_stoneout_2 ? 16 : 8);
7561 int cx = ABS(dx) * (TILEX / num_steps);
7562 int cy = ABS(dy) * (TILEY / num_steps);
7563 int step_frame = (i == Ydrip_s2 ? j + 8 :
7564 i == Ydrip_s2B ? j + 8 :
7565 i == Xsand_stonein_2 ? j + 8 :
7566 i == Xsand_stonein_3 ? j + 16 :
7567 i == Xsand_stonein_4 ? j + 24 :
7568 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7569 int step = (is_backside ? step_frame : num_steps - step_frame);
7571 if (is_backside) /* tile where movement starts */
7573 if (dx < 0 || dy < 0)
7575 g_em->src_offset_x = cx * step;
7576 g_em->src_offset_y = cy * step;
7580 g_em->dst_offset_x = cx * step;
7581 g_em->dst_offset_y = cy * step;
7584 else /* tile where movement ends */
7586 if (dx < 0 || dy < 0)
7588 g_em->dst_offset_x = cx * step;
7589 g_em->dst_offset_y = cy * step;
7593 g_em->src_offset_x = cx * step;
7594 g_em->src_offset_y = cy * step;
7598 g_em->width = TILEX - cx * step;
7599 g_em->height = TILEY - cy * step;
7602 /* create unique graphic identifier to decide if tile must be redrawn */
7603 /* bit 31 - 16 (16 bit): EM style graphic
7604 bit 15 - 12 ( 4 bit): EM style frame
7605 bit 11 - 6 ( 6 bit): graphic width
7606 bit 5 - 0 ( 6 bit): graphic height */
7607 g_em->unique_identifier =
7608 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7612 /* skip check for EMC elements not contained in original EMC artwork */
7613 if (element == EL_EMC_FAKE_ACID)
7616 if (g_em->bitmap != debug_bitmap ||
7617 g_em->src_x != debug_src_x ||
7618 g_em->src_y != debug_src_y ||
7619 g_em->src_offset_x != 0 ||
7620 g_em->src_offset_y != 0 ||
7621 g_em->dst_offset_x != 0 ||
7622 g_em->dst_offset_y != 0 ||
7623 g_em->width != TILEX ||
7624 g_em->height != TILEY)
7626 static int last_i = -1;
7634 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7635 i, element, element_info[element].token_name,
7636 element_action_info[effective_action].suffix, direction);
7638 if (element != effective_element)
7639 printf(" [%d ('%s')]",
7641 element_info[effective_element].token_name);
7645 if (g_em->bitmap != debug_bitmap)
7646 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7647 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7649 if (g_em->src_x != debug_src_x ||
7650 g_em->src_y != debug_src_y)
7651 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7652 j, (is_backside ? 'B' : 'F'),
7653 g_em->src_x, g_em->src_y,
7654 g_em->src_x / 32, g_em->src_y / 32,
7655 debug_src_x, debug_src_y,
7656 debug_src_x / 32, debug_src_y / 32);
7658 if (g_em->src_offset_x != 0 ||
7659 g_em->src_offset_y != 0 ||
7660 g_em->dst_offset_x != 0 ||
7661 g_em->dst_offset_y != 0)
7662 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7664 g_em->src_offset_x, g_em->src_offset_y,
7665 g_em->dst_offset_x, g_em->dst_offset_y);
7667 if (g_em->width != TILEX ||
7668 g_em->height != TILEY)
7669 printf(" %d (%d): size %d,%d should be %d,%d\n",
7671 g_em->width, g_em->height, TILEX, TILEY);
7673 num_em_gfx_errors++;
7680 for (i = 0; i < TILE_MAX; i++)
7682 for (j = 0; j < 8; j++)
7684 int element = object_mapping[i].element_rnd;
7685 int action = object_mapping[i].action;
7686 int direction = object_mapping[i].direction;
7687 boolean is_backside = object_mapping[i].is_backside;
7688 int graphic_action = el_act_dir2img(element, action, direction);
7689 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7691 if ((action == ACTION_SMASHED_BY_ROCK ||
7692 action == ACTION_SMASHED_BY_SPRING ||
7693 action == ACTION_EATING) &&
7694 graphic_action == graphic_default)
7696 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7697 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7698 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7699 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7702 /* no separate animation for "smashed by rock" -- use rock instead */
7703 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7704 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7706 g_em->bitmap = g_xx->bitmap;
7707 g_em->src_x = g_xx->src_x;
7708 g_em->src_y = g_xx->src_y;
7709 g_em->src_offset_x = g_xx->src_offset_x;
7710 g_em->src_offset_y = g_xx->src_offset_y;
7711 g_em->dst_offset_x = g_xx->dst_offset_x;
7712 g_em->dst_offset_y = g_xx->dst_offset_y;
7713 g_em->width = g_xx->width;
7714 g_em->height = g_xx->height;
7715 g_em->unique_identifier = g_xx->unique_identifier;
7718 g_em->preserve_background = TRUE;
7723 for (p = 0; p < MAX_PLAYERS; p++)
7725 for (i = 0; i < SPR_MAX; i++)
7727 int element = player_mapping[p][i].element_rnd;
7728 int action = player_mapping[p][i].action;
7729 int direction = player_mapping[p][i].direction;
7731 for (j = 0; j < 8; j++)
7733 int effective_element = element;
7734 int effective_action = action;
7735 int graphic = (direction == MV_NONE ?
7736 el_act2img(effective_element, effective_action) :
7737 el_act_dir2img(effective_element, effective_action,
7739 struct GraphicInfo *g = &graphic_info[graphic];
7740 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7746 Bitmap *debug_bitmap = g_em->bitmap;
7747 int debug_src_x = g_em->src_x;
7748 int debug_src_y = g_em->src_y;
7751 int frame = getAnimationFrame(g->anim_frames,
7754 g->anim_start_frame,
7757 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7759 g_em->bitmap = src_bitmap;
7760 g_em->src_x = src_x;
7761 g_em->src_y = src_y;
7762 g_em->src_offset_x = 0;
7763 g_em->src_offset_y = 0;
7764 g_em->dst_offset_x = 0;
7765 g_em->dst_offset_y = 0;
7766 g_em->width = TILEX;
7767 g_em->height = TILEY;
7771 /* skip check for EMC elements not contained in original EMC artwork */
7772 if (element == EL_PLAYER_3 ||
7773 element == EL_PLAYER_4)
7776 if (g_em->bitmap != debug_bitmap ||
7777 g_em->src_x != debug_src_x ||
7778 g_em->src_y != debug_src_y)
7780 static int last_i = -1;
7788 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7789 p, i, element, element_info[element].token_name,
7790 element_action_info[effective_action].suffix, direction);
7792 if (element != effective_element)
7793 printf(" [%d ('%s')]",
7795 element_info[effective_element].token_name);
7799 if (g_em->bitmap != debug_bitmap)
7800 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7801 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7803 if (g_em->src_x != debug_src_x ||
7804 g_em->src_y != debug_src_y)
7805 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7807 g_em->src_x, g_em->src_y,
7808 g_em->src_x / 32, g_em->src_y / 32,
7809 debug_src_x, debug_src_y,
7810 debug_src_x / 32, debug_src_y / 32);
7812 num_em_gfx_errors++;
7822 printf("::: [%d errors found]\n", num_em_gfx_errors);
7828 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7829 boolean any_player_moving,
7830 boolean any_player_snapping,
7831 boolean any_player_dropping)
7833 static boolean player_was_waiting = TRUE;
7835 if (frame == 0 && !any_player_dropping)
7837 if (!player_was_waiting)
7839 if (!SaveEngineSnapshotToList())
7842 player_was_waiting = TRUE;
7845 else if (any_player_moving || any_player_snapping || any_player_dropping)
7847 player_was_waiting = FALSE;
7851 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7852 boolean murphy_is_dropping)
7854 static boolean player_was_waiting = TRUE;
7856 if (murphy_is_waiting)
7858 if (!player_was_waiting)
7860 if (!SaveEngineSnapshotToList())
7863 player_was_waiting = TRUE;
7868 player_was_waiting = FALSE;
7872 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7873 boolean any_player_moving,
7874 boolean any_player_snapping,
7875 boolean any_player_dropping)
7877 if (tape.single_step && tape.recording && !tape.pausing)
7878 if (frame == 0 && !any_player_dropping)
7879 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7881 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7882 any_player_snapping, any_player_dropping);
7885 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7886 boolean murphy_is_dropping)
7888 if (tape.single_step && tape.recording && !tape.pausing)
7889 if (murphy_is_waiting)
7890 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7892 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7895 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7896 int graphic, int sync_frame, int x, int y)
7898 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7900 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7903 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7905 return (IS_NEXT_FRAME(sync_frame, graphic));
7908 int getGraphicInfo_Delay(int graphic)
7910 return graphic_info[graphic].anim_delay;
7913 void PlayMenuSoundExt(int sound)
7915 if (sound == SND_UNDEFINED)
7918 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7919 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7922 if (IS_LOOP_SOUND(sound))
7923 PlaySoundLoop(sound);
7928 void PlayMenuSound()
7930 PlayMenuSoundExt(menu.sound[game_status]);
7933 void PlayMenuSoundStereo(int sound, int stereo_position)
7935 if (sound == SND_UNDEFINED)
7938 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7939 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7942 if (IS_LOOP_SOUND(sound))
7943 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7945 PlaySoundStereo(sound, stereo_position);
7948 void PlayMenuSoundIfLoopExt(int sound)
7950 if (sound == SND_UNDEFINED)
7953 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7954 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7957 if (IS_LOOP_SOUND(sound))
7958 PlaySoundLoop(sound);
7961 void PlayMenuSoundIfLoop()
7963 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7966 void PlayMenuMusicExt(int music)
7968 if (music == MUS_UNDEFINED)
7971 if (!setup.sound_music)
7977 void PlayMenuMusic()
7979 PlayMenuMusicExt(menu.music[game_status]);
7982 void PlaySoundActivating()
7985 PlaySound(SND_MENU_ITEM_ACTIVATING);
7989 void PlaySoundSelecting()
7992 PlaySound(SND_MENU_ITEM_SELECTING);
7996 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7998 boolean change_fullscreen = (setup.fullscreen !=
7999 video.fullscreen_enabled);
8000 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8001 !strEqual(setup.fullscreen_mode,
8002 video.fullscreen_mode_current));
8003 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8004 setup.window_scaling_percent !=
8005 video.window_scaling_percent);
8007 if (change_window_scaling_percent && video.fullscreen_enabled)
8010 if (!change_window_scaling_percent && !video.fullscreen_available)
8013 #if defined(TARGET_SDL2)
8014 if (change_window_scaling_percent)
8016 SDLSetWindowScaling(setup.window_scaling_percent);
8020 else if (change_fullscreen)
8022 SDLSetWindowFullscreen(setup.fullscreen);
8024 /* set setup value according to successfully changed fullscreen mode */
8025 setup.fullscreen = video.fullscreen_enabled;
8031 if (change_fullscreen ||
8032 change_fullscreen_mode ||
8033 change_window_scaling_percent)
8035 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8037 /* save backbuffer content which gets lost when toggling fullscreen mode */
8038 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8040 if (change_fullscreen_mode)
8042 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8043 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8046 if (change_window_scaling_percent)
8048 /* keep window mode, but change window scaling */
8049 video.fullscreen_enabled = TRUE; /* force new window scaling */
8052 /* toggle fullscreen */
8053 ChangeVideoModeIfNeeded(setup.fullscreen);
8055 /* set setup value according to successfully changed fullscreen mode */
8056 setup.fullscreen = video.fullscreen_enabled;
8058 /* restore backbuffer content from temporary backbuffer backup bitmap */
8059 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8061 FreeBitmap(tmp_backbuffer);
8063 /* update visible window/screen */
8064 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8068 void JoinRectangles(int *x, int *y, int *width, int *height,
8069 int x2, int y2, int width2, int height2)
8071 // do not join with "off-screen" rectangle
8072 if (x2 == -1 || y2 == -1)
8077 *width = MAX(*width, width2);
8078 *height = MAX(*height, height2);
8081 void ChangeViewportPropertiesIfNeeded()
8083 int gfx_game_mode = game_status;
8084 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8086 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8087 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8088 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8089 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8090 int border_size = vp_playfield->border_size;
8091 int new_sx = vp_playfield->x + border_size;
8092 int new_sy = vp_playfield->y + border_size;
8093 int new_sxsize = vp_playfield->width - 2 * border_size;
8094 int new_sysize = vp_playfield->height - 2 * border_size;
8095 int new_real_sx = vp_playfield->x;
8096 int new_real_sy = vp_playfield->y;
8097 int new_full_sxsize = vp_playfield->width;
8098 int new_full_sysize = vp_playfield->height;
8099 int new_dx = vp_door_1->x;
8100 int new_dy = vp_door_1->y;
8101 int new_dxsize = vp_door_1->width;
8102 int new_dysize = vp_door_1->height;
8103 int new_vx = vp_door_2->x;
8104 int new_vy = vp_door_2->y;
8105 int new_vxsize = vp_door_2->width;
8106 int new_vysize = vp_door_2->height;
8107 int new_ex = vp_door_3->x;
8108 int new_ey = vp_door_3->y;
8109 int new_exsize = vp_door_3->width;
8110 int new_eysize = vp_door_3->height;
8111 int new_tilesize_var =
8112 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8114 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8115 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8116 int new_scr_fieldx = new_sxsize / tilesize;
8117 int new_scr_fieldy = new_sysize / tilesize;
8118 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8119 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8120 boolean init_gfx_buffers = FALSE;
8121 boolean init_video_buffer = FALSE;
8122 boolean init_gadgets_and_toons = FALSE;
8123 boolean init_em_graphics = FALSE;
8125 if (viewport.window.width != WIN_XSIZE ||
8126 viewport.window.height != WIN_YSIZE)
8128 WIN_XSIZE = viewport.window.width;
8129 WIN_YSIZE = viewport.window.height;
8131 init_video_buffer = TRUE;
8132 init_gfx_buffers = TRUE;
8134 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8137 if (new_scr_fieldx != SCR_FIELDX ||
8138 new_scr_fieldy != SCR_FIELDY)
8140 /* this always toggles between MAIN and GAME when using small tile size */
8142 SCR_FIELDX = new_scr_fieldx;
8143 SCR_FIELDY = new_scr_fieldy;
8145 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8156 new_sxsize != SXSIZE ||
8157 new_sysize != SYSIZE ||
8158 new_dxsize != DXSIZE ||
8159 new_dysize != DYSIZE ||
8160 new_vxsize != VXSIZE ||
8161 new_vysize != VYSIZE ||
8162 new_exsize != EXSIZE ||
8163 new_eysize != EYSIZE ||
8164 new_real_sx != REAL_SX ||
8165 new_real_sy != REAL_SY ||
8166 new_full_sxsize != FULL_SXSIZE ||
8167 new_full_sysize != FULL_SYSIZE ||
8168 new_tilesize_var != TILESIZE_VAR
8171 // ------------------------------------------------------------------------
8172 // determine next fading area for changed viewport definitions
8173 // ------------------------------------------------------------------------
8175 // start with current playfield area (default fading area)
8178 FADE_SXSIZE = FULL_SXSIZE;
8179 FADE_SYSIZE = FULL_SYSIZE;
8181 // add new playfield area if position or size has changed
8182 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8183 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8185 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8186 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8189 // add current and new door 1 area if position or size has changed
8190 if (new_dx != DX || new_dy != DY ||
8191 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8193 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8194 DX, DY, DXSIZE, DYSIZE);
8195 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8196 new_dx, new_dy, new_dxsize, new_dysize);
8199 // add current and new door 2 area if position or size has changed
8200 if (new_dx != VX || new_dy != VY ||
8201 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8203 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8204 VX, VY, VXSIZE, VYSIZE);
8205 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8206 new_vx, new_vy, new_vxsize, new_vysize);
8209 // ------------------------------------------------------------------------
8210 // handle changed tile size
8211 // ------------------------------------------------------------------------
8213 if (new_tilesize_var != TILESIZE_VAR)
8215 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8217 // changing tile size invalidates scroll values of engine snapshots
8218 FreeEngineSnapshotSingle();
8220 // changing tile size requires update of graphic mapping for EM engine
8221 init_em_graphics = TRUE;
8232 SXSIZE = new_sxsize;
8233 SYSIZE = new_sysize;
8234 DXSIZE = new_dxsize;
8235 DYSIZE = new_dysize;
8236 VXSIZE = new_vxsize;
8237 VYSIZE = new_vysize;
8238 EXSIZE = new_exsize;
8239 EYSIZE = new_eysize;
8240 REAL_SX = new_real_sx;
8241 REAL_SY = new_real_sy;
8242 FULL_SXSIZE = new_full_sxsize;
8243 FULL_SYSIZE = new_full_sysize;
8244 TILESIZE_VAR = new_tilesize_var;
8246 init_gfx_buffers = TRUE;
8247 init_gadgets_and_toons = TRUE;
8249 // printf("::: viewports: init_gfx_buffers\n");
8250 // printf("::: viewports: init_gadgets_and_toons\n");
8253 if (init_gfx_buffers)
8255 // printf("::: init_gfx_buffers\n");
8257 SCR_FIELDX = new_scr_fieldx_buffers;
8258 SCR_FIELDY = new_scr_fieldy_buffers;
8262 SCR_FIELDX = new_scr_fieldx;
8263 SCR_FIELDY = new_scr_fieldy;
8265 SetDrawDeactivationMask(REDRAW_NONE);
8266 SetDrawBackgroundMask(REDRAW_FIELD);
8269 if (init_video_buffer)
8271 // printf("::: init_video_buffer\n");
8273 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8276 if (init_gadgets_and_toons)
8278 // printf("::: init_gadgets_and_toons\n");
8284 if (init_em_graphics)
8286 InitGraphicInfo_EM();