1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static char *print_if_not_empty(int element)
178 static char *s = NULL;
179 char *token_name = element_info[element].token_name;
184 s = checked_malloc(strlen(token_name) + 10 + 1);
186 if (element != EL_EMPTY)
187 sprintf(s, "%d\t['%s']", element, token_name);
189 sprintf(s, "%d", element);
194 void DumpTile(int x, int y)
199 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
205 printf_line("-", 79);
206 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
207 printf_line("-", 79);
209 if (!IN_LEV_FIELD(x, y))
211 printf("(not in level field)\n");
217 printf(" Feld: %d\t['%s']\n", Feld[x][y],
218 element_info[Feld[x][y]].token_name);
219 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
220 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
221 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
222 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
223 printf(" MovPos: %d\n", MovPos[x][y]);
224 printf(" MovDir: %d\n", MovDir[x][y]);
225 printf(" MovDelay: %d\n", MovDelay[x][y]);
226 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
227 printf(" CustomValue: %d\n", CustomValue[x][y]);
228 printf(" GfxElement: %d\n", GfxElement[x][y]);
229 printf(" GfxAction: %d\n", GfxAction[x][y]);
230 printf(" GfxFrame: %d\n", GfxFrame[x][y]);
234 void SetDrawtoField(int mode)
236 if (mode == DRAW_FIELDBUFFER)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
245 drawto_field = fieldbuffer;
247 else /* DRAW_BACKBUFFER */
253 BX2 = SCR_FIELDX - 1;
254 BY2 = SCR_FIELDY - 1;
256 drawto_field = backbuffer;
260 static void RedrawPlayfield_RND()
262 if (game.envelope_active)
265 DrawLevel(REDRAW_ALL);
269 void RedrawPlayfield()
271 if (game_status != GAME_MODE_PLAYING)
274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
275 RedrawPlayfield_EM(TRUE);
276 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
277 RedrawPlayfield_SP(TRUE);
278 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
279 RedrawPlayfield_RND();
281 BlitScreenToBitmap(backbuffer);
283 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
287 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
289 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
291 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
294 void DrawMaskedBorder_FIELD()
296 if (global.border_status >= GAME_MODE_TITLE &&
297 global.border_status <= GAME_MODE_PLAYING &&
298 border.draw_masked[global.border_status])
299 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
302 void DrawMaskedBorder_DOOR_1()
304 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
305 (global.border_status != GAME_MODE_EDITOR ||
306 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
307 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
310 void DrawMaskedBorder_DOOR_2()
312 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
313 global.border_status != GAME_MODE_EDITOR)
314 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
317 void DrawMaskedBorder_DOOR_3()
319 /* currently not available */
322 void DrawMaskedBorder_ALL()
324 DrawMaskedBorder_FIELD();
325 DrawMaskedBorder_DOOR_1();
326 DrawMaskedBorder_DOOR_2();
327 DrawMaskedBorder_DOOR_3();
330 void DrawMaskedBorder(int redraw_mask)
332 /* never draw masked screen borders on borderless screens */
333 if (effectiveGameStatus() == GAME_MODE_LOADING ||
334 effectiveGameStatus() == GAME_MODE_TITLE)
337 if (redraw_mask & REDRAW_ALL)
338 DrawMaskedBorder_ALL();
341 if (redraw_mask & REDRAW_FIELD)
342 DrawMaskedBorder_FIELD();
343 if (redraw_mask & REDRAW_DOOR_1)
344 DrawMaskedBorder_DOOR_1();
345 if (redraw_mask & REDRAW_DOOR_2)
346 DrawMaskedBorder_DOOR_2();
347 if (redraw_mask & REDRAW_DOOR_3)
348 DrawMaskedBorder_DOOR_3();
352 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
354 int fx = FX, fy = FY;
355 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
356 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
358 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
359 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
360 int dx_var = dx * TILESIZE_VAR / TILESIZE;
361 int dy_var = dy * TILESIZE_VAR / TILESIZE;
364 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
365 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
367 if (EVEN(SCR_FIELDX))
369 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
370 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
372 fx += (dx_var > 0 ? TILEX_VAR : 0);
379 if (EVEN(SCR_FIELDY))
381 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
382 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
384 fy += (dy_var > 0 ? TILEY_VAR : 0);
391 if (full_lev_fieldx <= SCR_FIELDX)
393 if (EVEN(SCR_FIELDX))
394 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
396 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
399 if (full_lev_fieldy <= SCR_FIELDY)
401 if (EVEN(SCR_FIELDY))
402 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
404 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
407 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
410 void BlitScreenToBitmap(Bitmap *target_bitmap)
412 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
413 BlitScreenToBitmap_EM(target_bitmap);
414 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
415 BlitScreenToBitmap_SP(target_bitmap);
416 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
417 BlitScreenToBitmap_RND(target_bitmap);
419 redraw_mask |= REDRAW_FIELD;
422 void DrawFramesPerSecond()
425 int font_nr = FONT_TEXT_2;
426 int font_width = getFontWidth(font_nr);
428 sprintf(text, "%04.1f fps", global.frames_per_second);
430 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
431 font_nr, BLIT_OPAQUE);
436 if (redraw_mask == REDRAW_NONE)
439 // draw masked border to all viewports, if defined
440 DrawMaskedBorder(redraw_mask);
442 // draw frames per second (only if debug mode is enabled)
443 if (redraw_mask & REDRAW_FPS)
444 DrawFramesPerSecond();
446 // redraw complete window if both playfield and (some) doors need redraw
447 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
448 redraw_mask = REDRAW_ALL;
450 /* although redrawing the whole window would be fine for normal gameplay,
451 being able to only redraw the playfield is required for deactivating
452 certain drawing areas (mainly playfield) to work, which is needed for
453 warp-forward to be fast enough (by skipping redraw of most frames) */
455 if (redraw_mask & REDRAW_ALL)
457 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
459 else if (redraw_mask & REDRAW_FIELD)
461 BlitBitmap(backbuffer, window,
462 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
464 else if (redraw_mask & REDRAW_DOORS)
466 if (redraw_mask & REDRAW_DOOR_1)
467 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
469 if (redraw_mask & REDRAW_DOOR_2)
470 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
472 if (redraw_mask & REDRAW_DOOR_3)
473 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
476 redraw_mask = REDRAW_NONE;
479 static void FadeCrossSaveBackbuffer()
481 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
484 static void FadeCrossRestoreBackbuffer()
486 int redraw_mask_last = redraw_mask;
488 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
490 // do not change redraw mask when restoring backbuffer after cross-fading
491 redraw_mask = redraw_mask_last;
494 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
496 static int fade_type_skip = FADE_TYPE_NONE;
497 void (*draw_border_function)(void) = NULL;
498 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
499 int x, y, width, height;
500 int fade_delay, post_delay;
502 if (fade_type == FADE_TYPE_FADE_OUT)
504 if (fade_type_skip != FADE_TYPE_NONE)
506 /* skip all fade operations until specified fade operation */
507 if (fade_type & fade_type_skip)
508 fade_type_skip = FADE_TYPE_NONE;
514 FadeCrossSaveBackbuffer();
517 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
520 FadeCrossSaveBackbuffer();
527 redraw_mask |= fade_mask;
529 if (fade_type == FADE_TYPE_SKIP)
531 fade_type_skip = fade_mode;
536 fade_delay = fading.fade_delay;
537 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
539 if (fade_type_skip != FADE_TYPE_NONE)
541 /* skip all fade operations until specified fade operation */
542 if (fade_type & fade_type_skip)
543 fade_type_skip = FADE_TYPE_NONE;
548 if (global.autoplay_leveldir)
553 if (fade_mask == REDRAW_FIELD)
558 height = FADE_SYSIZE;
560 if (border.draw_masked_when_fading)
561 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
563 DrawMaskedBorder_FIELD(); /* draw once */
565 else /* REDRAW_ALL */
573 if (!setup.fade_screens ||
575 fading.fade_mode == FADE_MODE_NONE)
577 if (fade_mode == FADE_MODE_FADE_OUT)
580 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
582 redraw_mask &= ~fade_mask;
587 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
588 draw_border_function);
590 if (fade_type == FADE_TYPE_FADE_OUT)
591 FadeCrossRestoreBackbuffer();
593 redraw_mask &= ~fade_mask;
596 void FadeIn(int fade_mask)
598 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
599 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
601 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
605 FADE_SXSIZE = FULL_SXSIZE;
606 FADE_SYSIZE = FULL_SYSIZE;
609 void FadeOut(int fade_mask)
611 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
612 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
614 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
616 global.border_status = game_status;
619 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
621 static struct TitleFadingInfo fading_leave_stored;
624 fading_leave_stored = fading_leave;
626 fading = fading_leave_stored;
629 void FadeSetEnterMenu()
631 fading = menu.enter_menu;
633 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
636 void FadeSetLeaveMenu()
638 fading = menu.leave_menu;
640 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
643 void FadeSetEnterScreen()
645 fading = menu.enter_screen[game_status];
647 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
650 void FadeSetNextScreen()
652 fading = menu.next_screen;
654 // (do not overwrite fade mode set by FadeSetEnterScreen)
655 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
658 void FadeSetLeaveScreen()
660 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
663 void FadeSetFromType(int type)
665 if (type & TYPE_ENTER_SCREEN)
666 FadeSetEnterScreen();
667 else if (type & TYPE_ENTER)
669 else if (type & TYPE_LEAVE)
673 void FadeSetDisabled()
675 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
677 fading = fading_none;
680 void FadeSkipNextFadeIn()
682 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
685 void FadeSkipNextFadeOut()
687 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
690 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
692 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
694 return (graphic == IMG_UNDEFINED ? NULL :
695 graphic_info[graphic].bitmap != NULL || redefined ?
696 graphic_info[graphic].bitmap :
697 graphic_info[default_graphic].bitmap);
700 Bitmap *getBackgroundBitmap(int graphic)
702 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
705 Bitmap *getGlobalBorderBitmap(int graphic)
707 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
710 Bitmap *getGlobalBorderBitmapFromGameStatus()
713 (game_status == GAME_MODE_MAIN ||
714 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
715 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
716 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
717 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
720 return getGlobalBorderBitmap(graphic);
723 void SetWindowBackgroundImageIfDefined(int graphic)
725 if (graphic_info[graphic].bitmap)
726 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
729 void SetMainBackgroundImageIfDefined(int graphic)
731 if (graphic_info[graphic].bitmap)
732 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
735 void SetDoorBackgroundImageIfDefined(int graphic)
737 if (graphic_info[graphic].bitmap)
738 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
741 void SetWindowBackgroundImage(int graphic)
743 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
746 void SetMainBackgroundImage(int graphic)
748 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
751 void SetDoorBackgroundImage(int graphic)
753 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
756 void SetPanelBackground()
758 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
760 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
761 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
763 SetDoorBackgroundBitmap(bitmap_db_panel);
766 void DrawBackground(int x, int y, int width, int height)
768 /* "drawto" might still point to playfield buffer here (hall of fame) */
769 ClearRectangleOnBackground(backbuffer, x, y, width, height);
771 if (IN_GFX_FIELD_FULL(x, y))
772 redraw_mask |= REDRAW_FIELD;
773 else if (IN_GFX_DOOR_1(x, y))
774 redraw_mask |= REDRAW_DOOR_1;
775 else if (IN_GFX_DOOR_2(x, y))
776 redraw_mask |= REDRAW_DOOR_2;
777 else if (IN_GFX_DOOR_3(x, y))
778 redraw_mask |= REDRAW_DOOR_3;
781 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
783 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
785 if (font->bitmap == NULL)
788 DrawBackground(x, y, width, height);
791 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
793 struct GraphicInfo *g = &graphic_info[graphic];
795 if (g->bitmap == NULL)
798 DrawBackground(x, y, width, height);
801 static int game_status_last = -1;
802 static Bitmap *global_border_bitmap_last = NULL;
803 static Bitmap *global_border_bitmap = NULL;
804 static int real_sx_last = -1, real_sy_last = -1;
805 static int full_sxsize_last = -1, full_sysize_last = -1;
806 static int dx_last = -1, dy_last = -1;
807 static int dxsize_last = -1, dysize_last = -1;
808 static int vx_last = -1, vy_last = -1;
809 static int vxsize_last = -1, vysize_last = -1;
811 boolean CheckIfGlobalBorderHasChanged()
813 // if game status has not changed, global border has not changed either
814 if (game_status == game_status_last)
817 // determine and store new global border bitmap for current game status
818 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
820 return (global_border_bitmap_last != global_border_bitmap);
823 boolean CheckIfGlobalBorderRedrawIsNeeded()
825 // if game status has not changed, nothing has to be redrawn
826 if (game_status == game_status_last)
829 // redraw if global screen border has changed
830 if (CheckIfGlobalBorderHasChanged())
833 // redraw if position or size of playfield area has changed
834 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
835 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
838 // redraw if position or size of door area has changed
839 if (dx_last != DX || dy_last != DY ||
840 dxsize_last != DXSIZE || dysize_last != DYSIZE)
843 // redraw if position or size of tape area has changed
844 if (vx_last != VX || vy_last != VY ||
845 vxsize_last != VXSIZE || vysize_last != VYSIZE)
851 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
854 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
856 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
859 void RedrawGlobalBorder()
861 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
863 RedrawGlobalBorderFromBitmap(bitmap);
865 redraw_mask = REDRAW_ALL;
868 static void RedrawGlobalBorderIfNeeded()
870 if (game_status == game_status_last)
873 // copy current draw buffer to later copy back areas that have not changed
874 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
876 if (CheckIfGlobalBorderRedrawIsNeeded())
878 // redraw global screen border (or clear, if defined to be empty)
879 RedrawGlobalBorderFromBitmap(global_border_bitmap);
881 // copy previous playfield and door areas, if they are defined on both
882 // previous and current screen and if they still have the same size
884 if (real_sx_last != -1 && real_sy_last != -1 &&
885 REAL_SX != -1 && REAL_SY != -1 &&
886 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
887 BlitBitmap(bitmap_db_store, backbuffer,
888 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
891 if (dx_last != -1 && dy_last != -1 &&
892 DX != -1 && DY != -1 &&
893 dxsize_last == DXSIZE && dysize_last == DYSIZE)
894 BlitBitmap(bitmap_db_store, backbuffer,
895 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
897 if (vx_last != -1 && vy_last != -1 &&
898 VX != -1 && VY != -1 &&
899 vxsize_last == VXSIZE && vysize_last == VYSIZE)
900 BlitBitmap(bitmap_db_store, backbuffer,
901 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
903 redraw_mask = REDRAW_ALL;
906 game_status_last = game_status;
908 global_border_bitmap_last = global_border_bitmap;
910 real_sx_last = REAL_SX;
911 real_sy_last = REAL_SY;
912 full_sxsize_last = FULL_SXSIZE;
913 full_sysize_last = FULL_SYSIZE;
916 dxsize_last = DXSIZE;
917 dysize_last = DYSIZE;
920 vxsize_last = VXSIZE;
921 vysize_last = VYSIZE;
926 RedrawGlobalBorderIfNeeded();
928 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
929 /* (when entering hall of fame after playing) */
930 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
932 /* !!! maybe this should be done before clearing the background !!! */
933 if (game_status == GAME_MODE_PLAYING)
935 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
936 SetDrawtoField(DRAW_FIELDBUFFER);
940 SetDrawtoField(DRAW_BACKBUFFER);
944 void MarkTileDirty(int x, int y)
946 redraw_mask |= REDRAW_FIELD;
949 void SetBorderElement()
953 BorderElement = EL_EMPTY;
955 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
957 for (x = 0; x < lev_fieldx; x++)
959 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
960 BorderElement = EL_STEELWALL;
962 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
968 void FloodFillLevel(int from_x, int from_y, int fill_element,
969 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
970 int max_fieldx, int max_fieldy)
974 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
975 static int safety = 0;
977 /* check if starting field still has the desired content */
978 if (field[from_x][from_y] == fill_element)
983 if (safety > max_fieldx * max_fieldy)
984 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
986 old_element = field[from_x][from_y];
987 field[from_x][from_y] = fill_element;
989 for (i = 0; i < 4; i++)
991 x = from_x + check[i][0];
992 y = from_y + check[i][1];
994 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
995 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1001 void SetRandomAnimationValue(int x, int y)
1003 gfx.anim_random_frame = GfxRandom[x][y];
1006 int getGraphicAnimationFrame(int graphic, int sync_frame)
1008 /* animation synchronized with global frame counter, not move position */
1009 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1010 sync_frame = FrameCounter;
1012 return getAnimationFrame(graphic_info[graphic].anim_frames,
1013 graphic_info[graphic].anim_delay,
1014 graphic_info[graphic].anim_mode,
1015 graphic_info[graphic].anim_start_frame,
1019 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1020 Bitmap **bitmap, int *x, int *y,
1021 boolean get_backside)
1023 struct GraphicInfo *g = &graphic_info[graphic];
1024 Bitmap *src_bitmap = g->bitmap;
1025 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1026 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1027 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1029 // if no in-game graphics defined, always use standard graphic size
1030 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1031 tilesize = TILESIZE;
1033 if (tilesize == gfx.standard_tile_size)
1034 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1035 else if (tilesize == game.tile_size)
1036 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1038 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1040 if (g->offset_y == 0) /* frames are ordered horizontally */
1042 int max_width = g->anim_frames_per_line * g->width;
1043 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1045 src_x = pos % max_width;
1046 src_y = src_y % g->height + pos / max_width * g->height;
1048 else if (g->offset_x == 0) /* frames are ordered vertically */
1050 int max_height = g->anim_frames_per_line * g->height;
1051 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1053 src_x = src_x % g->width + pos / max_height * g->width;
1054 src_y = pos % max_height;
1056 else /* frames are ordered diagonally */
1058 src_x = src_x + frame * g->offset_x;
1059 src_y = src_y + frame * g->offset_y;
1062 *bitmap = src_bitmap;
1063 *x = src_x * tilesize / TILESIZE;
1064 *y = src_y * tilesize / TILESIZE;
1067 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1068 int *x, int *y, boolean get_backside)
1070 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1074 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1075 Bitmap **bitmap, int *x, int *y)
1077 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1080 void getFixedGraphicSource(int graphic, int frame,
1081 Bitmap **bitmap, int *x, int *y)
1083 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1086 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1088 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1091 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1092 int *x, int *y, boolean get_backside)
1094 struct GraphicInfo *g = &graphic_info[graphic];
1095 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1096 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1098 if (TILESIZE_VAR != TILESIZE)
1099 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1102 *bitmap = g->bitmap;
1104 if (g->offset_y == 0) /* frames are ordered horizontally */
1106 int max_width = g->anim_frames_per_line * g->width;
1107 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1109 *x = pos % max_width;
1110 *y = src_y % g->height + pos / max_width * g->height;
1112 else if (g->offset_x == 0) /* frames are ordered vertically */
1114 int max_height = g->anim_frames_per_line * g->height;
1115 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1117 *x = src_x % g->width + pos / max_height * g->width;
1118 *y = pos % max_height;
1120 else /* frames are ordered diagonally */
1122 *x = src_x + frame * g->offset_x;
1123 *y = src_y + frame * g->offset_y;
1127 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1129 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1132 void DrawGraphic(int x, int y, int graphic, int frame)
1135 if (!IN_SCR_FIELD(x, y))
1137 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1138 printf("DrawGraphic(): This should never happen!\n");
1143 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1146 MarkTileDirty(x, y);
1149 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1152 if (!IN_SCR_FIELD(x, y))
1154 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1155 printf("DrawGraphic(): This should never happen!\n");
1160 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1162 MarkTileDirty(x, y);
1165 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1171 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1173 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1176 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1182 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1183 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1186 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1189 if (!IN_SCR_FIELD(x, y))
1191 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1192 printf("DrawGraphicThruMask(): This should never happen!\n");
1197 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1200 MarkTileDirty(x, y);
1203 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1206 if (!IN_SCR_FIELD(x, y))
1208 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1209 printf("DrawGraphicThruMask(): This should never happen!\n");
1214 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1216 MarkTileDirty(x, y);
1219 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1225 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1227 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1231 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1232 int graphic, int frame)
1234 struct GraphicInfo *g = &graphic_info[graphic];
1238 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1240 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1244 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1246 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1248 MarkTileDirty(x / tilesize, y / tilesize);
1251 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1257 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1258 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1261 void DrawMiniGraphic(int x, int y, int graphic)
1263 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1264 MarkTileDirty(x / 2, y / 2);
1267 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1272 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1273 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1276 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1277 int graphic, int frame,
1278 int cut_mode, int mask_mode)
1283 int width = TILEX, height = TILEY;
1286 if (dx || dy) /* shifted graphic */
1288 if (x < BX1) /* object enters playfield from the left */
1295 else if (x > BX2) /* object enters playfield from the right */
1301 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1307 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1309 else if (dx) /* general horizontal movement */
1310 MarkTileDirty(x + SIGN(dx), y);
1312 if (y < BY1) /* object enters playfield from the top */
1314 if (cut_mode==CUT_BELOW) /* object completely above top border */
1322 else if (y > BY2) /* object enters playfield from the bottom */
1328 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1334 else if (dy > 0 && cut_mode == CUT_ABOVE)
1336 if (y == BY2) /* object completely above bottom border */
1342 MarkTileDirty(x, y + 1);
1343 } /* object leaves playfield to the bottom */
1344 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1346 else if (dy) /* general vertical movement */
1347 MarkTileDirty(x, y + SIGN(dy));
1351 if (!IN_SCR_FIELD(x, y))
1353 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1354 printf("DrawGraphicShifted(): This should never happen!\n");
1359 width = width * TILESIZE_VAR / TILESIZE;
1360 height = height * TILESIZE_VAR / TILESIZE;
1361 cx = cx * TILESIZE_VAR / TILESIZE;
1362 cy = cy * TILESIZE_VAR / TILESIZE;
1363 dx = dx * TILESIZE_VAR / TILESIZE;
1364 dy = dy * TILESIZE_VAR / TILESIZE;
1366 if (width > 0 && height > 0)
1368 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1373 dst_x = FX + x * TILEX_VAR + dx;
1374 dst_y = FY + y * TILEY_VAR + dy;
1376 if (mask_mode == USE_MASKING)
1377 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1380 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1383 MarkTileDirty(x, y);
1387 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1388 int graphic, int frame,
1389 int cut_mode, int mask_mode)
1394 int width = TILEX_VAR, height = TILEY_VAR;
1397 int x2 = x + SIGN(dx);
1398 int y2 = y + SIGN(dy);
1400 /* movement with two-tile animations must be sync'ed with movement position,
1401 not with current GfxFrame (which can be higher when using slow movement) */
1402 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1403 int anim_frames = graphic_info[graphic].anim_frames;
1405 /* (we also need anim_delay here for movement animations with less frames) */
1406 int anim_delay = graphic_info[graphic].anim_delay;
1407 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1409 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1410 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1412 /* re-calculate animation frame for two-tile movement animation */
1413 frame = getGraphicAnimationFrame(graphic, sync_frame);
1415 /* check if movement start graphic inside screen area and should be drawn */
1416 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1418 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1420 dst_x = FX + x1 * TILEX_VAR;
1421 dst_y = FY + y1 * TILEY_VAR;
1423 if (mask_mode == USE_MASKING)
1424 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1427 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1430 MarkTileDirty(x1, y1);
1433 /* check if movement end graphic inside screen area and should be drawn */
1434 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1436 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1438 dst_x = FX + x2 * TILEX_VAR;
1439 dst_y = FY + y2 * TILEY_VAR;
1441 if (mask_mode == USE_MASKING)
1442 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1445 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1448 MarkTileDirty(x2, y2);
1452 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1453 int graphic, int frame,
1454 int cut_mode, int mask_mode)
1458 DrawGraphic(x, y, graphic, frame);
1463 if (graphic_info[graphic].double_movement) /* EM style movement images */
1464 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1466 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1469 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1470 int frame, int cut_mode)
1472 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1475 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1476 int cut_mode, int mask_mode)
1478 int lx = LEVELX(x), ly = LEVELY(y);
1482 if (IN_LEV_FIELD(lx, ly))
1484 SetRandomAnimationValue(lx, ly);
1486 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1487 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1489 /* do not use double (EM style) movement graphic when not moving */
1490 if (graphic_info[graphic].double_movement && !dx && !dy)
1492 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1493 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1496 else /* border element */
1498 graphic = el2img(element);
1499 frame = getGraphicAnimationFrame(graphic, -1);
1502 if (element == EL_EXPANDABLE_WALL)
1504 boolean left_stopped = FALSE, right_stopped = FALSE;
1506 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1507 left_stopped = TRUE;
1508 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1509 right_stopped = TRUE;
1511 if (left_stopped && right_stopped)
1513 else if (left_stopped)
1515 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1516 frame = graphic_info[graphic].anim_frames - 1;
1518 else if (right_stopped)
1520 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1521 frame = graphic_info[graphic].anim_frames - 1;
1526 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1527 else if (mask_mode == USE_MASKING)
1528 DrawGraphicThruMask(x, y, graphic, frame);
1530 DrawGraphic(x, y, graphic, frame);
1533 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1534 int cut_mode, int mask_mode)
1536 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1537 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1538 cut_mode, mask_mode);
1541 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1544 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1547 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1550 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1553 void DrawLevelElementThruMask(int x, int y, int element)
1555 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1558 void DrawLevelFieldThruMask(int x, int y)
1560 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1563 /* !!! implementation of quicksand is totally broken !!! */
1564 #define IS_CRUMBLED_TILE(x, y, e) \
1565 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1566 !IS_MOVING(x, y) || \
1567 (e) == EL_QUICKSAND_EMPTYING || \
1568 (e) == EL_QUICKSAND_FAST_EMPTYING))
1570 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1575 int width, height, cx, cy;
1576 int sx = SCREENX(x), sy = SCREENY(y);
1577 int crumbled_border_size = graphic_info[graphic].border_size;
1580 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1582 for (i = 1; i < 4; i++)
1584 int dxx = (i & 1 ? dx : 0);
1585 int dyy = (i & 2 ? dy : 0);
1588 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1591 /* check if neighbour field is of same crumble type */
1592 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1593 graphic_info[graphic].class ==
1594 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1596 /* return if check prevents inner corner */
1597 if (same == (dxx == dx && dyy == dy))
1601 /* if we reach this point, we have an inner corner */
1603 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1605 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1606 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1607 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1608 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1610 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1611 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1614 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1619 int width, height, bx, by, cx, cy;
1620 int sx = SCREENX(x), sy = SCREENY(y);
1621 int crumbled_border_size = graphic_info[graphic].border_size;
1622 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1623 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1626 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1628 /* draw simple, sloppy, non-corner-accurate crumbled border */
1630 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1631 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1632 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1633 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1635 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1636 FX + sx * TILEX_VAR + cx,
1637 FY + sy * TILEY_VAR + cy);
1639 /* (remaining middle border part must be at least as big as corner part) */
1640 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1641 crumbled_border_size >= TILESIZE / 3)
1644 /* correct corners of crumbled border, if needed */
1646 for (i = -1; i <= 1; i += 2)
1648 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1649 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1650 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1653 /* check if neighbour field is of same crumble type */
1654 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1655 graphic_info[graphic].class ==
1656 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1658 /* no crumbled corner, but continued crumbled border */
1660 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1661 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1662 int b1 = (i == 1 ? crumbled_border_size_var :
1663 TILESIZE_VAR - 2 * crumbled_border_size_var);
1665 width = crumbled_border_size_var;
1666 height = crumbled_border_size_var;
1668 if (dir == 1 || dir == 2)
1683 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1685 FX + sx * TILEX_VAR + cx,
1686 FY + sy * TILEY_VAR + cy);
1691 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1693 int sx = SCREENX(x), sy = SCREENY(y);
1696 static int xy[4][2] =
1704 if (!IN_LEV_FIELD(x, y))
1707 element = TILE_GFX_ELEMENT(x, y);
1709 /* crumble field itself */
1710 if (IS_CRUMBLED_TILE(x, y, element))
1712 if (!IN_SCR_FIELD(sx, sy))
1715 for (i = 0; i < 4; i++)
1717 int xx = x + xy[i][0];
1718 int yy = y + xy[i][1];
1720 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1723 /* check if neighbour field is of same crumble type */
1724 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1725 graphic_info[graphic].class ==
1726 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1729 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1732 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1733 graphic_info[graphic].anim_frames == 2)
1735 for (i = 0; i < 4; i++)
1737 int dx = (i & 1 ? +1 : -1);
1738 int dy = (i & 2 ? +1 : -1);
1740 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1744 MarkTileDirty(sx, sy);
1746 else /* center field not crumbled -- crumble neighbour fields */
1748 for (i = 0; i < 4; i++)
1750 int xx = x + xy[i][0];
1751 int yy = y + xy[i][1];
1752 int sxx = sx + xy[i][0];
1753 int syy = sy + xy[i][1];
1755 if (!IN_LEV_FIELD(xx, yy) ||
1756 !IN_SCR_FIELD(sxx, syy))
1759 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1762 element = TILE_GFX_ELEMENT(xx, yy);
1764 if (!IS_CRUMBLED_TILE(xx, yy, element))
1767 graphic = el_act2crm(element, ACTION_DEFAULT);
1769 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1771 MarkTileDirty(sxx, syy);
1776 void DrawLevelFieldCrumbled(int x, int y)
1780 if (!IN_LEV_FIELD(x, y))
1783 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1784 GfxElement[x][y] != EL_UNDEFINED &&
1785 GFX_CRUMBLED(GfxElement[x][y]))
1787 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1792 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1794 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1797 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1800 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1801 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1802 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1803 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1804 int sx = SCREENX(x), sy = SCREENY(y);
1806 DrawGraphic(sx, sy, graphic1, frame1);
1807 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1810 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1812 int sx = SCREENX(x), sy = SCREENY(y);
1813 static int xy[4][2] =
1822 for (i = 0; i < 4; i++)
1824 int xx = x + xy[i][0];
1825 int yy = y + xy[i][1];
1826 int sxx = sx + xy[i][0];
1827 int syy = sy + xy[i][1];
1829 if (!IN_LEV_FIELD(xx, yy) ||
1830 !IN_SCR_FIELD(sxx, syy) ||
1831 !GFX_CRUMBLED(Feld[xx][yy]) ||
1835 DrawLevelField(xx, yy);
1839 static int getBorderElement(int x, int y)
1843 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1844 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1845 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1846 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1847 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1848 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1849 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1851 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1852 int steel_position = (x == -1 && y == -1 ? 0 :
1853 x == lev_fieldx && y == -1 ? 1 :
1854 x == -1 && y == lev_fieldy ? 2 :
1855 x == lev_fieldx && y == lev_fieldy ? 3 :
1856 x == -1 || x == lev_fieldx ? 4 :
1857 y == -1 || y == lev_fieldy ? 5 : 6);
1859 return border[steel_position][steel_type];
1862 void DrawScreenElement(int x, int y, int element)
1864 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1865 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1868 void DrawLevelElement(int x, int y, int element)
1870 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1871 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1874 void DrawScreenField(int x, int y)
1876 int lx = LEVELX(x), ly = LEVELY(y);
1877 int element, content;
1879 if (!IN_LEV_FIELD(lx, ly))
1881 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1884 element = getBorderElement(lx, ly);
1886 DrawScreenElement(x, y, element);
1891 element = Feld[lx][ly];
1892 content = Store[lx][ly];
1894 if (IS_MOVING(lx, ly))
1896 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1897 boolean cut_mode = NO_CUTTING;
1899 if (element == EL_QUICKSAND_EMPTYING ||
1900 element == EL_QUICKSAND_FAST_EMPTYING ||
1901 element == EL_MAGIC_WALL_EMPTYING ||
1902 element == EL_BD_MAGIC_WALL_EMPTYING ||
1903 element == EL_DC_MAGIC_WALL_EMPTYING ||
1904 element == EL_AMOEBA_DROPPING)
1905 cut_mode = CUT_ABOVE;
1906 else if (element == EL_QUICKSAND_FILLING ||
1907 element == EL_QUICKSAND_FAST_FILLING ||
1908 element == EL_MAGIC_WALL_FILLING ||
1909 element == EL_BD_MAGIC_WALL_FILLING ||
1910 element == EL_DC_MAGIC_WALL_FILLING)
1911 cut_mode = CUT_BELOW;
1913 if (cut_mode == CUT_ABOVE)
1914 DrawScreenElement(x, y, element);
1916 DrawScreenElement(x, y, EL_EMPTY);
1919 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1920 else if (cut_mode == NO_CUTTING)
1921 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1924 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1926 if (cut_mode == CUT_BELOW &&
1927 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1928 DrawLevelElement(lx, ly + 1, element);
1931 if (content == EL_ACID)
1933 int dir = MovDir[lx][ly];
1934 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1935 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1937 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1940 else if (IS_BLOCKED(lx, ly))
1945 boolean cut_mode = NO_CUTTING;
1946 int element_old, content_old;
1948 Blocked2Moving(lx, ly, &oldx, &oldy);
1951 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1952 MovDir[oldx][oldy] == MV_RIGHT);
1954 element_old = Feld[oldx][oldy];
1955 content_old = Store[oldx][oldy];
1957 if (element_old == EL_QUICKSAND_EMPTYING ||
1958 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1959 element_old == EL_MAGIC_WALL_EMPTYING ||
1960 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1961 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1962 element_old == EL_AMOEBA_DROPPING)
1963 cut_mode = CUT_ABOVE;
1965 DrawScreenElement(x, y, EL_EMPTY);
1968 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1970 else if (cut_mode == NO_CUTTING)
1971 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1974 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1977 else if (IS_DRAWABLE(element))
1978 DrawScreenElement(x, y, element);
1980 DrawScreenElement(x, y, EL_EMPTY);
1983 void DrawLevelField(int x, int y)
1985 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1986 DrawScreenField(SCREENX(x), SCREENY(y));
1987 else if (IS_MOVING(x, y))
1991 Moving2Blocked(x, y, &newx, &newy);
1992 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1993 DrawScreenField(SCREENX(newx), SCREENY(newy));
1995 else if (IS_BLOCKED(x, y))
1999 Blocked2Moving(x, y, &oldx, &oldy);
2000 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2001 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2005 void DrawSizedElement(int x, int y, int element, int tilesize)
2009 graphic = el2edimg(element);
2010 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2013 void DrawMiniElement(int x, int y, int element)
2017 graphic = el2edimg(element);
2018 DrawMiniGraphic(x, y, graphic);
2021 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2024 int x = sx + scroll_x, y = sy + scroll_y;
2026 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2027 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2028 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2029 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2031 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2034 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2036 int x = sx + scroll_x, y = sy + scroll_y;
2038 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2039 DrawMiniElement(sx, sy, EL_EMPTY);
2040 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2041 DrawMiniElement(sx, sy, Feld[x][y]);
2043 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2046 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2047 int x, int y, int xsize, int ysize,
2048 int tile_width, int tile_height)
2052 int dst_x = startx + x * tile_width;
2053 int dst_y = starty + y * tile_height;
2054 int width = graphic_info[graphic].width;
2055 int height = graphic_info[graphic].height;
2056 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2057 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2058 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2059 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2060 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2061 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2062 boolean draw_masked = graphic_info[graphic].draw_masked;
2064 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2066 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2068 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2072 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2073 inner_sx + (x - 1) * tile_width % inner_width);
2074 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2075 inner_sy + (y - 1) * tile_height % inner_height);
2078 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2081 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2085 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2086 int x, int y, int xsize, int ysize, int font_nr)
2088 int font_width = getFontWidth(font_nr);
2089 int font_height = getFontHeight(font_nr);
2091 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2092 font_width, font_height);
2095 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2097 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2098 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2099 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2100 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2101 boolean no_delay = (tape.warp_forward);
2102 unsigned int anim_delay = 0;
2103 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2104 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2105 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2106 int font_width = getFontWidth(font_nr);
2107 int font_height = getFontHeight(font_nr);
2108 int max_xsize = level.envelope[envelope_nr].xsize;
2109 int max_ysize = level.envelope[envelope_nr].ysize;
2110 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2111 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2112 int xend = max_xsize;
2113 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2114 int xstep = (xstart < xend ? 1 : 0);
2115 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2117 int end = MAX(xend - xstart, yend - ystart);
2120 for (i = start; i <= end; i++)
2122 int last_frame = end; // last frame of this "for" loop
2123 int x = xstart + i * xstep;
2124 int y = ystart + i * ystep;
2125 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2126 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2127 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2128 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2131 SetDrawtoField(DRAW_FIELDBUFFER);
2133 BlitScreenToBitmap(backbuffer);
2135 SetDrawtoField(DRAW_BACKBUFFER);
2137 for (yy = 0; yy < ysize; yy++)
2138 for (xx = 0; xx < xsize; xx++)
2139 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2141 DrawTextBuffer(sx + font_width, sy + font_height,
2142 level.envelope[envelope_nr].text, font_nr, max_xsize,
2143 xsize - 2, ysize - 2, 0, mask_mode,
2144 level.envelope[envelope_nr].autowrap,
2145 level.envelope[envelope_nr].centered, FALSE);
2147 redraw_mask |= REDRAW_FIELD;
2150 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2154 void ShowEnvelope(int envelope_nr)
2156 int element = EL_ENVELOPE_1 + envelope_nr;
2157 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2158 int sound_opening = element_info[element].sound[ACTION_OPENING];
2159 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2160 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2161 boolean no_delay = (tape.warp_forward);
2162 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2163 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2164 int anim_mode = graphic_info[graphic].anim_mode;
2165 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2166 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2168 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2170 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2172 if (anim_mode == ANIM_DEFAULT)
2173 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2175 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2178 Delay(wait_delay_value);
2180 WaitForEventToContinue();
2182 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2184 if (anim_mode != ANIM_NONE)
2185 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2187 if (anim_mode == ANIM_DEFAULT)
2188 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2190 game.envelope_active = FALSE;
2192 SetDrawtoField(DRAW_FIELDBUFFER);
2194 redraw_mask |= REDRAW_FIELD;
2198 static void setRequestBasePosition(int *x, int *y)
2200 int sx_base, sy_base;
2202 if (request.x != -1)
2203 sx_base = request.x;
2204 else if (request.align == ALIGN_LEFT)
2206 else if (request.align == ALIGN_RIGHT)
2207 sx_base = SX + SXSIZE;
2209 sx_base = SX + SXSIZE / 2;
2211 if (request.y != -1)
2212 sy_base = request.y;
2213 else if (request.valign == VALIGN_TOP)
2215 else if (request.valign == VALIGN_BOTTOM)
2216 sy_base = SY + SYSIZE;
2218 sy_base = SY + SYSIZE / 2;
2224 static void setRequestPositionExt(int *x, int *y, int width, int height,
2225 boolean add_border_size)
2227 int border_size = request.border_size;
2228 int sx_base, sy_base;
2231 setRequestBasePosition(&sx_base, &sy_base);
2233 if (request.align == ALIGN_LEFT)
2235 else if (request.align == ALIGN_RIGHT)
2236 sx = sx_base - width;
2238 sx = sx_base - width / 2;
2240 if (request.valign == VALIGN_TOP)
2242 else if (request.valign == VALIGN_BOTTOM)
2243 sy = sy_base - height;
2245 sy = sy_base - height / 2;
2247 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2248 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2250 if (add_border_size)
2260 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2262 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2265 void DrawEnvelopeRequest(char *text)
2267 char *text_final = text;
2268 char *text_door_style = NULL;
2269 int graphic = IMG_BACKGROUND_REQUEST;
2270 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2271 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2272 int font_nr = FONT_REQUEST;
2273 int font_width = getFontWidth(font_nr);
2274 int font_height = getFontHeight(font_nr);
2275 int border_size = request.border_size;
2276 int line_spacing = request.line_spacing;
2277 int line_height = font_height + line_spacing;
2278 int text_width = request.width - 2 * border_size;
2279 int text_height = request.height - 2 * border_size;
2280 int line_length = text_width / font_width;
2281 int max_lines = text_height / line_height;
2282 int width = request.width;
2283 int height = request.height;
2284 int tile_size = request.step_offset;
2285 int x_steps = width / tile_size;
2286 int y_steps = height / tile_size;
2290 if (request.wrap_single_words)
2292 char *src_text_ptr, *dst_text_ptr;
2294 text_door_style = checked_malloc(2 * strlen(text) + 1);
2296 src_text_ptr = text;
2297 dst_text_ptr = text_door_style;
2299 while (*src_text_ptr)
2301 if (*src_text_ptr == ' ' ||
2302 *src_text_ptr == '?' ||
2303 *src_text_ptr == '!')
2304 *dst_text_ptr++ = '\n';
2306 if (*src_text_ptr != ' ')
2307 *dst_text_ptr++ = *src_text_ptr;
2312 *dst_text_ptr = '\0';
2314 text_final = text_door_style;
2317 setRequestPosition(&sx, &sy, FALSE);
2319 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2321 for (y = 0; y < y_steps; y++)
2322 for (x = 0; x < x_steps; x++)
2323 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2324 x, y, x_steps, y_steps,
2325 tile_size, tile_size);
2327 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2328 line_length, -1, max_lines, line_spacing, mask_mode,
2329 request.autowrap, request.centered, FALSE);
2331 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2332 RedrawGadget(tool_gadget[i]);
2334 // store readily prepared envelope request for later use when animating
2335 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2337 if (text_door_style)
2338 free(text_door_style);
2341 void AnimateEnvelopeRequest(int anim_mode, int action)
2343 int graphic = IMG_BACKGROUND_REQUEST;
2344 boolean draw_masked = graphic_info[graphic].draw_masked;
2345 int delay_value_normal = request.step_delay;
2346 int delay_value_fast = delay_value_normal / 2;
2347 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2348 boolean no_delay = (tape.warp_forward);
2349 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2350 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2351 unsigned int anim_delay = 0;
2353 int tile_size = request.step_offset;
2354 int max_xsize = request.width / tile_size;
2355 int max_ysize = request.height / tile_size;
2356 int max_xsize_inner = max_xsize - 2;
2357 int max_ysize_inner = max_ysize - 2;
2359 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2360 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2361 int xend = max_xsize_inner;
2362 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2363 int xstep = (xstart < xend ? 1 : 0);
2364 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2366 int end = MAX(xend - xstart, yend - ystart);
2369 if (setup.quick_doors)
2377 if (action == ACTION_OPENING)
2378 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2379 else if (action == ACTION_CLOSING)
2380 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2383 for (i = start; i <= end; i++)
2385 int last_frame = end; // last frame of this "for" loop
2386 int x = xstart + i * xstep;
2387 int y = ystart + i * ystep;
2388 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2389 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2390 int xsize_size_left = (xsize - 1) * tile_size;
2391 int ysize_size_top = (ysize - 1) * tile_size;
2392 int max_xsize_pos = (max_xsize - 1) * tile_size;
2393 int max_ysize_pos = (max_ysize - 1) * tile_size;
2394 int width = xsize * tile_size;
2395 int height = ysize * tile_size;
2400 setRequestPosition(&src_x, &src_y, FALSE);
2401 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2403 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2405 for (yy = 0; yy < 2; yy++)
2407 for (xx = 0; xx < 2; xx++)
2409 int src_xx = src_x + xx * max_xsize_pos;
2410 int src_yy = src_y + yy * max_ysize_pos;
2411 int dst_xx = dst_x + xx * xsize_size_left;
2412 int dst_yy = dst_y + yy * ysize_size_top;
2413 int xx_size = (xx ? tile_size : xsize_size_left);
2414 int yy_size = (yy ? tile_size : ysize_size_top);
2417 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2418 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2420 BlitBitmap(bitmap_db_cross, backbuffer,
2421 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2425 redraw_mask |= REDRAW_FIELD;
2430 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2434 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2436 int last_game_status = game_status; /* save current game status */
2437 int graphic = IMG_BACKGROUND_REQUEST;
2438 int sound_opening = SND_REQUEST_OPENING;
2439 int sound_closing = SND_REQUEST_CLOSING;
2440 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2441 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2442 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2443 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2444 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2446 if (game_status == GAME_MODE_PLAYING)
2447 BlitScreenToBitmap(backbuffer);
2449 SetDrawtoField(DRAW_BACKBUFFER);
2451 // SetDrawBackgroundMask(REDRAW_NONE);
2453 if (action == ACTION_OPENING)
2455 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2457 if (req_state & REQ_ASK)
2459 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2460 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2462 else if (req_state & REQ_CONFIRM)
2464 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2466 else if (req_state & REQ_PLAYER)
2468 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2469 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2470 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2471 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2474 DrawEnvelopeRequest(text);
2476 if (game_status != GAME_MODE_MAIN)
2480 /* force DOOR font inside door area */
2481 game_status = GAME_MODE_PSEUDO_DOOR;
2483 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2485 if (action == ACTION_OPENING)
2487 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2489 if (anim_mode == ANIM_DEFAULT)
2490 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2492 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2496 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2498 if (anim_mode != ANIM_NONE)
2499 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2501 if (anim_mode == ANIM_DEFAULT)
2502 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2505 game.envelope_active = FALSE;
2507 game_status = last_game_status; /* restore current game status */
2509 if (action == ACTION_CLOSING)
2511 if (game_status != GAME_MODE_MAIN)
2514 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2517 // SetDrawBackgroundMask(last_draw_background_mask);
2519 redraw_mask |= REDRAW_FIELD;
2521 if (game_status == GAME_MODE_MAIN)
2526 if (action == ACTION_CLOSING &&
2527 game_status == GAME_MODE_PLAYING &&
2528 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2529 SetDrawtoField(DRAW_FIELDBUFFER);
2532 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2536 int graphic = el2preimg(element);
2538 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2539 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2542 void DrawLevel(int draw_background_mask)
2546 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2547 SetDrawBackgroundMask(draw_background_mask);
2551 for (x = BX1; x <= BX2; x++)
2552 for (y = BY1; y <= BY2; y++)
2553 DrawScreenField(x, y);
2555 redraw_mask |= REDRAW_FIELD;
2558 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2563 for (x = 0; x < size_x; x++)
2564 for (y = 0; y < size_y; y++)
2565 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2567 redraw_mask |= REDRAW_FIELD;
2570 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2574 for (x = 0; x < size_x; x++)
2575 for (y = 0; y < size_y; y++)
2576 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2578 redraw_mask |= REDRAW_FIELD;
2581 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2583 boolean show_level_border = (BorderElement != EL_EMPTY);
2584 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2585 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2586 int tile_size = preview.tile_size;
2587 int preview_width = preview.xsize * tile_size;
2588 int preview_height = preview.ysize * tile_size;
2589 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2590 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2591 int real_preview_width = real_preview_xsize * tile_size;
2592 int real_preview_height = real_preview_ysize * tile_size;
2593 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2594 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2597 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2600 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2602 dst_x += (preview_width - real_preview_width) / 2;
2603 dst_y += (preview_height - real_preview_height) / 2;
2605 for (x = 0; x < real_preview_xsize; x++)
2607 for (y = 0; y < real_preview_ysize; y++)
2609 int lx = from_x + x + (show_level_border ? -1 : 0);
2610 int ly = from_y + y + (show_level_border ? -1 : 0);
2611 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2612 getBorderElement(lx, ly));
2614 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2615 element, tile_size);
2619 redraw_mask |= REDRAW_FIELD;
2622 #define MICROLABEL_EMPTY 0
2623 #define MICROLABEL_LEVEL_NAME 1
2624 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2625 #define MICROLABEL_LEVEL_AUTHOR 3
2626 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2627 #define MICROLABEL_IMPORTED_FROM 5
2628 #define MICROLABEL_IMPORTED_BY_HEAD 6
2629 #define MICROLABEL_IMPORTED_BY 7
2631 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2633 int max_text_width = SXSIZE;
2634 int font_width = getFontWidth(font_nr);
2636 if (pos->align == ALIGN_CENTER)
2637 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2638 else if (pos->align == ALIGN_RIGHT)
2639 max_text_width = pos->x;
2641 max_text_width = SXSIZE - pos->x;
2643 return max_text_width / font_width;
2646 static void DrawPreviewLevelLabelExt(int mode)
2648 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2649 char label_text[MAX_OUTPUT_LINESIZE + 1];
2650 int max_len_label_text;
2651 int font_nr = pos->font;
2654 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2657 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2658 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2659 mode == MICROLABEL_IMPORTED_BY_HEAD)
2660 font_nr = pos->font_alt;
2662 max_len_label_text = getMaxTextLength(pos, font_nr);
2664 if (pos->size != -1)
2665 max_len_label_text = pos->size;
2667 for (i = 0; i < max_len_label_text; i++)
2668 label_text[i] = ' ';
2669 label_text[max_len_label_text] = '\0';
2671 if (strlen(label_text) > 0)
2672 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2675 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2676 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2677 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2678 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2679 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2680 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2681 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2682 max_len_label_text);
2683 label_text[max_len_label_text] = '\0';
2685 if (strlen(label_text) > 0)
2686 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2688 redraw_mask |= REDRAW_FIELD;
2691 static void DrawPreviewLevelExt(boolean restart)
2693 static unsigned int scroll_delay = 0;
2694 static unsigned int label_delay = 0;
2695 static int from_x, from_y, scroll_direction;
2696 static int label_state, label_counter;
2697 unsigned int scroll_delay_value = preview.step_delay;
2698 boolean show_level_border = (BorderElement != EL_EMPTY);
2699 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2700 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2701 int last_game_status = game_status; /* save current game status */
2708 if (preview.anim_mode == ANIM_CENTERED)
2710 if (level_xsize > preview.xsize)
2711 from_x = (level_xsize - preview.xsize) / 2;
2712 if (level_ysize > preview.ysize)
2713 from_y = (level_ysize - preview.ysize) / 2;
2716 from_x += preview.xoffset;
2717 from_y += preview.yoffset;
2719 scroll_direction = MV_RIGHT;
2723 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2724 DrawPreviewLevelLabelExt(label_state);
2726 /* initialize delay counters */
2727 DelayReached(&scroll_delay, 0);
2728 DelayReached(&label_delay, 0);
2730 if (leveldir_current->name)
2732 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2733 char label_text[MAX_OUTPUT_LINESIZE + 1];
2734 int font_nr = pos->font;
2735 int max_len_label_text = getMaxTextLength(pos, font_nr);
2737 if (pos->size != -1)
2738 max_len_label_text = pos->size;
2740 strncpy(label_text, leveldir_current->name, max_len_label_text);
2741 label_text[max_len_label_text] = '\0';
2743 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2744 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2747 game_status = last_game_status; /* restore current game status */
2752 /* scroll preview level, if needed */
2753 if (preview.anim_mode != ANIM_NONE &&
2754 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2755 DelayReached(&scroll_delay, scroll_delay_value))
2757 switch (scroll_direction)
2762 from_x -= preview.step_offset;
2763 from_x = (from_x < 0 ? 0 : from_x);
2766 scroll_direction = MV_UP;
2770 if (from_x < level_xsize - preview.xsize)
2772 from_x += preview.step_offset;
2773 from_x = (from_x > level_xsize - preview.xsize ?
2774 level_xsize - preview.xsize : from_x);
2777 scroll_direction = MV_DOWN;
2783 from_y -= preview.step_offset;
2784 from_y = (from_y < 0 ? 0 : from_y);
2787 scroll_direction = MV_RIGHT;
2791 if (from_y < level_ysize - preview.ysize)
2793 from_y += preview.step_offset;
2794 from_y = (from_y > level_ysize - preview.ysize ?
2795 level_ysize - preview.ysize : from_y);
2798 scroll_direction = MV_LEFT;
2805 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2808 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2809 /* redraw micro level label, if needed */
2810 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2811 !strEqual(level.author, ANONYMOUS_NAME) &&
2812 !strEqual(level.author, leveldir_current->name) &&
2813 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2815 int max_label_counter = 23;
2817 if (leveldir_current->imported_from != NULL &&
2818 strlen(leveldir_current->imported_from) > 0)
2819 max_label_counter += 14;
2820 if (leveldir_current->imported_by != NULL &&
2821 strlen(leveldir_current->imported_by) > 0)
2822 max_label_counter += 14;
2824 label_counter = (label_counter + 1) % max_label_counter;
2825 label_state = (label_counter >= 0 && label_counter <= 7 ?
2826 MICROLABEL_LEVEL_NAME :
2827 label_counter >= 9 && label_counter <= 12 ?
2828 MICROLABEL_LEVEL_AUTHOR_HEAD :
2829 label_counter >= 14 && label_counter <= 21 ?
2830 MICROLABEL_LEVEL_AUTHOR :
2831 label_counter >= 23 && label_counter <= 26 ?
2832 MICROLABEL_IMPORTED_FROM_HEAD :
2833 label_counter >= 28 && label_counter <= 35 ?
2834 MICROLABEL_IMPORTED_FROM :
2835 label_counter >= 37 && label_counter <= 40 ?
2836 MICROLABEL_IMPORTED_BY_HEAD :
2837 label_counter >= 42 && label_counter <= 49 ?
2838 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2840 if (leveldir_current->imported_from == NULL &&
2841 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2842 label_state == MICROLABEL_IMPORTED_FROM))
2843 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2844 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2846 DrawPreviewLevelLabelExt(label_state);
2849 game_status = last_game_status; /* restore current game status */
2852 void DrawPreviewLevelInitial()
2854 DrawPreviewLevelExt(TRUE);
2857 void DrawPreviewLevelAnimation()
2859 DrawPreviewLevelExt(FALSE);
2862 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2863 int graphic, int sync_frame,
2866 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2868 if (mask_mode == USE_MASKING)
2869 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2871 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2874 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2875 int graphic, int sync_frame, int mask_mode)
2877 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2879 if (mask_mode == USE_MASKING)
2880 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2882 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2885 inline static void DrawGraphicAnimation(int x, int y, int graphic)
2887 int lx = LEVELX(x), ly = LEVELY(y);
2889 if (!IN_SCR_FIELD(x, y))
2892 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2893 graphic, GfxFrame[lx][ly], NO_MASKING);
2895 MarkTileDirty(x, y);
2898 void DrawFixedGraphicAnimation(int x, int y, int graphic)
2900 int lx = LEVELX(x), ly = LEVELY(y);
2902 if (!IN_SCR_FIELD(x, y))
2905 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2906 graphic, GfxFrame[lx][ly], NO_MASKING);
2907 MarkTileDirty(x, y);
2910 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2912 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2915 void DrawLevelElementAnimation(int x, int y, int element)
2917 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2919 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2922 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2924 int sx = SCREENX(x), sy = SCREENY(y);
2926 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2929 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2932 DrawGraphicAnimation(sx, sy, graphic);
2935 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2936 DrawLevelFieldCrumbled(x, y);
2938 if (GFX_CRUMBLED(Feld[x][y]))
2939 DrawLevelFieldCrumbled(x, y);
2943 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2945 int sx = SCREENX(x), sy = SCREENY(y);
2948 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2951 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2953 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2956 DrawGraphicAnimation(sx, sy, graphic);
2958 if (GFX_CRUMBLED(element))
2959 DrawLevelFieldCrumbled(x, y);
2962 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2964 if (player->use_murphy)
2966 /* this works only because currently only one player can be "murphy" ... */
2967 static int last_horizontal_dir = MV_LEFT;
2968 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2970 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2971 last_horizontal_dir = move_dir;
2973 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2975 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2977 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2983 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2986 static boolean equalGraphics(int graphic1, int graphic2)
2988 struct GraphicInfo *g1 = &graphic_info[graphic1];
2989 struct GraphicInfo *g2 = &graphic_info[graphic2];
2991 return (g1->bitmap == g2->bitmap &&
2992 g1->src_x == g2->src_x &&
2993 g1->src_y == g2->src_y &&
2994 g1->anim_frames == g2->anim_frames &&
2995 g1->anim_delay == g2->anim_delay &&
2996 g1->anim_mode == g2->anim_mode);
2999 void DrawAllPlayers()
3003 for (i = 0; i < MAX_PLAYERS; i++)
3004 if (stored_player[i].active)
3005 DrawPlayer(&stored_player[i]);
3008 void DrawPlayerField(int x, int y)
3010 if (!IS_PLAYER(x, y))
3013 DrawPlayer(PLAYERINFO(x, y));
3016 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3018 void DrawPlayer(struct PlayerInfo *player)
3020 int jx = player->jx;
3021 int jy = player->jy;
3022 int move_dir = player->MovDir;
3023 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3024 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3025 int last_jx = (player->is_moving ? jx - dx : jx);
3026 int last_jy = (player->is_moving ? jy - dy : jy);
3027 int next_jx = jx + dx;
3028 int next_jy = jy + dy;
3029 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3030 boolean player_is_opaque = FALSE;
3031 int sx = SCREENX(jx), sy = SCREENY(jy);
3032 int sxx = 0, syy = 0;
3033 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3035 int action = ACTION_DEFAULT;
3036 int last_player_graphic = getPlayerGraphic(player, move_dir);
3037 int last_player_frame = player->Frame;
3040 /* GfxElement[][] is set to the element the player is digging or collecting;
3041 remove also for off-screen player if the player is not moving anymore */
3042 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3043 GfxElement[jx][jy] = EL_UNDEFINED;
3045 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3049 if (!IN_LEV_FIELD(jx, jy))
3051 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3052 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3053 printf("DrawPlayerField(): This should never happen!\n");
3058 if (element == EL_EXPLOSION)
3061 action = (player->is_pushing ? ACTION_PUSHING :
3062 player->is_digging ? ACTION_DIGGING :
3063 player->is_collecting ? ACTION_COLLECTING :
3064 player->is_moving ? ACTION_MOVING :
3065 player->is_snapping ? ACTION_SNAPPING :
3066 player->is_dropping ? ACTION_DROPPING :
3067 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3069 if (player->is_waiting)
3070 move_dir = player->dir_waiting;
3072 InitPlayerGfxAnimation(player, action, move_dir);
3074 /* ----------------------------------------------------------------------- */
3075 /* draw things in the field the player is leaving, if needed */
3076 /* ----------------------------------------------------------------------- */
3078 if (player->is_moving)
3080 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3082 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3084 if (last_element == EL_DYNAMITE_ACTIVE ||
3085 last_element == EL_EM_DYNAMITE_ACTIVE ||
3086 last_element == EL_SP_DISK_RED_ACTIVE)
3087 DrawDynamite(last_jx, last_jy);
3089 DrawLevelFieldThruMask(last_jx, last_jy);
3091 else if (last_element == EL_DYNAMITE_ACTIVE ||
3092 last_element == EL_EM_DYNAMITE_ACTIVE ||
3093 last_element == EL_SP_DISK_RED_ACTIVE)
3094 DrawDynamite(last_jx, last_jy);
3096 /* !!! this is not enough to prevent flickering of players which are
3097 moving next to each others without a free tile between them -- this
3098 can only be solved by drawing all players layer by layer (first the
3099 background, then the foreground etc.) !!! => TODO */
3100 else if (!IS_PLAYER(last_jx, last_jy))
3101 DrawLevelField(last_jx, last_jy);
3104 DrawLevelField(last_jx, last_jy);
3107 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3108 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3111 if (!IN_SCR_FIELD(sx, sy))
3114 /* ----------------------------------------------------------------------- */
3115 /* draw things behind the player, if needed */
3116 /* ----------------------------------------------------------------------- */
3119 DrawLevelElement(jx, jy, Back[jx][jy]);
3120 else if (IS_ACTIVE_BOMB(element))
3121 DrawLevelElement(jx, jy, EL_EMPTY);
3124 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3126 int old_element = GfxElement[jx][jy];
3127 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3128 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3130 if (GFX_CRUMBLED(old_element))
3131 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3133 DrawGraphic(sx, sy, old_graphic, frame);
3135 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3136 player_is_opaque = TRUE;
3140 GfxElement[jx][jy] = EL_UNDEFINED;
3142 /* make sure that pushed elements are drawn with correct frame rate */
3143 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3145 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3146 GfxFrame[jx][jy] = player->StepFrame;
3148 DrawLevelField(jx, jy);
3152 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3153 /* ----------------------------------------------------------------------- */
3154 /* draw player himself */
3155 /* ----------------------------------------------------------------------- */
3157 graphic = getPlayerGraphic(player, move_dir);
3159 /* in the case of changed player action or direction, prevent the current
3160 animation frame from being restarted for identical animations */
3161 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3162 player->Frame = last_player_frame;
3164 frame = getGraphicAnimationFrame(graphic, player->Frame);
3168 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3169 sxx = player->GfxPos;
3171 syy = player->GfxPos;
3174 if (player_is_opaque)
3175 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3177 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3179 if (SHIELD_ON(player))
3181 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3182 IMG_SHIELD_NORMAL_ACTIVE);
3183 int frame = getGraphicAnimationFrame(graphic, -1);
3185 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3189 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3192 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3193 sxx = player->GfxPos;
3195 syy = player->GfxPos;
3199 /* ----------------------------------------------------------------------- */
3200 /* draw things the player is pushing, if needed */
3201 /* ----------------------------------------------------------------------- */
3203 if (player->is_pushing && player->is_moving)
3205 int px = SCREENX(jx), py = SCREENY(jy);
3206 int pxx = (TILEX - ABS(sxx)) * dx;
3207 int pyy = (TILEY - ABS(syy)) * dy;
3208 int gfx_frame = GfxFrame[jx][jy];
3214 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3216 element = Feld[next_jx][next_jy];
3217 gfx_frame = GfxFrame[next_jx][next_jy];
3220 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3222 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3223 frame = getGraphicAnimationFrame(graphic, sync_frame);
3225 /* draw background element under pushed element (like the Sokoban field) */
3226 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3228 /* this allows transparent pushing animation over non-black background */
3231 DrawLevelElement(jx, jy, Back[jx][jy]);
3233 DrawLevelElement(jx, jy, EL_EMPTY);
3235 if (Back[next_jx][next_jy])
3236 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3238 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3240 else if (Back[next_jx][next_jy])
3241 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3244 /* do not draw (EM style) pushing animation when pushing is finished */
3245 /* (two-tile animations usually do not contain start and end frame) */
3246 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3247 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3249 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3251 /* masked drawing is needed for EMC style (double) movement graphics */
3252 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3253 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3257 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3258 /* ----------------------------------------------------------------------- */
3259 /* draw player himself */
3260 /* ----------------------------------------------------------------------- */
3262 graphic = getPlayerGraphic(player, move_dir);
3264 /* in the case of changed player action or direction, prevent the current
3265 animation frame from being restarted for identical animations */
3266 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3267 player->Frame = last_player_frame;
3269 frame = getGraphicAnimationFrame(graphic, player->Frame);
3273 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3274 sxx = player->GfxPos;
3276 syy = player->GfxPos;
3279 if (player_is_opaque)
3280 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3282 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3284 if (SHIELD_ON(player))
3286 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3287 IMG_SHIELD_NORMAL_ACTIVE);
3288 int frame = getGraphicAnimationFrame(graphic, -1);
3290 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3294 /* ----------------------------------------------------------------------- */
3295 /* draw things in front of player (active dynamite or dynabombs) */
3296 /* ----------------------------------------------------------------------- */
3298 if (IS_ACTIVE_BOMB(element))
3300 graphic = el2img(element);
3301 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3303 if (game.emulation == EMU_SUPAPLEX)
3304 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3306 DrawGraphicThruMask(sx, sy, graphic, frame);
3309 if (player_is_moving && last_element == EL_EXPLOSION)
3311 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3312 GfxElement[last_jx][last_jy] : EL_EMPTY);
3313 int graphic = el_act2img(element, ACTION_EXPLODING);
3314 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3315 int phase = ExplodePhase[last_jx][last_jy] - 1;
3316 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3319 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3322 /* ----------------------------------------------------------------------- */
3323 /* draw elements the player is just walking/passing through/under */
3324 /* ----------------------------------------------------------------------- */
3326 if (player_is_moving)
3328 /* handle the field the player is leaving ... */
3329 if (IS_ACCESSIBLE_INSIDE(last_element))
3330 DrawLevelField(last_jx, last_jy);
3331 else if (IS_ACCESSIBLE_UNDER(last_element))
3332 DrawLevelFieldThruMask(last_jx, last_jy);
3335 /* do not redraw accessible elements if the player is just pushing them */
3336 if (!player_is_moving || !player->is_pushing)
3338 /* ... and the field the player is entering */
3339 if (IS_ACCESSIBLE_INSIDE(element))
3340 DrawLevelField(jx, jy);
3341 else if (IS_ACCESSIBLE_UNDER(element))
3342 DrawLevelFieldThruMask(jx, jy);
3345 MarkTileDirty(sx, sy);
3348 /* ------------------------------------------------------------------------- */
3350 void WaitForEventToContinue()
3352 boolean still_wait = TRUE;
3354 /* simulate releasing mouse button over last gadget, if still pressed */
3356 HandleGadgets(-1, -1, 0);
3358 button_status = MB_RELEASED;
3372 case EVENT_BUTTONPRESS:
3373 case EVENT_KEYPRESS:
3377 case EVENT_KEYRELEASE:
3378 ClearPlayerAction();
3382 HandleOtherEvents(&event);
3386 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3393 /* don't eat all CPU time */
3398 #define MAX_REQUEST_LINES 13
3399 #define MAX_REQUEST_LINE_FONT1_LEN 7
3400 #define MAX_REQUEST_LINE_FONT2_LEN 10
3402 static int RequestHandleEvents(unsigned int req_state)
3404 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3405 local_player->LevelSolved_GameEnd);
3406 int width = request.width;
3407 int height = request.height;
3411 setRequestPosition(&sx, &sy, FALSE);
3413 button_status = MB_RELEASED;
3415 request_gadget_id = -1;
3422 SetDrawtoField(DRAW_FIELDBUFFER);
3424 HandleGameActions();
3426 SetDrawtoField(DRAW_BACKBUFFER);
3428 if (global.use_envelope_request)
3430 /* copy current state of request area to middle of playfield area */
3431 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3439 while (NextValidEvent(&event))
3443 case EVENT_BUTTONPRESS:
3444 case EVENT_BUTTONRELEASE:
3445 case EVENT_MOTIONNOTIFY:
3449 if (event.type == EVENT_MOTIONNOTIFY)
3454 motion_status = TRUE;
3455 mx = ((MotionEvent *) &event)->x;
3456 my = ((MotionEvent *) &event)->y;
3460 motion_status = FALSE;
3461 mx = ((ButtonEvent *) &event)->x;
3462 my = ((ButtonEvent *) &event)->y;
3463 if (event.type == EVENT_BUTTONPRESS)
3464 button_status = ((ButtonEvent *) &event)->button;
3466 button_status = MB_RELEASED;
3469 /* this sets 'request_gadget_id' */
3470 HandleGadgets(mx, my, button_status);
3472 switch (request_gadget_id)
3474 case TOOL_CTRL_ID_YES:
3477 case TOOL_CTRL_ID_NO:
3480 case TOOL_CTRL_ID_CONFIRM:
3481 result = TRUE | FALSE;
3484 case TOOL_CTRL_ID_PLAYER_1:
3487 case TOOL_CTRL_ID_PLAYER_2:
3490 case TOOL_CTRL_ID_PLAYER_3:
3493 case TOOL_CTRL_ID_PLAYER_4:
3504 case EVENT_KEYPRESS:
3505 switch (GetEventKey((KeyEvent *)&event, TRUE))
3508 if (req_state & REQ_CONFIRM)
3513 #if defined(TARGET_SDL2)
3520 #if defined(TARGET_SDL2)
3530 if (req_state & REQ_PLAYER)
3534 case EVENT_KEYRELEASE:
3535 ClearPlayerAction();
3539 HandleOtherEvents(&event);
3544 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3546 int joy = AnyJoystick();
3548 if (joy & JOY_BUTTON_1)
3550 else if (joy & JOY_BUTTON_2)
3556 if (global.use_envelope_request)
3558 /* copy back current state of pressed buttons inside request area */
3559 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3566 if (!PendingEvent()) /* delay only if no pending events */
3576 static boolean RequestDoor(char *text, unsigned int req_state)
3578 unsigned int old_door_state;
3579 int last_game_status = game_status; /* save current game status */
3580 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3581 int font_nr = FONT_TEXT_2;
3586 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3588 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3589 font_nr = FONT_TEXT_1;
3592 if (game_status == GAME_MODE_PLAYING)
3593 BlitScreenToBitmap(backbuffer);
3595 /* disable deactivated drawing when quick-loading level tape recording */
3596 if (tape.playing && tape.deactivate_display)
3597 TapeDeactivateDisplayOff(TRUE);
3599 SetMouseCursor(CURSOR_DEFAULT);
3601 #if defined(NETWORK_AVALIABLE)
3602 /* pause network game while waiting for request to answer */
3603 if (options.network &&
3604 game_status == GAME_MODE_PLAYING &&
3605 req_state & REQUEST_WAIT_FOR_INPUT)
3606 SendToServer_PausePlaying();
3609 old_door_state = GetDoorState();
3611 /* simulate releasing mouse button over last gadget, if still pressed */
3613 HandleGadgets(-1, -1, 0);
3617 /* draw released gadget before proceeding */
3620 if (old_door_state & DOOR_OPEN_1)
3622 CloseDoor(DOOR_CLOSE_1);
3624 /* save old door content */
3625 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3626 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3629 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3630 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3632 /* clear door drawing field */
3633 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3635 /* force DOOR font inside door area */
3636 game_status = GAME_MODE_PSEUDO_DOOR;
3638 /* write text for request */
3639 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3641 char text_line[max_request_line_len + 1];
3647 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3649 tc = *(text_ptr + tx);
3650 // if (!tc || tc == ' ')
3651 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3655 if ((tc == '?' || tc == '!') && tl == 0)
3665 strncpy(text_line, text_ptr, tl);
3668 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3669 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3670 text_line, font_nr);
3672 text_ptr += tl + (tc == ' ' ? 1 : 0);
3673 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3676 game_status = last_game_status; /* restore current game status */
3678 if (req_state & REQ_ASK)
3680 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3681 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3683 else if (req_state & REQ_CONFIRM)
3685 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3687 else if (req_state & REQ_PLAYER)
3689 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3690 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3691 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3692 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3695 /* copy request gadgets to door backbuffer */
3696 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3698 OpenDoor(DOOR_OPEN_1);
3700 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3702 if (game_status == GAME_MODE_PLAYING)
3704 SetPanelBackground();
3705 SetDrawBackgroundMask(REDRAW_DOOR_1);
3709 SetDrawBackgroundMask(REDRAW_FIELD);
3715 if (game_status != GAME_MODE_MAIN)
3718 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3720 // ---------- handle request buttons ----------
3721 result = RequestHandleEvents(req_state);
3723 if (game_status != GAME_MODE_MAIN)
3728 if (!(req_state & REQ_STAY_OPEN))
3730 CloseDoor(DOOR_CLOSE_1);
3732 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3733 (req_state & REQ_REOPEN))
3734 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3739 if (game_status == GAME_MODE_PLAYING)
3741 SetPanelBackground();
3742 SetDrawBackgroundMask(REDRAW_DOOR_1);
3746 SetDrawBackgroundMask(REDRAW_FIELD);
3749 #if defined(NETWORK_AVALIABLE)
3750 /* continue network game after request */
3751 if (options.network &&
3752 game_status == GAME_MODE_PLAYING &&
3753 req_state & REQUEST_WAIT_FOR_INPUT)
3754 SendToServer_ContinuePlaying();
3757 /* restore deactivated drawing when quick-loading level tape recording */
3758 if (tape.playing && tape.deactivate_display)
3759 TapeDeactivateDisplayOn();
3764 static boolean RequestEnvelope(char *text, unsigned int req_state)
3768 if (game_status == GAME_MODE_PLAYING)
3769 BlitScreenToBitmap(backbuffer);
3771 /* disable deactivated drawing when quick-loading level tape recording */
3772 if (tape.playing && tape.deactivate_display)
3773 TapeDeactivateDisplayOff(TRUE);
3775 SetMouseCursor(CURSOR_DEFAULT);
3777 #if defined(NETWORK_AVALIABLE)
3778 /* pause network game while waiting for request to answer */
3779 if (options.network &&
3780 game_status == GAME_MODE_PLAYING &&
3781 req_state & REQUEST_WAIT_FOR_INPUT)
3782 SendToServer_PausePlaying();
3785 /* simulate releasing mouse button over last gadget, if still pressed */
3787 HandleGadgets(-1, -1, 0);
3791 // (replace with setting corresponding request background)
3792 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3793 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3795 /* clear door drawing field */
3796 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3798 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3800 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3802 if (game_status == GAME_MODE_PLAYING)
3804 SetPanelBackground();
3805 SetDrawBackgroundMask(REDRAW_DOOR_1);
3809 SetDrawBackgroundMask(REDRAW_FIELD);
3815 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3817 // ---------- handle request buttons ----------
3818 result = RequestHandleEvents(req_state);
3820 if (game_status != GAME_MODE_MAIN)
3825 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3829 if (game_status == GAME_MODE_PLAYING)
3831 SetPanelBackground();
3832 SetDrawBackgroundMask(REDRAW_DOOR_1);
3836 SetDrawBackgroundMask(REDRAW_FIELD);
3839 #if defined(NETWORK_AVALIABLE)
3840 /* continue network game after request */
3841 if (options.network &&
3842 game_status == GAME_MODE_PLAYING &&
3843 req_state & REQUEST_WAIT_FOR_INPUT)
3844 SendToServer_ContinuePlaying();
3847 /* restore deactivated drawing when quick-loading level tape recording */
3848 if (tape.playing && tape.deactivate_display)
3849 TapeDeactivateDisplayOn();
3854 boolean Request(char *text, unsigned int req_state)
3856 if (global.use_envelope_request)
3857 return RequestEnvelope(text, req_state);
3859 return RequestDoor(text, req_state);
3862 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3864 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3865 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3868 if (dpo1->sort_priority != dpo2->sort_priority)
3869 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3871 compare_result = dpo1->nr - dpo2->nr;
3873 return compare_result;
3876 void InitGraphicCompatibilityInfo_Doors()
3882 struct DoorInfo *door;
3886 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3887 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3889 { -1, -1, -1, NULL }
3891 struct Rect door_rect_list[] =
3893 { DX, DY, DXSIZE, DYSIZE },
3894 { VX, VY, VXSIZE, VYSIZE }
3898 for (i = 0; doors[i].door_token != -1; i++)
3900 int door_token = doors[i].door_token;
3901 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3902 int part_1 = doors[i].part_1;
3903 int part_8 = doors[i].part_8;
3904 int part_2 = part_1 + 1;
3905 int part_3 = part_1 + 2;
3906 struct DoorInfo *door = doors[i].door;
3907 struct Rect *door_rect = &door_rect_list[door_index];
3908 boolean door_gfx_redefined = FALSE;
3910 /* check if any door part graphic definitions have been redefined */
3912 for (j = 0; door_part_controls[j].door_token != -1; j++)
3914 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3915 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3917 if (dpc->door_token == door_token && fi->redefined)
3918 door_gfx_redefined = TRUE;
3921 /* check for old-style door graphic/animation modifications */
3923 if (!door_gfx_redefined)
3925 if (door->anim_mode & ANIM_STATIC_PANEL)
3927 door->panel.step_xoffset = 0;
3928 door->panel.step_yoffset = 0;
3931 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3933 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3934 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3935 int num_door_steps, num_panel_steps;
3937 /* remove door part graphics other than the two default wings */
3939 for (j = 0; door_part_controls[j].door_token != -1; j++)
3941 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3942 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3944 if (dpc->graphic >= part_3 &&
3945 dpc->graphic <= part_8)
3949 /* set graphics and screen positions of the default wings */
3951 g_part_1->width = door_rect->width;
3952 g_part_1->height = door_rect->height;
3953 g_part_2->width = door_rect->width;
3954 g_part_2->height = door_rect->height;
3955 g_part_2->src_x = door_rect->width;
3956 g_part_2->src_y = g_part_1->src_y;
3958 door->part_2.x = door->part_1.x;
3959 door->part_2.y = door->part_1.y;
3961 if (door->width != -1)
3963 g_part_1->width = door->width;
3964 g_part_2->width = door->width;
3966 // special treatment for graphics and screen position of right wing
3967 g_part_2->src_x += door_rect->width - door->width;
3968 door->part_2.x += door_rect->width - door->width;
3971 if (door->height != -1)
3973 g_part_1->height = door->height;
3974 g_part_2->height = door->height;
3976 // special treatment for graphics and screen position of bottom wing
3977 g_part_2->src_y += door_rect->height - door->height;
3978 door->part_2.y += door_rect->height - door->height;
3981 /* set animation delays for the default wings and panels */
3983 door->part_1.step_delay = door->step_delay;
3984 door->part_2.step_delay = door->step_delay;
3985 door->panel.step_delay = door->step_delay;
3987 /* set animation draw order for the default wings */
3989 door->part_1.sort_priority = 2; /* draw left wing over ... */
3990 door->part_2.sort_priority = 1; /* ... right wing */
3992 /* set animation draw offset for the default wings */
3994 if (door->anim_mode & ANIM_HORIZONTAL)
3996 door->part_1.step_xoffset = door->step_offset;
3997 door->part_1.step_yoffset = 0;
3998 door->part_2.step_xoffset = door->step_offset * -1;
3999 door->part_2.step_yoffset = 0;
4001 num_door_steps = g_part_1->width / door->step_offset;
4003 else // ANIM_VERTICAL
4005 door->part_1.step_xoffset = 0;
4006 door->part_1.step_yoffset = door->step_offset;
4007 door->part_2.step_xoffset = 0;
4008 door->part_2.step_yoffset = door->step_offset * -1;
4010 num_door_steps = g_part_1->height / door->step_offset;
4013 /* set animation draw offset for the default panels */
4015 if (door->step_offset > 1)
4017 num_panel_steps = 2 * door_rect->height / door->step_offset;
4018 door->panel.start_step = num_panel_steps - num_door_steps;
4019 door->panel.start_step_closing = door->panel.start_step;
4023 num_panel_steps = door_rect->height / door->step_offset;
4024 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4025 door->panel.start_step_closing = door->panel.start_step;
4026 door->panel.step_delay *= 2;
4037 for (i = 0; door_part_controls[i].door_token != -1; i++)
4039 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4040 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4042 /* initialize "start_step_opening" and "start_step_closing", if needed */
4043 if (dpc->pos->start_step_opening == 0 &&
4044 dpc->pos->start_step_closing == 0)
4046 // dpc->pos->start_step_opening = dpc->pos->start_step;
4047 dpc->pos->start_step_closing = dpc->pos->start_step;
4050 /* fill structure for door part draw order (sorted below) */
4052 dpo->sort_priority = dpc->pos->sort_priority;
4055 /* sort door part controls according to sort_priority and graphic number */
4056 qsort(door_part_order, MAX_DOOR_PARTS,
4057 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4060 unsigned int OpenDoor(unsigned int door_state)
4062 if (door_state & DOOR_COPY_BACK)
4064 if (door_state & DOOR_OPEN_1)
4065 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4066 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4068 if (door_state & DOOR_OPEN_2)
4069 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4070 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4072 door_state &= ~DOOR_COPY_BACK;
4075 return MoveDoor(door_state);
4078 unsigned int CloseDoor(unsigned int door_state)
4080 unsigned int old_door_state = GetDoorState();
4082 if (!(door_state & DOOR_NO_COPY_BACK))
4084 if (old_door_state & DOOR_OPEN_1)
4085 BlitBitmap(backbuffer, bitmap_db_door_1,
4086 DX, DY, DXSIZE, DYSIZE, 0, 0);
4088 if (old_door_state & DOOR_OPEN_2)
4089 BlitBitmap(backbuffer, bitmap_db_door_2,
4090 VX, VY, VXSIZE, VYSIZE, 0, 0);
4092 door_state &= ~DOOR_NO_COPY_BACK;
4095 return MoveDoor(door_state);
4098 unsigned int GetDoorState()
4100 return MoveDoor(DOOR_GET_STATE);
4103 unsigned int SetDoorState(unsigned int door_state)
4105 return MoveDoor(door_state | DOOR_SET_STATE);
4108 int euclid(int a, int b)
4110 return (b ? euclid(b, a % b) : a);
4113 unsigned int MoveDoor(unsigned int door_state)
4115 struct Rect door_rect_list[] =
4117 { DX, DY, DXSIZE, DYSIZE },
4118 { VX, VY, VXSIZE, VYSIZE }
4120 static int door1 = DOOR_OPEN_1;
4121 static int door2 = DOOR_CLOSE_2;
4122 unsigned int door_delay = 0;
4123 unsigned int door_delay_value;
4126 if (door_state == DOOR_GET_STATE)
4127 return (door1 | door2);
4129 if (door_state & DOOR_SET_STATE)
4131 if (door_state & DOOR_ACTION_1)
4132 door1 = door_state & DOOR_ACTION_1;
4133 if (door_state & DOOR_ACTION_2)
4134 door2 = door_state & DOOR_ACTION_2;
4136 return (door1 | door2);
4139 if (!(door_state & DOOR_FORCE_REDRAW))
4141 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4142 door_state &= ~DOOR_OPEN_1;
4143 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4144 door_state &= ~DOOR_CLOSE_1;
4145 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4146 door_state &= ~DOOR_OPEN_2;
4147 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4148 door_state &= ~DOOR_CLOSE_2;
4151 if (global.autoplay_leveldir)
4153 door_state |= DOOR_NO_DELAY;
4154 door_state &= ~DOOR_CLOSE_ALL;
4157 if (game_status == GAME_MODE_EDITOR)
4158 door_state |= DOOR_NO_DELAY;
4160 if (door_state & DOOR_ACTION)
4162 boolean door_panel_drawn[NUM_DOORS];
4163 boolean panel_has_doors[NUM_DOORS];
4164 boolean door_part_skip[MAX_DOOR_PARTS];
4165 boolean door_part_done[MAX_DOOR_PARTS];
4166 boolean door_part_done_all;
4167 int num_steps[MAX_DOOR_PARTS];
4168 int max_move_delay = 0; // delay for complete animations of all doors
4169 int max_step_delay = 0; // delay (ms) between two animation frames
4170 int num_move_steps = 0; // number of animation steps for all doors
4171 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4172 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4173 int current_move_delay = 0;
4177 for (i = 0; i < NUM_DOORS; i++)
4178 panel_has_doors[i] = FALSE;
4180 for (i = 0; i < MAX_DOOR_PARTS; i++)
4182 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4183 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4184 int door_token = dpc->door_token;
4186 door_part_done[i] = FALSE;
4187 door_part_skip[i] = (!(door_state & door_token) ||
4191 for (i = 0; i < MAX_DOOR_PARTS; i++)
4193 int nr = door_part_order[i].nr;
4194 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4195 struct DoorPartPosInfo *pos = dpc->pos;
4196 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4197 int door_token = dpc->door_token;
4198 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4199 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4200 int step_xoffset = ABS(pos->step_xoffset);
4201 int step_yoffset = ABS(pos->step_yoffset);
4202 int step_delay = pos->step_delay;
4203 int current_door_state = door_state & door_token;
4204 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4205 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4206 boolean part_opening = (is_panel ? door_closing : door_opening);
4207 int start_step = (part_opening ? pos->start_step_opening :
4208 pos->start_step_closing);
4209 float move_xsize = (step_xoffset ? g->width : 0);
4210 float move_ysize = (step_yoffset ? g->height : 0);
4211 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4212 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4213 int move_steps = (move_xsteps && move_ysteps ?
4214 MIN(move_xsteps, move_ysteps) :
4215 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4216 int move_delay = move_steps * step_delay;
4218 if (door_part_skip[nr])
4221 max_move_delay = MAX(max_move_delay, move_delay);
4222 max_step_delay = (max_step_delay == 0 ? step_delay :
4223 euclid(max_step_delay, step_delay));
4224 num_steps[nr] = move_steps;
4228 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4230 panel_has_doors[door_index] = TRUE;
4234 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4236 num_move_steps = max_move_delay / max_step_delay;
4237 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4239 door_delay_value = max_step_delay;
4241 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4243 start = num_move_steps - 1;
4247 /* opening door sound has priority over simultaneously closing door */
4248 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4249 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4250 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4251 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4254 for (k = start; k < num_move_steps; k++)
4256 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4258 door_part_done_all = TRUE;
4260 for (i = 0; i < NUM_DOORS; i++)
4261 door_panel_drawn[i] = FALSE;
4263 for (i = 0; i < MAX_DOOR_PARTS; i++)
4265 int nr = door_part_order[i].nr;
4266 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4267 struct DoorPartPosInfo *pos = dpc->pos;
4268 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4269 int door_token = dpc->door_token;
4270 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4271 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4272 boolean is_panel_and_door_has_closed = FALSE;
4273 struct Rect *door_rect = &door_rect_list[door_index];
4274 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4276 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4277 int current_door_state = door_state & door_token;
4278 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4279 boolean door_closing = !door_opening;
4280 boolean part_opening = (is_panel ? door_closing : door_opening);
4281 boolean part_closing = !part_opening;
4282 int start_step = (part_opening ? pos->start_step_opening :
4283 pos->start_step_closing);
4284 int step_delay = pos->step_delay;
4285 int step_factor = step_delay / max_step_delay;
4286 int k1 = (step_factor ? k / step_factor + 1 : k);
4287 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4288 int kk = MAX(0, k2);
4291 int src_x, src_y, src_xx, src_yy;
4292 int dst_x, dst_y, dst_xx, dst_yy;
4295 if (door_part_skip[nr])
4298 if (!(door_state & door_token))
4306 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4307 int kk_door = MAX(0, k2_door);
4308 int sync_frame = kk_door * door_delay_value;
4309 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4311 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4316 if (!door_panel_drawn[door_index])
4318 ClearRectangle(drawto, door_rect->x, door_rect->y,
4319 door_rect->width, door_rect->height);
4321 door_panel_drawn[door_index] = TRUE;
4324 // draw opening or closing door parts
4326 if (pos->step_xoffset < 0) // door part on right side
4329 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4332 if (dst_xx + width > door_rect->width)
4333 width = door_rect->width - dst_xx;
4335 else // door part on left side
4338 dst_xx = pos->x - kk * pos->step_xoffset;
4342 src_xx = ABS(dst_xx);
4346 width = g->width - src_xx;
4348 if (width > door_rect->width)
4349 width = door_rect->width;
4351 // printf("::: k == %d [%d] \n", k, start_step);
4354 if (pos->step_yoffset < 0) // door part on bottom side
4357 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4360 if (dst_yy + height > door_rect->height)
4361 height = door_rect->height - dst_yy;
4363 else // door part on top side
4366 dst_yy = pos->y - kk * pos->step_yoffset;
4370 src_yy = ABS(dst_yy);
4374 height = g->height - src_yy;
4377 src_x = g_src_x + src_xx;
4378 src_y = g_src_y + src_yy;
4380 dst_x = door_rect->x + dst_xx;
4381 dst_y = door_rect->y + dst_yy;
4383 is_panel_and_door_has_closed =
4386 panel_has_doors[door_index] &&
4387 k >= num_move_steps_doors_only - 1);
4389 if (width >= 0 && width <= g->width &&
4390 height >= 0 && height <= g->height &&
4391 !is_panel_and_door_has_closed)
4393 if (is_panel || !pos->draw_masked)
4394 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4397 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4401 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4403 if ((part_opening && (width < 0 || height < 0)) ||
4404 (part_closing && (width >= g->width && height >= g->height)))
4405 door_part_done[nr] = TRUE;
4407 // continue door part animations, but not panel after door has closed
4408 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4409 door_part_done_all = FALSE;
4412 if (!(door_state & DOOR_NO_DELAY))
4416 if (game_status == GAME_MODE_MAIN)
4419 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4421 current_move_delay += max_step_delay;
4424 if (door_part_done_all)
4429 if (door_state & DOOR_ACTION_1)
4430 door1 = door_state & DOOR_ACTION_1;
4431 if (door_state & DOOR_ACTION_2)
4432 door2 = door_state & DOOR_ACTION_2;
4434 return (door1 | door2);
4437 static boolean useSpecialEditorDoor()
4439 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4440 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4442 // do not draw special editor door if editor border defined or redefined
4443 if (graphic_info[graphic].bitmap != NULL || redefined)
4446 // do not draw special editor door if global border defined to be empty
4447 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4450 // do not draw special editor door if viewport definitions do not match
4454 EY + EYSIZE != VY + VYSIZE)
4460 void DrawSpecialEditorDoor()
4462 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4463 int top_border_width = gfx1->width;
4464 int top_border_height = gfx1->height;
4465 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4466 int ex = EX - outer_border;
4467 int ey = EY - outer_border;
4468 int vy = VY - outer_border;
4469 int exsize = EXSIZE + 2 * outer_border;
4471 if (!useSpecialEditorDoor())
4474 /* draw bigger level editor toolbox window */
4475 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4476 top_border_width, top_border_height, ex, ey - top_border_height);
4477 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4478 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4480 redraw_mask |= REDRAW_ALL;
4483 void UndrawSpecialEditorDoor()
4485 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4486 int top_border_width = gfx1->width;
4487 int top_border_height = gfx1->height;
4488 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4489 int ex = EX - outer_border;
4490 int ey = EY - outer_border;
4491 int ey_top = ey - top_border_height;
4492 int exsize = EXSIZE + 2 * outer_border;
4493 int eysize = EYSIZE + 2 * outer_border;
4495 if (!useSpecialEditorDoor())
4498 /* draw normal tape recorder window */
4499 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4501 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4502 ex, ey_top, top_border_width, top_border_height,
4504 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4505 ex, ey, exsize, eysize, ex, ey);
4509 // if screen background is set to "[NONE]", clear editor toolbox window
4510 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4511 ClearRectangle(drawto, ex, ey, exsize, eysize);
4514 redraw_mask |= REDRAW_ALL;
4518 /* ---------- new tool button stuff ---------------------------------------- */
4523 struct TextPosInfo *pos;
4526 } toolbutton_info[NUM_TOOL_BUTTONS] =
4529 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4530 TOOL_CTRL_ID_YES, "yes"
4533 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4534 TOOL_CTRL_ID_NO, "no"
4537 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4538 TOOL_CTRL_ID_CONFIRM, "confirm"
4541 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4542 TOOL_CTRL_ID_PLAYER_1, "player 1"
4545 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4546 TOOL_CTRL_ID_PLAYER_2, "player 2"
4549 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4550 TOOL_CTRL_ID_PLAYER_3, "player 3"
4553 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4554 TOOL_CTRL_ID_PLAYER_4, "player 4"
4558 void CreateToolButtons()
4562 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4564 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4565 struct TextPosInfo *pos = toolbutton_info[i].pos;
4566 struct GadgetInfo *gi;
4567 Bitmap *deco_bitmap = None;
4568 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4569 unsigned int event_mask = GD_EVENT_RELEASED;
4572 int gd_x = gfx->src_x;
4573 int gd_y = gfx->src_y;
4574 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4575 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4578 if (global.use_envelope_request)
4579 setRequestPosition(&dx, &dy, TRUE);
4581 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4583 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4585 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4586 pos->size, &deco_bitmap, &deco_x, &deco_y);
4587 deco_xpos = (gfx->width - pos->size) / 2;
4588 deco_ypos = (gfx->height - pos->size) / 2;
4591 gi = CreateGadget(GDI_CUSTOM_ID, id,
4592 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4593 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4594 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4595 GDI_WIDTH, gfx->width,
4596 GDI_HEIGHT, gfx->height,
4597 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4598 GDI_STATE, GD_BUTTON_UNPRESSED,
4599 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4600 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4601 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4602 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4603 GDI_DECORATION_SIZE, pos->size, pos->size,
4604 GDI_DECORATION_SHIFTING, 1, 1,
4605 GDI_DIRECT_DRAW, FALSE,
4606 GDI_EVENT_MASK, event_mask,
4607 GDI_CALLBACK_ACTION, HandleToolButtons,
4611 Error(ERR_EXIT, "cannot create gadget");
4613 tool_gadget[id] = gi;
4617 void FreeToolButtons()
4621 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4622 FreeGadget(tool_gadget[i]);
4625 static void UnmapToolButtons()
4629 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4630 UnmapGadget(tool_gadget[i]);
4633 static void HandleToolButtons(struct GadgetInfo *gi)
4635 request_gadget_id = gi->custom_id;
4638 static struct Mapping_EM_to_RND_object
4641 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4642 boolean is_backside; /* backside of moving element */
4648 em_object_mapping_list[] =
4651 Xblank, TRUE, FALSE,
4655 Yacid_splash_eB, FALSE, FALSE,
4656 EL_ACID_SPLASH_RIGHT, -1, -1
4659 Yacid_splash_wB, FALSE, FALSE,
4660 EL_ACID_SPLASH_LEFT, -1, -1
4663 #ifdef EM_ENGINE_BAD_ROLL
4665 Xstone_force_e, FALSE, FALSE,
4666 EL_ROCK, -1, MV_BIT_RIGHT
4669 Xstone_force_w, FALSE, FALSE,
4670 EL_ROCK, -1, MV_BIT_LEFT
4673 Xnut_force_e, FALSE, FALSE,
4674 EL_NUT, -1, MV_BIT_RIGHT
4677 Xnut_force_w, FALSE, FALSE,
4678 EL_NUT, -1, MV_BIT_LEFT
4681 Xspring_force_e, FALSE, FALSE,
4682 EL_SPRING, -1, MV_BIT_RIGHT
4685 Xspring_force_w, FALSE, FALSE,
4686 EL_SPRING, -1, MV_BIT_LEFT
4689 Xemerald_force_e, FALSE, FALSE,
4690 EL_EMERALD, -1, MV_BIT_RIGHT
4693 Xemerald_force_w, FALSE, FALSE,
4694 EL_EMERALD, -1, MV_BIT_LEFT
4697 Xdiamond_force_e, FALSE, FALSE,
4698 EL_DIAMOND, -1, MV_BIT_RIGHT
4701 Xdiamond_force_w, FALSE, FALSE,
4702 EL_DIAMOND, -1, MV_BIT_LEFT
4705 Xbomb_force_e, FALSE, FALSE,
4706 EL_BOMB, -1, MV_BIT_RIGHT
4709 Xbomb_force_w, FALSE, FALSE,
4710 EL_BOMB, -1, MV_BIT_LEFT
4712 #endif /* EM_ENGINE_BAD_ROLL */
4715 Xstone, TRUE, FALSE,
4719 Xstone_pause, FALSE, FALSE,
4723 Xstone_fall, FALSE, FALSE,
4727 Ystone_s, FALSE, FALSE,
4728 EL_ROCK, ACTION_FALLING, -1
4731 Ystone_sB, FALSE, TRUE,
4732 EL_ROCK, ACTION_FALLING, -1
4735 Ystone_e, FALSE, FALSE,
4736 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4739 Ystone_eB, FALSE, TRUE,
4740 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4743 Ystone_w, FALSE, FALSE,
4744 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4747 Ystone_wB, FALSE, TRUE,
4748 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4755 Xnut_pause, FALSE, FALSE,
4759 Xnut_fall, FALSE, FALSE,
4763 Ynut_s, FALSE, FALSE,
4764 EL_NUT, ACTION_FALLING, -1
4767 Ynut_sB, FALSE, TRUE,
4768 EL_NUT, ACTION_FALLING, -1
4771 Ynut_e, FALSE, FALSE,
4772 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4775 Ynut_eB, FALSE, TRUE,
4776 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4779 Ynut_w, FALSE, FALSE,
4780 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4783 Ynut_wB, FALSE, TRUE,
4784 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4787 Xbug_n, TRUE, FALSE,
4791 Xbug_e, TRUE, FALSE,
4792 EL_BUG_RIGHT, -1, -1
4795 Xbug_s, TRUE, FALSE,
4799 Xbug_w, TRUE, FALSE,
4803 Xbug_gon, FALSE, FALSE,
4807 Xbug_goe, FALSE, FALSE,
4808 EL_BUG_RIGHT, -1, -1
4811 Xbug_gos, FALSE, FALSE,
4815 Xbug_gow, FALSE, FALSE,
4819 Ybug_n, FALSE, FALSE,
4820 EL_BUG, ACTION_MOVING, MV_BIT_UP
4823 Ybug_nB, FALSE, TRUE,
4824 EL_BUG, ACTION_MOVING, MV_BIT_UP
4827 Ybug_e, FALSE, FALSE,
4828 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4831 Ybug_eB, FALSE, TRUE,
4832 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4835 Ybug_s, FALSE, FALSE,
4836 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4839 Ybug_sB, FALSE, TRUE,
4840 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4843 Ybug_w, FALSE, FALSE,
4844 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4847 Ybug_wB, FALSE, TRUE,
4848 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4851 Ybug_w_n, FALSE, FALSE,
4852 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4855 Ybug_n_e, FALSE, FALSE,
4856 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4859 Ybug_e_s, FALSE, FALSE,
4860 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4863 Ybug_s_w, FALSE, FALSE,
4864 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4867 Ybug_e_n, FALSE, FALSE,
4868 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4871 Ybug_s_e, FALSE, FALSE,
4872 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4875 Ybug_w_s, FALSE, FALSE,
4876 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4879 Ybug_n_w, FALSE, FALSE,
4880 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4883 Ybug_stone, FALSE, FALSE,
4884 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4887 Ybug_spring, FALSE, FALSE,
4888 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4891 Xtank_n, TRUE, FALSE,
4892 EL_SPACESHIP_UP, -1, -1
4895 Xtank_e, TRUE, FALSE,
4896 EL_SPACESHIP_RIGHT, -1, -1
4899 Xtank_s, TRUE, FALSE,
4900 EL_SPACESHIP_DOWN, -1, -1
4903 Xtank_w, TRUE, FALSE,
4904 EL_SPACESHIP_LEFT, -1, -1
4907 Xtank_gon, FALSE, FALSE,
4908 EL_SPACESHIP_UP, -1, -1
4911 Xtank_goe, FALSE, FALSE,
4912 EL_SPACESHIP_RIGHT, -1, -1
4915 Xtank_gos, FALSE, FALSE,
4916 EL_SPACESHIP_DOWN, -1, -1
4919 Xtank_gow, FALSE, FALSE,
4920 EL_SPACESHIP_LEFT, -1, -1
4923 Ytank_n, FALSE, FALSE,
4924 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4927 Ytank_nB, FALSE, TRUE,
4928 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4931 Ytank_e, FALSE, FALSE,
4932 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4935 Ytank_eB, FALSE, TRUE,
4936 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4939 Ytank_s, FALSE, FALSE,
4940 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4943 Ytank_sB, FALSE, TRUE,
4944 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4947 Ytank_w, FALSE, FALSE,
4948 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4951 Ytank_wB, FALSE, TRUE,
4952 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4955 Ytank_w_n, FALSE, FALSE,
4956 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4959 Ytank_n_e, FALSE, FALSE,
4960 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4963 Ytank_e_s, FALSE, FALSE,
4964 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4967 Ytank_s_w, FALSE, FALSE,
4968 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4971 Ytank_e_n, FALSE, FALSE,
4972 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4975 Ytank_s_e, FALSE, FALSE,
4976 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4979 Ytank_w_s, FALSE, FALSE,
4980 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4983 Ytank_n_w, FALSE, FALSE,
4984 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4987 Ytank_stone, FALSE, FALSE,
4988 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4991 Ytank_spring, FALSE, FALSE,
4992 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4995 Xandroid, TRUE, FALSE,
4996 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4999 Xandroid_1_n, FALSE, FALSE,
5000 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5003 Xandroid_2_n, FALSE, FALSE,
5004 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5007 Xandroid_1_e, FALSE, FALSE,
5008 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5011 Xandroid_2_e, FALSE, FALSE,
5012 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5015 Xandroid_1_w, FALSE, FALSE,
5016 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5019 Xandroid_2_w, FALSE, FALSE,
5020 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5023 Xandroid_1_s, FALSE, FALSE,
5024 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5027 Xandroid_2_s, FALSE, FALSE,
5028 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5031 Yandroid_n, FALSE, FALSE,
5032 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5035 Yandroid_nB, FALSE, TRUE,
5036 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5039 Yandroid_ne, FALSE, FALSE,
5040 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5043 Yandroid_neB, FALSE, TRUE,
5044 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5047 Yandroid_e, FALSE, FALSE,
5048 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5051 Yandroid_eB, FALSE, TRUE,
5052 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5055 Yandroid_se, FALSE, FALSE,
5056 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5059 Yandroid_seB, FALSE, TRUE,
5060 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5063 Yandroid_s, FALSE, FALSE,
5064 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5067 Yandroid_sB, FALSE, TRUE,
5068 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5071 Yandroid_sw, FALSE, FALSE,
5072 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5075 Yandroid_swB, FALSE, TRUE,
5076 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5079 Yandroid_w, FALSE, FALSE,
5080 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5083 Yandroid_wB, FALSE, TRUE,
5084 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5087 Yandroid_nw, FALSE, FALSE,
5088 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5091 Yandroid_nwB, FALSE, TRUE,
5092 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5095 Xspring, TRUE, FALSE,
5099 Xspring_pause, FALSE, FALSE,
5103 Xspring_e, FALSE, FALSE,
5107 Xspring_w, FALSE, FALSE,
5111 Xspring_fall, FALSE, FALSE,
5115 Yspring_s, FALSE, FALSE,
5116 EL_SPRING, ACTION_FALLING, -1
5119 Yspring_sB, FALSE, TRUE,
5120 EL_SPRING, ACTION_FALLING, -1
5123 Yspring_e, FALSE, FALSE,
5124 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5127 Yspring_eB, FALSE, TRUE,
5128 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5131 Yspring_w, FALSE, FALSE,
5132 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5135 Yspring_wB, FALSE, TRUE,
5136 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5139 Yspring_kill_e, FALSE, FALSE,
5140 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5143 Yspring_kill_eB, FALSE, TRUE,
5144 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5147 Yspring_kill_w, FALSE, FALSE,
5148 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5151 Yspring_kill_wB, FALSE, TRUE,
5152 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5155 Xeater_n, TRUE, FALSE,
5156 EL_YAMYAM_UP, -1, -1
5159 Xeater_e, TRUE, FALSE,
5160 EL_YAMYAM_RIGHT, -1, -1
5163 Xeater_w, TRUE, FALSE,
5164 EL_YAMYAM_LEFT, -1, -1
5167 Xeater_s, TRUE, FALSE,
5168 EL_YAMYAM_DOWN, -1, -1
5171 Yeater_n, FALSE, FALSE,
5172 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5175 Yeater_nB, FALSE, TRUE,
5176 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5179 Yeater_e, FALSE, FALSE,
5180 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5183 Yeater_eB, FALSE, TRUE,
5184 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5187 Yeater_s, FALSE, FALSE,
5188 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5191 Yeater_sB, FALSE, TRUE,
5192 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5195 Yeater_w, FALSE, FALSE,
5196 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5199 Yeater_wB, FALSE, TRUE,
5200 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5203 Yeater_stone, FALSE, FALSE,
5204 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5207 Yeater_spring, FALSE, FALSE,
5208 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5211 Xalien, TRUE, FALSE,
5215 Xalien_pause, FALSE, FALSE,
5219 Yalien_n, FALSE, FALSE,
5220 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5223 Yalien_nB, FALSE, TRUE,
5224 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5227 Yalien_e, FALSE, FALSE,
5228 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5231 Yalien_eB, FALSE, TRUE,
5232 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5235 Yalien_s, FALSE, FALSE,
5236 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5239 Yalien_sB, FALSE, TRUE,
5240 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5243 Yalien_w, FALSE, FALSE,
5244 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5247 Yalien_wB, FALSE, TRUE,
5248 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5251 Yalien_stone, FALSE, FALSE,
5252 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5255 Yalien_spring, FALSE, FALSE,
5256 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5259 Xemerald, TRUE, FALSE,
5263 Xemerald_pause, FALSE, FALSE,
5267 Xemerald_fall, FALSE, FALSE,
5271 Xemerald_shine, FALSE, FALSE,
5272 EL_EMERALD, ACTION_TWINKLING, -1
5275 Yemerald_s, FALSE, FALSE,
5276 EL_EMERALD, ACTION_FALLING, -1
5279 Yemerald_sB, FALSE, TRUE,
5280 EL_EMERALD, ACTION_FALLING, -1
5283 Yemerald_e, FALSE, FALSE,
5284 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5287 Yemerald_eB, FALSE, TRUE,
5288 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5291 Yemerald_w, FALSE, FALSE,
5292 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5295 Yemerald_wB, FALSE, TRUE,
5296 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5299 Yemerald_eat, FALSE, FALSE,
5300 EL_EMERALD, ACTION_COLLECTING, -1
5303 Yemerald_stone, FALSE, FALSE,
5304 EL_NUT, ACTION_BREAKING, -1
5307 Xdiamond, TRUE, FALSE,
5311 Xdiamond_pause, FALSE, FALSE,
5315 Xdiamond_fall, FALSE, FALSE,
5319 Xdiamond_shine, FALSE, FALSE,
5320 EL_DIAMOND, ACTION_TWINKLING, -1
5323 Ydiamond_s, FALSE, FALSE,
5324 EL_DIAMOND, ACTION_FALLING, -1
5327 Ydiamond_sB, FALSE, TRUE,
5328 EL_DIAMOND, ACTION_FALLING, -1
5331 Ydiamond_e, FALSE, FALSE,
5332 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5335 Ydiamond_eB, FALSE, TRUE,
5336 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5339 Ydiamond_w, FALSE, FALSE,
5340 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5343 Ydiamond_wB, FALSE, TRUE,
5344 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5347 Ydiamond_eat, FALSE, FALSE,
5348 EL_DIAMOND, ACTION_COLLECTING, -1
5351 Ydiamond_stone, FALSE, FALSE,
5352 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5355 Xdrip_fall, TRUE, FALSE,
5356 EL_AMOEBA_DROP, -1, -1
5359 Xdrip_stretch, FALSE, FALSE,
5360 EL_AMOEBA_DROP, ACTION_FALLING, -1
5363 Xdrip_stretchB, FALSE, TRUE,
5364 EL_AMOEBA_DROP, ACTION_FALLING, -1
5367 Xdrip_eat, FALSE, FALSE,
5368 EL_AMOEBA_DROP, ACTION_GROWING, -1
5371 Ydrip_s1, FALSE, FALSE,
5372 EL_AMOEBA_DROP, ACTION_FALLING, -1
5375 Ydrip_s1B, FALSE, TRUE,
5376 EL_AMOEBA_DROP, ACTION_FALLING, -1
5379 Ydrip_s2, FALSE, FALSE,
5380 EL_AMOEBA_DROP, ACTION_FALLING, -1
5383 Ydrip_s2B, FALSE, TRUE,
5384 EL_AMOEBA_DROP, ACTION_FALLING, -1
5391 Xbomb_pause, FALSE, FALSE,
5395 Xbomb_fall, FALSE, FALSE,
5399 Ybomb_s, FALSE, FALSE,
5400 EL_BOMB, ACTION_FALLING, -1
5403 Ybomb_sB, FALSE, TRUE,
5404 EL_BOMB, ACTION_FALLING, -1
5407 Ybomb_e, FALSE, FALSE,
5408 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5411 Ybomb_eB, FALSE, TRUE,
5412 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5415 Ybomb_w, FALSE, FALSE,
5416 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5419 Ybomb_wB, FALSE, TRUE,
5420 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5423 Ybomb_eat, FALSE, FALSE,
5424 EL_BOMB, ACTION_ACTIVATING, -1
5427 Xballoon, TRUE, FALSE,
5431 Yballoon_n, FALSE, FALSE,
5432 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5435 Yballoon_nB, FALSE, TRUE,
5436 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5439 Yballoon_e, FALSE, FALSE,
5440 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5443 Yballoon_eB, FALSE, TRUE,
5444 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5447 Yballoon_s, FALSE, FALSE,
5448 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5451 Yballoon_sB, FALSE, TRUE,
5452 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5455 Yballoon_w, FALSE, FALSE,
5456 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5459 Yballoon_wB, FALSE, TRUE,
5460 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5463 Xgrass, TRUE, FALSE,
5464 EL_EMC_GRASS, -1, -1
5467 Ygrass_nB, FALSE, FALSE,
5468 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5471 Ygrass_eB, FALSE, FALSE,
5472 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5475 Ygrass_sB, FALSE, FALSE,
5476 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5479 Ygrass_wB, FALSE, FALSE,
5480 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5487 Ydirt_nB, FALSE, FALSE,
5488 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5491 Ydirt_eB, FALSE, FALSE,
5492 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5495 Ydirt_sB, FALSE, FALSE,
5496 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5499 Ydirt_wB, FALSE, FALSE,
5500 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5503 Xacid_ne, TRUE, FALSE,
5504 EL_ACID_POOL_TOPRIGHT, -1, -1
5507 Xacid_se, TRUE, FALSE,
5508 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5511 Xacid_s, TRUE, FALSE,
5512 EL_ACID_POOL_BOTTOM, -1, -1
5515 Xacid_sw, TRUE, FALSE,
5516 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5519 Xacid_nw, TRUE, FALSE,
5520 EL_ACID_POOL_TOPLEFT, -1, -1
5523 Xacid_1, TRUE, FALSE,
5527 Xacid_2, FALSE, FALSE,
5531 Xacid_3, FALSE, FALSE,
5535 Xacid_4, FALSE, FALSE,
5539 Xacid_5, FALSE, FALSE,
5543 Xacid_6, FALSE, FALSE,
5547 Xacid_7, FALSE, FALSE,
5551 Xacid_8, FALSE, FALSE,
5555 Xball_1, TRUE, FALSE,
5556 EL_EMC_MAGIC_BALL, -1, -1
5559 Xball_1B, FALSE, FALSE,
5560 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5563 Xball_2, FALSE, FALSE,
5564 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5567 Xball_2B, FALSE, FALSE,
5568 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5571 Yball_eat, FALSE, FALSE,
5572 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5575 Ykey_1_eat, FALSE, FALSE,
5576 EL_EM_KEY_1, ACTION_COLLECTING, -1
5579 Ykey_2_eat, FALSE, FALSE,
5580 EL_EM_KEY_2, ACTION_COLLECTING, -1
5583 Ykey_3_eat, FALSE, FALSE,
5584 EL_EM_KEY_3, ACTION_COLLECTING, -1
5587 Ykey_4_eat, FALSE, FALSE,
5588 EL_EM_KEY_4, ACTION_COLLECTING, -1
5591 Ykey_5_eat, FALSE, FALSE,
5592 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5595 Ykey_6_eat, FALSE, FALSE,
5596 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5599 Ykey_7_eat, FALSE, FALSE,
5600 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5603 Ykey_8_eat, FALSE, FALSE,
5604 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5607 Ylenses_eat, FALSE, FALSE,
5608 EL_EMC_LENSES, ACTION_COLLECTING, -1
5611 Ymagnify_eat, FALSE, FALSE,
5612 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5615 Ygrass_eat, FALSE, FALSE,
5616 EL_EMC_GRASS, ACTION_SNAPPING, -1
5619 Ydirt_eat, FALSE, FALSE,
5620 EL_SAND, ACTION_SNAPPING, -1
5623 Xgrow_ns, TRUE, FALSE,
5624 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5627 Ygrow_ns_eat, FALSE, FALSE,
5628 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5631 Xgrow_ew, TRUE, FALSE,
5632 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5635 Ygrow_ew_eat, FALSE, FALSE,
5636 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5639 Xwonderwall, TRUE, FALSE,
5640 EL_MAGIC_WALL, -1, -1
5643 XwonderwallB, FALSE, FALSE,
5644 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5647 Xamoeba_1, TRUE, FALSE,
5648 EL_AMOEBA_DRY, ACTION_OTHER, -1
5651 Xamoeba_2, FALSE, FALSE,
5652 EL_AMOEBA_DRY, ACTION_OTHER, -1
5655 Xamoeba_3, FALSE, FALSE,
5656 EL_AMOEBA_DRY, ACTION_OTHER, -1
5659 Xamoeba_4, FALSE, FALSE,
5660 EL_AMOEBA_DRY, ACTION_OTHER, -1
5663 Xamoeba_5, TRUE, FALSE,
5664 EL_AMOEBA_WET, ACTION_OTHER, -1
5667 Xamoeba_6, FALSE, FALSE,
5668 EL_AMOEBA_WET, ACTION_OTHER, -1
5671 Xamoeba_7, FALSE, FALSE,
5672 EL_AMOEBA_WET, ACTION_OTHER, -1
5675 Xamoeba_8, FALSE, FALSE,
5676 EL_AMOEBA_WET, ACTION_OTHER, -1
5679 Xdoor_1, TRUE, FALSE,
5680 EL_EM_GATE_1, -1, -1
5683 Xdoor_2, TRUE, FALSE,
5684 EL_EM_GATE_2, -1, -1
5687 Xdoor_3, TRUE, FALSE,
5688 EL_EM_GATE_3, -1, -1
5691 Xdoor_4, TRUE, FALSE,
5692 EL_EM_GATE_4, -1, -1
5695 Xdoor_5, TRUE, FALSE,
5696 EL_EMC_GATE_5, -1, -1
5699 Xdoor_6, TRUE, FALSE,
5700 EL_EMC_GATE_6, -1, -1
5703 Xdoor_7, TRUE, FALSE,
5704 EL_EMC_GATE_7, -1, -1
5707 Xdoor_8, TRUE, FALSE,
5708 EL_EMC_GATE_8, -1, -1
5711 Xkey_1, TRUE, FALSE,
5715 Xkey_2, TRUE, FALSE,
5719 Xkey_3, TRUE, FALSE,
5723 Xkey_4, TRUE, FALSE,
5727 Xkey_5, TRUE, FALSE,
5728 EL_EMC_KEY_5, -1, -1
5731 Xkey_6, TRUE, FALSE,
5732 EL_EMC_KEY_6, -1, -1
5735 Xkey_7, TRUE, FALSE,
5736 EL_EMC_KEY_7, -1, -1
5739 Xkey_8, TRUE, FALSE,
5740 EL_EMC_KEY_8, -1, -1
5743 Xwind_n, TRUE, FALSE,
5744 EL_BALLOON_SWITCH_UP, -1, -1
5747 Xwind_e, TRUE, FALSE,
5748 EL_BALLOON_SWITCH_RIGHT, -1, -1
5751 Xwind_s, TRUE, FALSE,
5752 EL_BALLOON_SWITCH_DOWN, -1, -1
5755 Xwind_w, TRUE, FALSE,
5756 EL_BALLOON_SWITCH_LEFT, -1, -1
5759 Xwind_nesw, TRUE, FALSE,
5760 EL_BALLOON_SWITCH_ANY, -1, -1
5763 Xwind_stop, TRUE, FALSE,
5764 EL_BALLOON_SWITCH_NONE, -1, -1
5768 EL_EM_EXIT_CLOSED, -1, -1
5771 Xexit_1, TRUE, FALSE,
5772 EL_EM_EXIT_OPEN, -1, -1
5775 Xexit_2, FALSE, FALSE,
5776 EL_EM_EXIT_OPEN, -1, -1
5779 Xexit_3, FALSE, FALSE,
5780 EL_EM_EXIT_OPEN, -1, -1
5783 Xdynamite, TRUE, FALSE,
5784 EL_EM_DYNAMITE, -1, -1
5787 Ydynamite_eat, FALSE, FALSE,
5788 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5791 Xdynamite_1, TRUE, FALSE,
5792 EL_EM_DYNAMITE_ACTIVE, -1, -1
5795 Xdynamite_2, FALSE, FALSE,
5796 EL_EM_DYNAMITE_ACTIVE, -1, -1
5799 Xdynamite_3, FALSE, FALSE,
5800 EL_EM_DYNAMITE_ACTIVE, -1, -1
5803 Xdynamite_4, FALSE, FALSE,
5804 EL_EM_DYNAMITE_ACTIVE, -1, -1
5807 Xbumper, TRUE, FALSE,
5808 EL_EMC_SPRING_BUMPER, -1, -1
5811 XbumperB, FALSE, FALSE,
5812 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5815 Xwheel, TRUE, FALSE,
5816 EL_ROBOT_WHEEL, -1, -1
5819 XwheelB, FALSE, FALSE,
5820 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5823 Xswitch, TRUE, FALSE,
5824 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5827 XswitchB, FALSE, FALSE,
5828 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5832 EL_QUICKSAND_EMPTY, -1, -1
5835 Xsand_stone, TRUE, FALSE,
5836 EL_QUICKSAND_FULL, -1, -1
5839 Xsand_stonein_1, FALSE, TRUE,
5840 EL_ROCK, ACTION_FILLING, -1
5843 Xsand_stonein_2, FALSE, TRUE,
5844 EL_ROCK, ACTION_FILLING, -1
5847 Xsand_stonein_3, FALSE, TRUE,
5848 EL_ROCK, ACTION_FILLING, -1
5851 Xsand_stonein_4, FALSE, TRUE,
5852 EL_ROCK, ACTION_FILLING, -1
5855 Xsand_stonesand_1, FALSE, FALSE,
5856 EL_QUICKSAND_EMPTYING, -1, -1
5859 Xsand_stonesand_2, FALSE, FALSE,
5860 EL_QUICKSAND_EMPTYING, -1, -1
5863 Xsand_stonesand_3, FALSE, FALSE,
5864 EL_QUICKSAND_EMPTYING, -1, -1
5867 Xsand_stonesand_4, FALSE, FALSE,
5868 EL_QUICKSAND_EMPTYING, -1, -1
5871 Xsand_stonesand_quickout_1, FALSE, FALSE,
5872 EL_QUICKSAND_EMPTYING, -1, -1
5875 Xsand_stonesand_quickout_2, FALSE, FALSE,
5876 EL_QUICKSAND_EMPTYING, -1, -1
5879 Xsand_stoneout_1, FALSE, FALSE,
5880 EL_ROCK, ACTION_EMPTYING, -1
5883 Xsand_stoneout_2, FALSE, FALSE,
5884 EL_ROCK, ACTION_EMPTYING, -1
5887 Xsand_sandstone_1, FALSE, FALSE,
5888 EL_QUICKSAND_FILLING, -1, -1
5891 Xsand_sandstone_2, FALSE, FALSE,
5892 EL_QUICKSAND_FILLING, -1, -1
5895 Xsand_sandstone_3, FALSE, FALSE,
5896 EL_QUICKSAND_FILLING, -1, -1
5899 Xsand_sandstone_4, FALSE, FALSE,
5900 EL_QUICKSAND_FILLING, -1, -1
5903 Xplant, TRUE, FALSE,
5904 EL_EMC_PLANT, -1, -1
5907 Yplant, FALSE, FALSE,
5908 EL_EMC_PLANT, -1, -1
5911 Xlenses, TRUE, FALSE,
5912 EL_EMC_LENSES, -1, -1
5915 Xmagnify, TRUE, FALSE,
5916 EL_EMC_MAGNIFIER, -1, -1
5919 Xdripper, TRUE, FALSE,
5920 EL_EMC_DRIPPER, -1, -1
5923 XdripperB, FALSE, FALSE,
5924 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5927 Xfake_blank, TRUE, FALSE,
5928 EL_INVISIBLE_WALL, -1, -1
5931 Xfake_blankB, FALSE, FALSE,
5932 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5935 Xfake_grass, TRUE, FALSE,
5936 EL_EMC_FAKE_GRASS, -1, -1
5939 Xfake_grassB, FALSE, FALSE,
5940 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5943 Xfake_door_1, TRUE, FALSE,
5944 EL_EM_GATE_1_GRAY, -1, -1
5947 Xfake_door_2, TRUE, FALSE,
5948 EL_EM_GATE_2_GRAY, -1, -1
5951 Xfake_door_3, TRUE, FALSE,
5952 EL_EM_GATE_3_GRAY, -1, -1
5955 Xfake_door_4, TRUE, FALSE,
5956 EL_EM_GATE_4_GRAY, -1, -1
5959 Xfake_door_5, TRUE, FALSE,
5960 EL_EMC_GATE_5_GRAY, -1, -1
5963 Xfake_door_6, TRUE, FALSE,
5964 EL_EMC_GATE_6_GRAY, -1, -1
5967 Xfake_door_7, TRUE, FALSE,
5968 EL_EMC_GATE_7_GRAY, -1, -1
5971 Xfake_door_8, TRUE, FALSE,
5972 EL_EMC_GATE_8_GRAY, -1, -1
5975 Xfake_acid_1, TRUE, FALSE,
5976 EL_EMC_FAKE_ACID, -1, -1
5979 Xfake_acid_2, FALSE, FALSE,
5980 EL_EMC_FAKE_ACID, -1, -1
5983 Xfake_acid_3, FALSE, FALSE,
5984 EL_EMC_FAKE_ACID, -1, -1
5987 Xfake_acid_4, FALSE, FALSE,
5988 EL_EMC_FAKE_ACID, -1, -1
5991 Xfake_acid_5, FALSE, FALSE,
5992 EL_EMC_FAKE_ACID, -1, -1
5995 Xfake_acid_6, FALSE, FALSE,
5996 EL_EMC_FAKE_ACID, -1, -1
5999 Xfake_acid_7, FALSE, FALSE,
6000 EL_EMC_FAKE_ACID, -1, -1
6003 Xfake_acid_8, FALSE, FALSE,
6004 EL_EMC_FAKE_ACID, -1, -1
6007 Xsteel_1, TRUE, FALSE,
6008 EL_STEELWALL, -1, -1
6011 Xsteel_2, TRUE, FALSE,
6012 EL_EMC_STEELWALL_2, -1, -1
6015 Xsteel_3, TRUE, FALSE,
6016 EL_EMC_STEELWALL_3, -1, -1
6019 Xsteel_4, TRUE, FALSE,
6020 EL_EMC_STEELWALL_4, -1, -1
6023 Xwall_1, TRUE, FALSE,
6027 Xwall_2, TRUE, FALSE,
6028 EL_EMC_WALL_14, -1, -1
6031 Xwall_3, TRUE, FALSE,
6032 EL_EMC_WALL_15, -1, -1
6035 Xwall_4, TRUE, FALSE,
6036 EL_EMC_WALL_16, -1, -1
6039 Xround_wall_1, TRUE, FALSE,
6040 EL_WALL_SLIPPERY, -1, -1
6043 Xround_wall_2, TRUE, FALSE,
6044 EL_EMC_WALL_SLIPPERY_2, -1, -1
6047 Xround_wall_3, TRUE, FALSE,
6048 EL_EMC_WALL_SLIPPERY_3, -1, -1
6051 Xround_wall_4, TRUE, FALSE,
6052 EL_EMC_WALL_SLIPPERY_4, -1, -1
6055 Xdecor_1, TRUE, FALSE,
6056 EL_EMC_WALL_8, -1, -1
6059 Xdecor_2, TRUE, FALSE,
6060 EL_EMC_WALL_6, -1, -1
6063 Xdecor_3, TRUE, FALSE,
6064 EL_EMC_WALL_4, -1, -1
6067 Xdecor_4, TRUE, FALSE,
6068 EL_EMC_WALL_7, -1, -1
6071 Xdecor_5, TRUE, FALSE,
6072 EL_EMC_WALL_5, -1, -1
6075 Xdecor_6, TRUE, FALSE,
6076 EL_EMC_WALL_9, -1, -1
6079 Xdecor_7, TRUE, FALSE,
6080 EL_EMC_WALL_10, -1, -1
6083 Xdecor_8, TRUE, FALSE,
6084 EL_EMC_WALL_1, -1, -1
6087 Xdecor_9, TRUE, FALSE,
6088 EL_EMC_WALL_2, -1, -1
6091 Xdecor_10, TRUE, FALSE,
6092 EL_EMC_WALL_3, -1, -1
6095 Xdecor_11, TRUE, FALSE,
6096 EL_EMC_WALL_11, -1, -1
6099 Xdecor_12, TRUE, FALSE,
6100 EL_EMC_WALL_12, -1, -1
6103 Xalpha_0, TRUE, FALSE,
6104 EL_CHAR('0'), -1, -1
6107 Xalpha_1, TRUE, FALSE,
6108 EL_CHAR('1'), -1, -1
6111 Xalpha_2, TRUE, FALSE,
6112 EL_CHAR('2'), -1, -1
6115 Xalpha_3, TRUE, FALSE,
6116 EL_CHAR('3'), -1, -1
6119 Xalpha_4, TRUE, FALSE,
6120 EL_CHAR('4'), -1, -1
6123 Xalpha_5, TRUE, FALSE,
6124 EL_CHAR('5'), -1, -1
6127 Xalpha_6, TRUE, FALSE,
6128 EL_CHAR('6'), -1, -1
6131 Xalpha_7, TRUE, FALSE,
6132 EL_CHAR('7'), -1, -1
6135 Xalpha_8, TRUE, FALSE,
6136 EL_CHAR('8'), -1, -1
6139 Xalpha_9, TRUE, FALSE,
6140 EL_CHAR('9'), -1, -1
6143 Xalpha_excla, TRUE, FALSE,
6144 EL_CHAR('!'), -1, -1
6147 Xalpha_quote, TRUE, FALSE,
6148 EL_CHAR('"'), -1, -1
6151 Xalpha_comma, TRUE, FALSE,
6152 EL_CHAR(','), -1, -1
6155 Xalpha_minus, TRUE, FALSE,
6156 EL_CHAR('-'), -1, -1
6159 Xalpha_perio, TRUE, FALSE,
6160 EL_CHAR('.'), -1, -1
6163 Xalpha_colon, TRUE, FALSE,
6164 EL_CHAR(':'), -1, -1
6167 Xalpha_quest, TRUE, FALSE,
6168 EL_CHAR('?'), -1, -1
6171 Xalpha_a, TRUE, FALSE,
6172 EL_CHAR('A'), -1, -1
6175 Xalpha_b, TRUE, FALSE,
6176 EL_CHAR('B'), -1, -1
6179 Xalpha_c, TRUE, FALSE,
6180 EL_CHAR('C'), -1, -1
6183 Xalpha_d, TRUE, FALSE,
6184 EL_CHAR('D'), -1, -1
6187 Xalpha_e, TRUE, FALSE,
6188 EL_CHAR('E'), -1, -1
6191 Xalpha_f, TRUE, FALSE,
6192 EL_CHAR('F'), -1, -1
6195 Xalpha_g, TRUE, FALSE,
6196 EL_CHAR('G'), -1, -1
6199 Xalpha_h, TRUE, FALSE,
6200 EL_CHAR('H'), -1, -1
6203 Xalpha_i, TRUE, FALSE,
6204 EL_CHAR('I'), -1, -1
6207 Xalpha_j, TRUE, FALSE,
6208 EL_CHAR('J'), -1, -1
6211 Xalpha_k, TRUE, FALSE,
6212 EL_CHAR('K'), -1, -1
6215 Xalpha_l, TRUE, FALSE,
6216 EL_CHAR('L'), -1, -1
6219 Xalpha_m, TRUE, FALSE,
6220 EL_CHAR('M'), -1, -1
6223 Xalpha_n, TRUE, FALSE,
6224 EL_CHAR('N'), -1, -1
6227 Xalpha_o, TRUE, FALSE,
6228 EL_CHAR('O'), -1, -1
6231 Xalpha_p, TRUE, FALSE,
6232 EL_CHAR('P'), -1, -1
6235 Xalpha_q, TRUE, FALSE,
6236 EL_CHAR('Q'), -1, -1
6239 Xalpha_r, TRUE, FALSE,
6240 EL_CHAR('R'), -1, -1
6243 Xalpha_s, TRUE, FALSE,
6244 EL_CHAR('S'), -1, -1
6247 Xalpha_t, TRUE, FALSE,
6248 EL_CHAR('T'), -1, -1
6251 Xalpha_u, TRUE, FALSE,
6252 EL_CHAR('U'), -1, -1
6255 Xalpha_v, TRUE, FALSE,
6256 EL_CHAR('V'), -1, -1
6259 Xalpha_w, TRUE, FALSE,
6260 EL_CHAR('W'), -1, -1
6263 Xalpha_x, TRUE, FALSE,
6264 EL_CHAR('X'), -1, -1
6267 Xalpha_y, TRUE, FALSE,
6268 EL_CHAR('Y'), -1, -1
6271 Xalpha_z, TRUE, FALSE,
6272 EL_CHAR('Z'), -1, -1
6275 Xalpha_arrow_e, TRUE, FALSE,
6276 EL_CHAR('>'), -1, -1
6279 Xalpha_arrow_w, TRUE, FALSE,
6280 EL_CHAR('<'), -1, -1
6283 Xalpha_copyr, TRUE, FALSE,
6284 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6288 Xboom_bug, FALSE, FALSE,
6289 EL_BUG, ACTION_EXPLODING, -1
6292 Xboom_bomb, FALSE, FALSE,
6293 EL_BOMB, ACTION_EXPLODING, -1
6296 Xboom_android, FALSE, FALSE,
6297 EL_EMC_ANDROID, ACTION_OTHER, -1
6300 Xboom_1, FALSE, FALSE,
6301 EL_DEFAULT, ACTION_EXPLODING, -1
6304 Xboom_2, FALSE, FALSE,
6305 EL_DEFAULT, ACTION_EXPLODING, -1
6308 Znormal, FALSE, FALSE,
6312 Zdynamite, FALSE, FALSE,
6316 Zplayer, FALSE, FALSE,
6320 ZBORDER, FALSE, FALSE,
6330 static struct Mapping_EM_to_RND_player
6339 em_player_mapping_list[] =
6343 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6347 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6351 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6355 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6359 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6363 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6367 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6371 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6375 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6379 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6383 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6387 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6391 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6395 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6399 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6403 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6407 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6411 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6415 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6419 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6423 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6427 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6431 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6435 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6439 EL_PLAYER_1, ACTION_DEFAULT, -1,
6443 EL_PLAYER_2, ACTION_DEFAULT, -1,
6447 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6451 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6455 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6459 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6463 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6467 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6471 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6475 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6479 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6483 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6487 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6491 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6495 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6499 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6503 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6507 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6511 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6515 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6519 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6523 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6527 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6531 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6535 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6539 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6543 EL_PLAYER_3, ACTION_DEFAULT, -1,
6547 EL_PLAYER_4, ACTION_DEFAULT, -1,
6556 int map_element_RND_to_EM(int element_rnd)
6558 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6559 static boolean mapping_initialized = FALSE;
6561 if (!mapping_initialized)
6565 /* return "Xalpha_quest" for all undefined elements in mapping array */
6566 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6567 mapping_RND_to_EM[i] = Xalpha_quest;
6569 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6570 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6571 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6572 em_object_mapping_list[i].element_em;
6574 mapping_initialized = TRUE;
6577 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6578 return mapping_RND_to_EM[element_rnd];
6580 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6585 int map_element_EM_to_RND(int element_em)
6587 static unsigned short mapping_EM_to_RND[TILE_MAX];
6588 static boolean mapping_initialized = FALSE;
6590 if (!mapping_initialized)
6594 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6595 for (i = 0; i < TILE_MAX; i++)
6596 mapping_EM_to_RND[i] = EL_UNKNOWN;
6598 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6599 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6600 em_object_mapping_list[i].element_rnd;
6602 mapping_initialized = TRUE;
6605 if (element_em >= 0 && element_em < TILE_MAX)
6606 return mapping_EM_to_RND[element_em];
6608 Error(ERR_WARN, "invalid EM level element %d", element_em);
6613 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6615 struct LevelInfo_EM *level_em = level->native_em_level;
6616 struct LEVEL *lev = level_em->lev;
6619 for (i = 0; i < TILE_MAX; i++)
6620 lev->android_array[i] = Xblank;
6622 for (i = 0; i < level->num_android_clone_elements; i++)
6624 int element_rnd = level->android_clone_element[i];
6625 int element_em = map_element_RND_to_EM(element_rnd);
6627 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6628 if (em_object_mapping_list[j].element_rnd == element_rnd)
6629 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6633 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6635 struct LevelInfo_EM *level_em = level->native_em_level;
6636 struct LEVEL *lev = level_em->lev;
6639 level->num_android_clone_elements = 0;
6641 for (i = 0; i < TILE_MAX; i++)
6643 int element_em = lev->android_array[i];
6645 boolean element_found = FALSE;
6647 if (element_em == Xblank)
6650 element_rnd = map_element_EM_to_RND(element_em);
6652 for (j = 0; j < level->num_android_clone_elements; j++)
6653 if (level->android_clone_element[j] == element_rnd)
6654 element_found = TRUE;
6658 level->android_clone_element[level->num_android_clone_elements++] =
6661 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6666 if (level->num_android_clone_elements == 0)
6668 level->num_android_clone_elements = 1;
6669 level->android_clone_element[0] = EL_EMPTY;
6673 int map_direction_RND_to_EM(int direction)
6675 return (direction == MV_UP ? 0 :
6676 direction == MV_RIGHT ? 1 :
6677 direction == MV_DOWN ? 2 :
6678 direction == MV_LEFT ? 3 :
6682 int map_direction_EM_to_RND(int direction)
6684 return (direction == 0 ? MV_UP :
6685 direction == 1 ? MV_RIGHT :
6686 direction == 2 ? MV_DOWN :
6687 direction == 3 ? MV_LEFT :
6691 int map_element_RND_to_SP(int element_rnd)
6693 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6695 if (element_rnd >= EL_SP_START &&
6696 element_rnd <= EL_SP_END)
6697 element_sp = element_rnd - EL_SP_START;
6698 else if (element_rnd == EL_EMPTY_SPACE)
6700 else if (element_rnd == EL_INVISIBLE_WALL)
6706 int map_element_SP_to_RND(int element_sp)
6708 int element_rnd = EL_UNKNOWN;
6710 if (element_sp >= 0x00 &&
6712 element_rnd = EL_SP_START + element_sp;
6713 else if (element_sp == 0x28)
6714 element_rnd = EL_INVISIBLE_WALL;
6719 int map_action_SP_to_RND(int action_sp)
6723 case actActive: return ACTION_ACTIVE;
6724 case actImpact: return ACTION_IMPACT;
6725 case actExploding: return ACTION_EXPLODING;
6726 case actDigging: return ACTION_DIGGING;
6727 case actSnapping: return ACTION_SNAPPING;
6728 case actCollecting: return ACTION_COLLECTING;
6729 case actPassing: return ACTION_PASSING;
6730 case actPushing: return ACTION_PUSHING;
6731 case actDropping: return ACTION_DROPPING;
6733 default: return ACTION_DEFAULT;
6737 int get_next_element(int element)
6741 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6742 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6743 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6744 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6745 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6746 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6747 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6748 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6749 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6750 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6751 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6753 default: return element;
6757 int el_act_dir2img(int element, int action, int direction)
6759 element = GFX_ELEMENT(element);
6760 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6762 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6763 return element_info[element].direction_graphic[action][direction];
6766 static int el_act_dir2crm(int element, int action, int direction)
6768 element = GFX_ELEMENT(element);
6769 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6771 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6772 return element_info[element].direction_crumbled[action][direction];
6775 int el_act2img(int element, int action)
6777 element = GFX_ELEMENT(element);
6779 return element_info[element].graphic[action];
6782 int el_act2crm(int element, int action)
6784 element = GFX_ELEMENT(element);
6786 return element_info[element].crumbled[action];
6789 int el_dir2img(int element, int direction)
6791 element = GFX_ELEMENT(element);
6793 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6796 int el2baseimg(int element)
6798 return element_info[element].graphic[ACTION_DEFAULT];
6801 int el2img(int element)
6803 element = GFX_ELEMENT(element);
6805 return element_info[element].graphic[ACTION_DEFAULT];
6808 int el2edimg(int element)
6810 element = GFX_ELEMENT(element);
6812 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6815 int el2preimg(int element)
6817 element = GFX_ELEMENT(element);
6819 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6822 int el2panelimg(int element)
6824 element = GFX_ELEMENT(element);
6826 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6829 int font2baseimg(int font_nr)
6831 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6834 int getBeltNrFromBeltElement(int element)
6836 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6837 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6838 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6841 int getBeltNrFromBeltActiveElement(int element)
6843 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6844 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6845 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6848 int getBeltNrFromBeltSwitchElement(int element)
6850 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6851 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6852 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6855 int getBeltDirNrFromBeltElement(int element)
6857 static int belt_base_element[4] =
6859 EL_CONVEYOR_BELT_1_LEFT,
6860 EL_CONVEYOR_BELT_2_LEFT,
6861 EL_CONVEYOR_BELT_3_LEFT,
6862 EL_CONVEYOR_BELT_4_LEFT
6865 int belt_nr = getBeltNrFromBeltElement(element);
6866 int belt_dir_nr = element - belt_base_element[belt_nr];
6868 return (belt_dir_nr % 3);
6871 int getBeltDirNrFromBeltSwitchElement(int element)
6873 static int belt_base_element[4] =
6875 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6876 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6877 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6878 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6881 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6882 int belt_dir_nr = element - belt_base_element[belt_nr];
6884 return (belt_dir_nr % 3);
6887 int getBeltDirFromBeltElement(int element)
6889 static int belt_move_dir[3] =
6896 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6898 return belt_move_dir[belt_dir_nr];
6901 int getBeltDirFromBeltSwitchElement(int element)
6903 static int belt_move_dir[3] =
6910 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6912 return belt_move_dir[belt_dir_nr];
6915 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6917 static int belt_base_element[4] =
6919 EL_CONVEYOR_BELT_1_LEFT,
6920 EL_CONVEYOR_BELT_2_LEFT,
6921 EL_CONVEYOR_BELT_3_LEFT,
6922 EL_CONVEYOR_BELT_4_LEFT
6925 return belt_base_element[belt_nr] + belt_dir_nr;
6928 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6930 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6932 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6935 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6937 static int belt_base_element[4] =
6939 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6940 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6941 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6942 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6945 return belt_base_element[belt_nr] + belt_dir_nr;
6948 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6950 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6952 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6955 boolean getTeamMode_EM()
6957 return game.team_mode;
6960 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6962 int game_frame_delay_value;
6964 game_frame_delay_value =
6965 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6966 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6969 if (tape.playing && tape.warp_forward && !tape.pausing)
6970 game_frame_delay_value = 0;
6972 return game_frame_delay_value;
6975 unsigned int InitRND(int seed)
6977 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6978 return InitEngineRandom_EM(seed);
6979 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6980 return InitEngineRandom_SP(seed);
6982 return InitEngineRandom_RND(seed);
6985 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6986 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6988 inline static int get_effective_element_EM(int tile, int frame_em)
6990 int element = object_mapping[tile].element_rnd;
6991 int action = object_mapping[tile].action;
6992 boolean is_backside = object_mapping[tile].is_backside;
6993 boolean action_removing = (action == ACTION_DIGGING ||
6994 action == ACTION_SNAPPING ||
6995 action == ACTION_COLLECTING);
7001 case Yacid_splash_eB:
7002 case Yacid_splash_wB:
7003 return (frame_em > 5 ? EL_EMPTY : element);
7009 else /* frame_em == 7 */
7013 case Yacid_splash_eB:
7014 case Yacid_splash_wB:
7017 case Yemerald_stone:
7020 case Ydiamond_stone:
7024 case Xdrip_stretchB:
7043 case Xsand_stonein_1:
7044 case Xsand_stonein_2:
7045 case Xsand_stonein_3:
7046 case Xsand_stonein_4:
7050 return (is_backside || action_removing ? EL_EMPTY : element);
7055 inline static boolean check_linear_animation_EM(int tile)
7059 case Xsand_stonesand_1:
7060 case Xsand_stonesand_quickout_1:
7061 case Xsand_sandstone_1:
7062 case Xsand_stonein_1:
7063 case Xsand_stoneout_1:
7082 case Yacid_splash_eB:
7083 case Yacid_splash_wB:
7084 case Yemerald_stone:
7091 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7092 boolean has_crumbled_graphics,
7093 int crumbled, int sync_frame)
7095 /* if element can be crumbled, but certain action graphics are just empty
7096 space (like instantly snapping sand to empty space in 1 frame), do not
7097 treat these empty space graphics as crumbled graphics in EMC engine */
7098 if (crumbled == IMG_EMPTY_SPACE)
7099 has_crumbled_graphics = FALSE;
7101 if (has_crumbled_graphics)
7103 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7104 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7105 g_crumbled->anim_delay,
7106 g_crumbled->anim_mode,
7107 g_crumbled->anim_start_frame,
7110 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7111 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7113 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7115 g_em->has_crumbled_graphics = TRUE;
7119 g_em->crumbled_bitmap = NULL;
7120 g_em->crumbled_src_x = 0;
7121 g_em->crumbled_src_y = 0;
7122 g_em->crumbled_border_size = 0;
7124 g_em->has_crumbled_graphics = FALSE;
7128 void ResetGfxAnimation_EM(int x, int y, int tile)
7133 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7134 int tile, int frame_em, int x, int y)
7136 int action = object_mapping[tile].action;
7137 int direction = object_mapping[tile].direction;
7138 int effective_element = get_effective_element_EM(tile, frame_em);
7139 int graphic = (direction == MV_NONE ?
7140 el_act2img(effective_element, action) :
7141 el_act_dir2img(effective_element, action, direction));
7142 struct GraphicInfo *g = &graphic_info[graphic];
7144 boolean action_removing = (action == ACTION_DIGGING ||
7145 action == ACTION_SNAPPING ||
7146 action == ACTION_COLLECTING);
7147 boolean action_moving = (action == ACTION_FALLING ||
7148 action == ACTION_MOVING ||
7149 action == ACTION_PUSHING ||
7150 action == ACTION_EATING ||
7151 action == ACTION_FILLING ||
7152 action == ACTION_EMPTYING);
7153 boolean action_falling = (action == ACTION_FALLING ||
7154 action == ACTION_FILLING ||
7155 action == ACTION_EMPTYING);
7157 /* special case: graphic uses "2nd movement tile" and has defined
7158 7 frames for movement animation (or less) => use default graphic
7159 for last (8th) frame which ends the movement animation */
7160 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7162 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7163 graphic = (direction == MV_NONE ?
7164 el_act2img(effective_element, action) :
7165 el_act_dir2img(effective_element, action, direction));
7167 g = &graphic_info[graphic];
7170 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7174 else if (action_moving)
7176 boolean is_backside = object_mapping[tile].is_backside;
7180 int direction = object_mapping[tile].direction;
7181 int move_dir = (action_falling ? MV_DOWN : direction);
7186 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7187 if (g->double_movement && frame_em == 0)
7191 if (move_dir == MV_LEFT)
7192 GfxFrame[x - 1][y] = GfxFrame[x][y];
7193 else if (move_dir == MV_RIGHT)
7194 GfxFrame[x + 1][y] = GfxFrame[x][y];
7195 else if (move_dir == MV_UP)
7196 GfxFrame[x][y - 1] = GfxFrame[x][y];
7197 else if (move_dir == MV_DOWN)
7198 GfxFrame[x][y + 1] = GfxFrame[x][y];
7205 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7206 if (tile == Xsand_stonesand_quickout_1 ||
7207 tile == Xsand_stonesand_quickout_2)
7211 if (graphic_info[graphic].anim_global_sync)
7212 sync_frame = FrameCounter;
7213 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7214 sync_frame = GfxFrame[x][y];
7216 sync_frame = 0; /* playfield border (pseudo steel) */
7218 SetRandomAnimationValue(x, y);
7220 int frame = getAnimationFrame(g->anim_frames,
7223 g->anim_start_frame,
7226 g_em->unique_identifier =
7227 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7230 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7231 int tile, int frame_em, int x, int y)
7233 int action = object_mapping[tile].action;
7234 int direction = object_mapping[tile].direction;
7235 boolean is_backside = object_mapping[tile].is_backside;
7236 int effective_element = get_effective_element_EM(tile, frame_em);
7237 int effective_action = action;
7238 int graphic = (direction == MV_NONE ?
7239 el_act2img(effective_element, effective_action) :
7240 el_act_dir2img(effective_element, effective_action,
7242 int crumbled = (direction == MV_NONE ?
7243 el_act2crm(effective_element, effective_action) :
7244 el_act_dir2crm(effective_element, effective_action,
7246 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7247 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7248 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7249 struct GraphicInfo *g = &graphic_info[graphic];
7252 /* special case: graphic uses "2nd movement tile" and has defined
7253 7 frames for movement animation (or less) => use default graphic
7254 for last (8th) frame which ends the movement animation */
7255 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7257 effective_action = ACTION_DEFAULT;
7258 graphic = (direction == MV_NONE ?
7259 el_act2img(effective_element, effective_action) :
7260 el_act_dir2img(effective_element, effective_action,
7262 crumbled = (direction == MV_NONE ?
7263 el_act2crm(effective_element, effective_action) :
7264 el_act_dir2crm(effective_element, effective_action,
7267 g = &graphic_info[graphic];
7270 if (graphic_info[graphic].anim_global_sync)
7271 sync_frame = FrameCounter;
7272 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7273 sync_frame = GfxFrame[x][y];
7275 sync_frame = 0; /* playfield border (pseudo steel) */
7277 SetRandomAnimationValue(x, y);
7279 int frame = getAnimationFrame(g->anim_frames,
7282 g->anim_start_frame,
7285 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7286 g->double_movement && is_backside);
7288 /* (updating the "crumbled" graphic definitions is probably not really needed,
7289 as animations for crumbled graphics can't be longer than one EMC cycle) */
7290 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7294 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7295 int player_nr, int anim, int frame_em)
7297 int element = player_mapping[player_nr][anim].element_rnd;
7298 int action = player_mapping[player_nr][anim].action;
7299 int direction = player_mapping[player_nr][anim].direction;
7300 int graphic = (direction == MV_NONE ?
7301 el_act2img(element, action) :
7302 el_act_dir2img(element, action, direction));
7303 struct GraphicInfo *g = &graphic_info[graphic];
7306 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7308 stored_player[player_nr].StepFrame = frame_em;
7310 sync_frame = stored_player[player_nr].Frame;
7312 int frame = getAnimationFrame(g->anim_frames,
7315 g->anim_start_frame,
7318 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7319 &g_em->src_x, &g_em->src_y, FALSE);
7322 void InitGraphicInfo_EM(void)
7327 int num_em_gfx_errors = 0;
7329 if (graphic_info_em_object[0][0].bitmap == NULL)
7331 /* EM graphics not yet initialized in em_open_all() */
7336 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7339 /* always start with reliable default values */
7340 for (i = 0; i < TILE_MAX; i++)
7342 object_mapping[i].element_rnd = EL_UNKNOWN;
7343 object_mapping[i].is_backside = FALSE;
7344 object_mapping[i].action = ACTION_DEFAULT;
7345 object_mapping[i].direction = MV_NONE;
7348 /* always start with reliable default values */
7349 for (p = 0; p < MAX_PLAYERS; p++)
7351 for (i = 0; i < SPR_MAX; i++)
7353 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7354 player_mapping[p][i].action = ACTION_DEFAULT;
7355 player_mapping[p][i].direction = MV_NONE;
7359 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7361 int e = em_object_mapping_list[i].element_em;
7363 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7364 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7366 if (em_object_mapping_list[i].action != -1)
7367 object_mapping[e].action = em_object_mapping_list[i].action;
7369 if (em_object_mapping_list[i].direction != -1)
7370 object_mapping[e].direction =
7371 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7374 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7376 int a = em_player_mapping_list[i].action_em;
7377 int p = em_player_mapping_list[i].player_nr;
7379 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7381 if (em_player_mapping_list[i].action != -1)
7382 player_mapping[p][a].action = em_player_mapping_list[i].action;
7384 if (em_player_mapping_list[i].direction != -1)
7385 player_mapping[p][a].direction =
7386 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7389 for (i = 0; i < TILE_MAX; i++)
7391 int element = object_mapping[i].element_rnd;
7392 int action = object_mapping[i].action;
7393 int direction = object_mapping[i].direction;
7394 boolean is_backside = object_mapping[i].is_backside;
7395 boolean action_exploding = ((action == ACTION_EXPLODING ||
7396 action == ACTION_SMASHED_BY_ROCK ||
7397 action == ACTION_SMASHED_BY_SPRING) &&
7398 element != EL_DIAMOND);
7399 boolean action_active = (action == ACTION_ACTIVE);
7400 boolean action_other = (action == ACTION_OTHER);
7402 for (j = 0; j < 8; j++)
7404 int effective_element = get_effective_element_EM(i, j);
7405 int effective_action = (j < 7 ? action :
7406 i == Xdrip_stretch ? action :
7407 i == Xdrip_stretchB ? action :
7408 i == Ydrip_s1 ? action :
7409 i == Ydrip_s1B ? action :
7410 i == Xball_1B ? action :
7411 i == Xball_2 ? action :
7412 i == Xball_2B ? action :
7413 i == Yball_eat ? action :
7414 i == Ykey_1_eat ? action :
7415 i == Ykey_2_eat ? action :
7416 i == Ykey_3_eat ? action :
7417 i == Ykey_4_eat ? action :
7418 i == Ykey_5_eat ? action :
7419 i == Ykey_6_eat ? action :
7420 i == Ykey_7_eat ? action :
7421 i == Ykey_8_eat ? action :
7422 i == Ylenses_eat ? action :
7423 i == Ymagnify_eat ? action :
7424 i == Ygrass_eat ? action :
7425 i == Ydirt_eat ? action :
7426 i == Xsand_stonein_1 ? action :
7427 i == Xsand_stonein_2 ? action :
7428 i == Xsand_stonein_3 ? action :
7429 i == Xsand_stonein_4 ? action :
7430 i == Xsand_stoneout_1 ? action :
7431 i == Xsand_stoneout_2 ? action :
7432 i == Xboom_android ? ACTION_EXPLODING :
7433 action_exploding ? ACTION_EXPLODING :
7434 action_active ? action :
7435 action_other ? action :
7437 int graphic = (el_act_dir2img(effective_element, effective_action,
7439 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7441 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7442 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7443 boolean has_action_graphics = (graphic != base_graphic);
7444 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7445 struct GraphicInfo *g = &graphic_info[graphic];
7446 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7449 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7450 boolean special_animation = (action != ACTION_DEFAULT &&
7451 g->anim_frames == 3 &&
7452 g->anim_delay == 2 &&
7453 g->anim_mode & ANIM_LINEAR);
7454 int sync_frame = (i == Xdrip_stretch ? 7 :
7455 i == Xdrip_stretchB ? 7 :
7456 i == Ydrip_s2 ? j + 8 :
7457 i == Ydrip_s2B ? j + 8 :
7466 i == Xfake_acid_1 ? 0 :
7467 i == Xfake_acid_2 ? 10 :
7468 i == Xfake_acid_3 ? 20 :
7469 i == Xfake_acid_4 ? 30 :
7470 i == Xfake_acid_5 ? 40 :
7471 i == Xfake_acid_6 ? 50 :
7472 i == Xfake_acid_7 ? 60 :
7473 i == Xfake_acid_8 ? 70 :
7475 i == Xball_2B ? j + 8 :
7476 i == Yball_eat ? j + 1 :
7477 i == Ykey_1_eat ? j + 1 :
7478 i == Ykey_2_eat ? j + 1 :
7479 i == Ykey_3_eat ? j + 1 :
7480 i == Ykey_4_eat ? j + 1 :
7481 i == Ykey_5_eat ? j + 1 :
7482 i == Ykey_6_eat ? j + 1 :
7483 i == Ykey_7_eat ? j + 1 :
7484 i == Ykey_8_eat ? j + 1 :
7485 i == Ylenses_eat ? j + 1 :
7486 i == Ymagnify_eat ? j + 1 :
7487 i == Ygrass_eat ? j + 1 :
7488 i == Ydirt_eat ? j + 1 :
7489 i == Xamoeba_1 ? 0 :
7490 i == Xamoeba_2 ? 1 :
7491 i == Xamoeba_3 ? 2 :
7492 i == Xamoeba_4 ? 3 :
7493 i == Xamoeba_5 ? 0 :
7494 i == Xamoeba_6 ? 1 :
7495 i == Xamoeba_7 ? 2 :
7496 i == Xamoeba_8 ? 3 :
7497 i == Xexit_2 ? j + 8 :
7498 i == Xexit_3 ? j + 16 :
7499 i == Xdynamite_1 ? 0 :
7500 i == Xdynamite_2 ? 8 :
7501 i == Xdynamite_3 ? 16 :
7502 i == Xdynamite_4 ? 24 :
7503 i == Xsand_stonein_1 ? j + 1 :
7504 i == Xsand_stonein_2 ? j + 9 :
7505 i == Xsand_stonein_3 ? j + 17 :
7506 i == Xsand_stonein_4 ? j + 25 :
7507 i == Xsand_stoneout_1 && j == 0 ? 0 :
7508 i == Xsand_stoneout_1 && j == 1 ? 0 :
7509 i == Xsand_stoneout_1 && j == 2 ? 1 :
7510 i == Xsand_stoneout_1 && j == 3 ? 2 :
7511 i == Xsand_stoneout_1 && j == 4 ? 2 :
7512 i == Xsand_stoneout_1 && j == 5 ? 3 :
7513 i == Xsand_stoneout_1 && j == 6 ? 4 :
7514 i == Xsand_stoneout_1 && j == 7 ? 4 :
7515 i == Xsand_stoneout_2 && j == 0 ? 5 :
7516 i == Xsand_stoneout_2 && j == 1 ? 6 :
7517 i == Xsand_stoneout_2 && j == 2 ? 7 :
7518 i == Xsand_stoneout_2 && j == 3 ? 8 :
7519 i == Xsand_stoneout_2 && j == 4 ? 9 :
7520 i == Xsand_stoneout_2 && j == 5 ? 11 :
7521 i == Xsand_stoneout_2 && j == 6 ? 13 :
7522 i == Xsand_stoneout_2 && j == 7 ? 15 :
7523 i == Xboom_bug && j == 1 ? 2 :
7524 i == Xboom_bug && j == 2 ? 2 :
7525 i == Xboom_bug && j == 3 ? 4 :
7526 i == Xboom_bug && j == 4 ? 4 :
7527 i == Xboom_bug && j == 5 ? 2 :
7528 i == Xboom_bug && j == 6 ? 2 :
7529 i == Xboom_bug && j == 7 ? 0 :
7530 i == Xboom_bomb && j == 1 ? 2 :
7531 i == Xboom_bomb && j == 2 ? 2 :
7532 i == Xboom_bomb && j == 3 ? 4 :
7533 i == Xboom_bomb && j == 4 ? 4 :
7534 i == Xboom_bomb && j == 5 ? 2 :
7535 i == Xboom_bomb && j == 6 ? 2 :
7536 i == Xboom_bomb && j == 7 ? 0 :
7537 i == Xboom_android && j == 7 ? 6 :
7538 i == Xboom_1 && j == 1 ? 2 :
7539 i == Xboom_1 && j == 2 ? 2 :
7540 i == Xboom_1 && j == 3 ? 4 :
7541 i == Xboom_1 && j == 4 ? 4 :
7542 i == Xboom_1 && j == 5 ? 6 :
7543 i == Xboom_1 && j == 6 ? 6 :
7544 i == Xboom_1 && j == 7 ? 8 :
7545 i == Xboom_2 && j == 0 ? 8 :
7546 i == Xboom_2 && j == 1 ? 8 :
7547 i == Xboom_2 && j == 2 ? 10 :
7548 i == Xboom_2 && j == 3 ? 10 :
7549 i == Xboom_2 && j == 4 ? 10 :
7550 i == Xboom_2 && j == 5 ? 12 :
7551 i == Xboom_2 && j == 6 ? 12 :
7552 i == Xboom_2 && j == 7 ? 12 :
7553 special_animation && j == 4 ? 3 :
7554 effective_action != action ? 0 :
7558 Bitmap *debug_bitmap = g_em->bitmap;
7559 int debug_src_x = g_em->src_x;
7560 int debug_src_y = g_em->src_y;
7563 int frame = getAnimationFrame(g->anim_frames,
7566 g->anim_start_frame,
7569 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7570 g->double_movement && is_backside);
7572 g_em->bitmap = src_bitmap;
7573 g_em->src_x = src_x;
7574 g_em->src_y = src_y;
7575 g_em->src_offset_x = 0;
7576 g_em->src_offset_y = 0;
7577 g_em->dst_offset_x = 0;
7578 g_em->dst_offset_y = 0;
7579 g_em->width = TILEX;
7580 g_em->height = TILEY;
7582 g_em->preserve_background = FALSE;
7584 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7587 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7588 effective_action == ACTION_MOVING ||
7589 effective_action == ACTION_PUSHING ||
7590 effective_action == ACTION_EATING)) ||
7591 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7592 effective_action == ACTION_EMPTYING)))
7595 (effective_action == ACTION_FALLING ||
7596 effective_action == ACTION_FILLING ||
7597 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7598 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7599 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7600 int num_steps = (i == Ydrip_s1 ? 16 :
7601 i == Ydrip_s1B ? 16 :
7602 i == Ydrip_s2 ? 16 :
7603 i == Ydrip_s2B ? 16 :
7604 i == Xsand_stonein_1 ? 32 :
7605 i == Xsand_stonein_2 ? 32 :
7606 i == Xsand_stonein_3 ? 32 :
7607 i == Xsand_stonein_4 ? 32 :
7608 i == Xsand_stoneout_1 ? 16 :
7609 i == Xsand_stoneout_2 ? 16 : 8);
7610 int cx = ABS(dx) * (TILEX / num_steps);
7611 int cy = ABS(dy) * (TILEY / num_steps);
7612 int step_frame = (i == Ydrip_s2 ? j + 8 :
7613 i == Ydrip_s2B ? j + 8 :
7614 i == Xsand_stonein_2 ? j + 8 :
7615 i == Xsand_stonein_3 ? j + 16 :
7616 i == Xsand_stonein_4 ? j + 24 :
7617 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7618 int step = (is_backside ? step_frame : num_steps - step_frame);
7620 if (is_backside) /* tile where movement starts */
7622 if (dx < 0 || dy < 0)
7624 g_em->src_offset_x = cx * step;
7625 g_em->src_offset_y = cy * step;
7629 g_em->dst_offset_x = cx * step;
7630 g_em->dst_offset_y = cy * step;
7633 else /* tile where movement ends */
7635 if (dx < 0 || dy < 0)
7637 g_em->dst_offset_x = cx * step;
7638 g_em->dst_offset_y = cy * step;
7642 g_em->src_offset_x = cx * step;
7643 g_em->src_offset_y = cy * step;
7647 g_em->width = TILEX - cx * step;
7648 g_em->height = TILEY - cy * step;
7651 /* create unique graphic identifier to decide if tile must be redrawn */
7652 /* bit 31 - 16 (16 bit): EM style graphic
7653 bit 15 - 12 ( 4 bit): EM style frame
7654 bit 11 - 6 ( 6 bit): graphic width
7655 bit 5 - 0 ( 6 bit): graphic height */
7656 g_em->unique_identifier =
7657 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7661 /* skip check for EMC elements not contained in original EMC artwork */
7662 if (element == EL_EMC_FAKE_ACID)
7665 if (g_em->bitmap != debug_bitmap ||
7666 g_em->src_x != debug_src_x ||
7667 g_em->src_y != debug_src_y ||
7668 g_em->src_offset_x != 0 ||
7669 g_em->src_offset_y != 0 ||
7670 g_em->dst_offset_x != 0 ||
7671 g_em->dst_offset_y != 0 ||
7672 g_em->width != TILEX ||
7673 g_em->height != TILEY)
7675 static int last_i = -1;
7683 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7684 i, element, element_info[element].token_name,
7685 element_action_info[effective_action].suffix, direction);
7687 if (element != effective_element)
7688 printf(" [%d ('%s')]",
7690 element_info[effective_element].token_name);
7694 if (g_em->bitmap != debug_bitmap)
7695 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7696 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7698 if (g_em->src_x != debug_src_x ||
7699 g_em->src_y != debug_src_y)
7700 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7701 j, (is_backside ? 'B' : 'F'),
7702 g_em->src_x, g_em->src_y,
7703 g_em->src_x / 32, g_em->src_y / 32,
7704 debug_src_x, debug_src_y,
7705 debug_src_x / 32, debug_src_y / 32);
7707 if (g_em->src_offset_x != 0 ||
7708 g_em->src_offset_y != 0 ||
7709 g_em->dst_offset_x != 0 ||
7710 g_em->dst_offset_y != 0)
7711 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7713 g_em->src_offset_x, g_em->src_offset_y,
7714 g_em->dst_offset_x, g_em->dst_offset_y);
7716 if (g_em->width != TILEX ||
7717 g_em->height != TILEY)
7718 printf(" %d (%d): size %d,%d should be %d,%d\n",
7720 g_em->width, g_em->height, TILEX, TILEY);
7722 num_em_gfx_errors++;
7729 for (i = 0; i < TILE_MAX; i++)
7731 for (j = 0; j < 8; j++)
7733 int element = object_mapping[i].element_rnd;
7734 int action = object_mapping[i].action;
7735 int direction = object_mapping[i].direction;
7736 boolean is_backside = object_mapping[i].is_backside;
7737 int graphic_action = el_act_dir2img(element, action, direction);
7738 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7740 if ((action == ACTION_SMASHED_BY_ROCK ||
7741 action == ACTION_SMASHED_BY_SPRING ||
7742 action == ACTION_EATING) &&
7743 graphic_action == graphic_default)
7745 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7746 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7747 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7748 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7751 /* no separate animation for "smashed by rock" -- use rock instead */
7752 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7753 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7755 g_em->bitmap = g_xx->bitmap;
7756 g_em->src_x = g_xx->src_x;
7757 g_em->src_y = g_xx->src_y;
7758 g_em->src_offset_x = g_xx->src_offset_x;
7759 g_em->src_offset_y = g_xx->src_offset_y;
7760 g_em->dst_offset_x = g_xx->dst_offset_x;
7761 g_em->dst_offset_y = g_xx->dst_offset_y;
7762 g_em->width = g_xx->width;
7763 g_em->height = g_xx->height;
7764 g_em->unique_identifier = g_xx->unique_identifier;
7767 g_em->preserve_background = TRUE;
7772 for (p = 0; p < MAX_PLAYERS; p++)
7774 for (i = 0; i < SPR_MAX; i++)
7776 int element = player_mapping[p][i].element_rnd;
7777 int action = player_mapping[p][i].action;
7778 int direction = player_mapping[p][i].direction;
7780 for (j = 0; j < 8; j++)
7782 int effective_element = element;
7783 int effective_action = action;
7784 int graphic = (direction == MV_NONE ?
7785 el_act2img(effective_element, effective_action) :
7786 el_act_dir2img(effective_element, effective_action,
7788 struct GraphicInfo *g = &graphic_info[graphic];
7789 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7795 Bitmap *debug_bitmap = g_em->bitmap;
7796 int debug_src_x = g_em->src_x;
7797 int debug_src_y = g_em->src_y;
7800 int frame = getAnimationFrame(g->anim_frames,
7803 g->anim_start_frame,
7806 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7808 g_em->bitmap = src_bitmap;
7809 g_em->src_x = src_x;
7810 g_em->src_y = src_y;
7811 g_em->src_offset_x = 0;
7812 g_em->src_offset_y = 0;
7813 g_em->dst_offset_x = 0;
7814 g_em->dst_offset_y = 0;
7815 g_em->width = TILEX;
7816 g_em->height = TILEY;
7820 /* skip check for EMC elements not contained in original EMC artwork */
7821 if (element == EL_PLAYER_3 ||
7822 element == EL_PLAYER_4)
7825 if (g_em->bitmap != debug_bitmap ||
7826 g_em->src_x != debug_src_x ||
7827 g_em->src_y != debug_src_y)
7829 static int last_i = -1;
7837 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7838 p, i, element, element_info[element].token_name,
7839 element_action_info[effective_action].suffix, direction);
7841 if (element != effective_element)
7842 printf(" [%d ('%s')]",
7844 element_info[effective_element].token_name);
7848 if (g_em->bitmap != debug_bitmap)
7849 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7850 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7852 if (g_em->src_x != debug_src_x ||
7853 g_em->src_y != debug_src_y)
7854 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7856 g_em->src_x, g_em->src_y,
7857 g_em->src_x / 32, g_em->src_y / 32,
7858 debug_src_x, debug_src_y,
7859 debug_src_x / 32, debug_src_y / 32);
7861 num_em_gfx_errors++;
7871 printf("::: [%d errors found]\n", num_em_gfx_errors);
7877 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7878 boolean any_player_moving,
7879 boolean any_player_snapping,
7880 boolean any_player_dropping)
7882 static boolean player_was_waiting = TRUE;
7884 if (frame == 0 && !any_player_dropping)
7886 if (!player_was_waiting)
7888 if (!SaveEngineSnapshotToList())
7891 player_was_waiting = TRUE;
7894 else if (any_player_moving || any_player_snapping || any_player_dropping)
7896 player_was_waiting = FALSE;
7900 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7901 boolean murphy_is_dropping)
7903 static boolean player_was_waiting = TRUE;
7905 if (murphy_is_waiting)
7907 if (!player_was_waiting)
7909 if (!SaveEngineSnapshotToList())
7912 player_was_waiting = TRUE;
7917 player_was_waiting = FALSE;
7921 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7922 boolean any_player_moving,
7923 boolean any_player_snapping,
7924 boolean any_player_dropping)
7926 if (tape.single_step && tape.recording && !tape.pausing)
7927 if (frame == 0 && !any_player_dropping)
7928 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7930 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7931 any_player_snapping, any_player_dropping);
7934 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7935 boolean murphy_is_dropping)
7937 if (tape.single_step && tape.recording && !tape.pausing)
7938 if (murphy_is_waiting)
7939 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7941 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7944 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7945 int graphic, int sync_frame, int x, int y)
7947 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7949 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7952 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7954 return (IS_NEXT_FRAME(sync_frame, graphic));
7957 int getGraphicInfo_Delay(int graphic)
7959 return graphic_info[graphic].anim_delay;
7962 void PlayMenuSoundExt(int sound)
7964 if (sound == SND_UNDEFINED)
7967 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7968 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7971 if (IS_LOOP_SOUND(sound))
7972 PlaySoundLoop(sound);
7977 void PlayMenuSound()
7979 PlayMenuSoundExt(menu.sound[game_status]);
7982 void PlayMenuSoundStereo(int sound, int stereo_position)
7984 if (sound == SND_UNDEFINED)
7987 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7988 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7991 if (IS_LOOP_SOUND(sound))
7992 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7994 PlaySoundStereo(sound, stereo_position);
7997 void PlayMenuSoundIfLoopExt(int sound)
7999 if (sound == SND_UNDEFINED)
8002 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8003 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8006 if (IS_LOOP_SOUND(sound))
8007 PlaySoundLoop(sound);
8010 void PlayMenuSoundIfLoop()
8012 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8015 void PlayMenuMusicExt(int music)
8017 if (music == MUS_UNDEFINED)
8020 if (!setup.sound_music)
8026 void PlayMenuMusic()
8028 PlayMenuMusicExt(menu.music[game_status]);
8031 void PlaySoundActivating()
8034 PlaySound(SND_MENU_ITEM_ACTIVATING);
8038 void PlaySoundSelecting()
8041 PlaySound(SND_MENU_ITEM_SELECTING);
8045 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8047 boolean change_fullscreen = (setup.fullscreen !=
8048 video.fullscreen_enabled);
8049 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8050 !strEqual(setup.fullscreen_mode,
8051 video.fullscreen_mode_current));
8052 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8053 setup.window_scaling_percent !=
8054 video.window_scaling_percent);
8056 if (change_window_scaling_percent && video.fullscreen_enabled)
8059 if (!change_window_scaling_percent && !video.fullscreen_available)
8062 #if defined(TARGET_SDL2)
8063 if (change_window_scaling_percent)
8065 SDLSetWindowScaling(setup.window_scaling_percent);
8069 else if (change_fullscreen)
8071 SDLSetWindowFullscreen(setup.fullscreen);
8073 /* set setup value according to successfully changed fullscreen mode */
8074 setup.fullscreen = video.fullscreen_enabled;
8080 if (change_fullscreen ||
8081 change_fullscreen_mode ||
8082 change_window_scaling_percent)
8084 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8086 /* save backbuffer content which gets lost when toggling fullscreen mode */
8087 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8089 if (change_fullscreen_mode)
8091 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8092 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8095 if (change_window_scaling_percent)
8097 /* keep window mode, but change window scaling */
8098 video.fullscreen_enabled = TRUE; /* force new window scaling */
8101 /* toggle fullscreen */
8102 ChangeVideoModeIfNeeded(setup.fullscreen);
8104 /* set setup value according to successfully changed fullscreen mode */
8105 setup.fullscreen = video.fullscreen_enabled;
8107 /* restore backbuffer content from temporary backbuffer backup bitmap */
8108 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8110 FreeBitmap(tmp_backbuffer);
8112 /* update visible window/screen */
8113 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8117 void JoinRectangles(int *x, int *y, int *width, int *height,
8118 int x2, int y2, int width2, int height2)
8120 // do not join with "off-screen" rectangle
8121 if (x2 == -1 || y2 == -1)
8126 *width = MAX(*width, width2);
8127 *height = MAX(*height, height2);
8130 void ChangeViewportPropertiesIfNeeded()
8132 int gfx_game_mode = game_status;
8133 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8135 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8136 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8137 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8138 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8139 int border_size = vp_playfield->border_size;
8140 int new_sx = vp_playfield->x + border_size;
8141 int new_sy = vp_playfield->y + border_size;
8142 int new_sxsize = vp_playfield->width - 2 * border_size;
8143 int new_sysize = vp_playfield->height - 2 * border_size;
8144 int new_real_sx = vp_playfield->x;
8145 int new_real_sy = vp_playfield->y;
8146 int new_full_sxsize = vp_playfield->width;
8147 int new_full_sysize = vp_playfield->height;
8148 int new_dx = vp_door_1->x;
8149 int new_dy = vp_door_1->y;
8150 int new_dxsize = vp_door_1->width;
8151 int new_dysize = vp_door_1->height;
8152 int new_vx = vp_door_2->x;
8153 int new_vy = vp_door_2->y;
8154 int new_vxsize = vp_door_2->width;
8155 int new_vysize = vp_door_2->height;
8156 int new_ex = vp_door_3->x;
8157 int new_ey = vp_door_3->y;
8158 int new_exsize = vp_door_3->width;
8159 int new_eysize = vp_door_3->height;
8160 int new_tilesize_var =
8161 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8163 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8164 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8165 int new_scr_fieldx = new_sxsize / tilesize;
8166 int new_scr_fieldy = new_sysize / tilesize;
8167 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8168 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8169 boolean init_gfx_buffers = FALSE;
8170 boolean init_video_buffer = FALSE;
8171 boolean init_gadgets_and_toons = FALSE;
8172 boolean init_em_graphics = FALSE;
8174 if (viewport.window.width != WIN_XSIZE ||
8175 viewport.window.height != WIN_YSIZE)
8177 WIN_XSIZE = viewport.window.width;
8178 WIN_YSIZE = viewport.window.height;
8180 init_video_buffer = TRUE;
8181 init_gfx_buffers = TRUE;
8183 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8186 if (new_scr_fieldx != SCR_FIELDX ||
8187 new_scr_fieldy != SCR_FIELDY)
8189 /* this always toggles between MAIN and GAME when using small tile size */
8191 SCR_FIELDX = new_scr_fieldx;
8192 SCR_FIELDY = new_scr_fieldy;
8194 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8205 new_sxsize != SXSIZE ||
8206 new_sysize != SYSIZE ||
8207 new_dxsize != DXSIZE ||
8208 new_dysize != DYSIZE ||
8209 new_vxsize != VXSIZE ||
8210 new_vysize != VYSIZE ||
8211 new_exsize != EXSIZE ||
8212 new_eysize != EYSIZE ||
8213 new_real_sx != REAL_SX ||
8214 new_real_sy != REAL_SY ||
8215 new_full_sxsize != FULL_SXSIZE ||
8216 new_full_sysize != FULL_SYSIZE ||
8217 new_tilesize_var != TILESIZE_VAR
8220 // ------------------------------------------------------------------------
8221 // determine next fading area for changed viewport definitions
8222 // ------------------------------------------------------------------------
8224 // start with current playfield area (default fading area)
8227 FADE_SXSIZE = FULL_SXSIZE;
8228 FADE_SYSIZE = FULL_SYSIZE;
8230 // add new playfield area if position or size has changed
8231 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8232 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8234 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8235 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8238 // add current and new door 1 area if position or size has changed
8239 if (new_dx != DX || new_dy != DY ||
8240 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8242 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8243 DX, DY, DXSIZE, DYSIZE);
8244 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8245 new_dx, new_dy, new_dxsize, new_dysize);
8248 // add current and new door 2 area if position or size has changed
8249 if (new_dx != VX || new_dy != VY ||
8250 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8252 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8253 VX, VY, VXSIZE, VYSIZE);
8254 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8255 new_vx, new_vy, new_vxsize, new_vysize);
8258 // ------------------------------------------------------------------------
8259 // handle changed tile size
8260 // ------------------------------------------------------------------------
8262 if (new_tilesize_var != TILESIZE_VAR)
8264 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8266 // changing tile size invalidates scroll values of engine snapshots
8267 FreeEngineSnapshotSingle();
8269 // changing tile size requires update of graphic mapping for EM engine
8270 init_em_graphics = TRUE;
8281 SXSIZE = new_sxsize;
8282 SYSIZE = new_sysize;
8283 DXSIZE = new_dxsize;
8284 DYSIZE = new_dysize;
8285 VXSIZE = new_vxsize;
8286 VYSIZE = new_vysize;
8287 EXSIZE = new_exsize;
8288 EYSIZE = new_eysize;
8289 REAL_SX = new_real_sx;
8290 REAL_SY = new_real_sy;
8291 FULL_SXSIZE = new_full_sxsize;
8292 FULL_SYSIZE = new_full_sysize;
8293 TILESIZE_VAR = new_tilesize_var;
8295 init_gfx_buffers = TRUE;
8296 init_gadgets_and_toons = TRUE;
8298 // printf("::: viewports: init_gfx_buffers\n");
8299 // printf("::: viewports: init_gadgets_and_toons\n");
8302 if (init_gfx_buffers)
8304 // printf("::: init_gfx_buffers\n");
8306 SCR_FIELDX = new_scr_fieldx_buffers;
8307 SCR_FIELDY = new_scr_fieldy_buffers;
8311 SCR_FIELDX = new_scr_fieldx;
8312 SCR_FIELDY = new_scr_fieldy;
8314 SetDrawDeactivationMask(REDRAW_NONE);
8315 SetDrawBackgroundMask(REDRAW_FIELD);
8318 if (init_video_buffer)
8320 // printf("::: init_video_buffer\n");
8322 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8325 if (init_gadgets_and_toons)
8327 // printf("::: init_gadgets_and_toons\n");
8333 if (init_em_graphics)
8335 InitGraphicInfo_EM();