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 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
692 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
694 return (graphic == IMG_UNDEFINED ? NULL :
695 graphic_info[graphic].bitmap != NULL || redefined ?
696 graphic_info[graphic].bitmap :
697 graphic_info[default_graphic].bitmap);
700 Bitmap *getBackgroundBitmap(int graphic)
702 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
705 Bitmap *getGlobalBorderBitmap(int graphic)
707 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
710 Bitmap *getGlobalBorderBitmapFromGameStatus()
712 int graphic = (game_status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
713 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
714 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
715 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
718 return getGlobalBorderBitmap(graphic);
721 void SetWindowBackgroundImageIfDefined(int graphic)
723 if (graphic_info[graphic].bitmap)
724 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
727 void SetMainBackgroundImageIfDefined(int graphic)
729 if (graphic_info[graphic].bitmap)
730 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
733 void SetDoorBackgroundImageIfDefined(int graphic)
735 if (graphic_info[graphic].bitmap)
736 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
739 void SetWindowBackgroundImage(int graphic)
741 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
744 void SetMainBackgroundImage(int graphic)
746 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
749 void SetDoorBackgroundImage(int graphic)
751 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
754 void SetPanelBackground()
756 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
758 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
759 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
761 SetDoorBackgroundBitmap(bitmap_db_panel);
764 void DrawBackground(int x, int y, int width, int height)
766 /* "drawto" might still point to playfield buffer here (hall of fame) */
767 ClearRectangleOnBackground(backbuffer, x, y, width, height);
769 if (IN_GFX_FIELD_FULL(x, y))
770 redraw_mask |= REDRAW_FIELD;
771 else if (IN_GFX_DOOR_1(x, y))
772 redraw_mask |= REDRAW_DOOR_1;
773 else if (IN_GFX_DOOR_2(x, y))
774 redraw_mask |= REDRAW_DOOR_2;
775 else if (IN_GFX_DOOR_3(x, y))
776 redraw_mask |= REDRAW_DOOR_3;
779 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
781 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
783 if (font->bitmap == NULL)
786 DrawBackground(x, y, width, height);
789 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
791 struct GraphicInfo *g = &graphic_info[graphic];
793 if (g->bitmap == NULL)
796 DrawBackground(x, y, width, height);
799 static int game_status_last = -1;
800 static Bitmap *global_border_bitmap_last = NULL;
801 static Bitmap *global_border_bitmap = NULL;
802 static int real_sx_last = -1, real_sy_last = -1;
803 static int full_sxsize_last = -1, full_sysize_last = -1;
804 static int dx_last = -1, dy_last = -1;
805 static int dxsize_last = -1, dysize_last = -1;
806 static int vx_last = -1, vy_last = -1;
807 static int vxsize_last = -1, vysize_last = -1;
809 boolean CheckIfGlobalBorderHasChanged()
811 // if game status has not changed, global border has not changed either
812 if (game_status == game_status_last)
815 // determine and store new global border bitmap for current game status
816 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
818 return (global_border_bitmap_last != global_border_bitmap);
821 boolean CheckIfGlobalBorderRedrawIsNeeded()
823 // if game status has not changed, nothing has to be redrawn
824 if (game_status == game_status_last)
827 // redraw if global screen border has changed
828 if (CheckIfGlobalBorderHasChanged())
831 // redraw if position or size of playfield area has changed
832 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
833 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
836 // redraw if position or size of door area has changed
837 if (dx_last != DX || dy_last != DY ||
838 dxsize_last != DXSIZE || dysize_last != DYSIZE)
841 // redraw if position or size of tape area has changed
842 if (vx_last != VX || vy_last != VY ||
843 vxsize_last != VXSIZE || vysize_last != VYSIZE)
849 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
852 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
854 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
857 void RedrawGlobalBorder()
859 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
861 RedrawGlobalBorderFromBitmap(bitmap);
863 redraw_mask = REDRAW_ALL;
866 static void RedrawGlobalBorderIfNeeded()
868 if (game_status == game_status_last)
871 // copy current draw buffer to later copy back areas that have not changed
872 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
874 if (CheckIfGlobalBorderRedrawIsNeeded())
876 // redraw global screen border (or clear, if defined to be empty)
877 RedrawGlobalBorderFromBitmap(global_border_bitmap);
879 // copy previous playfield and door areas, if they are defined on both
880 // previous and current screen and if they still have the same size
882 if (real_sx_last != -1 && real_sy_last != -1 &&
883 REAL_SX != -1 && REAL_SY != -1 &&
884 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
885 BlitBitmap(bitmap_db_store, backbuffer,
886 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
889 if (dx_last != -1 && dy_last != -1 &&
890 DX != -1 && DY != -1 &&
891 dxsize_last == DXSIZE && dysize_last == DYSIZE)
892 BlitBitmap(bitmap_db_store, backbuffer,
893 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
895 if (vx_last != -1 && vy_last != -1 &&
896 VX != -1 && VY != -1 &&
897 vxsize_last == VXSIZE && vysize_last == VYSIZE)
898 BlitBitmap(bitmap_db_store, backbuffer,
899 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
901 redraw_mask = REDRAW_ALL;
904 game_status_last = game_status;
906 global_border_bitmap_last = global_border_bitmap;
908 real_sx_last = REAL_SX;
909 real_sy_last = REAL_SY;
910 full_sxsize_last = FULL_SXSIZE;
911 full_sysize_last = FULL_SYSIZE;
914 dxsize_last = DXSIZE;
915 dysize_last = DYSIZE;
918 vxsize_last = VXSIZE;
919 vysize_last = VYSIZE;
924 RedrawGlobalBorderIfNeeded();
926 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
927 /* (when entering hall of fame after playing) */
928 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
930 /* !!! maybe this should be done before clearing the background !!! */
931 if (game_status == GAME_MODE_PLAYING)
933 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
934 SetDrawtoField(DRAW_FIELDBUFFER);
938 SetDrawtoField(DRAW_BACKBUFFER);
942 void MarkTileDirty(int x, int y)
944 redraw_mask |= REDRAW_FIELD;
947 void SetBorderElement()
951 BorderElement = EL_EMPTY;
953 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
955 for (x = 0; x < lev_fieldx; x++)
957 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
958 BorderElement = EL_STEELWALL;
960 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
966 void FloodFillLevel(int from_x, int from_y, int fill_element,
967 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
968 int max_fieldx, int max_fieldy)
972 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
973 static int safety = 0;
975 /* check if starting field still has the desired content */
976 if (field[from_x][from_y] == fill_element)
981 if (safety > max_fieldx * max_fieldy)
982 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
984 old_element = field[from_x][from_y];
985 field[from_x][from_y] = fill_element;
987 for (i = 0; i < 4; i++)
989 x = from_x + check[i][0];
990 y = from_y + check[i][1];
992 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
993 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
999 void SetRandomAnimationValue(int x, int y)
1001 gfx.anim_random_frame = GfxRandom[x][y];
1004 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
1006 /* animation synchronized with global frame counter, not move position */
1007 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1008 sync_frame = FrameCounter;
1010 return getAnimationFrame(graphic_info[graphic].anim_frames,
1011 graphic_info[graphic].anim_delay,
1012 graphic_info[graphic].anim_mode,
1013 graphic_info[graphic].anim_start_frame,
1017 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1018 Bitmap **bitmap, int *x, int *y,
1019 boolean get_backside)
1021 struct GraphicInfo *g = &graphic_info[graphic];
1022 Bitmap *src_bitmap = g->bitmap;
1023 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1024 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1025 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1027 // if no in-game graphics defined, always use standard graphic size
1028 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1029 tilesize = TILESIZE;
1031 if (tilesize == gfx.standard_tile_size)
1032 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1033 else if (tilesize == game.tile_size)
1034 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1036 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1038 if (g->offset_y == 0) /* frames are ordered horizontally */
1040 int max_width = g->anim_frames_per_line * g->width;
1041 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1043 src_x = pos % max_width;
1044 src_y = src_y % g->height + pos / max_width * g->height;
1046 else if (g->offset_x == 0) /* frames are ordered vertically */
1048 int max_height = g->anim_frames_per_line * g->height;
1049 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1051 src_x = src_x % g->width + pos / max_height * g->width;
1052 src_y = pos % max_height;
1054 else /* frames are ordered diagonally */
1056 src_x = src_x + frame * g->offset_x;
1057 src_y = src_y + frame * g->offset_y;
1060 *bitmap = src_bitmap;
1061 *x = src_x * tilesize / TILESIZE;
1062 *y = src_y * tilesize / TILESIZE;
1065 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1066 int *x, int *y, boolean get_backside)
1068 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1072 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1073 Bitmap **bitmap, int *x, int *y)
1075 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1078 void getFixedGraphicSource(int graphic, int frame,
1079 Bitmap **bitmap, int *x, int *y)
1081 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1084 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1086 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1089 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1090 int *x, int *y, boolean get_backside)
1092 struct GraphicInfo *g = &graphic_info[graphic];
1093 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1094 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1096 if (TILESIZE_VAR != TILESIZE)
1097 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1100 *bitmap = g->bitmap;
1102 if (g->offset_y == 0) /* frames are ordered horizontally */
1104 int max_width = g->anim_frames_per_line * g->width;
1105 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1107 *x = pos % max_width;
1108 *y = src_y % g->height + pos / max_width * g->height;
1110 else if (g->offset_x == 0) /* frames are ordered vertically */
1112 int max_height = g->anim_frames_per_line * g->height;
1113 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1115 *x = src_x % g->width + pos / max_height * g->width;
1116 *y = pos % max_height;
1118 else /* frames are ordered diagonally */
1120 *x = src_x + frame * g->offset_x;
1121 *y = src_y + frame * g->offset_y;
1125 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1127 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1130 void DrawGraphic(int x, int y, int graphic, int frame)
1133 if (!IN_SCR_FIELD(x, y))
1135 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1136 printf("DrawGraphic(): This should never happen!\n");
1141 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1144 MarkTileDirty(x, y);
1147 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1150 if (!IN_SCR_FIELD(x, y))
1152 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1153 printf("DrawGraphic(): This should never happen!\n");
1158 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1160 MarkTileDirty(x, y);
1163 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1169 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1171 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1174 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1180 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1181 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1184 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1187 if (!IN_SCR_FIELD(x, y))
1189 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1190 printf("DrawGraphicThruMask(): This should never happen!\n");
1195 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1198 MarkTileDirty(x, y);
1201 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1204 if (!IN_SCR_FIELD(x, y))
1206 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1207 printf("DrawGraphicThruMask(): This should never happen!\n");
1212 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1214 MarkTileDirty(x, y);
1217 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1223 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1225 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1229 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1230 int graphic, int frame)
1232 struct GraphicInfo *g = &graphic_info[graphic];
1236 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1238 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1242 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1244 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1246 MarkTileDirty(x / tilesize, y / tilesize);
1249 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1255 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1256 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1259 void DrawMiniGraphic(int x, int y, int graphic)
1261 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1262 MarkTileDirty(x / 2, y / 2);
1265 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1270 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1271 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1274 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1275 int graphic, int frame,
1276 int cut_mode, int mask_mode)
1281 int width = TILEX, height = TILEY;
1284 if (dx || dy) /* shifted graphic */
1286 if (x < BX1) /* object enters playfield from the left */
1293 else if (x > BX2) /* object enters playfield from the right */
1299 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1305 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1307 else if (dx) /* general horizontal movement */
1308 MarkTileDirty(x + SIGN(dx), y);
1310 if (y < BY1) /* object enters playfield from the top */
1312 if (cut_mode==CUT_BELOW) /* object completely above top border */
1320 else if (y > BY2) /* object enters playfield from the bottom */
1326 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1332 else if (dy > 0 && cut_mode == CUT_ABOVE)
1334 if (y == BY2) /* object completely above bottom border */
1340 MarkTileDirty(x, y + 1);
1341 } /* object leaves playfield to the bottom */
1342 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1344 else if (dy) /* general vertical movement */
1345 MarkTileDirty(x, y + SIGN(dy));
1349 if (!IN_SCR_FIELD(x, y))
1351 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1352 printf("DrawGraphicShifted(): This should never happen!\n");
1357 width = width * TILESIZE_VAR / TILESIZE;
1358 height = height * TILESIZE_VAR / TILESIZE;
1359 cx = cx * TILESIZE_VAR / TILESIZE;
1360 cy = cy * TILESIZE_VAR / TILESIZE;
1361 dx = dx * TILESIZE_VAR / TILESIZE;
1362 dy = dy * TILESIZE_VAR / TILESIZE;
1364 if (width > 0 && height > 0)
1366 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1371 dst_x = FX + x * TILEX_VAR + dx;
1372 dst_y = FY + y * TILEY_VAR + dy;
1374 if (mask_mode == USE_MASKING)
1375 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1378 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1381 MarkTileDirty(x, y);
1385 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1386 int graphic, int frame,
1387 int cut_mode, int mask_mode)
1392 int width = TILEX_VAR, height = TILEY_VAR;
1395 int x2 = x + SIGN(dx);
1396 int y2 = y + SIGN(dy);
1398 /* movement with two-tile animations must be sync'ed with movement position,
1399 not with current GfxFrame (which can be higher when using slow movement) */
1400 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1401 int anim_frames = graphic_info[graphic].anim_frames;
1403 /* (we also need anim_delay here for movement animations with less frames) */
1404 int anim_delay = graphic_info[graphic].anim_delay;
1405 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1407 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1408 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1410 /* re-calculate animation frame for two-tile movement animation */
1411 frame = getGraphicAnimationFrame(graphic, sync_frame);
1413 /* check if movement start graphic inside screen area and should be drawn */
1414 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1416 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1418 dst_x = FX + x1 * TILEX_VAR;
1419 dst_y = FY + y1 * TILEY_VAR;
1421 if (mask_mode == USE_MASKING)
1422 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1425 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1428 MarkTileDirty(x1, y1);
1431 /* check if movement end graphic inside screen area and should be drawn */
1432 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1434 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1436 dst_x = FX + x2 * TILEX_VAR;
1437 dst_y = FY + y2 * TILEY_VAR;
1439 if (mask_mode == USE_MASKING)
1440 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1443 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1446 MarkTileDirty(x2, y2);
1450 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1451 int graphic, int frame,
1452 int cut_mode, int mask_mode)
1456 DrawGraphic(x, y, graphic, frame);
1461 if (graphic_info[graphic].double_movement) /* EM style movement images */
1462 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1464 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1467 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1468 int frame, int cut_mode)
1470 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1473 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1474 int cut_mode, int mask_mode)
1476 int lx = LEVELX(x), ly = LEVELY(y);
1480 if (IN_LEV_FIELD(lx, ly))
1482 SetRandomAnimationValue(lx, ly);
1484 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1485 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1487 /* do not use double (EM style) movement graphic when not moving */
1488 if (graphic_info[graphic].double_movement && !dx && !dy)
1490 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1491 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1494 else /* border element */
1496 graphic = el2img(element);
1497 frame = getGraphicAnimationFrame(graphic, -1);
1500 if (element == EL_EXPANDABLE_WALL)
1502 boolean left_stopped = FALSE, right_stopped = FALSE;
1504 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1505 left_stopped = TRUE;
1506 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1507 right_stopped = TRUE;
1509 if (left_stopped && right_stopped)
1511 else if (left_stopped)
1513 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1514 frame = graphic_info[graphic].anim_frames - 1;
1516 else if (right_stopped)
1518 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1519 frame = graphic_info[graphic].anim_frames - 1;
1524 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1525 else if (mask_mode == USE_MASKING)
1526 DrawGraphicThruMask(x, y, graphic, frame);
1528 DrawGraphic(x, y, graphic, frame);
1531 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1532 int cut_mode, int mask_mode)
1534 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1535 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1536 cut_mode, mask_mode);
1539 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1542 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1545 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1548 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1551 void DrawLevelElementThruMask(int x, int y, int element)
1553 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1556 void DrawLevelFieldThruMask(int x, int y)
1558 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1561 /* !!! implementation of quicksand is totally broken !!! */
1562 #define IS_CRUMBLED_TILE(x, y, e) \
1563 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1564 !IS_MOVING(x, y) || \
1565 (e) == EL_QUICKSAND_EMPTYING || \
1566 (e) == EL_QUICKSAND_FAST_EMPTYING))
1568 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1573 int width, height, cx, cy;
1574 int sx = SCREENX(x), sy = SCREENY(y);
1575 int crumbled_border_size = graphic_info[graphic].border_size;
1578 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1580 for (i = 1; i < 4; i++)
1582 int dxx = (i & 1 ? dx : 0);
1583 int dyy = (i & 2 ? dy : 0);
1586 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1589 /* check if neighbour field is of same crumble type */
1590 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1591 graphic_info[graphic].class ==
1592 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1594 /* return if check prevents inner corner */
1595 if (same == (dxx == dx && dyy == dy))
1599 /* if we reach this point, we have an inner corner */
1601 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1603 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1604 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1605 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1606 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1608 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1609 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1612 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1617 int width, height, bx, by, cx, cy;
1618 int sx = SCREENX(x), sy = SCREENY(y);
1619 int crumbled_border_size = graphic_info[graphic].border_size;
1620 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1621 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1624 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1626 /* draw simple, sloppy, non-corner-accurate crumbled border */
1628 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1629 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1630 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1631 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1633 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1634 FX + sx * TILEX_VAR + cx,
1635 FY + sy * TILEY_VAR + cy);
1637 /* (remaining middle border part must be at least as big as corner part) */
1638 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1639 crumbled_border_size >= TILESIZE / 3)
1642 /* correct corners of crumbled border, if needed */
1644 for (i = -1; i <= 1; i += 2)
1646 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1647 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1648 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1651 /* check if neighbour field is of same crumble type */
1652 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1653 graphic_info[graphic].class ==
1654 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1656 /* no crumbled corner, but continued crumbled border */
1658 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1659 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1660 int b1 = (i == 1 ? crumbled_border_size_var :
1661 TILESIZE_VAR - 2 * crumbled_border_size_var);
1663 width = crumbled_border_size_var;
1664 height = crumbled_border_size_var;
1666 if (dir == 1 || dir == 2)
1681 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1683 FX + sx * TILEX_VAR + cx,
1684 FY + sy * TILEY_VAR + cy);
1689 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1691 int sx = SCREENX(x), sy = SCREENY(y);
1694 static int xy[4][2] =
1702 if (!IN_LEV_FIELD(x, y))
1705 element = TILE_GFX_ELEMENT(x, y);
1707 /* crumble field itself */
1708 if (IS_CRUMBLED_TILE(x, y, element))
1710 if (!IN_SCR_FIELD(sx, sy))
1713 for (i = 0; i < 4; i++)
1715 int xx = x + xy[i][0];
1716 int yy = y + xy[i][1];
1718 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1721 /* check if neighbour field is of same crumble type */
1722 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1723 graphic_info[graphic].class ==
1724 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1727 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1730 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1731 graphic_info[graphic].anim_frames == 2)
1733 for (i = 0; i < 4; i++)
1735 int dx = (i & 1 ? +1 : -1);
1736 int dy = (i & 2 ? +1 : -1);
1738 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1742 MarkTileDirty(sx, sy);
1744 else /* center field not crumbled -- crumble neighbour fields */
1746 for (i = 0; i < 4; i++)
1748 int xx = x + xy[i][0];
1749 int yy = y + xy[i][1];
1750 int sxx = sx + xy[i][0];
1751 int syy = sy + xy[i][1];
1753 if (!IN_LEV_FIELD(xx, yy) ||
1754 !IN_SCR_FIELD(sxx, syy))
1757 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1760 element = TILE_GFX_ELEMENT(xx, yy);
1762 if (!IS_CRUMBLED_TILE(xx, yy, element))
1765 graphic = el_act2crm(element, ACTION_DEFAULT);
1767 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1769 MarkTileDirty(sxx, syy);
1774 void DrawLevelFieldCrumbled(int x, int y)
1778 if (!IN_LEV_FIELD(x, y))
1781 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1782 GfxElement[x][y] != EL_UNDEFINED &&
1783 GFX_CRUMBLED(GfxElement[x][y]))
1785 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1790 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1792 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1795 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1798 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1799 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1800 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1801 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1802 int sx = SCREENX(x), sy = SCREENY(y);
1804 DrawGraphic(sx, sy, graphic1, frame1);
1805 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1808 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1810 int sx = SCREENX(x), sy = SCREENY(y);
1811 static int xy[4][2] =
1820 for (i = 0; i < 4; i++)
1822 int xx = x + xy[i][0];
1823 int yy = y + xy[i][1];
1824 int sxx = sx + xy[i][0];
1825 int syy = sy + xy[i][1];
1827 if (!IN_LEV_FIELD(xx, yy) ||
1828 !IN_SCR_FIELD(sxx, syy) ||
1829 !GFX_CRUMBLED(Feld[xx][yy]) ||
1833 DrawLevelField(xx, yy);
1837 static int getBorderElement(int x, int y)
1841 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1842 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1843 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1844 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1845 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1846 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1847 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1849 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1850 int steel_position = (x == -1 && y == -1 ? 0 :
1851 x == lev_fieldx && y == -1 ? 1 :
1852 x == -1 && y == lev_fieldy ? 2 :
1853 x == lev_fieldx && y == lev_fieldy ? 3 :
1854 x == -1 || x == lev_fieldx ? 4 :
1855 y == -1 || y == lev_fieldy ? 5 : 6);
1857 return border[steel_position][steel_type];
1860 void DrawScreenElement(int x, int y, int element)
1862 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1863 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1866 void DrawLevelElement(int x, int y, int element)
1868 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1869 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1872 void DrawScreenField(int x, int y)
1874 int lx = LEVELX(x), ly = LEVELY(y);
1875 int element, content;
1877 if (!IN_LEV_FIELD(lx, ly))
1879 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1882 element = getBorderElement(lx, ly);
1884 DrawScreenElement(x, y, element);
1889 element = Feld[lx][ly];
1890 content = Store[lx][ly];
1892 if (IS_MOVING(lx, ly))
1894 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1895 boolean cut_mode = NO_CUTTING;
1897 if (element == EL_QUICKSAND_EMPTYING ||
1898 element == EL_QUICKSAND_FAST_EMPTYING ||
1899 element == EL_MAGIC_WALL_EMPTYING ||
1900 element == EL_BD_MAGIC_WALL_EMPTYING ||
1901 element == EL_DC_MAGIC_WALL_EMPTYING ||
1902 element == EL_AMOEBA_DROPPING)
1903 cut_mode = CUT_ABOVE;
1904 else if (element == EL_QUICKSAND_FILLING ||
1905 element == EL_QUICKSAND_FAST_FILLING ||
1906 element == EL_MAGIC_WALL_FILLING ||
1907 element == EL_BD_MAGIC_WALL_FILLING ||
1908 element == EL_DC_MAGIC_WALL_FILLING)
1909 cut_mode = CUT_BELOW;
1911 if (cut_mode == CUT_ABOVE)
1912 DrawScreenElement(x, y, element);
1914 DrawScreenElement(x, y, EL_EMPTY);
1917 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1918 else if (cut_mode == NO_CUTTING)
1919 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1922 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1924 if (cut_mode == CUT_BELOW &&
1925 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1926 DrawLevelElement(lx, ly + 1, element);
1929 if (content == EL_ACID)
1931 int dir = MovDir[lx][ly];
1932 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1933 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1935 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1938 else if (IS_BLOCKED(lx, ly))
1943 boolean cut_mode = NO_CUTTING;
1944 int element_old, content_old;
1946 Blocked2Moving(lx, ly, &oldx, &oldy);
1949 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1950 MovDir[oldx][oldy] == MV_RIGHT);
1952 element_old = Feld[oldx][oldy];
1953 content_old = Store[oldx][oldy];
1955 if (element_old == EL_QUICKSAND_EMPTYING ||
1956 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1957 element_old == EL_MAGIC_WALL_EMPTYING ||
1958 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1959 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1960 element_old == EL_AMOEBA_DROPPING)
1961 cut_mode = CUT_ABOVE;
1963 DrawScreenElement(x, y, EL_EMPTY);
1966 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1968 else if (cut_mode == NO_CUTTING)
1969 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1972 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1975 else if (IS_DRAWABLE(element))
1976 DrawScreenElement(x, y, element);
1978 DrawScreenElement(x, y, EL_EMPTY);
1981 void DrawLevelField(int x, int y)
1983 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1984 DrawScreenField(SCREENX(x), SCREENY(y));
1985 else if (IS_MOVING(x, y))
1989 Moving2Blocked(x, y, &newx, &newy);
1990 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1991 DrawScreenField(SCREENX(newx), SCREENY(newy));
1993 else if (IS_BLOCKED(x, y))
1997 Blocked2Moving(x, y, &oldx, &oldy);
1998 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
1999 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2003 void DrawSizedElement(int x, int y, int element, int tilesize)
2007 graphic = el2edimg(element);
2008 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2011 void DrawMiniElement(int x, int y, int element)
2015 graphic = el2edimg(element);
2016 DrawMiniGraphic(x, y, graphic);
2019 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2022 int x = sx + scroll_x, y = sy + scroll_y;
2024 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2025 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2026 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2027 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2029 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2032 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2034 int x = sx + scroll_x, y = sy + scroll_y;
2036 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2037 DrawMiniElement(sx, sy, EL_EMPTY);
2038 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2039 DrawMiniElement(sx, sy, Feld[x][y]);
2041 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2044 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2045 int x, int y, int xsize, int ysize,
2046 int tile_width, int tile_height)
2050 int dst_x = startx + x * tile_width;
2051 int dst_y = starty + y * tile_height;
2052 int width = graphic_info[graphic].width;
2053 int height = graphic_info[graphic].height;
2054 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2055 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2056 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2057 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2058 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2059 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2060 boolean draw_masked = graphic_info[graphic].draw_masked;
2062 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2064 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2066 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2070 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2071 inner_sx + (x - 1) * tile_width % inner_width);
2072 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2073 inner_sy + (y - 1) * tile_height % inner_height);
2076 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2079 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2083 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2084 int x, int y, int xsize, int ysize, int font_nr)
2086 int font_width = getFontWidth(font_nr);
2087 int font_height = getFontHeight(font_nr);
2089 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2090 font_width, font_height);
2093 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2095 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2096 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2097 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2098 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2099 boolean no_delay = (tape.warp_forward);
2100 unsigned int anim_delay = 0;
2101 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2102 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2103 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2104 int font_width = getFontWidth(font_nr);
2105 int font_height = getFontHeight(font_nr);
2106 int max_xsize = level.envelope[envelope_nr].xsize;
2107 int max_ysize = level.envelope[envelope_nr].ysize;
2108 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2109 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2110 int xend = max_xsize;
2111 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2112 int xstep = (xstart < xend ? 1 : 0);
2113 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2115 int end = MAX(xend - xstart, yend - ystart);
2118 for (i = start; i <= end; i++)
2120 int last_frame = end; // last frame of this "for" loop
2121 int x = xstart + i * xstep;
2122 int y = ystart + i * ystep;
2123 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2124 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2125 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2126 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2129 SetDrawtoField(DRAW_FIELDBUFFER);
2131 BlitScreenToBitmap(backbuffer);
2133 SetDrawtoField(DRAW_BACKBUFFER);
2135 for (yy = 0; yy < ysize; yy++)
2136 for (xx = 0; xx < xsize; xx++)
2137 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2139 DrawTextBuffer(sx + font_width, sy + font_height,
2140 level.envelope[envelope_nr].text, font_nr, max_xsize,
2141 xsize - 2, ysize - 2, 0, mask_mode,
2142 level.envelope[envelope_nr].autowrap,
2143 level.envelope[envelope_nr].centered, FALSE);
2145 redraw_mask |= REDRAW_FIELD;
2148 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2152 void ShowEnvelope(int envelope_nr)
2154 int element = EL_ENVELOPE_1 + envelope_nr;
2155 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2156 int sound_opening = element_info[element].sound[ACTION_OPENING];
2157 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2158 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2159 boolean no_delay = (tape.warp_forward);
2160 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2161 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2162 int anim_mode = graphic_info[graphic].anim_mode;
2163 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2164 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2166 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2168 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2170 if (anim_mode == ANIM_DEFAULT)
2171 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2173 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2176 Delay(wait_delay_value);
2178 WaitForEventToContinue();
2180 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2182 if (anim_mode != ANIM_NONE)
2183 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2185 if (anim_mode == ANIM_DEFAULT)
2186 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2188 game.envelope_active = FALSE;
2190 SetDrawtoField(DRAW_FIELDBUFFER);
2192 redraw_mask |= REDRAW_FIELD;
2196 static void setRequestBasePosition(int *x, int *y)
2198 int sx_base, sy_base;
2200 if (request.x != -1)
2201 sx_base = request.x;
2202 else if (request.align == ALIGN_LEFT)
2204 else if (request.align == ALIGN_RIGHT)
2205 sx_base = SX + SXSIZE;
2207 sx_base = SX + SXSIZE / 2;
2209 if (request.y != -1)
2210 sy_base = request.y;
2211 else if (request.valign == VALIGN_TOP)
2213 else if (request.valign == VALIGN_BOTTOM)
2214 sy_base = SY + SYSIZE;
2216 sy_base = SY + SYSIZE / 2;
2222 static void setRequestPositionExt(int *x, int *y, int width, int height,
2223 boolean add_border_size)
2225 int border_size = request.border_size;
2226 int sx_base, sy_base;
2229 setRequestBasePosition(&sx_base, &sy_base);
2231 if (request.align == ALIGN_LEFT)
2233 else if (request.align == ALIGN_RIGHT)
2234 sx = sx_base - width;
2236 sx = sx_base - width / 2;
2238 if (request.valign == VALIGN_TOP)
2240 else if (request.valign == VALIGN_BOTTOM)
2241 sy = sy_base - height;
2243 sy = sy_base - height / 2;
2245 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2246 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2248 if (add_border_size)
2258 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2260 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2263 void DrawEnvelopeRequest(char *text)
2265 char *text_final = text;
2266 char *text_door_style = NULL;
2267 int graphic = IMG_BACKGROUND_REQUEST;
2268 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2269 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2270 int font_nr = FONT_REQUEST;
2271 int font_width = getFontWidth(font_nr);
2272 int font_height = getFontHeight(font_nr);
2273 int border_size = request.border_size;
2274 int line_spacing = request.line_spacing;
2275 int line_height = font_height + line_spacing;
2276 int text_width = request.width - 2 * border_size;
2277 int text_height = request.height - 2 * border_size;
2278 int line_length = text_width / font_width;
2279 int max_lines = text_height / line_height;
2280 int width = request.width;
2281 int height = request.height;
2282 int tile_size = request.step_offset;
2283 int x_steps = width / tile_size;
2284 int y_steps = height / tile_size;
2288 if (request.wrap_single_words)
2290 char *src_text_ptr, *dst_text_ptr;
2292 text_door_style = checked_malloc(2 * strlen(text) + 1);
2294 src_text_ptr = text;
2295 dst_text_ptr = text_door_style;
2297 while (*src_text_ptr)
2299 if (*src_text_ptr == ' ' ||
2300 *src_text_ptr == '?' ||
2301 *src_text_ptr == '!')
2302 *dst_text_ptr++ = '\n';
2304 if (*src_text_ptr != ' ')
2305 *dst_text_ptr++ = *src_text_ptr;
2310 *dst_text_ptr = '\0';
2312 text_final = text_door_style;
2315 setRequestPosition(&sx, &sy, FALSE);
2317 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2319 for (y = 0; y < y_steps; y++)
2320 for (x = 0; x < x_steps; x++)
2321 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2322 x, y, x_steps, y_steps,
2323 tile_size, tile_size);
2325 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2326 line_length, -1, max_lines, line_spacing, mask_mode,
2327 request.autowrap, request.centered, FALSE);
2329 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2330 RedrawGadget(tool_gadget[i]);
2332 // store readily prepared envelope request for later use when animating
2333 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2335 if (text_door_style)
2336 free(text_door_style);
2339 void AnimateEnvelopeRequest(int anim_mode, int action)
2341 int graphic = IMG_BACKGROUND_REQUEST;
2342 boolean draw_masked = graphic_info[graphic].draw_masked;
2343 int delay_value_normal = request.step_delay;
2344 int delay_value_fast = delay_value_normal / 2;
2345 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2346 boolean no_delay = (tape.warp_forward);
2347 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2348 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2349 unsigned int anim_delay = 0;
2351 int tile_size = request.step_offset;
2352 int max_xsize = request.width / tile_size;
2353 int max_ysize = request.height / tile_size;
2354 int max_xsize_inner = max_xsize - 2;
2355 int max_ysize_inner = max_ysize - 2;
2357 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2358 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2359 int xend = max_xsize_inner;
2360 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2361 int xstep = (xstart < xend ? 1 : 0);
2362 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2364 int end = MAX(xend - xstart, yend - ystart);
2367 if (setup.quick_doors)
2375 if (action == ACTION_OPENING)
2376 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2377 else if (action == ACTION_CLOSING)
2378 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2381 for (i = start; i <= end; i++)
2383 int last_frame = end; // last frame of this "for" loop
2384 int x = xstart + i * xstep;
2385 int y = ystart + i * ystep;
2386 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2387 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2388 int xsize_size_left = (xsize - 1) * tile_size;
2389 int ysize_size_top = (ysize - 1) * tile_size;
2390 int max_xsize_pos = (max_xsize - 1) * tile_size;
2391 int max_ysize_pos = (max_ysize - 1) * tile_size;
2392 int width = xsize * tile_size;
2393 int height = ysize * tile_size;
2398 setRequestPosition(&src_x, &src_y, FALSE);
2399 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2401 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2403 for (yy = 0; yy < 2; yy++)
2405 for (xx = 0; xx < 2; xx++)
2407 int src_xx = src_x + xx * max_xsize_pos;
2408 int src_yy = src_y + yy * max_ysize_pos;
2409 int dst_xx = dst_x + xx * xsize_size_left;
2410 int dst_yy = dst_y + yy * ysize_size_top;
2411 int xx_size = (xx ? tile_size : xsize_size_left);
2412 int yy_size = (yy ? tile_size : ysize_size_top);
2415 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2416 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2418 BlitBitmap(bitmap_db_cross, backbuffer,
2419 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2423 redraw_mask |= REDRAW_FIELD;
2428 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2432 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2434 int last_game_status = game_status; /* save current game status */
2435 int graphic = IMG_BACKGROUND_REQUEST;
2436 int sound_opening = SND_REQUEST_OPENING;
2437 int sound_closing = SND_REQUEST_CLOSING;
2438 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2439 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2440 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2441 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2442 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2444 if (game_status == GAME_MODE_PLAYING)
2445 BlitScreenToBitmap(backbuffer);
2447 SetDrawtoField(DRAW_BACKBUFFER);
2449 // SetDrawBackgroundMask(REDRAW_NONE);
2451 if (action == ACTION_OPENING)
2453 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2455 if (req_state & REQ_ASK)
2457 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2458 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2460 else if (req_state & REQ_CONFIRM)
2462 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2464 else if (req_state & REQ_PLAYER)
2466 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2467 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2468 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2469 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2472 DrawEnvelopeRequest(text);
2474 if (game_status != GAME_MODE_MAIN)
2478 /* force DOOR font inside door area */
2479 game_status = GAME_MODE_PSEUDO_DOOR;
2481 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2483 if (action == ACTION_OPENING)
2485 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2487 if (anim_mode == ANIM_DEFAULT)
2488 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2490 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2494 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2496 if (anim_mode != ANIM_NONE)
2497 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2499 if (anim_mode == ANIM_DEFAULT)
2500 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2503 game.envelope_active = FALSE;
2505 game_status = last_game_status; /* restore current game status */
2507 if (action == ACTION_CLOSING)
2509 if (game_status != GAME_MODE_MAIN)
2512 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2515 // SetDrawBackgroundMask(last_draw_background_mask);
2517 redraw_mask |= REDRAW_FIELD;
2519 if (game_status == GAME_MODE_MAIN)
2524 if (action == ACTION_CLOSING &&
2525 game_status == GAME_MODE_PLAYING &&
2526 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2527 SetDrawtoField(DRAW_FIELDBUFFER);
2530 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2534 int graphic = el2preimg(element);
2536 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2537 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2540 void DrawLevel(int draw_background_mask)
2544 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2545 SetDrawBackgroundMask(draw_background_mask);
2549 for (x = BX1; x <= BX2; x++)
2550 for (y = BY1; y <= BY2; y++)
2551 DrawScreenField(x, y);
2553 redraw_mask |= REDRAW_FIELD;
2556 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2561 for (x = 0; x < size_x; x++)
2562 for (y = 0; y < size_y; y++)
2563 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2565 redraw_mask |= REDRAW_FIELD;
2568 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2572 for (x = 0; x < size_x; x++)
2573 for (y = 0; y < size_y; y++)
2574 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2576 redraw_mask |= REDRAW_FIELD;
2579 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2581 boolean show_level_border = (BorderElement != EL_EMPTY);
2582 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2583 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2584 int tile_size = preview.tile_size;
2585 int preview_width = preview.xsize * tile_size;
2586 int preview_height = preview.ysize * tile_size;
2587 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2588 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2589 int real_preview_width = real_preview_xsize * tile_size;
2590 int real_preview_height = real_preview_ysize * tile_size;
2591 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2592 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2595 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2598 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2600 dst_x += (preview_width - real_preview_width) / 2;
2601 dst_y += (preview_height - real_preview_height) / 2;
2603 for (x = 0; x < real_preview_xsize; x++)
2605 for (y = 0; y < real_preview_ysize; y++)
2607 int lx = from_x + x + (show_level_border ? -1 : 0);
2608 int ly = from_y + y + (show_level_border ? -1 : 0);
2609 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2610 getBorderElement(lx, ly));
2612 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2613 element, tile_size);
2617 redraw_mask |= REDRAW_FIELD;
2620 #define MICROLABEL_EMPTY 0
2621 #define MICROLABEL_LEVEL_NAME 1
2622 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2623 #define MICROLABEL_LEVEL_AUTHOR 3
2624 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2625 #define MICROLABEL_IMPORTED_FROM 5
2626 #define MICROLABEL_IMPORTED_BY_HEAD 6
2627 #define MICROLABEL_IMPORTED_BY 7
2629 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2631 int max_text_width = SXSIZE;
2632 int font_width = getFontWidth(font_nr);
2634 if (pos->align == ALIGN_CENTER)
2635 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2636 else if (pos->align == ALIGN_RIGHT)
2637 max_text_width = pos->x;
2639 max_text_width = SXSIZE - pos->x;
2641 return max_text_width / font_width;
2644 static void DrawPreviewLevelLabelExt(int mode)
2646 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2647 char label_text[MAX_OUTPUT_LINESIZE + 1];
2648 int max_len_label_text;
2649 int font_nr = pos->font;
2652 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2655 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2656 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2657 mode == MICROLABEL_IMPORTED_BY_HEAD)
2658 font_nr = pos->font_alt;
2660 max_len_label_text = getMaxTextLength(pos, font_nr);
2662 if (pos->size != -1)
2663 max_len_label_text = pos->size;
2665 for (i = 0; i < max_len_label_text; i++)
2666 label_text[i] = ' ';
2667 label_text[max_len_label_text] = '\0';
2669 if (strlen(label_text) > 0)
2670 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2673 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2674 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2675 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2676 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2677 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2678 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2679 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2680 max_len_label_text);
2681 label_text[max_len_label_text] = '\0';
2683 if (strlen(label_text) > 0)
2684 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2686 redraw_mask |= REDRAW_FIELD;
2689 static void DrawPreviewLevelExt(boolean restart)
2691 static unsigned int scroll_delay = 0;
2692 static unsigned int label_delay = 0;
2693 static int from_x, from_y, scroll_direction;
2694 static int label_state, label_counter;
2695 unsigned int scroll_delay_value = preview.step_delay;
2696 boolean show_level_border = (BorderElement != EL_EMPTY);
2697 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2698 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2699 int last_game_status = game_status; /* save current game status */
2706 if (preview.anim_mode == ANIM_CENTERED)
2708 if (level_xsize > preview.xsize)
2709 from_x = (level_xsize - preview.xsize) / 2;
2710 if (level_ysize > preview.ysize)
2711 from_y = (level_ysize - preview.ysize) / 2;
2714 from_x += preview.xoffset;
2715 from_y += preview.yoffset;
2717 scroll_direction = MV_RIGHT;
2721 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2722 DrawPreviewLevelLabelExt(label_state);
2724 /* initialize delay counters */
2725 DelayReached(&scroll_delay, 0);
2726 DelayReached(&label_delay, 0);
2728 if (leveldir_current->name)
2730 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2731 char label_text[MAX_OUTPUT_LINESIZE + 1];
2732 int font_nr = pos->font;
2733 int max_len_label_text = getMaxTextLength(pos, font_nr);
2735 if (pos->size != -1)
2736 max_len_label_text = pos->size;
2738 strncpy(label_text, leveldir_current->name, max_len_label_text);
2739 label_text[max_len_label_text] = '\0';
2741 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2742 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2745 game_status = last_game_status; /* restore current game status */
2750 /* scroll preview level, if needed */
2751 if (preview.anim_mode != ANIM_NONE &&
2752 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2753 DelayReached(&scroll_delay, scroll_delay_value))
2755 switch (scroll_direction)
2760 from_x -= preview.step_offset;
2761 from_x = (from_x < 0 ? 0 : from_x);
2764 scroll_direction = MV_UP;
2768 if (from_x < level_xsize - preview.xsize)
2770 from_x += preview.step_offset;
2771 from_x = (from_x > level_xsize - preview.xsize ?
2772 level_xsize - preview.xsize : from_x);
2775 scroll_direction = MV_DOWN;
2781 from_y -= preview.step_offset;
2782 from_y = (from_y < 0 ? 0 : from_y);
2785 scroll_direction = MV_RIGHT;
2789 if (from_y < level_ysize - preview.ysize)
2791 from_y += preview.step_offset;
2792 from_y = (from_y > level_ysize - preview.ysize ?
2793 level_ysize - preview.ysize : from_y);
2796 scroll_direction = MV_LEFT;
2803 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2806 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2807 /* redraw micro level label, if needed */
2808 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2809 !strEqual(level.author, ANONYMOUS_NAME) &&
2810 !strEqual(level.author, leveldir_current->name) &&
2811 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2813 int max_label_counter = 23;
2815 if (leveldir_current->imported_from != NULL &&
2816 strlen(leveldir_current->imported_from) > 0)
2817 max_label_counter += 14;
2818 if (leveldir_current->imported_by != NULL &&
2819 strlen(leveldir_current->imported_by) > 0)
2820 max_label_counter += 14;
2822 label_counter = (label_counter + 1) % max_label_counter;
2823 label_state = (label_counter >= 0 && label_counter <= 7 ?
2824 MICROLABEL_LEVEL_NAME :
2825 label_counter >= 9 && label_counter <= 12 ?
2826 MICROLABEL_LEVEL_AUTHOR_HEAD :
2827 label_counter >= 14 && label_counter <= 21 ?
2828 MICROLABEL_LEVEL_AUTHOR :
2829 label_counter >= 23 && label_counter <= 26 ?
2830 MICROLABEL_IMPORTED_FROM_HEAD :
2831 label_counter >= 28 && label_counter <= 35 ?
2832 MICROLABEL_IMPORTED_FROM :
2833 label_counter >= 37 && label_counter <= 40 ?
2834 MICROLABEL_IMPORTED_BY_HEAD :
2835 label_counter >= 42 && label_counter <= 49 ?
2836 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2838 if (leveldir_current->imported_from == NULL &&
2839 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2840 label_state == MICROLABEL_IMPORTED_FROM))
2841 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2842 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2844 DrawPreviewLevelLabelExt(label_state);
2847 game_status = last_game_status; /* restore current game status */
2850 void DrawPreviewLevelInitial()
2852 DrawPreviewLevelExt(TRUE);
2855 void DrawPreviewLevelAnimation()
2857 DrawPreviewLevelExt(FALSE);
2860 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2861 int graphic, int sync_frame, int mask_mode)
2863 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2865 if (mask_mode == USE_MASKING)
2866 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2868 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2871 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2872 int graphic, int sync_frame,
2875 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2877 if (mask_mode == USE_MASKING)
2878 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2880 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2883 inline void DrawGraphicAnimation(int x, int y, int graphic)
2885 int lx = LEVELX(x), ly = LEVELY(y);
2887 if (!IN_SCR_FIELD(x, y))
2890 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2891 graphic, GfxFrame[lx][ly], NO_MASKING);
2893 MarkTileDirty(x, y);
2896 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2898 int lx = LEVELX(x), ly = LEVELY(y);
2900 if (!IN_SCR_FIELD(x, y))
2903 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2904 graphic, GfxFrame[lx][ly], NO_MASKING);
2905 MarkTileDirty(x, y);
2908 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2910 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2913 void DrawLevelElementAnimation(int x, int y, int element)
2915 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2917 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2920 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2922 int sx = SCREENX(x), sy = SCREENY(y);
2924 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2927 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2930 DrawGraphicAnimation(sx, sy, graphic);
2933 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2934 DrawLevelFieldCrumbled(x, y);
2936 if (GFX_CRUMBLED(Feld[x][y]))
2937 DrawLevelFieldCrumbled(x, y);
2941 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2943 int sx = SCREENX(x), sy = SCREENY(y);
2946 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2949 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2951 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2954 DrawGraphicAnimation(sx, sy, graphic);
2956 if (GFX_CRUMBLED(element))
2957 DrawLevelFieldCrumbled(x, y);
2960 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2962 if (player->use_murphy)
2964 /* this works only because currently only one player can be "murphy" ... */
2965 static int last_horizontal_dir = MV_LEFT;
2966 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2968 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2969 last_horizontal_dir = move_dir;
2971 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2973 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2975 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2981 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2984 static boolean equalGraphics(int graphic1, int graphic2)
2986 struct GraphicInfo *g1 = &graphic_info[graphic1];
2987 struct GraphicInfo *g2 = &graphic_info[graphic2];
2989 return (g1->bitmap == g2->bitmap &&
2990 g1->src_x == g2->src_x &&
2991 g1->src_y == g2->src_y &&
2992 g1->anim_frames == g2->anim_frames &&
2993 g1->anim_delay == g2->anim_delay &&
2994 g1->anim_mode == g2->anim_mode);
2997 void DrawAllPlayers()
3001 for (i = 0; i < MAX_PLAYERS; i++)
3002 if (stored_player[i].active)
3003 DrawPlayer(&stored_player[i]);
3006 void DrawPlayerField(int x, int y)
3008 if (!IS_PLAYER(x, y))
3011 DrawPlayer(PLAYERINFO(x, y));
3014 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3016 void DrawPlayer(struct PlayerInfo *player)
3018 int jx = player->jx;
3019 int jy = player->jy;
3020 int move_dir = player->MovDir;
3021 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3022 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3023 int last_jx = (player->is_moving ? jx - dx : jx);
3024 int last_jy = (player->is_moving ? jy - dy : jy);
3025 int next_jx = jx + dx;
3026 int next_jy = jy + dy;
3027 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3028 boolean player_is_opaque = FALSE;
3029 int sx = SCREENX(jx), sy = SCREENY(jy);
3030 int sxx = 0, syy = 0;
3031 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3033 int action = ACTION_DEFAULT;
3034 int last_player_graphic = getPlayerGraphic(player, move_dir);
3035 int last_player_frame = player->Frame;
3038 /* GfxElement[][] is set to the element the player is digging or collecting;
3039 remove also for off-screen player if the player is not moving anymore */
3040 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3041 GfxElement[jx][jy] = EL_UNDEFINED;
3043 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3047 if (!IN_LEV_FIELD(jx, jy))
3049 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3050 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3051 printf("DrawPlayerField(): This should never happen!\n");
3056 if (element == EL_EXPLOSION)
3059 action = (player->is_pushing ? ACTION_PUSHING :
3060 player->is_digging ? ACTION_DIGGING :
3061 player->is_collecting ? ACTION_COLLECTING :
3062 player->is_moving ? ACTION_MOVING :
3063 player->is_snapping ? ACTION_SNAPPING :
3064 player->is_dropping ? ACTION_DROPPING :
3065 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3067 if (player->is_waiting)
3068 move_dir = player->dir_waiting;
3070 InitPlayerGfxAnimation(player, action, move_dir);
3072 /* ----------------------------------------------------------------------- */
3073 /* draw things in the field the player is leaving, if needed */
3074 /* ----------------------------------------------------------------------- */
3076 if (player->is_moving)
3078 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3080 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3082 if (last_element == EL_DYNAMITE_ACTIVE ||
3083 last_element == EL_EM_DYNAMITE_ACTIVE ||
3084 last_element == EL_SP_DISK_RED_ACTIVE)
3085 DrawDynamite(last_jx, last_jy);
3087 DrawLevelFieldThruMask(last_jx, last_jy);
3089 else if (last_element == EL_DYNAMITE_ACTIVE ||
3090 last_element == EL_EM_DYNAMITE_ACTIVE ||
3091 last_element == EL_SP_DISK_RED_ACTIVE)
3092 DrawDynamite(last_jx, last_jy);
3094 /* !!! this is not enough to prevent flickering of players which are
3095 moving next to each others without a free tile between them -- this
3096 can only be solved by drawing all players layer by layer (first the
3097 background, then the foreground etc.) !!! => TODO */
3098 else if (!IS_PLAYER(last_jx, last_jy))
3099 DrawLevelField(last_jx, last_jy);
3102 DrawLevelField(last_jx, last_jy);
3105 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3106 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3109 if (!IN_SCR_FIELD(sx, sy))
3112 /* ----------------------------------------------------------------------- */
3113 /* draw things behind the player, if needed */
3114 /* ----------------------------------------------------------------------- */
3117 DrawLevelElement(jx, jy, Back[jx][jy]);
3118 else if (IS_ACTIVE_BOMB(element))
3119 DrawLevelElement(jx, jy, EL_EMPTY);
3122 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3124 int old_element = GfxElement[jx][jy];
3125 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3126 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3128 if (GFX_CRUMBLED(old_element))
3129 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3131 DrawGraphic(sx, sy, old_graphic, frame);
3133 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3134 player_is_opaque = TRUE;
3138 GfxElement[jx][jy] = EL_UNDEFINED;
3140 /* make sure that pushed elements are drawn with correct frame rate */
3141 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3143 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3144 GfxFrame[jx][jy] = player->StepFrame;
3146 DrawLevelField(jx, jy);
3150 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3151 /* ----------------------------------------------------------------------- */
3152 /* draw player himself */
3153 /* ----------------------------------------------------------------------- */
3155 graphic = getPlayerGraphic(player, move_dir);
3157 /* in the case of changed player action or direction, prevent the current
3158 animation frame from being restarted for identical animations */
3159 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3160 player->Frame = last_player_frame;
3162 frame = getGraphicAnimationFrame(graphic, player->Frame);
3166 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3167 sxx = player->GfxPos;
3169 syy = player->GfxPos;
3172 if (player_is_opaque)
3173 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3175 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3177 if (SHIELD_ON(player))
3179 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3180 IMG_SHIELD_NORMAL_ACTIVE);
3181 int frame = getGraphicAnimationFrame(graphic, -1);
3183 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3187 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3190 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3191 sxx = player->GfxPos;
3193 syy = player->GfxPos;
3197 /* ----------------------------------------------------------------------- */
3198 /* draw things the player is pushing, if needed */
3199 /* ----------------------------------------------------------------------- */
3201 if (player->is_pushing && player->is_moving)
3203 int px = SCREENX(jx), py = SCREENY(jy);
3204 int pxx = (TILEX - ABS(sxx)) * dx;
3205 int pyy = (TILEY - ABS(syy)) * dy;
3206 int gfx_frame = GfxFrame[jx][jy];
3212 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3214 element = Feld[next_jx][next_jy];
3215 gfx_frame = GfxFrame[next_jx][next_jy];
3218 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3220 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3221 frame = getGraphicAnimationFrame(graphic, sync_frame);
3223 /* draw background element under pushed element (like the Sokoban field) */
3224 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3226 /* this allows transparent pushing animation over non-black background */
3229 DrawLevelElement(jx, jy, Back[jx][jy]);
3231 DrawLevelElement(jx, jy, EL_EMPTY);
3233 if (Back[next_jx][next_jy])
3234 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3236 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3238 else if (Back[next_jx][next_jy])
3239 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3242 /* do not draw (EM style) pushing animation when pushing is finished */
3243 /* (two-tile animations usually do not contain start and end frame) */
3244 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3245 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3247 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3249 /* masked drawing is needed for EMC style (double) movement graphics */
3250 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3251 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3255 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3256 /* ----------------------------------------------------------------------- */
3257 /* draw player himself */
3258 /* ----------------------------------------------------------------------- */
3260 graphic = getPlayerGraphic(player, move_dir);
3262 /* in the case of changed player action or direction, prevent the current
3263 animation frame from being restarted for identical animations */
3264 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3265 player->Frame = last_player_frame;
3267 frame = getGraphicAnimationFrame(graphic, player->Frame);
3271 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3272 sxx = player->GfxPos;
3274 syy = player->GfxPos;
3277 if (player_is_opaque)
3278 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3280 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3282 if (SHIELD_ON(player))
3284 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3285 IMG_SHIELD_NORMAL_ACTIVE);
3286 int frame = getGraphicAnimationFrame(graphic, -1);
3288 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3292 /* ----------------------------------------------------------------------- */
3293 /* draw things in front of player (active dynamite or dynabombs) */
3294 /* ----------------------------------------------------------------------- */
3296 if (IS_ACTIVE_BOMB(element))
3298 graphic = el2img(element);
3299 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3301 if (game.emulation == EMU_SUPAPLEX)
3302 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3304 DrawGraphicThruMask(sx, sy, graphic, frame);
3307 if (player_is_moving && last_element == EL_EXPLOSION)
3309 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3310 GfxElement[last_jx][last_jy] : EL_EMPTY);
3311 int graphic = el_act2img(element, ACTION_EXPLODING);
3312 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3313 int phase = ExplodePhase[last_jx][last_jy] - 1;
3314 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3317 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3320 /* ----------------------------------------------------------------------- */
3321 /* draw elements the player is just walking/passing through/under */
3322 /* ----------------------------------------------------------------------- */
3324 if (player_is_moving)
3326 /* handle the field the player is leaving ... */
3327 if (IS_ACCESSIBLE_INSIDE(last_element))
3328 DrawLevelField(last_jx, last_jy);
3329 else if (IS_ACCESSIBLE_UNDER(last_element))
3330 DrawLevelFieldThruMask(last_jx, last_jy);
3333 /* do not redraw accessible elements if the player is just pushing them */
3334 if (!player_is_moving || !player->is_pushing)
3336 /* ... and the field the player is entering */
3337 if (IS_ACCESSIBLE_INSIDE(element))
3338 DrawLevelField(jx, jy);
3339 else if (IS_ACCESSIBLE_UNDER(element))
3340 DrawLevelFieldThruMask(jx, jy);
3343 MarkTileDirty(sx, sy);
3346 /* ------------------------------------------------------------------------- */
3348 void WaitForEventToContinue()
3350 boolean still_wait = TRUE;
3352 /* simulate releasing mouse button over last gadget, if still pressed */
3354 HandleGadgets(-1, -1, 0);
3356 button_status = MB_RELEASED;
3370 case EVENT_BUTTONPRESS:
3371 case EVENT_KEYPRESS:
3375 case EVENT_KEYRELEASE:
3376 ClearPlayerAction();
3380 HandleOtherEvents(&event);
3384 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3391 /* don't eat all CPU time */
3396 #define MAX_REQUEST_LINES 13
3397 #define MAX_REQUEST_LINE_FONT1_LEN 7
3398 #define MAX_REQUEST_LINE_FONT2_LEN 10
3400 static int RequestHandleEvents(unsigned int req_state)
3402 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3403 local_player->LevelSolved_GameEnd);
3404 int last_game_status = game_status; /* save current game status */
3405 int width = request.width;
3406 int height = request.height;
3410 setRequestPosition(&sx, &sy, FALSE);
3412 button_status = MB_RELEASED;
3414 request_gadget_id = -1;
3421 SetDrawtoField(DRAW_FIELDBUFFER);
3423 HandleGameActions();
3425 SetDrawtoField(DRAW_BACKBUFFER);
3427 if (global.use_envelope_request)
3429 /* copy current state of request area to middle of playfield area */
3430 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3438 while (NextValidEvent(&event))
3442 case EVENT_BUTTONPRESS:
3443 case EVENT_BUTTONRELEASE:
3444 case EVENT_MOTIONNOTIFY:
3448 if (event.type == EVENT_MOTIONNOTIFY)
3453 motion_status = TRUE;
3454 mx = ((MotionEvent *) &event)->x;
3455 my = ((MotionEvent *) &event)->y;
3459 motion_status = FALSE;
3460 mx = ((ButtonEvent *) &event)->x;
3461 my = ((ButtonEvent *) &event)->y;
3462 if (event.type == EVENT_BUTTONPRESS)
3463 button_status = ((ButtonEvent *) &event)->button;
3465 button_status = MB_RELEASED;
3468 /* this sets 'request_gadget_id' */
3469 HandleGadgets(mx, my, button_status);
3471 switch (request_gadget_id)
3473 case TOOL_CTRL_ID_YES:
3476 case TOOL_CTRL_ID_NO:
3479 case TOOL_CTRL_ID_CONFIRM:
3480 result = TRUE | FALSE;
3483 case TOOL_CTRL_ID_PLAYER_1:
3486 case TOOL_CTRL_ID_PLAYER_2:
3489 case TOOL_CTRL_ID_PLAYER_3:
3492 case TOOL_CTRL_ID_PLAYER_4:
3503 case EVENT_KEYPRESS:
3504 switch (GetEventKey((KeyEvent *)&event, TRUE))
3507 if (req_state & REQ_CONFIRM)
3512 #if defined(TARGET_SDL2)
3519 #if defined(TARGET_SDL2)
3529 if (req_state & REQ_PLAYER)
3533 case EVENT_KEYRELEASE:
3534 ClearPlayerAction();
3538 HandleOtherEvents(&event);
3543 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3545 int joy = AnyJoystick();
3547 if (joy & JOY_BUTTON_1)
3549 else if (joy & JOY_BUTTON_2)
3555 if (global.use_envelope_request)
3557 /* copy back current state of pressed buttons inside request area */
3558 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3565 if (!PendingEvent()) /* delay only if no pending events */
3569 game_status = GAME_MODE_PSEUDO_DOOR;
3573 game_status = last_game_status; /* restore current game status */
3579 static boolean RequestDoor(char *text, unsigned int req_state)
3581 unsigned int old_door_state;
3582 int last_game_status = game_status; /* save current game status */
3583 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3584 int font_nr = FONT_TEXT_2;
3589 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3591 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3592 font_nr = FONT_TEXT_1;
3595 if (game_status == GAME_MODE_PLAYING)
3596 BlitScreenToBitmap(backbuffer);
3598 /* disable deactivated drawing when quick-loading level tape recording */
3599 if (tape.playing && tape.deactivate_display)
3600 TapeDeactivateDisplayOff(TRUE);
3602 SetMouseCursor(CURSOR_DEFAULT);
3604 #if defined(NETWORK_AVALIABLE)
3605 /* pause network game while waiting for request to answer */
3606 if (options.network &&
3607 game_status == GAME_MODE_PLAYING &&
3608 req_state & REQUEST_WAIT_FOR_INPUT)
3609 SendToServer_PausePlaying();
3612 old_door_state = GetDoorState();
3614 /* simulate releasing mouse button over last gadget, if still pressed */
3616 HandleGadgets(-1, -1, 0);
3620 /* draw released gadget before proceeding */
3623 if (old_door_state & DOOR_OPEN_1)
3625 CloseDoor(DOOR_CLOSE_1);
3627 /* save old door content */
3628 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3629 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3632 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3633 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3635 /* clear door drawing field */
3636 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3638 /* force DOOR font inside door area */
3639 game_status = GAME_MODE_PSEUDO_DOOR;
3641 /* write text for request */
3642 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3644 char text_line[max_request_line_len + 1];
3650 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3652 tc = *(text_ptr + tx);
3653 // if (!tc || tc == ' ')
3654 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3658 if ((tc == '?' || tc == '!') && tl == 0)
3668 strncpy(text_line, text_ptr, tl);
3671 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3672 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3673 text_line, font_nr);
3675 text_ptr += tl + (tc == ' ' ? 1 : 0);
3676 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3679 game_status = last_game_status; /* restore current game status */
3681 if (req_state & REQ_ASK)
3683 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3684 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3686 else if (req_state & REQ_CONFIRM)
3688 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3690 else if (req_state & REQ_PLAYER)
3692 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3693 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3694 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3695 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3698 /* copy request gadgets to door backbuffer */
3699 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3701 OpenDoor(DOOR_OPEN_1);
3703 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3705 if (game_status == GAME_MODE_PLAYING)
3707 SetPanelBackground();
3708 SetDrawBackgroundMask(REDRAW_DOOR_1);
3712 SetDrawBackgroundMask(REDRAW_FIELD);
3718 if (game_status != GAME_MODE_MAIN)
3721 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3723 // ---------- handle request buttons ----------
3724 result = RequestHandleEvents(req_state);
3726 if (game_status != GAME_MODE_MAIN)
3731 if (!(req_state & REQ_STAY_OPEN))
3733 CloseDoor(DOOR_CLOSE_1);
3735 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3736 (req_state & REQ_REOPEN))
3737 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3742 if (game_status == GAME_MODE_PLAYING)
3744 SetPanelBackground();
3745 SetDrawBackgroundMask(REDRAW_DOOR_1);
3749 SetDrawBackgroundMask(REDRAW_FIELD);
3752 #if defined(NETWORK_AVALIABLE)
3753 /* continue network game after request */
3754 if (options.network &&
3755 game_status == GAME_MODE_PLAYING &&
3756 req_state & REQUEST_WAIT_FOR_INPUT)
3757 SendToServer_ContinuePlaying();
3760 /* restore deactivated drawing when quick-loading level tape recording */
3761 if (tape.playing && tape.deactivate_display)
3762 TapeDeactivateDisplayOn();
3767 static boolean RequestEnvelope(char *text, unsigned int req_state)
3771 if (game_status == GAME_MODE_PLAYING)
3772 BlitScreenToBitmap(backbuffer);
3774 /* disable deactivated drawing when quick-loading level tape recording */
3775 if (tape.playing && tape.deactivate_display)
3776 TapeDeactivateDisplayOff(TRUE);
3778 SetMouseCursor(CURSOR_DEFAULT);
3780 #if defined(NETWORK_AVALIABLE)
3781 /* pause network game while waiting for request to answer */
3782 if (options.network &&
3783 game_status == GAME_MODE_PLAYING &&
3784 req_state & REQUEST_WAIT_FOR_INPUT)
3785 SendToServer_PausePlaying();
3788 /* simulate releasing mouse button over last gadget, if still pressed */
3790 HandleGadgets(-1, -1, 0);
3794 // (replace with setting corresponding request background)
3795 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3796 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3798 /* clear door drawing field */
3799 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3801 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3803 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3805 if (game_status == GAME_MODE_PLAYING)
3807 SetPanelBackground();
3808 SetDrawBackgroundMask(REDRAW_DOOR_1);
3812 SetDrawBackgroundMask(REDRAW_FIELD);
3818 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3820 // ---------- handle request buttons ----------
3821 result = RequestHandleEvents(req_state);
3823 if (game_status != GAME_MODE_MAIN)
3828 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3832 if (game_status == GAME_MODE_PLAYING)
3834 SetPanelBackground();
3835 SetDrawBackgroundMask(REDRAW_DOOR_1);
3839 SetDrawBackgroundMask(REDRAW_FIELD);
3842 #if defined(NETWORK_AVALIABLE)
3843 /* continue network game after request */
3844 if (options.network &&
3845 game_status == GAME_MODE_PLAYING &&
3846 req_state & REQUEST_WAIT_FOR_INPUT)
3847 SendToServer_ContinuePlaying();
3850 /* restore deactivated drawing when quick-loading level tape recording */
3851 if (tape.playing && tape.deactivate_display)
3852 TapeDeactivateDisplayOn();
3857 boolean Request(char *text, unsigned int req_state)
3859 if (global.use_envelope_request)
3860 return RequestEnvelope(text, req_state);
3862 return RequestDoor(text, req_state);
3865 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3867 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3868 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3871 if (dpo1->sort_priority != dpo2->sort_priority)
3872 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3874 compare_result = dpo1->nr - dpo2->nr;
3876 return compare_result;
3879 void InitGraphicCompatibilityInfo_Doors()
3885 struct DoorInfo *door;
3889 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3890 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3892 { -1, -1, -1, NULL }
3894 struct Rect door_rect_list[] =
3896 { DX, DY, DXSIZE, DYSIZE },
3897 { VX, VY, VXSIZE, VYSIZE }
3901 for (i = 0; doors[i].door_token != -1; i++)
3903 int door_token = doors[i].door_token;
3904 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3905 int part_1 = doors[i].part_1;
3906 int part_8 = doors[i].part_8;
3907 int part_2 = part_1 + 1;
3908 int part_3 = part_1 + 2;
3909 struct DoorInfo *door = doors[i].door;
3910 struct Rect *door_rect = &door_rect_list[door_index];
3911 boolean door_gfx_redefined = FALSE;
3913 /* check if any door part graphic definitions have been redefined */
3915 for (j = 0; door_part_controls[j].door_token != -1; j++)
3917 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3918 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3920 if (dpc->door_token == door_token && fi->redefined)
3921 door_gfx_redefined = TRUE;
3924 /* check for old-style door graphic/animation modifications */
3926 if (!door_gfx_redefined)
3928 if (door->anim_mode & ANIM_STATIC_PANEL)
3930 door->panel.step_xoffset = 0;
3931 door->panel.step_yoffset = 0;
3934 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3936 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3937 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3938 int num_door_steps, num_panel_steps;
3940 /* remove door part graphics other than the two default wings */
3942 for (j = 0; door_part_controls[j].door_token != -1; j++)
3944 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3945 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3947 if (dpc->graphic >= part_3 &&
3948 dpc->graphic <= part_8)
3952 /* set graphics and screen positions of the default wings */
3954 g_part_1->width = door_rect->width;
3955 g_part_1->height = door_rect->height;
3956 g_part_2->width = door_rect->width;
3957 g_part_2->height = door_rect->height;
3958 g_part_2->src_x = door_rect->width;
3959 g_part_2->src_y = g_part_1->src_y;
3961 door->part_2.x = door->part_1.x;
3962 door->part_2.y = door->part_1.y;
3964 if (door->width != -1)
3966 g_part_1->width = door->width;
3967 g_part_2->width = door->width;
3969 // special treatment for graphics and screen position of right wing
3970 g_part_2->src_x += door_rect->width - door->width;
3971 door->part_2.x += door_rect->width - door->width;
3974 if (door->height != -1)
3976 g_part_1->height = door->height;
3977 g_part_2->height = door->height;
3979 // special treatment for graphics and screen position of bottom wing
3980 g_part_2->src_y += door_rect->height - door->height;
3981 door->part_2.y += door_rect->height - door->height;
3984 /* set animation delays for the default wings and panels */
3986 door->part_1.step_delay = door->step_delay;
3987 door->part_2.step_delay = door->step_delay;
3988 door->panel.step_delay = door->step_delay;
3990 /* set animation draw order for the default wings */
3992 door->part_1.sort_priority = 2; /* draw left wing over ... */
3993 door->part_2.sort_priority = 1; /* ... right wing */
3995 /* set animation draw offset for the default wings */
3997 if (door->anim_mode & ANIM_HORIZONTAL)
3999 door->part_1.step_xoffset = door->step_offset;
4000 door->part_1.step_yoffset = 0;
4001 door->part_2.step_xoffset = door->step_offset * -1;
4002 door->part_2.step_yoffset = 0;
4004 num_door_steps = g_part_1->width / door->step_offset;
4006 else // ANIM_VERTICAL
4008 door->part_1.step_xoffset = 0;
4009 door->part_1.step_yoffset = door->step_offset;
4010 door->part_2.step_xoffset = 0;
4011 door->part_2.step_yoffset = door->step_offset * -1;
4013 num_door_steps = g_part_1->height / door->step_offset;
4016 /* set animation draw offset for the default panels */
4018 if (door->step_offset > 1)
4020 num_panel_steps = 2 * door_rect->height / door->step_offset;
4021 door->panel.start_step = num_panel_steps - num_door_steps;
4022 door->panel.start_step_closing = door->panel.start_step;
4026 num_panel_steps = door_rect->height / door->step_offset;
4027 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4028 door->panel.start_step_closing = door->panel.start_step;
4029 door->panel.step_delay *= 2;
4040 for (i = 0; door_part_controls[i].door_token != -1; i++)
4042 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4043 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4045 /* initialize "start_step_opening" and "start_step_closing", if needed */
4046 if (dpc->pos->start_step_opening == 0 &&
4047 dpc->pos->start_step_closing == 0)
4049 // dpc->pos->start_step_opening = dpc->pos->start_step;
4050 dpc->pos->start_step_closing = dpc->pos->start_step;
4053 /* fill structure for door part draw order (sorted below) */
4055 dpo->sort_priority = dpc->pos->sort_priority;
4058 /* sort door part controls according to sort_priority and graphic number */
4059 qsort(door_part_order, MAX_DOOR_PARTS,
4060 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4063 unsigned int OpenDoor(unsigned int door_state)
4065 if (door_state & DOOR_COPY_BACK)
4067 if (door_state & DOOR_OPEN_1)
4068 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4069 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4071 if (door_state & DOOR_OPEN_2)
4072 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4073 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4075 door_state &= ~DOOR_COPY_BACK;
4078 return MoveDoor(door_state);
4081 unsigned int CloseDoor(unsigned int door_state)
4083 unsigned int old_door_state = GetDoorState();
4085 if (!(door_state & DOOR_NO_COPY_BACK))
4087 if (old_door_state & DOOR_OPEN_1)
4088 BlitBitmap(backbuffer, bitmap_db_door_1,
4089 DX, DY, DXSIZE, DYSIZE, 0, 0);
4091 if (old_door_state & DOOR_OPEN_2)
4092 BlitBitmap(backbuffer, bitmap_db_door_2,
4093 VX, VY, VXSIZE, VYSIZE, 0, 0);
4095 door_state &= ~DOOR_NO_COPY_BACK;
4098 return MoveDoor(door_state);
4101 unsigned int GetDoorState()
4103 return MoveDoor(DOOR_GET_STATE);
4106 unsigned int SetDoorState(unsigned int door_state)
4108 return MoveDoor(door_state | DOOR_SET_STATE);
4111 int euclid(int a, int b)
4113 return (b ? euclid(b, a % b) : a);
4116 unsigned int MoveDoor(unsigned int door_state)
4118 struct Rect door_rect_list[] =
4120 { DX, DY, DXSIZE, DYSIZE },
4121 { VX, VY, VXSIZE, VYSIZE }
4123 static int door1 = DOOR_OPEN_1;
4124 static int door2 = DOOR_CLOSE_2;
4125 unsigned int door_delay = 0;
4126 unsigned int door_delay_value;
4129 if (door_state == DOOR_GET_STATE)
4130 return (door1 | door2);
4132 if (door_state & DOOR_SET_STATE)
4134 if (door_state & DOOR_ACTION_1)
4135 door1 = door_state & DOOR_ACTION_1;
4136 if (door_state & DOOR_ACTION_2)
4137 door2 = door_state & DOOR_ACTION_2;
4139 return (door1 | door2);
4142 if (!(door_state & DOOR_FORCE_REDRAW))
4144 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4145 door_state &= ~DOOR_OPEN_1;
4146 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4147 door_state &= ~DOOR_CLOSE_1;
4148 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4149 door_state &= ~DOOR_OPEN_2;
4150 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4151 door_state &= ~DOOR_CLOSE_2;
4154 if (global.autoplay_leveldir)
4156 door_state |= DOOR_NO_DELAY;
4157 door_state &= ~DOOR_CLOSE_ALL;
4160 if (game_status == GAME_MODE_EDITOR)
4161 door_state |= DOOR_NO_DELAY;
4163 if (door_state & DOOR_ACTION)
4165 boolean door_panel_drawn[NUM_DOORS];
4166 boolean panel_has_doors[NUM_DOORS];
4167 boolean door_part_skip[MAX_DOOR_PARTS];
4168 boolean door_part_done[MAX_DOOR_PARTS];
4169 boolean door_part_done_all;
4170 int num_steps[MAX_DOOR_PARTS];
4171 int max_move_delay = 0; // delay for complete animations of all doors
4172 int max_step_delay = 0; // delay (ms) between two animation frames
4173 int num_move_steps = 0; // number of animation steps for all doors
4174 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4175 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4176 int current_move_delay = 0;
4180 for (i = 0; i < NUM_DOORS; i++)
4181 panel_has_doors[i] = FALSE;
4183 for (i = 0; i < MAX_DOOR_PARTS; i++)
4185 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4186 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4187 int door_token = dpc->door_token;
4189 door_part_done[i] = FALSE;
4190 door_part_skip[i] = (!(door_state & door_token) ||
4194 for (i = 0; i < MAX_DOOR_PARTS; i++)
4196 int nr = door_part_order[i].nr;
4197 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4198 struct DoorPartPosInfo *pos = dpc->pos;
4199 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4200 int door_token = dpc->door_token;
4201 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4202 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4203 int step_xoffset = ABS(pos->step_xoffset);
4204 int step_yoffset = ABS(pos->step_yoffset);
4205 int step_delay = pos->step_delay;
4206 int current_door_state = door_state & door_token;
4207 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4208 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4209 boolean part_opening = (is_panel ? door_closing : door_opening);
4210 int start_step = (part_opening ? pos->start_step_opening :
4211 pos->start_step_closing);
4212 float move_xsize = (step_xoffset ? g->width : 0);
4213 float move_ysize = (step_yoffset ? g->height : 0);
4214 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4215 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4216 int move_steps = (move_xsteps && move_ysteps ?
4217 MIN(move_xsteps, move_ysteps) :
4218 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4219 int move_delay = move_steps * step_delay;
4221 if (door_part_skip[nr])
4224 max_move_delay = MAX(max_move_delay, move_delay);
4225 max_step_delay = (max_step_delay == 0 ? step_delay :
4226 euclid(max_step_delay, step_delay));
4227 num_steps[nr] = move_steps;
4231 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4233 panel_has_doors[door_index] = TRUE;
4237 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4239 num_move_steps = max_move_delay / max_step_delay;
4240 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4242 door_delay_value = max_step_delay;
4244 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4246 start = num_move_steps - 1;
4250 /* opening door sound has priority over simultaneously closing door */
4251 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4252 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4253 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4254 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4257 for (k = start; k < num_move_steps; k++)
4259 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4261 door_part_done_all = TRUE;
4263 for (i = 0; i < NUM_DOORS; i++)
4264 door_panel_drawn[i] = FALSE;
4266 for (i = 0; i < MAX_DOOR_PARTS; i++)
4268 int nr = door_part_order[i].nr;
4269 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4270 struct DoorPartPosInfo *pos = dpc->pos;
4271 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4272 int door_token = dpc->door_token;
4273 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4274 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4275 boolean is_panel_and_door_has_closed = FALSE;
4276 struct Rect *door_rect = &door_rect_list[door_index];
4277 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4279 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4280 int current_door_state = door_state & door_token;
4281 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4282 boolean door_closing = !door_opening;
4283 boolean part_opening = (is_panel ? door_closing : door_opening);
4284 boolean part_closing = !part_opening;
4285 int start_step = (part_opening ? pos->start_step_opening :
4286 pos->start_step_closing);
4287 int step_delay = pos->step_delay;
4288 int step_factor = step_delay / max_step_delay;
4289 int k1 = (step_factor ? k / step_factor + 1 : k);
4290 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4291 int kk = MAX(0, k2);
4294 int src_x, src_y, src_xx, src_yy;
4295 int dst_x, dst_y, dst_xx, dst_yy;
4298 if (door_part_skip[nr])
4301 if (!(door_state & door_token))
4309 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4310 int kk_door = MAX(0, k2_door);
4311 int sync_frame = kk_door * door_delay_value;
4312 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4314 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4319 if (!door_panel_drawn[door_index])
4321 ClearRectangle(drawto, door_rect->x, door_rect->y,
4322 door_rect->width, door_rect->height);
4324 door_panel_drawn[door_index] = TRUE;
4327 // draw opening or closing door parts
4329 if (pos->step_xoffset < 0) // door part on right side
4332 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4335 if (dst_xx + width > door_rect->width)
4336 width = door_rect->width - dst_xx;
4338 else // door part on left side
4341 dst_xx = pos->x - kk * pos->step_xoffset;
4345 src_xx = ABS(dst_xx);
4349 width = g->width - src_xx;
4351 if (width > door_rect->width)
4352 width = door_rect->width;
4354 // printf("::: k == %d [%d] \n", k, start_step);
4357 if (pos->step_yoffset < 0) // door part on bottom side
4360 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4363 if (dst_yy + height > door_rect->height)
4364 height = door_rect->height - dst_yy;
4366 else // door part on top side
4369 dst_yy = pos->y - kk * pos->step_yoffset;
4373 src_yy = ABS(dst_yy);
4377 height = g->height - src_yy;
4380 src_x = g_src_x + src_xx;
4381 src_y = g_src_y + src_yy;
4383 dst_x = door_rect->x + dst_xx;
4384 dst_y = door_rect->y + dst_yy;
4386 is_panel_and_door_has_closed =
4389 panel_has_doors[door_index] &&
4390 k >= num_move_steps_doors_only - 1);
4392 if (width >= 0 && width <= g->width &&
4393 height >= 0 && height <= g->height &&
4394 !is_panel_and_door_has_closed)
4396 if (is_panel || !pos->draw_masked)
4397 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4400 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4404 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4406 if ((part_opening && (width < 0 || height < 0)) ||
4407 (part_closing && (width >= g->width && height >= g->height)))
4408 door_part_done[nr] = TRUE;
4410 // continue door part animations, but not panel after door has closed
4411 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4412 door_part_done_all = FALSE;
4415 if (!(door_state & DOOR_NO_DELAY))
4419 if (game_status == GAME_MODE_MAIN)
4422 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4424 current_move_delay += max_step_delay;
4427 if (door_part_done_all)
4432 if (door_state & DOOR_ACTION_1)
4433 door1 = door_state & DOOR_ACTION_1;
4434 if (door_state & DOOR_ACTION_2)
4435 door2 = door_state & DOOR_ACTION_2;
4437 return (door1 | door2);
4440 static boolean useSpecialEditorDoor()
4442 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4443 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4445 // do not draw special editor door if editor border defined or redefined
4446 if (graphic_info[graphic].bitmap != NULL || redefined)
4449 // do not draw special editor door if global border defined to be empty
4450 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4453 // do not draw special editor door if viewport definitions do not match
4457 EY + EYSIZE != VY + VYSIZE)
4463 void DrawSpecialEditorDoor()
4465 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4466 int top_border_width = gfx1->width;
4467 int top_border_height = gfx1->height;
4468 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4469 int ex = EX - outer_border;
4470 int ey = EY - outer_border;
4471 int vy = VY - outer_border;
4472 int exsize = EXSIZE + 2 * outer_border;
4474 if (!useSpecialEditorDoor())
4477 /* draw bigger level editor toolbox window */
4478 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4479 top_border_width, top_border_height, ex, ey - top_border_height);
4480 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4481 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4483 redraw_mask |= REDRAW_ALL;
4486 void UndrawSpecialEditorDoor()
4488 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4489 int top_border_width = gfx1->width;
4490 int top_border_height = gfx1->height;
4491 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4492 int ex = EX - outer_border;
4493 int ey = EY - outer_border;
4494 int ey_top = ey - top_border_height;
4495 int exsize = EXSIZE + 2 * outer_border;
4496 int eysize = EYSIZE + 2 * outer_border;
4498 if (!useSpecialEditorDoor())
4501 /* draw normal tape recorder window */
4502 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4504 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4505 ex, ey_top, top_border_width, top_border_height,
4507 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4508 ex, ey, exsize, eysize, ex, ey);
4512 // if screen background is set to "[NONE]", clear editor toolbox window
4513 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4514 ClearRectangle(drawto, ex, ey, exsize, eysize);
4517 redraw_mask |= REDRAW_ALL;
4521 /* ---------- new tool button stuff ---------------------------------------- */
4526 struct TextPosInfo *pos;
4529 } toolbutton_info[NUM_TOOL_BUTTONS] =
4532 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4533 TOOL_CTRL_ID_YES, "yes"
4536 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4537 TOOL_CTRL_ID_NO, "no"
4540 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4541 TOOL_CTRL_ID_CONFIRM, "confirm"
4544 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4545 TOOL_CTRL_ID_PLAYER_1, "player 1"
4548 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4549 TOOL_CTRL_ID_PLAYER_2, "player 2"
4552 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4553 TOOL_CTRL_ID_PLAYER_3, "player 3"
4556 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4557 TOOL_CTRL_ID_PLAYER_4, "player 4"
4561 void CreateToolButtons()
4565 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4567 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4568 struct TextPosInfo *pos = toolbutton_info[i].pos;
4569 struct GadgetInfo *gi;
4570 Bitmap *deco_bitmap = None;
4571 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4572 unsigned int event_mask = GD_EVENT_RELEASED;
4575 int gd_x = gfx->src_x;
4576 int gd_y = gfx->src_y;
4577 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4578 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4581 if (global.use_envelope_request)
4582 setRequestPosition(&dx, &dy, TRUE);
4584 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4586 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4588 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4589 pos->size, &deco_bitmap, &deco_x, &deco_y);
4590 deco_xpos = (gfx->width - pos->size) / 2;
4591 deco_ypos = (gfx->height - pos->size) / 2;
4594 gi = CreateGadget(GDI_CUSTOM_ID, id,
4595 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4596 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4597 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4598 GDI_WIDTH, gfx->width,
4599 GDI_HEIGHT, gfx->height,
4600 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4601 GDI_STATE, GD_BUTTON_UNPRESSED,
4602 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4603 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4604 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4605 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4606 GDI_DECORATION_SIZE, pos->size, pos->size,
4607 GDI_DECORATION_SHIFTING, 1, 1,
4608 GDI_DIRECT_DRAW, FALSE,
4609 GDI_EVENT_MASK, event_mask,
4610 GDI_CALLBACK_ACTION, HandleToolButtons,
4614 Error(ERR_EXIT, "cannot create gadget");
4616 tool_gadget[id] = gi;
4620 void FreeToolButtons()
4624 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4625 FreeGadget(tool_gadget[i]);
4628 static void UnmapToolButtons()
4632 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4633 UnmapGadget(tool_gadget[i]);
4636 static void HandleToolButtons(struct GadgetInfo *gi)
4638 request_gadget_id = gi->custom_id;
4641 static struct Mapping_EM_to_RND_object
4644 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4645 boolean is_backside; /* backside of moving element */
4651 em_object_mapping_list[] =
4654 Xblank, TRUE, FALSE,
4658 Yacid_splash_eB, FALSE, FALSE,
4659 EL_ACID_SPLASH_RIGHT, -1, -1
4662 Yacid_splash_wB, FALSE, FALSE,
4663 EL_ACID_SPLASH_LEFT, -1, -1
4666 #ifdef EM_ENGINE_BAD_ROLL
4668 Xstone_force_e, FALSE, FALSE,
4669 EL_ROCK, -1, MV_BIT_RIGHT
4672 Xstone_force_w, FALSE, FALSE,
4673 EL_ROCK, -1, MV_BIT_LEFT
4676 Xnut_force_e, FALSE, FALSE,
4677 EL_NUT, -1, MV_BIT_RIGHT
4680 Xnut_force_w, FALSE, FALSE,
4681 EL_NUT, -1, MV_BIT_LEFT
4684 Xspring_force_e, FALSE, FALSE,
4685 EL_SPRING, -1, MV_BIT_RIGHT
4688 Xspring_force_w, FALSE, FALSE,
4689 EL_SPRING, -1, MV_BIT_LEFT
4692 Xemerald_force_e, FALSE, FALSE,
4693 EL_EMERALD, -1, MV_BIT_RIGHT
4696 Xemerald_force_w, FALSE, FALSE,
4697 EL_EMERALD, -1, MV_BIT_LEFT
4700 Xdiamond_force_e, FALSE, FALSE,
4701 EL_DIAMOND, -1, MV_BIT_RIGHT
4704 Xdiamond_force_w, FALSE, FALSE,
4705 EL_DIAMOND, -1, MV_BIT_LEFT
4708 Xbomb_force_e, FALSE, FALSE,
4709 EL_BOMB, -1, MV_BIT_RIGHT
4712 Xbomb_force_w, FALSE, FALSE,
4713 EL_BOMB, -1, MV_BIT_LEFT
4715 #endif /* EM_ENGINE_BAD_ROLL */
4718 Xstone, TRUE, FALSE,
4722 Xstone_pause, FALSE, FALSE,
4726 Xstone_fall, FALSE, FALSE,
4730 Ystone_s, FALSE, FALSE,
4731 EL_ROCK, ACTION_FALLING, -1
4734 Ystone_sB, FALSE, TRUE,
4735 EL_ROCK, ACTION_FALLING, -1
4738 Ystone_e, FALSE, FALSE,
4739 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4742 Ystone_eB, FALSE, TRUE,
4743 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4746 Ystone_w, FALSE, FALSE,
4747 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4750 Ystone_wB, FALSE, TRUE,
4751 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4758 Xnut_pause, FALSE, FALSE,
4762 Xnut_fall, FALSE, FALSE,
4766 Ynut_s, FALSE, FALSE,
4767 EL_NUT, ACTION_FALLING, -1
4770 Ynut_sB, FALSE, TRUE,
4771 EL_NUT, ACTION_FALLING, -1
4774 Ynut_e, FALSE, FALSE,
4775 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4778 Ynut_eB, FALSE, TRUE,
4779 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4782 Ynut_w, FALSE, FALSE,
4783 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4786 Ynut_wB, FALSE, TRUE,
4787 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4790 Xbug_n, TRUE, FALSE,
4794 Xbug_e, TRUE, FALSE,
4795 EL_BUG_RIGHT, -1, -1
4798 Xbug_s, TRUE, FALSE,
4802 Xbug_w, TRUE, FALSE,
4806 Xbug_gon, FALSE, FALSE,
4810 Xbug_goe, FALSE, FALSE,
4811 EL_BUG_RIGHT, -1, -1
4814 Xbug_gos, FALSE, FALSE,
4818 Xbug_gow, FALSE, FALSE,
4822 Ybug_n, FALSE, FALSE,
4823 EL_BUG, ACTION_MOVING, MV_BIT_UP
4826 Ybug_nB, FALSE, TRUE,
4827 EL_BUG, ACTION_MOVING, MV_BIT_UP
4830 Ybug_e, FALSE, FALSE,
4831 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4834 Ybug_eB, FALSE, TRUE,
4835 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4838 Ybug_s, FALSE, FALSE,
4839 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4842 Ybug_sB, FALSE, TRUE,
4843 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4846 Ybug_w, FALSE, FALSE,
4847 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4850 Ybug_wB, FALSE, TRUE,
4851 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4854 Ybug_w_n, FALSE, FALSE,
4855 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4858 Ybug_n_e, FALSE, FALSE,
4859 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4862 Ybug_e_s, FALSE, FALSE,
4863 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4866 Ybug_s_w, FALSE, FALSE,
4867 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4870 Ybug_e_n, FALSE, FALSE,
4871 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4874 Ybug_s_e, FALSE, FALSE,
4875 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4878 Ybug_w_s, FALSE, FALSE,
4879 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4882 Ybug_n_w, FALSE, FALSE,
4883 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4886 Ybug_stone, FALSE, FALSE,
4887 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4890 Ybug_spring, FALSE, FALSE,
4891 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4894 Xtank_n, TRUE, FALSE,
4895 EL_SPACESHIP_UP, -1, -1
4898 Xtank_e, TRUE, FALSE,
4899 EL_SPACESHIP_RIGHT, -1, -1
4902 Xtank_s, TRUE, FALSE,
4903 EL_SPACESHIP_DOWN, -1, -1
4906 Xtank_w, TRUE, FALSE,
4907 EL_SPACESHIP_LEFT, -1, -1
4910 Xtank_gon, FALSE, FALSE,
4911 EL_SPACESHIP_UP, -1, -1
4914 Xtank_goe, FALSE, FALSE,
4915 EL_SPACESHIP_RIGHT, -1, -1
4918 Xtank_gos, FALSE, FALSE,
4919 EL_SPACESHIP_DOWN, -1, -1
4922 Xtank_gow, FALSE, FALSE,
4923 EL_SPACESHIP_LEFT, -1, -1
4926 Ytank_n, FALSE, FALSE,
4927 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4930 Ytank_nB, FALSE, TRUE,
4931 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4934 Ytank_e, FALSE, FALSE,
4935 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4938 Ytank_eB, FALSE, TRUE,
4939 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4942 Ytank_s, FALSE, FALSE,
4943 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4946 Ytank_sB, FALSE, TRUE,
4947 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4950 Ytank_w, FALSE, FALSE,
4951 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4954 Ytank_wB, FALSE, TRUE,
4955 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4958 Ytank_w_n, FALSE, FALSE,
4959 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4962 Ytank_n_e, FALSE, FALSE,
4963 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4966 Ytank_e_s, FALSE, FALSE,
4967 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4970 Ytank_s_w, FALSE, FALSE,
4971 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4974 Ytank_e_n, FALSE, FALSE,
4975 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4978 Ytank_s_e, FALSE, FALSE,
4979 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4982 Ytank_w_s, FALSE, FALSE,
4983 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4986 Ytank_n_w, FALSE, FALSE,
4987 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4990 Ytank_stone, FALSE, FALSE,
4991 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4994 Ytank_spring, FALSE, FALSE,
4995 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4998 Xandroid, TRUE, FALSE,
4999 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5002 Xandroid_1_n, FALSE, FALSE,
5003 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5006 Xandroid_2_n, FALSE, FALSE,
5007 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5010 Xandroid_1_e, FALSE, FALSE,
5011 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5014 Xandroid_2_e, FALSE, FALSE,
5015 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5018 Xandroid_1_w, FALSE, FALSE,
5019 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5022 Xandroid_2_w, FALSE, FALSE,
5023 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5026 Xandroid_1_s, FALSE, FALSE,
5027 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5030 Xandroid_2_s, FALSE, FALSE,
5031 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5034 Yandroid_n, FALSE, FALSE,
5035 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5038 Yandroid_nB, FALSE, TRUE,
5039 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5042 Yandroid_ne, FALSE, FALSE,
5043 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5046 Yandroid_neB, FALSE, TRUE,
5047 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5050 Yandroid_e, FALSE, FALSE,
5051 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5054 Yandroid_eB, FALSE, TRUE,
5055 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5058 Yandroid_se, FALSE, FALSE,
5059 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5062 Yandroid_seB, FALSE, TRUE,
5063 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5066 Yandroid_s, FALSE, FALSE,
5067 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5070 Yandroid_sB, FALSE, TRUE,
5071 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5074 Yandroid_sw, FALSE, FALSE,
5075 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5078 Yandroid_swB, FALSE, TRUE,
5079 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5082 Yandroid_w, FALSE, FALSE,
5083 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5086 Yandroid_wB, FALSE, TRUE,
5087 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5090 Yandroid_nw, FALSE, FALSE,
5091 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5094 Yandroid_nwB, FALSE, TRUE,
5095 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5098 Xspring, TRUE, FALSE,
5102 Xspring_pause, FALSE, FALSE,
5106 Xspring_e, FALSE, FALSE,
5110 Xspring_w, FALSE, FALSE,
5114 Xspring_fall, FALSE, FALSE,
5118 Yspring_s, FALSE, FALSE,
5119 EL_SPRING, ACTION_FALLING, -1
5122 Yspring_sB, FALSE, TRUE,
5123 EL_SPRING, ACTION_FALLING, -1
5126 Yspring_e, FALSE, FALSE,
5127 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5130 Yspring_eB, FALSE, TRUE,
5131 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5134 Yspring_w, FALSE, FALSE,
5135 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5138 Yspring_wB, FALSE, TRUE,
5139 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5142 Yspring_kill_e, FALSE, FALSE,
5143 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5146 Yspring_kill_eB, FALSE, TRUE,
5147 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5150 Yspring_kill_w, FALSE, FALSE,
5151 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5154 Yspring_kill_wB, FALSE, TRUE,
5155 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5158 Xeater_n, TRUE, FALSE,
5159 EL_YAMYAM_UP, -1, -1
5162 Xeater_e, TRUE, FALSE,
5163 EL_YAMYAM_RIGHT, -1, -1
5166 Xeater_w, TRUE, FALSE,
5167 EL_YAMYAM_LEFT, -1, -1
5170 Xeater_s, TRUE, FALSE,
5171 EL_YAMYAM_DOWN, -1, -1
5174 Yeater_n, FALSE, FALSE,
5175 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5178 Yeater_nB, FALSE, TRUE,
5179 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5182 Yeater_e, FALSE, FALSE,
5183 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5186 Yeater_eB, FALSE, TRUE,
5187 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5190 Yeater_s, FALSE, FALSE,
5191 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5194 Yeater_sB, FALSE, TRUE,
5195 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5198 Yeater_w, FALSE, FALSE,
5199 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5202 Yeater_wB, FALSE, TRUE,
5203 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5206 Yeater_stone, FALSE, FALSE,
5207 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5210 Yeater_spring, FALSE, FALSE,
5211 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5214 Xalien, TRUE, FALSE,
5218 Xalien_pause, FALSE, FALSE,
5222 Yalien_n, FALSE, FALSE,
5223 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5226 Yalien_nB, FALSE, TRUE,
5227 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5230 Yalien_e, FALSE, FALSE,
5231 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5234 Yalien_eB, FALSE, TRUE,
5235 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5238 Yalien_s, FALSE, FALSE,
5239 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5242 Yalien_sB, FALSE, TRUE,
5243 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5246 Yalien_w, FALSE, FALSE,
5247 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5250 Yalien_wB, FALSE, TRUE,
5251 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5254 Yalien_stone, FALSE, FALSE,
5255 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5258 Yalien_spring, FALSE, FALSE,
5259 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5262 Xemerald, TRUE, FALSE,
5266 Xemerald_pause, FALSE, FALSE,
5270 Xemerald_fall, FALSE, FALSE,
5274 Xemerald_shine, FALSE, FALSE,
5275 EL_EMERALD, ACTION_TWINKLING, -1
5278 Yemerald_s, FALSE, FALSE,
5279 EL_EMERALD, ACTION_FALLING, -1
5282 Yemerald_sB, FALSE, TRUE,
5283 EL_EMERALD, ACTION_FALLING, -1
5286 Yemerald_e, FALSE, FALSE,
5287 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5290 Yemerald_eB, FALSE, TRUE,
5291 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5294 Yemerald_w, FALSE, FALSE,
5295 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5298 Yemerald_wB, FALSE, TRUE,
5299 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5302 Yemerald_eat, FALSE, FALSE,
5303 EL_EMERALD, ACTION_COLLECTING, -1
5306 Yemerald_stone, FALSE, FALSE,
5307 EL_NUT, ACTION_BREAKING, -1
5310 Xdiamond, TRUE, FALSE,
5314 Xdiamond_pause, FALSE, FALSE,
5318 Xdiamond_fall, FALSE, FALSE,
5322 Xdiamond_shine, FALSE, FALSE,
5323 EL_DIAMOND, ACTION_TWINKLING, -1
5326 Ydiamond_s, FALSE, FALSE,
5327 EL_DIAMOND, ACTION_FALLING, -1
5330 Ydiamond_sB, FALSE, TRUE,
5331 EL_DIAMOND, ACTION_FALLING, -1
5334 Ydiamond_e, FALSE, FALSE,
5335 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5338 Ydiamond_eB, FALSE, TRUE,
5339 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5342 Ydiamond_w, FALSE, FALSE,
5343 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5346 Ydiamond_wB, FALSE, TRUE,
5347 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5350 Ydiamond_eat, FALSE, FALSE,
5351 EL_DIAMOND, ACTION_COLLECTING, -1
5354 Ydiamond_stone, FALSE, FALSE,
5355 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5358 Xdrip_fall, TRUE, FALSE,
5359 EL_AMOEBA_DROP, -1, -1
5362 Xdrip_stretch, FALSE, FALSE,
5363 EL_AMOEBA_DROP, ACTION_FALLING, -1
5366 Xdrip_stretchB, FALSE, TRUE,
5367 EL_AMOEBA_DROP, ACTION_FALLING, -1
5370 Xdrip_eat, FALSE, FALSE,
5371 EL_AMOEBA_DROP, ACTION_GROWING, -1
5374 Ydrip_s1, FALSE, FALSE,
5375 EL_AMOEBA_DROP, ACTION_FALLING, -1
5378 Ydrip_s1B, FALSE, TRUE,
5379 EL_AMOEBA_DROP, ACTION_FALLING, -1
5382 Ydrip_s2, FALSE, FALSE,
5383 EL_AMOEBA_DROP, ACTION_FALLING, -1
5386 Ydrip_s2B, FALSE, TRUE,
5387 EL_AMOEBA_DROP, ACTION_FALLING, -1
5394 Xbomb_pause, FALSE, FALSE,
5398 Xbomb_fall, FALSE, FALSE,
5402 Ybomb_s, FALSE, FALSE,
5403 EL_BOMB, ACTION_FALLING, -1
5406 Ybomb_sB, FALSE, TRUE,
5407 EL_BOMB, ACTION_FALLING, -1
5410 Ybomb_e, FALSE, FALSE,
5411 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5414 Ybomb_eB, FALSE, TRUE,
5415 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5418 Ybomb_w, FALSE, FALSE,
5419 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5422 Ybomb_wB, FALSE, TRUE,
5423 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5426 Ybomb_eat, FALSE, FALSE,
5427 EL_BOMB, ACTION_ACTIVATING, -1
5430 Xballoon, TRUE, FALSE,
5434 Yballoon_n, FALSE, FALSE,
5435 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5438 Yballoon_nB, FALSE, TRUE,
5439 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5442 Yballoon_e, FALSE, FALSE,
5443 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5446 Yballoon_eB, FALSE, TRUE,
5447 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5450 Yballoon_s, FALSE, FALSE,
5451 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5454 Yballoon_sB, FALSE, TRUE,
5455 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5458 Yballoon_w, FALSE, FALSE,
5459 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5462 Yballoon_wB, FALSE, TRUE,
5463 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5466 Xgrass, TRUE, FALSE,
5467 EL_EMC_GRASS, -1, -1
5470 Ygrass_nB, FALSE, FALSE,
5471 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5474 Ygrass_eB, FALSE, FALSE,
5475 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5478 Ygrass_sB, FALSE, FALSE,
5479 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5482 Ygrass_wB, FALSE, FALSE,
5483 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5490 Ydirt_nB, FALSE, FALSE,
5491 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5494 Ydirt_eB, FALSE, FALSE,
5495 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5498 Ydirt_sB, FALSE, FALSE,
5499 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5502 Ydirt_wB, FALSE, FALSE,
5503 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5506 Xacid_ne, TRUE, FALSE,
5507 EL_ACID_POOL_TOPRIGHT, -1, -1
5510 Xacid_se, TRUE, FALSE,
5511 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5514 Xacid_s, TRUE, FALSE,
5515 EL_ACID_POOL_BOTTOM, -1, -1
5518 Xacid_sw, TRUE, FALSE,
5519 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5522 Xacid_nw, TRUE, FALSE,
5523 EL_ACID_POOL_TOPLEFT, -1, -1
5526 Xacid_1, TRUE, FALSE,
5530 Xacid_2, FALSE, FALSE,
5534 Xacid_3, FALSE, FALSE,
5538 Xacid_4, FALSE, FALSE,
5542 Xacid_5, FALSE, FALSE,
5546 Xacid_6, FALSE, FALSE,
5550 Xacid_7, FALSE, FALSE,
5554 Xacid_8, FALSE, FALSE,
5558 Xball_1, TRUE, FALSE,
5559 EL_EMC_MAGIC_BALL, -1, -1
5562 Xball_1B, FALSE, FALSE,
5563 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5566 Xball_2, FALSE, FALSE,
5567 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5570 Xball_2B, FALSE, FALSE,
5571 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5574 Yball_eat, FALSE, FALSE,
5575 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5578 Ykey_1_eat, FALSE, FALSE,
5579 EL_EM_KEY_1, ACTION_COLLECTING, -1
5582 Ykey_2_eat, FALSE, FALSE,
5583 EL_EM_KEY_2, ACTION_COLLECTING, -1
5586 Ykey_3_eat, FALSE, FALSE,
5587 EL_EM_KEY_3, ACTION_COLLECTING, -1
5590 Ykey_4_eat, FALSE, FALSE,
5591 EL_EM_KEY_4, ACTION_COLLECTING, -1
5594 Ykey_5_eat, FALSE, FALSE,
5595 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5598 Ykey_6_eat, FALSE, FALSE,
5599 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5602 Ykey_7_eat, FALSE, FALSE,
5603 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5606 Ykey_8_eat, FALSE, FALSE,
5607 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5610 Ylenses_eat, FALSE, FALSE,
5611 EL_EMC_LENSES, ACTION_COLLECTING, -1
5614 Ymagnify_eat, FALSE, FALSE,
5615 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5618 Ygrass_eat, FALSE, FALSE,
5619 EL_EMC_GRASS, ACTION_SNAPPING, -1
5622 Ydirt_eat, FALSE, FALSE,
5623 EL_SAND, ACTION_SNAPPING, -1
5626 Xgrow_ns, TRUE, FALSE,
5627 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5630 Ygrow_ns_eat, FALSE, FALSE,
5631 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5634 Xgrow_ew, TRUE, FALSE,
5635 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5638 Ygrow_ew_eat, FALSE, FALSE,
5639 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5642 Xwonderwall, TRUE, FALSE,
5643 EL_MAGIC_WALL, -1, -1
5646 XwonderwallB, FALSE, FALSE,
5647 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5650 Xamoeba_1, TRUE, FALSE,
5651 EL_AMOEBA_DRY, ACTION_OTHER, -1
5654 Xamoeba_2, FALSE, FALSE,
5655 EL_AMOEBA_DRY, ACTION_OTHER, -1
5658 Xamoeba_3, FALSE, FALSE,
5659 EL_AMOEBA_DRY, ACTION_OTHER, -1
5662 Xamoeba_4, FALSE, FALSE,
5663 EL_AMOEBA_DRY, ACTION_OTHER, -1
5666 Xamoeba_5, TRUE, FALSE,
5667 EL_AMOEBA_WET, ACTION_OTHER, -1
5670 Xamoeba_6, FALSE, FALSE,
5671 EL_AMOEBA_WET, ACTION_OTHER, -1
5674 Xamoeba_7, FALSE, FALSE,
5675 EL_AMOEBA_WET, ACTION_OTHER, -1
5678 Xamoeba_8, FALSE, FALSE,
5679 EL_AMOEBA_WET, ACTION_OTHER, -1
5682 Xdoor_1, TRUE, FALSE,
5683 EL_EM_GATE_1, -1, -1
5686 Xdoor_2, TRUE, FALSE,
5687 EL_EM_GATE_2, -1, -1
5690 Xdoor_3, TRUE, FALSE,
5691 EL_EM_GATE_3, -1, -1
5694 Xdoor_4, TRUE, FALSE,
5695 EL_EM_GATE_4, -1, -1
5698 Xdoor_5, TRUE, FALSE,
5699 EL_EMC_GATE_5, -1, -1
5702 Xdoor_6, TRUE, FALSE,
5703 EL_EMC_GATE_6, -1, -1
5706 Xdoor_7, TRUE, FALSE,
5707 EL_EMC_GATE_7, -1, -1
5710 Xdoor_8, TRUE, FALSE,
5711 EL_EMC_GATE_8, -1, -1
5714 Xkey_1, TRUE, FALSE,
5718 Xkey_2, TRUE, FALSE,
5722 Xkey_3, TRUE, FALSE,
5726 Xkey_4, TRUE, FALSE,
5730 Xkey_5, TRUE, FALSE,
5731 EL_EMC_KEY_5, -1, -1
5734 Xkey_6, TRUE, FALSE,
5735 EL_EMC_KEY_6, -1, -1
5738 Xkey_7, TRUE, FALSE,
5739 EL_EMC_KEY_7, -1, -1
5742 Xkey_8, TRUE, FALSE,
5743 EL_EMC_KEY_8, -1, -1
5746 Xwind_n, TRUE, FALSE,
5747 EL_BALLOON_SWITCH_UP, -1, -1
5750 Xwind_e, TRUE, FALSE,
5751 EL_BALLOON_SWITCH_RIGHT, -1, -1
5754 Xwind_s, TRUE, FALSE,
5755 EL_BALLOON_SWITCH_DOWN, -1, -1
5758 Xwind_w, TRUE, FALSE,
5759 EL_BALLOON_SWITCH_LEFT, -1, -1
5762 Xwind_nesw, TRUE, FALSE,
5763 EL_BALLOON_SWITCH_ANY, -1, -1
5766 Xwind_stop, TRUE, FALSE,
5767 EL_BALLOON_SWITCH_NONE, -1, -1
5771 EL_EM_EXIT_CLOSED, -1, -1
5774 Xexit_1, TRUE, FALSE,
5775 EL_EM_EXIT_OPEN, -1, -1
5778 Xexit_2, FALSE, FALSE,
5779 EL_EM_EXIT_OPEN, -1, -1
5782 Xexit_3, FALSE, FALSE,
5783 EL_EM_EXIT_OPEN, -1, -1
5786 Xdynamite, TRUE, FALSE,
5787 EL_EM_DYNAMITE, -1, -1
5790 Ydynamite_eat, FALSE, FALSE,
5791 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5794 Xdynamite_1, TRUE, FALSE,
5795 EL_EM_DYNAMITE_ACTIVE, -1, -1
5798 Xdynamite_2, FALSE, FALSE,
5799 EL_EM_DYNAMITE_ACTIVE, -1, -1
5802 Xdynamite_3, FALSE, FALSE,
5803 EL_EM_DYNAMITE_ACTIVE, -1, -1
5806 Xdynamite_4, FALSE, FALSE,
5807 EL_EM_DYNAMITE_ACTIVE, -1, -1
5810 Xbumper, TRUE, FALSE,
5811 EL_EMC_SPRING_BUMPER, -1, -1
5814 XbumperB, FALSE, FALSE,
5815 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5818 Xwheel, TRUE, FALSE,
5819 EL_ROBOT_WHEEL, -1, -1
5822 XwheelB, FALSE, FALSE,
5823 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5826 Xswitch, TRUE, FALSE,
5827 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5830 XswitchB, FALSE, FALSE,
5831 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5835 EL_QUICKSAND_EMPTY, -1, -1
5838 Xsand_stone, TRUE, FALSE,
5839 EL_QUICKSAND_FULL, -1, -1
5842 Xsand_stonein_1, FALSE, TRUE,
5843 EL_ROCK, ACTION_FILLING, -1
5846 Xsand_stonein_2, FALSE, TRUE,
5847 EL_ROCK, ACTION_FILLING, -1
5850 Xsand_stonein_3, FALSE, TRUE,
5851 EL_ROCK, ACTION_FILLING, -1
5854 Xsand_stonein_4, FALSE, TRUE,
5855 EL_ROCK, ACTION_FILLING, -1
5858 Xsand_stonesand_1, FALSE, FALSE,
5859 EL_QUICKSAND_EMPTYING, -1, -1
5862 Xsand_stonesand_2, FALSE, FALSE,
5863 EL_QUICKSAND_EMPTYING, -1, -1
5866 Xsand_stonesand_3, FALSE, FALSE,
5867 EL_QUICKSAND_EMPTYING, -1, -1
5870 Xsand_stonesand_4, FALSE, FALSE,
5871 EL_QUICKSAND_EMPTYING, -1, -1
5874 Xsand_stonesand_quickout_1, FALSE, FALSE,
5875 EL_QUICKSAND_EMPTYING, -1, -1
5878 Xsand_stonesand_quickout_2, FALSE, FALSE,
5879 EL_QUICKSAND_EMPTYING, -1, -1
5882 Xsand_stoneout_1, FALSE, FALSE,
5883 EL_ROCK, ACTION_EMPTYING, -1
5886 Xsand_stoneout_2, FALSE, FALSE,
5887 EL_ROCK, ACTION_EMPTYING, -1
5890 Xsand_sandstone_1, FALSE, FALSE,
5891 EL_QUICKSAND_FILLING, -1, -1
5894 Xsand_sandstone_2, FALSE, FALSE,
5895 EL_QUICKSAND_FILLING, -1, -1
5898 Xsand_sandstone_3, FALSE, FALSE,
5899 EL_QUICKSAND_FILLING, -1, -1
5902 Xsand_sandstone_4, FALSE, FALSE,
5903 EL_QUICKSAND_FILLING, -1, -1
5906 Xplant, TRUE, FALSE,
5907 EL_EMC_PLANT, -1, -1
5910 Yplant, FALSE, FALSE,
5911 EL_EMC_PLANT, -1, -1
5914 Xlenses, TRUE, FALSE,
5915 EL_EMC_LENSES, -1, -1
5918 Xmagnify, TRUE, FALSE,
5919 EL_EMC_MAGNIFIER, -1, -1
5922 Xdripper, TRUE, FALSE,
5923 EL_EMC_DRIPPER, -1, -1
5926 XdripperB, FALSE, FALSE,
5927 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5930 Xfake_blank, TRUE, FALSE,
5931 EL_INVISIBLE_WALL, -1, -1
5934 Xfake_blankB, FALSE, FALSE,
5935 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5938 Xfake_grass, TRUE, FALSE,
5939 EL_EMC_FAKE_GRASS, -1, -1
5942 Xfake_grassB, FALSE, FALSE,
5943 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5946 Xfake_door_1, TRUE, FALSE,
5947 EL_EM_GATE_1_GRAY, -1, -1
5950 Xfake_door_2, TRUE, FALSE,
5951 EL_EM_GATE_2_GRAY, -1, -1
5954 Xfake_door_3, TRUE, FALSE,
5955 EL_EM_GATE_3_GRAY, -1, -1
5958 Xfake_door_4, TRUE, FALSE,
5959 EL_EM_GATE_4_GRAY, -1, -1
5962 Xfake_door_5, TRUE, FALSE,
5963 EL_EMC_GATE_5_GRAY, -1, -1
5966 Xfake_door_6, TRUE, FALSE,
5967 EL_EMC_GATE_6_GRAY, -1, -1
5970 Xfake_door_7, TRUE, FALSE,
5971 EL_EMC_GATE_7_GRAY, -1, -1
5974 Xfake_door_8, TRUE, FALSE,
5975 EL_EMC_GATE_8_GRAY, -1, -1
5978 Xfake_acid_1, TRUE, FALSE,
5979 EL_EMC_FAKE_ACID, -1, -1
5982 Xfake_acid_2, FALSE, FALSE,
5983 EL_EMC_FAKE_ACID, -1, -1
5986 Xfake_acid_3, FALSE, FALSE,
5987 EL_EMC_FAKE_ACID, -1, -1
5990 Xfake_acid_4, FALSE, FALSE,
5991 EL_EMC_FAKE_ACID, -1, -1
5994 Xfake_acid_5, FALSE, FALSE,
5995 EL_EMC_FAKE_ACID, -1, -1
5998 Xfake_acid_6, FALSE, FALSE,
5999 EL_EMC_FAKE_ACID, -1, -1
6002 Xfake_acid_7, FALSE, FALSE,
6003 EL_EMC_FAKE_ACID, -1, -1
6006 Xfake_acid_8, FALSE, FALSE,
6007 EL_EMC_FAKE_ACID, -1, -1
6010 Xsteel_1, TRUE, FALSE,
6011 EL_STEELWALL, -1, -1
6014 Xsteel_2, TRUE, FALSE,
6015 EL_EMC_STEELWALL_2, -1, -1
6018 Xsteel_3, TRUE, FALSE,
6019 EL_EMC_STEELWALL_3, -1, -1
6022 Xsteel_4, TRUE, FALSE,
6023 EL_EMC_STEELWALL_4, -1, -1
6026 Xwall_1, TRUE, FALSE,
6030 Xwall_2, TRUE, FALSE,
6031 EL_EMC_WALL_14, -1, -1
6034 Xwall_3, TRUE, FALSE,
6035 EL_EMC_WALL_15, -1, -1
6038 Xwall_4, TRUE, FALSE,
6039 EL_EMC_WALL_16, -1, -1
6042 Xround_wall_1, TRUE, FALSE,
6043 EL_WALL_SLIPPERY, -1, -1
6046 Xround_wall_2, TRUE, FALSE,
6047 EL_EMC_WALL_SLIPPERY_2, -1, -1
6050 Xround_wall_3, TRUE, FALSE,
6051 EL_EMC_WALL_SLIPPERY_3, -1, -1
6054 Xround_wall_4, TRUE, FALSE,
6055 EL_EMC_WALL_SLIPPERY_4, -1, -1
6058 Xdecor_1, TRUE, FALSE,
6059 EL_EMC_WALL_8, -1, -1
6062 Xdecor_2, TRUE, FALSE,
6063 EL_EMC_WALL_6, -1, -1
6066 Xdecor_3, TRUE, FALSE,
6067 EL_EMC_WALL_4, -1, -1
6070 Xdecor_4, TRUE, FALSE,
6071 EL_EMC_WALL_7, -1, -1
6074 Xdecor_5, TRUE, FALSE,
6075 EL_EMC_WALL_5, -1, -1
6078 Xdecor_6, TRUE, FALSE,
6079 EL_EMC_WALL_9, -1, -1
6082 Xdecor_7, TRUE, FALSE,
6083 EL_EMC_WALL_10, -1, -1
6086 Xdecor_8, TRUE, FALSE,
6087 EL_EMC_WALL_1, -1, -1
6090 Xdecor_9, TRUE, FALSE,
6091 EL_EMC_WALL_2, -1, -1
6094 Xdecor_10, TRUE, FALSE,
6095 EL_EMC_WALL_3, -1, -1
6098 Xdecor_11, TRUE, FALSE,
6099 EL_EMC_WALL_11, -1, -1
6102 Xdecor_12, TRUE, FALSE,
6103 EL_EMC_WALL_12, -1, -1
6106 Xalpha_0, TRUE, FALSE,
6107 EL_CHAR('0'), -1, -1
6110 Xalpha_1, TRUE, FALSE,
6111 EL_CHAR('1'), -1, -1
6114 Xalpha_2, TRUE, FALSE,
6115 EL_CHAR('2'), -1, -1
6118 Xalpha_3, TRUE, FALSE,
6119 EL_CHAR('3'), -1, -1
6122 Xalpha_4, TRUE, FALSE,
6123 EL_CHAR('4'), -1, -1
6126 Xalpha_5, TRUE, FALSE,
6127 EL_CHAR('5'), -1, -1
6130 Xalpha_6, TRUE, FALSE,
6131 EL_CHAR('6'), -1, -1
6134 Xalpha_7, TRUE, FALSE,
6135 EL_CHAR('7'), -1, -1
6138 Xalpha_8, TRUE, FALSE,
6139 EL_CHAR('8'), -1, -1
6142 Xalpha_9, TRUE, FALSE,
6143 EL_CHAR('9'), -1, -1
6146 Xalpha_excla, TRUE, FALSE,
6147 EL_CHAR('!'), -1, -1
6150 Xalpha_quote, TRUE, FALSE,
6151 EL_CHAR('"'), -1, -1
6154 Xalpha_comma, TRUE, FALSE,
6155 EL_CHAR(','), -1, -1
6158 Xalpha_minus, TRUE, FALSE,
6159 EL_CHAR('-'), -1, -1
6162 Xalpha_perio, TRUE, FALSE,
6163 EL_CHAR('.'), -1, -1
6166 Xalpha_colon, TRUE, FALSE,
6167 EL_CHAR(':'), -1, -1
6170 Xalpha_quest, TRUE, FALSE,
6171 EL_CHAR('?'), -1, -1
6174 Xalpha_a, TRUE, FALSE,
6175 EL_CHAR('A'), -1, -1
6178 Xalpha_b, TRUE, FALSE,
6179 EL_CHAR('B'), -1, -1
6182 Xalpha_c, TRUE, FALSE,
6183 EL_CHAR('C'), -1, -1
6186 Xalpha_d, TRUE, FALSE,
6187 EL_CHAR('D'), -1, -1
6190 Xalpha_e, TRUE, FALSE,
6191 EL_CHAR('E'), -1, -1
6194 Xalpha_f, TRUE, FALSE,
6195 EL_CHAR('F'), -1, -1
6198 Xalpha_g, TRUE, FALSE,
6199 EL_CHAR('G'), -1, -1
6202 Xalpha_h, TRUE, FALSE,
6203 EL_CHAR('H'), -1, -1
6206 Xalpha_i, TRUE, FALSE,
6207 EL_CHAR('I'), -1, -1
6210 Xalpha_j, TRUE, FALSE,
6211 EL_CHAR('J'), -1, -1
6214 Xalpha_k, TRUE, FALSE,
6215 EL_CHAR('K'), -1, -1
6218 Xalpha_l, TRUE, FALSE,
6219 EL_CHAR('L'), -1, -1
6222 Xalpha_m, TRUE, FALSE,
6223 EL_CHAR('M'), -1, -1
6226 Xalpha_n, TRUE, FALSE,
6227 EL_CHAR('N'), -1, -1
6230 Xalpha_o, TRUE, FALSE,
6231 EL_CHAR('O'), -1, -1
6234 Xalpha_p, TRUE, FALSE,
6235 EL_CHAR('P'), -1, -1
6238 Xalpha_q, TRUE, FALSE,
6239 EL_CHAR('Q'), -1, -1
6242 Xalpha_r, TRUE, FALSE,
6243 EL_CHAR('R'), -1, -1
6246 Xalpha_s, TRUE, FALSE,
6247 EL_CHAR('S'), -1, -1
6250 Xalpha_t, TRUE, FALSE,
6251 EL_CHAR('T'), -1, -1
6254 Xalpha_u, TRUE, FALSE,
6255 EL_CHAR('U'), -1, -1
6258 Xalpha_v, TRUE, FALSE,
6259 EL_CHAR('V'), -1, -1
6262 Xalpha_w, TRUE, FALSE,
6263 EL_CHAR('W'), -1, -1
6266 Xalpha_x, TRUE, FALSE,
6267 EL_CHAR('X'), -1, -1
6270 Xalpha_y, TRUE, FALSE,
6271 EL_CHAR('Y'), -1, -1
6274 Xalpha_z, TRUE, FALSE,
6275 EL_CHAR('Z'), -1, -1
6278 Xalpha_arrow_e, TRUE, FALSE,
6279 EL_CHAR('>'), -1, -1
6282 Xalpha_arrow_w, TRUE, FALSE,
6283 EL_CHAR('<'), -1, -1
6286 Xalpha_copyr, TRUE, FALSE,
6287 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6291 Xboom_bug, FALSE, FALSE,
6292 EL_BUG, ACTION_EXPLODING, -1
6295 Xboom_bomb, FALSE, FALSE,
6296 EL_BOMB, ACTION_EXPLODING, -1
6299 Xboom_android, FALSE, FALSE,
6300 EL_EMC_ANDROID, ACTION_OTHER, -1
6303 Xboom_1, FALSE, FALSE,
6304 EL_DEFAULT, ACTION_EXPLODING, -1
6307 Xboom_2, FALSE, FALSE,
6308 EL_DEFAULT, ACTION_EXPLODING, -1
6311 Znormal, FALSE, FALSE,
6315 Zdynamite, FALSE, FALSE,
6319 Zplayer, FALSE, FALSE,
6323 ZBORDER, FALSE, FALSE,
6333 static struct Mapping_EM_to_RND_player
6342 em_player_mapping_list[] =
6346 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6350 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6354 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6358 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6362 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6366 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6370 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6374 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6378 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6382 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6386 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6390 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6394 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6398 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6402 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6406 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6410 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6414 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6418 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6422 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6426 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6430 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6434 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6438 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6442 EL_PLAYER_1, ACTION_DEFAULT, -1,
6446 EL_PLAYER_2, ACTION_DEFAULT, -1,
6450 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6454 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6458 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6462 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6466 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6470 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6474 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6478 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6482 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6486 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6490 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6494 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6498 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6502 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6506 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6510 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6514 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6518 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6522 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6526 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6530 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6534 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6538 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6542 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6546 EL_PLAYER_3, ACTION_DEFAULT, -1,
6550 EL_PLAYER_4, ACTION_DEFAULT, -1,
6559 int map_element_RND_to_EM(int element_rnd)
6561 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6562 static boolean mapping_initialized = FALSE;
6564 if (!mapping_initialized)
6568 /* return "Xalpha_quest" for all undefined elements in mapping array */
6569 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6570 mapping_RND_to_EM[i] = Xalpha_quest;
6572 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6573 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6574 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6575 em_object_mapping_list[i].element_em;
6577 mapping_initialized = TRUE;
6580 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6581 return mapping_RND_to_EM[element_rnd];
6583 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6588 int map_element_EM_to_RND(int element_em)
6590 static unsigned short mapping_EM_to_RND[TILE_MAX];
6591 static boolean mapping_initialized = FALSE;
6593 if (!mapping_initialized)
6597 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6598 for (i = 0; i < TILE_MAX; i++)
6599 mapping_EM_to_RND[i] = EL_UNKNOWN;
6601 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6602 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6603 em_object_mapping_list[i].element_rnd;
6605 mapping_initialized = TRUE;
6608 if (element_em >= 0 && element_em < TILE_MAX)
6609 return mapping_EM_to_RND[element_em];
6611 Error(ERR_WARN, "invalid EM level element %d", element_em);
6616 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6618 struct LevelInfo_EM *level_em = level->native_em_level;
6619 struct LEVEL *lev = level_em->lev;
6622 for (i = 0; i < TILE_MAX; i++)
6623 lev->android_array[i] = Xblank;
6625 for (i = 0; i < level->num_android_clone_elements; i++)
6627 int element_rnd = level->android_clone_element[i];
6628 int element_em = map_element_RND_to_EM(element_rnd);
6630 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6631 if (em_object_mapping_list[j].element_rnd == element_rnd)
6632 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6636 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6638 struct LevelInfo_EM *level_em = level->native_em_level;
6639 struct LEVEL *lev = level_em->lev;
6642 level->num_android_clone_elements = 0;
6644 for (i = 0; i < TILE_MAX; i++)
6646 int element_em = lev->android_array[i];
6648 boolean element_found = FALSE;
6650 if (element_em == Xblank)
6653 element_rnd = map_element_EM_to_RND(element_em);
6655 for (j = 0; j < level->num_android_clone_elements; j++)
6656 if (level->android_clone_element[j] == element_rnd)
6657 element_found = TRUE;
6661 level->android_clone_element[level->num_android_clone_elements++] =
6664 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6669 if (level->num_android_clone_elements == 0)
6671 level->num_android_clone_elements = 1;
6672 level->android_clone_element[0] = EL_EMPTY;
6676 int map_direction_RND_to_EM(int direction)
6678 return (direction == MV_UP ? 0 :
6679 direction == MV_RIGHT ? 1 :
6680 direction == MV_DOWN ? 2 :
6681 direction == MV_LEFT ? 3 :
6685 int map_direction_EM_to_RND(int direction)
6687 return (direction == 0 ? MV_UP :
6688 direction == 1 ? MV_RIGHT :
6689 direction == 2 ? MV_DOWN :
6690 direction == 3 ? MV_LEFT :
6694 int map_element_RND_to_SP(int element_rnd)
6696 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6698 if (element_rnd >= EL_SP_START &&
6699 element_rnd <= EL_SP_END)
6700 element_sp = element_rnd - EL_SP_START;
6701 else if (element_rnd == EL_EMPTY_SPACE)
6703 else if (element_rnd == EL_INVISIBLE_WALL)
6709 int map_element_SP_to_RND(int element_sp)
6711 int element_rnd = EL_UNKNOWN;
6713 if (element_sp >= 0x00 &&
6715 element_rnd = EL_SP_START + element_sp;
6716 else if (element_sp == 0x28)
6717 element_rnd = EL_INVISIBLE_WALL;
6722 int map_action_SP_to_RND(int action_sp)
6726 case actActive: return ACTION_ACTIVE;
6727 case actImpact: return ACTION_IMPACT;
6728 case actExploding: return ACTION_EXPLODING;
6729 case actDigging: return ACTION_DIGGING;
6730 case actSnapping: return ACTION_SNAPPING;
6731 case actCollecting: return ACTION_COLLECTING;
6732 case actPassing: return ACTION_PASSING;
6733 case actPushing: return ACTION_PUSHING;
6734 case actDropping: return ACTION_DROPPING;
6736 default: return ACTION_DEFAULT;
6740 int get_next_element(int element)
6744 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6745 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6746 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6747 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6748 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6749 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6750 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6751 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6752 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6753 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6754 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6756 default: return element;
6760 int el_act_dir2img(int element, int action, int direction)
6762 element = GFX_ELEMENT(element);
6763 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6765 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6766 return element_info[element].direction_graphic[action][direction];
6769 static int el_act_dir2crm(int element, int action, int direction)
6771 element = GFX_ELEMENT(element);
6772 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6774 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6775 return element_info[element].direction_crumbled[action][direction];
6778 int el_act2img(int element, int action)
6780 element = GFX_ELEMENT(element);
6782 return element_info[element].graphic[action];
6785 int el_act2crm(int element, int action)
6787 element = GFX_ELEMENT(element);
6789 return element_info[element].crumbled[action];
6792 int el_dir2img(int element, int direction)
6794 element = GFX_ELEMENT(element);
6796 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6799 int el2baseimg(int element)
6801 return element_info[element].graphic[ACTION_DEFAULT];
6804 int el2img(int element)
6806 element = GFX_ELEMENT(element);
6808 return element_info[element].graphic[ACTION_DEFAULT];
6811 int el2edimg(int element)
6813 element = GFX_ELEMENT(element);
6815 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6818 int el2preimg(int element)
6820 element = GFX_ELEMENT(element);
6822 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6825 int el2panelimg(int element)
6827 element = GFX_ELEMENT(element);
6829 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6832 int font2baseimg(int font_nr)
6834 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6837 int getBeltNrFromBeltElement(int element)
6839 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6840 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6841 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6844 int getBeltNrFromBeltActiveElement(int element)
6846 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6847 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6848 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6851 int getBeltNrFromBeltSwitchElement(int element)
6853 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6854 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6855 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6858 int getBeltDirNrFromBeltElement(int element)
6860 static int belt_base_element[4] =
6862 EL_CONVEYOR_BELT_1_LEFT,
6863 EL_CONVEYOR_BELT_2_LEFT,
6864 EL_CONVEYOR_BELT_3_LEFT,
6865 EL_CONVEYOR_BELT_4_LEFT
6868 int belt_nr = getBeltNrFromBeltElement(element);
6869 int belt_dir_nr = element - belt_base_element[belt_nr];
6871 return (belt_dir_nr % 3);
6874 int getBeltDirNrFromBeltSwitchElement(int element)
6876 static int belt_base_element[4] =
6878 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6879 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6880 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6881 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6884 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6885 int belt_dir_nr = element - belt_base_element[belt_nr];
6887 return (belt_dir_nr % 3);
6890 int getBeltDirFromBeltElement(int element)
6892 static int belt_move_dir[3] =
6899 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6901 return belt_move_dir[belt_dir_nr];
6904 int getBeltDirFromBeltSwitchElement(int element)
6906 static int belt_move_dir[3] =
6913 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6915 return belt_move_dir[belt_dir_nr];
6918 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6920 static int belt_base_element[4] =
6922 EL_CONVEYOR_BELT_1_LEFT,
6923 EL_CONVEYOR_BELT_2_LEFT,
6924 EL_CONVEYOR_BELT_3_LEFT,
6925 EL_CONVEYOR_BELT_4_LEFT
6928 return belt_base_element[belt_nr] + belt_dir_nr;
6931 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6933 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6935 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6938 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6940 static int belt_base_element[4] =
6942 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6943 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6944 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6945 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6948 return belt_base_element[belt_nr] + belt_dir_nr;
6951 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6953 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6955 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6958 boolean getTeamMode_EM()
6960 return game.team_mode;
6963 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6965 int game_frame_delay_value;
6967 game_frame_delay_value =
6968 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6969 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6972 if (tape.playing && tape.warp_forward && !tape.pausing)
6973 game_frame_delay_value = 0;
6975 return game_frame_delay_value;
6978 unsigned int InitRND(int seed)
6980 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6981 return InitEngineRandom_EM(seed);
6982 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6983 return InitEngineRandom_SP(seed);
6985 return InitEngineRandom_RND(seed);
6988 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6989 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6991 inline static int get_effective_element_EM(int tile, int frame_em)
6993 int element = object_mapping[tile].element_rnd;
6994 int action = object_mapping[tile].action;
6995 boolean is_backside = object_mapping[tile].is_backside;
6996 boolean action_removing = (action == ACTION_DIGGING ||
6997 action == ACTION_SNAPPING ||
6998 action == ACTION_COLLECTING);
7004 case Yacid_splash_eB:
7005 case Yacid_splash_wB:
7006 return (frame_em > 5 ? EL_EMPTY : element);
7012 else /* frame_em == 7 */
7016 case Yacid_splash_eB:
7017 case Yacid_splash_wB:
7020 case Yemerald_stone:
7023 case Ydiamond_stone:
7027 case Xdrip_stretchB:
7046 case Xsand_stonein_1:
7047 case Xsand_stonein_2:
7048 case Xsand_stonein_3:
7049 case Xsand_stonein_4:
7053 return (is_backside || action_removing ? EL_EMPTY : element);
7058 inline static boolean check_linear_animation_EM(int tile)
7062 case Xsand_stonesand_1:
7063 case Xsand_stonesand_quickout_1:
7064 case Xsand_sandstone_1:
7065 case Xsand_stonein_1:
7066 case Xsand_stoneout_1:
7085 case Yacid_splash_eB:
7086 case Yacid_splash_wB:
7087 case Yemerald_stone:
7094 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7095 boolean has_crumbled_graphics,
7096 int crumbled, int sync_frame)
7098 /* if element can be crumbled, but certain action graphics are just empty
7099 space (like instantly snapping sand to empty space in 1 frame), do not
7100 treat these empty space graphics as crumbled graphics in EMC engine */
7101 if (crumbled == IMG_EMPTY_SPACE)
7102 has_crumbled_graphics = FALSE;
7104 if (has_crumbled_graphics)
7106 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7107 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7108 g_crumbled->anim_delay,
7109 g_crumbled->anim_mode,
7110 g_crumbled->anim_start_frame,
7113 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7114 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7116 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7118 g_em->has_crumbled_graphics = TRUE;
7122 g_em->crumbled_bitmap = NULL;
7123 g_em->crumbled_src_x = 0;
7124 g_em->crumbled_src_y = 0;
7125 g_em->crumbled_border_size = 0;
7127 g_em->has_crumbled_graphics = FALSE;
7131 void ResetGfxAnimation_EM(int x, int y, int tile)
7136 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7137 int tile, int frame_em, int x, int y)
7139 int action = object_mapping[tile].action;
7140 int direction = object_mapping[tile].direction;
7141 int effective_element = get_effective_element_EM(tile, frame_em);
7142 int graphic = (direction == MV_NONE ?
7143 el_act2img(effective_element, action) :
7144 el_act_dir2img(effective_element, action, direction));
7145 struct GraphicInfo *g = &graphic_info[graphic];
7147 boolean action_removing = (action == ACTION_DIGGING ||
7148 action == ACTION_SNAPPING ||
7149 action == ACTION_COLLECTING);
7150 boolean action_moving = (action == ACTION_FALLING ||
7151 action == ACTION_MOVING ||
7152 action == ACTION_PUSHING ||
7153 action == ACTION_EATING ||
7154 action == ACTION_FILLING ||
7155 action == ACTION_EMPTYING);
7156 boolean action_falling = (action == ACTION_FALLING ||
7157 action == ACTION_FILLING ||
7158 action == ACTION_EMPTYING);
7160 /* special case: graphic uses "2nd movement tile" and has defined
7161 7 frames for movement animation (or less) => use default graphic
7162 for last (8th) frame which ends the movement animation */
7163 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7165 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7166 graphic = (direction == MV_NONE ?
7167 el_act2img(effective_element, action) :
7168 el_act_dir2img(effective_element, action, direction));
7170 g = &graphic_info[graphic];
7173 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7177 else if (action_moving)
7179 boolean is_backside = object_mapping[tile].is_backside;
7183 int direction = object_mapping[tile].direction;
7184 int move_dir = (action_falling ? MV_DOWN : direction);
7189 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7190 if (g->double_movement && frame_em == 0)
7194 if (move_dir == MV_LEFT)
7195 GfxFrame[x - 1][y] = GfxFrame[x][y];
7196 else if (move_dir == MV_RIGHT)
7197 GfxFrame[x + 1][y] = GfxFrame[x][y];
7198 else if (move_dir == MV_UP)
7199 GfxFrame[x][y - 1] = GfxFrame[x][y];
7200 else if (move_dir == MV_DOWN)
7201 GfxFrame[x][y + 1] = GfxFrame[x][y];
7208 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7209 if (tile == Xsand_stonesand_quickout_1 ||
7210 tile == Xsand_stonesand_quickout_2)
7214 if (graphic_info[graphic].anim_global_sync)
7215 sync_frame = FrameCounter;
7216 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7217 sync_frame = GfxFrame[x][y];
7219 sync_frame = 0; /* playfield border (pseudo steel) */
7221 SetRandomAnimationValue(x, y);
7223 int frame = getAnimationFrame(g->anim_frames,
7226 g->anim_start_frame,
7229 g_em->unique_identifier =
7230 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7233 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7234 int tile, int frame_em, int x, int y)
7236 int action = object_mapping[tile].action;
7237 int direction = object_mapping[tile].direction;
7238 boolean is_backside = object_mapping[tile].is_backside;
7239 int effective_element = get_effective_element_EM(tile, frame_em);
7240 int effective_action = action;
7241 int graphic = (direction == MV_NONE ?
7242 el_act2img(effective_element, effective_action) :
7243 el_act_dir2img(effective_element, effective_action,
7245 int crumbled = (direction == MV_NONE ?
7246 el_act2crm(effective_element, effective_action) :
7247 el_act_dir2crm(effective_element, effective_action,
7249 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7250 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7251 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7252 struct GraphicInfo *g = &graphic_info[graphic];
7255 /* special case: graphic uses "2nd movement tile" and has defined
7256 7 frames for movement animation (or less) => use default graphic
7257 for last (8th) frame which ends the movement animation */
7258 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7260 effective_action = ACTION_DEFAULT;
7261 graphic = (direction == MV_NONE ?
7262 el_act2img(effective_element, effective_action) :
7263 el_act_dir2img(effective_element, effective_action,
7265 crumbled = (direction == MV_NONE ?
7266 el_act2crm(effective_element, effective_action) :
7267 el_act_dir2crm(effective_element, effective_action,
7270 g = &graphic_info[graphic];
7273 if (graphic_info[graphic].anim_global_sync)
7274 sync_frame = FrameCounter;
7275 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7276 sync_frame = GfxFrame[x][y];
7278 sync_frame = 0; /* playfield border (pseudo steel) */
7280 SetRandomAnimationValue(x, y);
7282 int frame = getAnimationFrame(g->anim_frames,
7285 g->anim_start_frame,
7288 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7289 g->double_movement && is_backside);
7291 /* (updating the "crumbled" graphic definitions is probably not really needed,
7292 as animations for crumbled graphics can't be longer than one EMC cycle) */
7293 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7297 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7298 int player_nr, int anim, int frame_em)
7300 int element = player_mapping[player_nr][anim].element_rnd;
7301 int action = player_mapping[player_nr][anim].action;
7302 int direction = player_mapping[player_nr][anim].direction;
7303 int graphic = (direction == MV_NONE ?
7304 el_act2img(element, action) :
7305 el_act_dir2img(element, action, direction));
7306 struct GraphicInfo *g = &graphic_info[graphic];
7309 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7311 stored_player[player_nr].StepFrame = frame_em;
7313 sync_frame = stored_player[player_nr].Frame;
7315 int frame = getAnimationFrame(g->anim_frames,
7318 g->anim_start_frame,
7321 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7322 &g_em->src_x, &g_em->src_y, FALSE);
7325 void InitGraphicInfo_EM(void)
7330 int num_em_gfx_errors = 0;
7332 if (graphic_info_em_object[0][0].bitmap == NULL)
7334 /* EM graphics not yet initialized in em_open_all() */
7339 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7342 /* always start with reliable default values */
7343 for (i = 0; i < TILE_MAX; i++)
7345 object_mapping[i].element_rnd = EL_UNKNOWN;
7346 object_mapping[i].is_backside = FALSE;
7347 object_mapping[i].action = ACTION_DEFAULT;
7348 object_mapping[i].direction = MV_NONE;
7351 /* always start with reliable default values */
7352 for (p = 0; p < MAX_PLAYERS; p++)
7354 for (i = 0; i < SPR_MAX; i++)
7356 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7357 player_mapping[p][i].action = ACTION_DEFAULT;
7358 player_mapping[p][i].direction = MV_NONE;
7362 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7364 int e = em_object_mapping_list[i].element_em;
7366 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7367 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7369 if (em_object_mapping_list[i].action != -1)
7370 object_mapping[e].action = em_object_mapping_list[i].action;
7372 if (em_object_mapping_list[i].direction != -1)
7373 object_mapping[e].direction =
7374 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7377 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7379 int a = em_player_mapping_list[i].action_em;
7380 int p = em_player_mapping_list[i].player_nr;
7382 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7384 if (em_player_mapping_list[i].action != -1)
7385 player_mapping[p][a].action = em_player_mapping_list[i].action;
7387 if (em_player_mapping_list[i].direction != -1)
7388 player_mapping[p][a].direction =
7389 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7392 for (i = 0; i < TILE_MAX; i++)
7394 int element = object_mapping[i].element_rnd;
7395 int action = object_mapping[i].action;
7396 int direction = object_mapping[i].direction;
7397 boolean is_backside = object_mapping[i].is_backside;
7398 boolean action_exploding = ((action == ACTION_EXPLODING ||
7399 action == ACTION_SMASHED_BY_ROCK ||
7400 action == ACTION_SMASHED_BY_SPRING) &&
7401 element != EL_DIAMOND);
7402 boolean action_active = (action == ACTION_ACTIVE);
7403 boolean action_other = (action == ACTION_OTHER);
7405 for (j = 0; j < 8; j++)
7407 int effective_element = get_effective_element_EM(i, j);
7408 int effective_action = (j < 7 ? action :
7409 i == Xdrip_stretch ? action :
7410 i == Xdrip_stretchB ? action :
7411 i == Ydrip_s1 ? action :
7412 i == Ydrip_s1B ? action :
7413 i == Xball_1B ? action :
7414 i == Xball_2 ? action :
7415 i == Xball_2B ? action :
7416 i == Yball_eat ? action :
7417 i == Ykey_1_eat ? action :
7418 i == Ykey_2_eat ? action :
7419 i == Ykey_3_eat ? action :
7420 i == Ykey_4_eat ? action :
7421 i == Ykey_5_eat ? action :
7422 i == Ykey_6_eat ? action :
7423 i == Ykey_7_eat ? action :
7424 i == Ykey_8_eat ? action :
7425 i == Ylenses_eat ? action :
7426 i == Ymagnify_eat ? action :
7427 i == Ygrass_eat ? action :
7428 i == Ydirt_eat ? action :
7429 i == Xsand_stonein_1 ? action :
7430 i == Xsand_stonein_2 ? action :
7431 i == Xsand_stonein_3 ? action :
7432 i == Xsand_stonein_4 ? action :
7433 i == Xsand_stoneout_1 ? action :
7434 i == Xsand_stoneout_2 ? action :
7435 i == Xboom_android ? ACTION_EXPLODING :
7436 action_exploding ? ACTION_EXPLODING :
7437 action_active ? action :
7438 action_other ? action :
7440 int graphic = (el_act_dir2img(effective_element, effective_action,
7442 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7444 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7445 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7446 boolean has_action_graphics = (graphic != base_graphic);
7447 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7448 struct GraphicInfo *g = &graphic_info[graphic];
7449 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7452 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7453 boolean special_animation = (action != ACTION_DEFAULT &&
7454 g->anim_frames == 3 &&
7455 g->anim_delay == 2 &&
7456 g->anim_mode & ANIM_LINEAR);
7457 int sync_frame = (i == Xdrip_stretch ? 7 :
7458 i == Xdrip_stretchB ? 7 :
7459 i == Ydrip_s2 ? j + 8 :
7460 i == Ydrip_s2B ? j + 8 :
7469 i == Xfake_acid_1 ? 0 :
7470 i == Xfake_acid_2 ? 10 :
7471 i == Xfake_acid_3 ? 20 :
7472 i == Xfake_acid_4 ? 30 :
7473 i == Xfake_acid_5 ? 40 :
7474 i == Xfake_acid_6 ? 50 :
7475 i == Xfake_acid_7 ? 60 :
7476 i == Xfake_acid_8 ? 70 :
7478 i == Xball_2B ? j + 8 :
7479 i == Yball_eat ? j + 1 :
7480 i == Ykey_1_eat ? j + 1 :
7481 i == Ykey_2_eat ? j + 1 :
7482 i == Ykey_3_eat ? j + 1 :
7483 i == Ykey_4_eat ? j + 1 :
7484 i == Ykey_5_eat ? j + 1 :
7485 i == Ykey_6_eat ? j + 1 :
7486 i == Ykey_7_eat ? j + 1 :
7487 i == Ykey_8_eat ? j + 1 :
7488 i == Ylenses_eat ? j + 1 :
7489 i == Ymagnify_eat ? j + 1 :
7490 i == Ygrass_eat ? j + 1 :
7491 i == Ydirt_eat ? j + 1 :
7492 i == Xamoeba_1 ? 0 :
7493 i == Xamoeba_2 ? 1 :
7494 i == Xamoeba_3 ? 2 :
7495 i == Xamoeba_4 ? 3 :
7496 i == Xamoeba_5 ? 0 :
7497 i == Xamoeba_6 ? 1 :
7498 i == Xamoeba_7 ? 2 :
7499 i == Xamoeba_8 ? 3 :
7500 i == Xexit_2 ? j + 8 :
7501 i == Xexit_3 ? j + 16 :
7502 i == Xdynamite_1 ? 0 :
7503 i == Xdynamite_2 ? 8 :
7504 i == Xdynamite_3 ? 16 :
7505 i == Xdynamite_4 ? 24 :
7506 i == Xsand_stonein_1 ? j + 1 :
7507 i == Xsand_stonein_2 ? j + 9 :
7508 i == Xsand_stonein_3 ? j + 17 :
7509 i == Xsand_stonein_4 ? j + 25 :
7510 i == Xsand_stoneout_1 && j == 0 ? 0 :
7511 i == Xsand_stoneout_1 && j == 1 ? 0 :
7512 i == Xsand_stoneout_1 && j == 2 ? 1 :
7513 i == Xsand_stoneout_1 && j == 3 ? 2 :
7514 i == Xsand_stoneout_1 && j == 4 ? 2 :
7515 i == Xsand_stoneout_1 && j == 5 ? 3 :
7516 i == Xsand_stoneout_1 && j == 6 ? 4 :
7517 i == Xsand_stoneout_1 && j == 7 ? 4 :
7518 i == Xsand_stoneout_2 && j == 0 ? 5 :
7519 i == Xsand_stoneout_2 && j == 1 ? 6 :
7520 i == Xsand_stoneout_2 && j == 2 ? 7 :
7521 i == Xsand_stoneout_2 && j == 3 ? 8 :
7522 i == Xsand_stoneout_2 && j == 4 ? 9 :
7523 i == Xsand_stoneout_2 && j == 5 ? 11 :
7524 i == Xsand_stoneout_2 && j == 6 ? 13 :
7525 i == Xsand_stoneout_2 && j == 7 ? 15 :
7526 i == Xboom_bug && j == 1 ? 2 :
7527 i == Xboom_bug && j == 2 ? 2 :
7528 i == Xboom_bug && j == 3 ? 4 :
7529 i == Xboom_bug && j == 4 ? 4 :
7530 i == Xboom_bug && j == 5 ? 2 :
7531 i == Xboom_bug && j == 6 ? 2 :
7532 i == Xboom_bug && j == 7 ? 0 :
7533 i == Xboom_bomb && j == 1 ? 2 :
7534 i == Xboom_bomb && j == 2 ? 2 :
7535 i == Xboom_bomb && j == 3 ? 4 :
7536 i == Xboom_bomb && j == 4 ? 4 :
7537 i == Xboom_bomb && j == 5 ? 2 :
7538 i == Xboom_bomb && j == 6 ? 2 :
7539 i == Xboom_bomb && j == 7 ? 0 :
7540 i == Xboom_android && j == 7 ? 6 :
7541 i == Xboom_1 && j == 1 ? 2 :
7542 i == Xboom_1 && j == 2 ? 2 :
7543 i == Xboom_1 && j == 3 ? 4 :
7544 i == Xboom_1 && j == 4 ? 4 :
7545 i == Xboom_1 && j == 5 ? 6 :
7546 i == Xboom_1 && j == 6 ? 6 :
7547 i == Xboom_1 && j == 7 ? 8 :
7548 i == Xboom_2 && j == 0 ? 8 :
7549 i == Xboom_2 && j == 1 ? 8 :
7550 i == Xboom_2 && j == 2 ? 10 :
7551 i == Xboom_2 && j == 3 ? 10 :
7552 i == Xboom_2 && j == 4 ? 10 :
7553 i == Xboom_2 && j == 5 ? 12 :
7554 i == Xboom_2 && j == 6 ? 12 :
7555 i == Xboom_2 && j == 7 ? 12 :
7556 special_animation && j == 4 ? 3 :
7557 effective_action != action ? 0 :
7561 Bitmap *debug_bitmap = g_em->bitmap;
7562 int debug_src_x = g_em->src_x;
7563 int debug_src_y = g_em->src_y;
7566 int frame = getAnimationFrame(g->anim_frames,
7569 g->anim_start_frame,
7572 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7573 g->double_movement && is_backside);
7575 g_em->bitmap = src_bitmap;
7576 g_em->src_x = src_x;
7577 g_em->src_y = src_y;
7578 g_em->src_offset_x = 0;
7579 g_em->src_offset_y = 0;
7580 g_em->dst_offset_x = 0;
7581 g_em->dst_offset_y = 0;
7582 g_em->width = TILEX;
7583 g_em->height = TILEY;
7585 g_em->preserve_background = FALSE;
7587 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7590 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7591 effective_action == ACTION_MOVING ||
7592 effective_action == ACTION_PUSHING ||
7593 effective_action == ACTION_EATING)) ||
7594 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7595 effective_action == ACTION_EMPTYING)))
7598 (effective_action == ACTION_FALLING ||
7599 effective_action == ACTION_FILLING ||
7600 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7601 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7602 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7603 int num_steps = (i == Ydrip_s1 ? 16 :
7604 i == Ydrip_s1B ? 16 :
7605 i == Ydrip_s2 ? 16 :
7606 i == Ydrip_s2B ? 16 :
7607 i == Xsand_stonein_1 ? 32 :
7608 i == Xsand_stonein_2 ? 32 :
7609 i == Xsand_stonein_3 ? 32 :
7610 i == Xsand_stonein_4 ? 32 :
7611 i == Xsand_stoneout_1 ? 16 :
7612 i == Xsand_stoneout_2 ? 16 : 8);
7613 int cx = ABS(dx) * (TILEX / num_steps);
7614 int cy = ABS(dy) * (TILEY / num_steps);
7615 int step_frame = (i == Ydrip_s2 ? j + 8 :
7616 i == Ydrip_s2B ? j + 8 :
7617 i == Xsand_stonein_2 ? j + 8 :
7618 i == Xsand_stonein_3 ? j + 16 :
7619 i == Xsand_stonein_4 ? j + 24 :
7620 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7621 int step = (is_backside ? step_frame : num_steps - step_frame);
7623 if (is_backside) /* tile where movement starts */
7625 if (dx < 0 || dy < 0)
7627 g_em->src_offset_x = cx * step;
7628 g_em->src_offset_y = cy * step;
7632 g_em->dst_offset_x = cx * step;
7633 g_em->dst_offset_y = cy * step;
7636 else /* tile where movement ends */
7638 if (dx < 0 || dy < 0)
7640 g_em->dst_offset_x = cx * step;
7641 g_em->dst_offset_y = cy * step;
7645 g_em->src_offset_x = cx * step;
7646 g_em->src_offset_y = cy * step;
7650 g_em->width = TILEX - cx * step;
7651 g_em->height = TILEY - cy * step;
7654 /* create unique graphic identifier to decide if tile must be redrawn */
7655 /* bit 31 - 16 (16 bit): EM style graphic
7656 bit 15 - 12 ( 4 bit): EM style frame
7657 bit 11 - 6 ( 6 bit): graphic width
7658 bit 5 - 0 ( 6 bit): graphic height */
7659 g_em->unique_identifier =
7660 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7664 /* skip check for EMC elements not contained in original EMC artwork */
7665 if (element == EL_EMC_FAKE_ACID)
7668 if (g_em->bitmap != debug_bitmap ||
7669 g_em->src_x != debug_src_x ||
7670 g_em->src_y != debug_src_y ||
7671 g_em->src_offset_x != 0 ||
7672 g_em->src_offset_y != 0 ||
7673 g_em->dst_offset_x != 0 ||
7674 g_em->dst_offset_y != 0 ||
7675 g_em->width != TILEX ||
7676 g_em->height != TILEY)
7678 static int last_i = -1;
7686 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7687 i, element, element_info[element].token_name,
7688 element_action_info[effective_action].suffix, direction);
7690 if (element != effective_element)
7691 printf(" [%d ('%s')]",
7693 element_info[effective_element].token_name);
7697 if (g_em->bitmap != debug_bitmap)
7698 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7699 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7701 if (g_em->src_x != debug_src_x ||
7702 g_em->src_y != debug_src_y)
7703 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7704 j, (is_backside ? 'B' : 'F'),
7705 g_em->src_x, g_em->src_y,
7706 g_em->src_x / 32, g_em->src_y / 32,
7707 debug_src_x, debug_src_y,
7708 debug_src_x / 32, debug_src_y / 32);
7710 if (g_em->src_offset_x != 0 ||
7711 g_em->src_offset_y != 0 ||
7712 g_em->dst_offset_x != 0 ||
7713 g_em->dst_offset_y != 0)
7714 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7716 g_em->src_offset_x, g_em->src_offset_y,
7717 g_em->dst_offset_x, g_em->dst_offset_y);
7719 if (g_em->width != TILEX ||
7720 g_em->height != TILEY)
7721 printf(" %d (%d): size %d,%d should be %d,%d\n",
7723 g_em->width, g_em->height, TILEX, TILEY);
7725 num_em_gfx_errors++;
7732 for (i = 0; i < TILE_MAX; i++)
7734 for (j = 0; j < 8; j++)
7736 int element = object_mapping[i].element_rnd;
7737 int action = object_mapping[i].action;
7738 int direction = object_mapping[i].direction;
7739 boolean is_backside = object_mapping[i].is_backside;
7740 int graphic_action = el_act_dir2img(element, action, direction);
7741 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7743 if ((action == ACTION_SMASHED_BY_ROCK ||
7744 action == ACTION_SMASHED_BY_SPRING ||
7745 action == ACTION_EATING) &&
7746 graphic_action == graphic_default)
7748 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7749 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7750 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7751 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7754 /* no separate animation for "smashed by rock" -- use rock instead */
7755 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7756 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7758 g_em->bitmap = g_xx->bitmap;
7759 g_em->src_x = g_xx->src_x;
7760 g_em->src_y = g_xx->src_y;
7761 g_em->src_offset_x = g_xx->src_offset_x;
7762 g_em->src_offset_y = g_xx->src_offset_y;
7763 g_em->dst_offset_x = g_xx->dst_offset_x;
7764 g_em->dst_offset_y = g_xx->dst_offset_y;
7765 g_em->width = g_xx->width;
7766 g_em->height = g_xx->height;
7767 g_em->unique_identifier = g_xx->unique_identifier;
7770 g_em->preserve_background = TRUE;
7775 for (p = 0; p < MAX_PLAYERS; p++)
7777 for (i = 0; i < SPR_MAX; i++)
7779 int element = player_mapping[p][i].element_rnd;
7780 int action = player_mapping[p][i].action;
7781 int direction = player_mapping[p][i].direction;
7783 for (j = 0; j < 8; j++)
7785 int effective_element = element;
7786 int effective_action = action;
7787 int graphic = (direction == MV_NONE ?
7788 el_act2img(effective_element, effective_action) :
7789 el_act_dir2img(effective_element, effective_action,
7791 struct GraphicInfo *g = &graphic_info[graphic];
7792 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7798 Bitmap *debug_bitmap = g_em->bitmap;
7799 int debug_src_x = g_em->src_x;
7800 int debug_src_y = g_em->src_y;
7803 int frame = getAnimationFrame(g->anim_frames,
7806 g->anim_start_frame,
7809 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7811 g_em->bitmap = src_bitmap;
7812 g_em->src_x = src_x;
7813 g_em->src_y = src_y;
7814 g_em->src_offset_x = 0;
7815 g_em->src_offset_y = 0;
7816 g_em->dst_offset_x = 0;
7817 g_em->dst_offset_y = 0;
7818 g_em->width = TILEX;
7819 g_em->height = TILEY;
7823 /* skip check for EMC elements not contained in original EMC artwork */
7824 if (element == EL_PLAYER_3 ||
7825 element == EL_PLAYER_4)
7828 if (g_em->bitmap != debug_bitmap ||
7829 g_em->src_x != debug_src_x ||
7830 g_em->src_y != debug_src_y)
7832 static int last_i = -1;
7840 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7841 p, i, element, element_info[element].token_name,
7842 element_action_info[effective_action].suffix, direction);
7844 if (element != effective_element)
7845 printf(" [%d ('%s')]",
7847 element_info[effective_element].token_name);
7851 if (g_em->bitmap != debug_bitmap)
7852 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7853 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7855 if (g_em->src_x != debug_src_x ||
7856 g_em->src_y != debug_src_y)
7857 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7859 g_em->src_x, g_em->src_y,
7860 g_em->src_x / 32, g_em->src_y / 32,
7861 debug_src_x, debug_src_y,
7862 debug_src_x / 32, debug_src_y / 32);
7864 num_em_gfx_errors++;
7874 printf("::: [%d errors found]\n", num_em_gfx_errors);
7880 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7881 boolean any_player_moving,
7882 boolean any_player_snapping,
7883 boolean any_player_dropping)
7885 static boolean player_was_waiting = TRUE;
7887 if (frame == 0 && !any_player_dropping)
7889 if (!player_was_waiting)
7891 if (!SaveEngineSnapshotToList())
7894 player_was_waiting = TRUE;
7897 else if (any_player_moving || any_player_snapping || any_player_dropping)
7899 player_was_waiting = FALSE;
7903 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7904 boolean murphy_is_dropping)
7906 static boolean player_was_waiting = TRUE;
7908 if (murphy_is_waiting)
7910 if (!player_was_waiting)
7912 if (!SaveEngineSnapshotToList())
7915 player_was_waiting = TRUE;
7920 player_was_waiting = FALSE;
7924 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7925 boolean any_player_moving,
7926 boolean any_player_snapping,
7927 boolean any_player_dropping)
7929 if (tape.single_step && tape.recording && !tape.pausing)
7930 if (frame == 0 && !any_player_dropping)
7931 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7933 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7934 any_player_snapping, any_player_dropping);
7937 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7938 boolean murphy_is_dropping)
7940 if (tape.single_step && tape.recording && !tape.pausing)
7941 if (murphy_is_waiting)
7942 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7944 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7947 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7948 int graphic, int sync_frame, int x, int y)
7950 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7952 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7955 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7957 return (IS_NEXT_FRAME(sync_frame, graphic));
7960 int getGraphicInfo_Delay(int graphic)
7962 return graphic_info[graphic].anim_delay;
7965 void PlayMenuSoundExt(int sound)
7967 if (sound == SND_UNDEFINED)
7970 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7971 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7974 if (IS_LOOP_SOUND(sound))
7975 PlaySoundLoop(sound);
7980 void PlayMenuSound()
7982 PlayMenuSoundExt(menu.sound[game_status]);
7985 void PlayMenuSoundStereo(int sound, int stereo_position)
7987 if (sound == SND_UNDEFINED)
7990 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7991 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7994 if (IS_LOOP_SOUND(sound))
7995 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7997 PlaySoundStereo(sound, stereo_position);
8000 void PlayMenuSoundIfLoopExt(int sound)
8002 if (sound == SND_UNDEFINED)
8005 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8006 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8009 if (IS_LOOP_SOUND(sound))
8010 PlaySoundLoop(sound);
8013 void PlayMenuSoundIfLoop()
8015 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8018 void PlayMenuMusicExt(int music)
8020 if (music == MUS_UNDEFINED)
8023 if (!setup.sound_music)
8029 void PlayMenuMusic()
8031 PlayMenuMusicExt(menu.music[game_status]);
8034 void PlaySoundActivating()
8037 PlaySound(SND_MENU_ITEM_ACTIVATING);
8041 void PlaySoundSelecting()
8044 PlaySound(SND_MENU_ITEM_SELECTING);
8048 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8050 boolean change_fullscreen = (setup.fullscreen !=
8051 video.fullscreen_enabled);
8052 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8053 !strEqual(setup.fullscreen_mode,
8054 video.fullscreen_mode_current));
8055 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8056 setup.window_scaling_percent !=
8057 video.window_scaling_percent);
8059 if (change_window_scaling_percent && video.fullscreen_enabled)
8062 if (!change_window_scaling_percent && !video.fullscreen_available)
8065 #if defined(TARGET_SDL2)
8066 if (change_window_scaling_percent)
8068 SDLSetWindowScaling(setup.window_scaling_percent);
8072 else if (change_fullscreen)
8074 SDLSetWindowFullscreen(setup.fullscreen);
8076 /* set setup value according to successfully changed fullscreen mode */
8077 setup.fullscreen = video.fullscreen_enabled;
8083 if (change_fullscreen ||
8084 change_fullscreen_mode ||
8085 change_window_scaling_percent)
8087 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8089 /* save backbuffer content which gets lost when toggling fullscreen mode */
8090 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8092 if (change_fullscreen_mode)
8094 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8095 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8098 if (change_window_scaling_percent)
8100 /* keep window mode, but change window scaling */
8101 video.fullscreen_enabled = TRUE; /* force new window scaling */
8104 /* toggle fullscreen */
8105 ChangeVideoModeIfNeeded(setup.fullscreen);
8107 /* set setup value according to successfully changed fullscreen mode */
8108 setup.fullscreen = video.fullscreen_enabled;
8110 /* restore backbuffer content from temporary backbuffer backup bitmap */
8111 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8113 FreeBitmap(tmp_backbuffer);
8115 /* update visible window/screen */
8116 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8120 void JoinRectangles(int *x, int *y, int *width, int *height,
8121 int x2, int y2, int width2, int height2)
8123 // do not join with "off-screen" rectangle
8124 if (x2 == -1 || y2 == -1)
8129 *width = MAX(*width, width2);
8130 *height = MAX(*height, height2);
8133 void ChangeViewportPropertiesIfNeeded()
8135 int gfx_game_mode = game_status;
8136 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8138 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8139 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8140 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8141 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8142 int border_size = vp_playfield->border_size;
8143 int new_sx = vp_playfield->x + border_size;
8144 int new_sy = vp_playfield->y + border_size;
8145 int new_sxsize = vp_playfield->width - 2 * border_size;
8146 int new_sysize = vp_playfield->height - 2 * border_size;
8147 int new_real_sx = vp_playfield->x;
8148 int new_real_sy = vp_playfield->y;
8149 int new_full_sxsize = vp_playfield->width;
8150 int new_full_sysize = vp_playfield->height;
8151 int new_dx = vp_door_1->x;
8152 int new_dy = vp_door_1->y;
8153 int new_dxsize = vp_door_1->width;
8154 int new_dysize = vp_door_1->height;
8155 int new_vx = vp_door_2->x;
8156 int new_vy = vp_door_2->y;
8157 int new_vxsize = vp_door_2->width;
8158 int new_vysize = vp_door_2->height;
8159 int new_ex = vp_door_3->x;
8160 int new_ey = vp_door_3->y;
8161 int new_exsize = vp_door_3->width;
8162 int new_eysize = vp_door_3->height;
8163 int new_tilesize_var =
8164 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8166 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8167 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8168 int new_scr_fieldx = new_sxsize / tilesize;
8169 int new_scr_fieldy = new_sysize / tilesize;
8170 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8171 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8172 boolean init_gfx_buffers = FALSE;
8173 boolean init_video_buffer = FALSE;
8174 boolean init_gadgets_and_toons = FALSE;
8175 boolean init_em_graphics = FALSE;
8177 if (viewport.window.width != WIN_XSIZE ||
8178 viewport.window.height != WIN_YSIZE)
8180 WIN_XSIZE = viewport.window.width;
8181 WIN_YSIZE = viewport.window.height;
8183 init_video_buffer = TRUE;
8184 init_gfx_buffers = TRUE;
8186 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8189 if (new_scr_fieldx != SCR_FIELDX ||
8190 new_scr_fieldy != SCR_FIELDY)
8192 /* this always toggles between MAIN and GAME when using small tile size */
8194 SCR_FIELDX = new_scr_fieldx;
8195 SCR_FIELDY = new_scr_fieldy;
8197 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8208 new_sxsize != SXSIZE ||
8209 new_sysize != SYSIZE ||
8210 new_dxsize != DXSIZE ||
8211 new_dysize != DYSIZE ||
8212 new_vxsize != VXSIZE ||
8213 new_vysize != VYSIZE ||
8214 new_exsize != EXSIZE ||
8215 new_eysize != EYSIZE ||
8216 new_real_sx != REAL_SX ||
8217 new_real_sy != REAL_SY ||
8218 new_full_sxsize != FULL_SXSIZE ||
8219 new_full_sysize != FULL_SYSIZE ||
8220 new_tilesize_var != TILESIZE_VAR
8223 // ------------------------------------------------------------------------
8224 // determine next fading area for changed viewport definitions
8225 // ------------------------------------------------------------------------
8227 // start with current playfield area (default fading area)
8230 FADE_SXSIZE = FULL_SXSIZE;
8231 FADE_SYSIZE = FULL_SYSIZE;
8233 // add new playfield area if position or size has changed
8234 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8235 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8237 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8238 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8241 // add current and new door 1 area if position or size has changed
8242 if (new_dx != DX || new_dy != DY ||
8243 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8245 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8246 DX, DY, DXSIZE, DYSIZE);
8247 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8248 new_dx, new_dy, new_dxsize, new_dysize);
8251 // add current and new door 2 area if position or size has changed
8252 if (new_dx != VX || new_dy != VY ||
8253 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8255 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8256 VX, VY, VXSIZE, VYSIZE);
8257 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8258 new_vx, new_vy, new_vxsize, new_vysize);
8261 // ------------------------------------------------------------------------
8262 // handle changed tile size
8263 // ------------------------------------------------------------------------
8265 if (new_tilesize_var != TILESIZE_VAR)
8267 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8269 // changing tile size invalidates scroll values of engine snapshots
8270 FreeEngineSnapshotSingle();
8272 // changing tile size requires update of graphic mapping for EM engine
8273 init_em_graphics = TRUE;
8284 SXSIZE = new_sxsize;
8285 SYSIZE = new_sysize;
8286 DXSIZE = new_dxsize;
8287 DYSIZE = new_dysize;
8288 VXSIZE = new_vxsize;
8289 VYSIZE = new_vysize;
8290 EXSIZE = new_exsize;
8291 EYSIZE = new_eysize;
8292 REAL_SX = new_real_sx;
8293 REAL_SY = new_real_sy;
8294 FULL_SXSIZE = new_full_sxsize;
8295 FULL_SYSIZE = new_full_sysize;
8296 TILESIZE_VAR = new_tilesize_var;
8298 init_gfx_buffers = TRUE;
8299 init_gadgets_and_toons = TRUE;
8301 // printf("::: viewports: init_gfx_buffers\n");
8302 // printf("::: viewports: init_gadgets_and_toons\n");
8305 if (init_gfx_buffers)
8307 // printf("::: init_gfx_buffers\n");
8309 SCR_FIELDX = new_scr_fieldx_buffers;
8310 SCR_FIELDY = new_scr_fieldy_buffers;
8314 SCR_FIELDX = new_scr_fieldx;
8315 SCR_FIELDY = new_scr_fieldy;
8317 SetDrawDeactivationMask(REDRAW_NONE);
8318 SetDrawBackgroundMask(REDRAW_FIELD);
8321 if (init_video_buffer)
8323 // printf("::: init_video_buffer\n");
8325 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8328 if (init_gadgets_and_toons)
8330 // printf("::: init_gadgets_and_toons\n");
8336 if (init_em_graphics)
8338 InitGraphicInfo_EM();