1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static char *print_if_not_empty(int element)
178 static char *s = NULL;
179 char *token_name = element_info[element].token_name;
184 s = checked_malloc(strlen(token_name) + 10 + 1);
186 if (element != EL_EMPTY)
187 sprintf(s, "%d\t['%s']", element, token_name);
189 sprintf(s, "%d", element);
194 void DumpTile(int x, int y)
199 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
205 printf_line("-", 79);
206 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
207 printf_line("-", 79);
209 if (!IN_LEV_FIELD(x, y))
211 printf("(not in level field)\n");
217 printf(" Feld: %d\t['%s']\n", Feld[x][y],
218 element_info[Feld[x][y]].token_name);
219 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
220 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
221 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
222 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
223 printf(" MovPos: %d\n", MovPos[x][y]);
224 printf(" MovDir: %d\n", MovDir[x][y]);
225 printf(" MovDelay: %d\n", MovDelay[x][y]);
226 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
227 printf(" CustomValue: %d\n", CustomValue[x][y]);
228 printf(" GfxElement: %d\n", GfxElement[x][y]);
229 printf(" GfxAction: %d\n", GfxAction[x][y]);
230 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
234 void SetDrawtoField(int mode)
236 if (mode == DRAW_FIELDBUFFER)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
245 drawto_field = fieldbuffer;
247 else /* DRAW_BACKBUFFER */
253 BX2 = SCR_FIELDX - 1;
254 BY2 = SCR_FIELDY - 1;
256 drawto_field = backbuffer;
260 static void RedrawPlayfield_RND()
262 if (game.envelope_active)
265 DrawLevel(REDRAW_ALL);
269 void RedrawPlayfield()
271 if (game_status != GAME_MODE_PLAYING)
274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
275 RedrawPlayfield_EM(TRUE);
276 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
277 RedrawPlayfield_SP(TRUE);
278 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
279 RedrawPlayfield_RND();
281 BlitScreenToBitmap(backbuffer);
283 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
287 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
289 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
291 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
294 void DrawMaskedBorder_FIELD()
296 if (global.border_status >= GAME_MODE_TITLE &&
297 global.border_status <= GAME_MODE_PLAYING &&
298 border.draw_masked[global.border_status])
299 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
302 void DrawMaskedBorder_DOOR_1()
304 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
305 (global.border_status != GAME_MODE_EDITOR ||
306 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
307 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
310 void DrawMaskedBorder_DOOR_2()
312 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
313 global.border_status != GAME_MODE_EDITOR)
314 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
317 void DrawMaskedBorder_DOOR_3()
319 /* currently not available */
322 void DrawMaskedBorder_ALL()
324 DrawMaskedBorder_FIELD();
325 DrawMaskedBorder_DOOR_1();
326 DrawMaskedBorder_DOOR_2();
327 DrawMaskedBorder_DOOR_3();
330 void DrawMaskedBorder(int redraw_mask)
332 /* never draw masked screen borders on borderless screens */
333 if (game_status == GAME_MODE_LOADING ||
334 game_status == GAME_MODE_TITLE)
337 if (redraw_mask & REDRAW_ALL)
338 DrawMaskedBorder_ALL();
341 if (redraw_mask & REDRAW_FIELD)
342 DrawMaskedBorder_FIELD();
343 if (redraw_mask & REDRAW_DOOR_1)
344 DrawMaskedBorder_DOOR_1();
345 if (redraw_mask & REDRAW_DOOR_2)
346 DrawMaskedBorder_DOOR_2();
347 if (redraw_mask & REDRAW_DOOR_3)
348 DrawMaskedBorder_DOOR_3();
352 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
354 int fx = FX, fy = FY;
355 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
356 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
358 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
359 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
360 int dx_var = dx * TILESIZE_VAR / TILESIZE;
361 int dy_var = dy * TILESIZE_VAR / TILESIZE;
364 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
365 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
367 if (EVEN(SCR_FIELDX))
369 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
370 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
372 fx += (dx_var > 0 ? TILEX_VAR : 0);
379 if (EVEN(SCR_FIELDY))
381 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
382 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
384 fy += (dy_var > 0 ? TILEY_VAR : 0);
391 if (full_lev_fieldx <= SCR_FIELDX)
393 if (EVEN(SCR_FIELDX))
394 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
396 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
399 if (full_lev_fieldy <= SCR_FIELDY)
401 if (EVEN(SCR_FIELDY))
402 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
404 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
407 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
410 void BlitScreenToBitmap(Bitmap *target_bitmap)
412 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
413 BlitScreenToBitmap_EM(target_bitmap);
414 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
415 BlitScreenToBitmap_SP(target_bitmap);
416 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
417 BlitScreenToBitmap_RND(target_bitmap);
419 redraw_mask |= REDRAW_FIELD;
422 void DrawFramesPerSecond()
425 int font_nr = FONT_TEXT_2;
426 int font_width = getFontWidth(font_nr);
428 sprintf(text, "%04.1f fps", global.frames_per_second);
430 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
431 font_nr, BLIT_OPAQUE);
436 if (redraw_mask == REDRAW_NONE)
439 // draw masked border to all viewports, if defined
440 DrawMaskedBorder(redraw_mask);
442 // draw frames per second (only if debug mode is enabled)
443 if (redraw_mask & REDRAW_FPS)
444 DrawFramesPerSecond();
446 // redraw complete window if both playfield and (some) doors need redraw
447 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
448 redraw_mask = REDRAW_ALL;
450 /* although redrawing the whole window would be fine for normal gameplay,
451 being able to only redraw the playfield is required for deactivating
452 certain drawing areas (mainly playfield) to work, which is needed for
453 warp-forward to be fast enough (by skipping redraw of most frames) */
455 if (redraw_mask & REDRAW_ALL)
457 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
459 else if (redraw_mask & REDRAW_FIELD)
461 BlitBitmap(backbuffer, window,
462 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
464 else if (redraw_mask & REDRAW_DOORS)
466 if (redraw_mask & REDRAW_DOOR_1)
467 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
469 if (redraw_mask & REDRAW_DOOR_2)
470 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
472 if (redraw_mask & REDRAW_DOOR_3)
473 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
476 redraw_mask = REDRAW_NONE;
479 static void FadeCrossSaveBackbuffer()
481 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
484 static void FadeCrossRestoreBackbuffer()
486 int redraw_mask_last = redraw_mask;
488 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
490 // do not change redraw mask when restoring backbuffer after cross-fading
491 redraw_mask = redraw_mask_last;
494 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
496 static int fade_type_skip = FADE_TYPE_NONE;
497 void (*draw_border_function)(void) = NULL;
498 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
499 int x, y, width, height;
500 int fade_delay, post_delay;
502 if (fade_type == FADE_TYPE_FADE_OUT)
504 if (fade_type_skip != FADE_TYPE_NONE)
506 /* skip all fade operations until specified fade operation */
507 if (fade_type & fade_type_skip)
508 fade_type_skip = FADE_TYPE_NONE;
514 FadeCrossSaveBackbuffer();
517 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
520 FadeCrossSaveBackbuffer();
527 redraw_mask |= fade_mask;
529 if (fade_type == FADE_TYPE_SKIP)
531 fade_type_skip = fade_mode;
536 fade_delay = fading.fade_delay;
537 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
539 if (fade_type_skip != FADE_TYPE_NONE)
541 /* skip all fade operations until specified fade operation */
542 if (fade_type & fade_type_skip)
543 fade_type_skip = FADE_TYPE_NONE;
548 if (global.autoplay_leveldir)
553 if (fade_mask == REDRAW_FIELD)
558 height = FADE_SYSIZE;
560 if (border.draw_masked_when_fading)
561 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
563 DrawMaskedBorder_FIELD(); /* draw once */
565 else /* REDRAW_ALL */
573 if (!setup.fade_screens ||
575 fading.fade_mode == FADE_MODE_NONE)
577 if (fade_mode == FADE_MODE_FADE_OUT)
580 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
582 redraw_mask &= ~fade_mask;
587 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
588 draw_border_function);
590 if (fade_type == FADE_TYPE_FADE_OUT)
591 FadeCrossRestoreBackbuffer();
593 redraw_mask &= ~fade_mask;
596 void FadeIn(int fade_mask)
598 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
599 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
601 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
605 FADE_SXSIZE = FULL_SXSIZE;
606 FADE_SYSIZE = FULL_SYSIZE;
609 void FadeOut(int fade_mask)
611 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
612 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
614 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
616 global.border_status = game_status;
619 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
621 static struct TitleFadingInfo fading_leave_stored;
624 fading_leave_stored = fading_leave;
626 fading = fading_leave_stored;
629 void FadeSetEnterMenu()
631 fading = menu.enter_menu;
633 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
636 void FadeSetLeaveMenu()
638 fading = menu.leave_menu;
640 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
643 void FadeSetEnterScreen()
645 fading = menu.enter_screen[game_status];
647 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
650 void FadeSetNextScreen()
652 fading = menu.next_screen;
654 // (do not overwrite fade mode set by FadeSetEnterScreen)
655 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
658 void FadeSetLeaveScreen()
660 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
663 void FadeSetFromType(int type)
665 if (type & TYPE_ENTER_SCREEN)
666 FadeSetEnterScreen();
667 else if (type & TYPE_ENTER)
669 else if (type & TYPE_LEAVE)
673 void FadeSetDisabled()
675 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
677 fading = fading_none;
680 void FadeSkipNextFadeIn()
682 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
685 void FadeSkipNextFadeOut()
687 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
690 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
692 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
694 return (graphic == IMG_UNDEFINED ? NULL :
695 graphic_info[graphic].bitmap != NULL || redefined ?
696 graphic_info[graphic].bitmap :
697 graphic_info[default_graphic].bitmap);
700 Bitmap *getBackgroundBitmap(int graphic)
702 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
705 Bitmap *getGlobalBorderBitmap(int graphic)
707 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
710 Bitmap *getGlobalBorderBitmapFromGameStatus()
713 (game_status == GAME_MODE_MAIN ||
714 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
715 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
716 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
717 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
720 return getGlobalBorderBitmap(graphic);
723 void SetWindowBackgroundImageIfDefined(int graphic)
725 if (graphic_info[graphic].bitmap)
726 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
729 void SetMainBackgroundImageIfDefined(int graphic)
731 if (graphic_info[graphic].bitmap)
732 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
735 void SetDoorBackgroundImageIfDefined(int graphic)
737 if (graphic_info[graphic].bitmap)
738 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
741 void SetWindowBackgroundImage(int graphic)
743 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
746 void SetMainBackgroundImage(int graphic)
748 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
751 void SetDoorBackgroundImage(int graphic)
753 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
756 void SetPanelBackground()
758 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
760 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
761 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
763 SetDoorBackgroundBitmap(bitmap_db_panel);
766 void DrawBackground(int x, int y, int width, int height)
768 /* "drawto" might still point to playfield buffer here (hall of fame) */
769 ClearRectangleOnBackground(backbuffer, x, y, width, height);
771 if (IN_GFX_FIELD_FULL(x, y))
772 redraw_mask |= REDRAW_FIELD;
773 else if (IN_GFX_DOOR_1(x, y))
774 redraw_mask |= REDRAW_DOOR_1;
775 else if (IN_GFX_DOOR_2(x, y))
776 redraw_mask |= REDRAW_DOOR_2;
777 else if (IN_GFX_DOOR_3(x, y))
778 redraw_mask |= REDRAW_DOOR_3;
781 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
783 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
785 if (font->bitmap == NULL)
788 DrawBackground(x, y, width, height);
791 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
793 struct GraphicInfo *g = &graphic_info[graphic];
795 if (g->bitmap == NULL)
798 DrawBackground(x, y, width, height);
801 static int game_status_last = -1;
802 static Bitmap *global_border_bitmap_last = NULL;
803 static Bitmap *global_border_bitmap = NULL;
804 static int real_sx_last = -1, real_sy_last = -1;
805 static int full_sxsize_last = -1, full_sysize_last = -1;
806 static int dx_last = -1, dy_last = -1;
807 static int dxsize_last = -1, dysize_last = -1;
808 static int vx_last = -1, vy_last = -1;
809 static int vxsize_last = -1, vysize_last = -1;
811 boolean CheckIfGlobalBorderHasChanged()
813 // if game status has not changed, global border has not changed either
814 if (game_status == game_status_last)
817 // determine and store new global border bitmap for current game status
818 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
820 return (global_border_bitmap_last != global_border_bitmap);
823 boolean CheckIfGlobalBorderRedrawIsNeeded()
825 // if game status has not changed, nothing has to be redrawn
826 if (game_status == game_status_last)
829 // redraw if last screen was title screen
830 if (game_status_last == GAME_MODE_TITLE)
833 // redraw if global screen border has changed
834 if (CheckIfGlobalBorderHasChanged())
837 // redraw if position or size of playfield area has changed
838 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
839 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
842 // redraw if position or size of door area has changed
843 if (dx_last != DX || dy_last != DY ||
844 dxsize_last != DXSIZE || dysize_last != DYSIZE)
847 // redraw if position or size of tape area has changed
848 if (vx_last != VX || vy_last != VY ||
849 vxsize_last != VXSIZE || vysize_last != VYSIZE)
855 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
858 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
860 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
863 void RedrawGlobalBorder()
865 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
867 RedrawGlobalBorderFromBitmap(bitmap);
869 redraw_mask = REDRAW_ALL;
872 static void RedrawGlobalBorderIfNeeded()
874 if (game_status == game_status_last)
877 // copy current draw buffer to later copy back areas that have not changed
878 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
880 if (CheckIfGlobalBorderRedrawIsNeeded())
882 // redraw global screen border (or clear, if defined to be empty)
883 RedrawGlobalBorderFromBitmap(global_border_bitmap);
885 // copy previous playfield and door areas, if they are defined on both
886 // previous and current screen and if they still have the same size
888 if (real_sx_last != -1 && real_sy_last != -1 &&
889 REAL_SX != -1 && REAL_SY != -1 &&
890 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
891 BlitBitmap(bitmap_db_store, backbuffer,
892 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
895 if (dx_last != -1 && dy_last != -1 &&
896 DX != -1 && DY != -1 &&
897 dxsize_last == DXSIZE && dysize_last == DYSIZE)
898 BlitBitmap(bitmap_db_store, backbuffer,
899 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
901 if (vx_last != -1 && vy_last != -1 &&
902 VX != -1 && VY != -1 &&
903 vxsize_last == VXSIZE && vysize_last == VYSIZE)
904 BlitBitmap(bitmap_db_store, backbuffer,
905 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
907 redraw_mask = REDRAW_ALL;
910 game_status_last = game_status;
912 global_border_bitmap_last = global_border_bitmap;
914 real_sx_last = REAL_SX;
915 real_sy_last = REAL_SY;
916 full_sxsize_last = FULL_SXSIZE;
917 full_sysize_last = FULL_SYSIZE;
920 dxsize_last = DXSIZE;
921 dysize_last = DYSIZE;
924 vxsize_last = VXSIZE;
925 vysize_last = VYSIZE;
930 RedrawGlobalBorderIfNeeded();
932 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
933 /* (when entering hall of fame after playing) */
934 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
936 /* !!! maybe this should be done before clearing the background !!! */
937 if (game_status == GAME_MODE_PLAYING)
939 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
940 SetDrawtoField(DRAW_FIELDBUFFER);
944 SetDrawtoField(DRAW_BACKBUFFER);
948 void MarkTileDirty(int x, int y)
950 redraw_mask |= REDRAW_FIELD;
953 void SetBorderElement()
957 BorderElement = EL_EMPTY;
959 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
961 for (x = 0; x < lev_fieldx; x++)
963 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
964 BorderElement = EL_STEELWALL;
966 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
972 void FloodFillLevel(int from_x, int from_y, int fill_element,
973 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
974 int max_fieldx, int max_fieldy)
978 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
979 static int safety = 0;
981 /* check if starting field still has the desired content */
982 if (field[from_x][from_y] == fill_element)
987 if (safety > max_fieldx * max_fieldy)
988 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
990 old_element = field[from_x][from_y];
991 field[from_x][from_y] = fill_element;
993 for (i = 0; i < 4; i++)
995 x = from_x + check[i][0];
996 y = from_y + check[i][1];
998 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
999 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1005 void SetRandomAnimationValue(int x, int y)
1007 gfx.anim_random_frame = GfxRandom[x][y];
1010 int getGraphicAnimationFrame(int graphic, int sync_frame)
1012 /* animation synchronized with global frame counter, not move position */
1013 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1014 sync_frame = FrameCounter;
1016 return getAnimationFrame(graphic_info[graphic].anim_frames,
1017 graphic_info[graphic].anim_delay,
1018 graphic_info[graphic].anim_mode,
1019 graphic_info[graphic].anim_start_frame,
1023 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1024 Bitmap **bitmap, int *x, int *y,
1025 boolean get_backside)
1027 struct GraphicInfo *g = &graphic_info[graphic];
1028 Bitmap *src_bitmap = g->bitmap;
1029 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1030 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1031 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1033 // if no in-game graphics defined, always use standard graphic size
1034 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1035 tilesize = TILESIZE;
1037 if (tilesize == gfx.standard_tile_size)
1038 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1039 else if (tilesize == game.tile_size)
1040 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1042 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1044 if (g->offset_y == 0) /* frames are ordered horizontally */
1046 int max_width = g->anim_frames_per_line * g->width;
1047 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1049 src_x = pos % max_width;
1050 src_y = src_y % g->height + pos / max_width * g->height;
1052 else if (g->offset_x == 0) /* frames are ordered vertically */
1054 int max_height = g->anim_frames_per_line * g->height;
1055 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1057 src_x = src_x % g->width + pos / max_height * g->width;
1058 src_y = pos % max_height;
1060 else /* frames are ordered diagonally */
1062 src_x = src_x + frame * g->offset_x;
1063 src_y = src_y + frame * g->offset_y;
1066 *bitmap = src_bitmap;
1067 *x = src_x * tilesize / TILESIZE;
1068 *y = src_y * tilesize / TILESIZE;
1071 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1072 int *x, int *y, boolean get_backside)
1074 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1078 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1079 Bitmap **bitmap, int *x, int *y)
1081 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1084 void getFixedGraphicSource(int graphic, int frame,
1085 Bitmap **bitmap, int *x, int *y)
1087 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1090 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1092 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1095 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1096 int *x, int *y, boolean get_backside)
1098 struct GraphicInfo *g = &graphic_info[graphic];
1099 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1100 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1102 if (TILESIZE_VAR != TILESIZE)
1103 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1106 *bitmap = g->bitmap;
1108 if (g->offset_y == 0) /* frames are ordered horizontally */
1110 int max_width = g->anim_frames_per_line * g->width;
1111 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1113 *x = pos % max_width;
1114 *y = src_y % g->height + pos / max_width * g->height;
1116 else if (g->offset_x == 0) /* frames are ordered vertically */
1118 int max_height = g->anim_frames_per_line * g->height;
1119 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1121 *x = src_x % g->width + pos / max_height * g->width;
1122 *y = pos % max_height;
1124 else /* frames are ordered diagonally */
1126 *x = src_x + frame * g->offset_x;
1127 *y = src_y + frame * g->offset_y;
1131 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1133 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1136 void DrawGraphic(int x, int y, int graphic, int frame)
1139 if (!IN_SCR_FIELD(x, y))
1141 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1142 printf("DrawGraphic(): This should never happen!\n");
1147 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1150 MarkTileDirty(x, y);
1153 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1156 if (!IN_SCR_FIELD(x, y))
1158 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1159 printf("DrawGraphic(): This should never happen!\n");
1164 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1166 MarkTileDirty(x, y);
1169 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1175 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1177 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1180 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1186 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1187 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1190 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1193 if (!IN_SCR_FIELD(x, y))
1195 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1196 printf("DrawGraphicThruMask(): This should never happen!\n");
1201 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1204 MarkTileDirty(x, y);
1207 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1210 if (!IN_SCR_FIELD(x, y))
1212 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1213 printf("DrawGraphicThruMask(): This should never happen!\n");
1218 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1220 MarkTileDirty(x, y);
1223 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1229 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1231 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1235 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1236 int graphic, int frame)
1238 struct GraphicInfo *g = &graphic_info[graphic];
1242 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1244 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1248 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1250 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1252 MarkTileDirty(x / tilesize, y / tilesize);
1255 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1261 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1262 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1265 void DrawMiniGraphic(int x, int y, int graphic)
1267 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1268 MarkTileDirty(x / 2, y / 2);
1271 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1276 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1277 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1280 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1281 int graphic, int frame,
1282 int cut_mode, int mask_mode)
1287 int width = TILEX, height = TILEY;
1290 if (dx || dy) /* shifted graphic */
1292 if (x < BX1) /* object enters playfield from the left */
1299 else if (x > BX2) /* object enters playfield from the right */
1305 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1311 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1313 else if (dx) /* general horizontal movement */
1314 MarkTileDirty(x + SIGN(dx), y);
1316 if (y < BY1) /* object enters playfield from the top */
1318 if (cut_mode==CUT_BELOW) /* object completely above top border */
1326 else if (y > BY2) /* object enters playfield from the bottom */
1332 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1338 else if (dy > 0 && cut_mode == CUT_ABOVE)
1340 if (y == BY2) /* object completely above bottom border */
1346 MarkTileDirty(x, y + 1);
1347 } /* object leaves playfield to the bottom */
1348 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1350 else if (dy) /* general vertical movement */
1351 MarkTileDirty(x, y + SIGN(dy));
1355 if (!IN_SCR_FIELD(x, y))
1357 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1358 printf("DrawGraphicShifted(): This should never happen!\n");
1363 width = width * TILESIZE_VAR / TILESIZE;
1364 height = height * TILESIZE_VAR / TILESIZE;
1365 cx = cx * TILESIZE_VAR / TILESIZE;
1366 cy = cy * TILESIZE_VAR / TILESIZE;
1367 dx = dx * TILESIZE_VAR / TILESIZE;
1368 dy = dy * TILESIZE_VAR / TILESIZE;
1370 if (width > 0 && height > 0)
1372 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1377 dst_x = FX + x * TILEX_VAR + dx;
1378 dst_y = FY + y * TILEY_VAR + dy;
1380 if (mask_mode == USE_MASKING)
1381 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1384 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1387 MarkTileDirty(x, y);
1391 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1392 int graphic, int frame,
1393 int cut_mode, int mask_mode)
1398 int width = TILEX_VAR, height = TILEY_VAR;
1401 int x2 = x + SIGN(dx);
1402 int y2 = y + SIGN(dy);
1404 /* movement with two-tile animations must be sync'ed with movement position,
1405 not with current GfxFrame (which can be higher when using slow movement) */
1406 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1407 int anim_frames = graphic_info[graphic].anim_frames;
1409 /* (we also need anim_delay here for movement animations with less frames) */
1410 int anim_delay = graphic_info[graphic].anim_delay;
1411 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1413 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1414 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1416 /* re-calculate animation frame for two-tile movement animation */
1417 frame = getGraphicAnimationFrame(graphic, sync_frame);
1419 /* check if movement start graphic inside screen area and should be drawn */
1420 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1422 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1424 dst_x = FX + x1 * TILEX_VAR;
1425 dst_y = FY + y1 * TILEY_VAR;
1427 if (mask_mode == USE_MASKING)
1428 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1431 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1434 MarkTileDirty(x1, y1);
1437 /* check if movement end graphic inside screen area and should be drawn */
1438 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1440 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1442 dst_x = FX + x2 * TILEX_VAR;
1443 dst_y = FY + y2 * TILEY_VAR;
1445 if (mask_mode == USE_MASKING)
1446 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1449 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1452 MarkTileDirty(x2, y2);
1456 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1457 int graphic, int frame,
1458 int cut_mode, int mask_mode)
1462 DrawGraphic(x, y, graphic, frame);
1467 if (graphic_info[graphic].double_movement) /* EM style movement images */
1468 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1470 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1473 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1474 int frame, int cut_mode)
1476 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1479 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1480 int cut_mode, int mask_mode)
1482 int lx = LEVELX(x), ly = LEVELY(y);
1486 if (IN_LEV_FIELD(lx, ly))
1488 SetRandomAnimationValue(lx, ly);
1490 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1491 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1493 /* do not use double (EM style) movement graphic when not moving */
1494 if (graphic_info[graphic].double_movement && !dx && !dy)
1496 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1497 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1500 else /* border element */
1502 graphic = el2img(element);
1503 frame = getGraphicAnimationFrame(graphic, -1);
1506 if (element == EL_EXPANDABLE_WALL)
1508 boolean left_stopped = FALSE, right_stopped = FALSE;
1510 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1511 left_stopped = TRUE;
1512 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1513 right_stopped = TRUE;
1515 if (left_stopped && right_stopped)
1517 else if (left_stopped)
1519 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1520 frame = graphic_info[graphic].anim_frames - 1;
1522 else if (right_stopped)
1524 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1525 frame = graphic_info[graphic].anim_frames - 1;
1530 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1531 else if (mask_mode == USE_MASKING)
1532 DrawGraphicThruMask(x, y, graphic, frame);
1534 DrawGraphic(x, y, graphic, frame);
1537 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1538 int cut_mode, int mask_mode)
1540 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1541 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1542 cut_mode, mask_mode);
1545 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1548 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1551 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1554 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1557 void DrawLevelElementThruMask(int x, int y, int element)
1559 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1562 void DrawLevelFieldThruMask(int x, int y)
1564 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1567 /* !!! implementation of quicksand is totally broken !!! */
1568 #define IS_CRUMBLED_TILE(x, y, e) \
1569 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1570 !IS_MOVING(x, y) || \
1571 (e) == EL_QUICKSAND_EMPTYING || \
1572 (e) == EL_QUICKSAND_FAST_EMPTYING))
1574 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1579 int width, height, cx, cy;
1580 int sx = SCREENX(x), sy = SCREENY(y);
1581 int crumbled_border_size = graphic_info[graphic].border_size;
1584 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1586 for (i = 1; i < 4; i++)
1588 int dxx = (i & 1 ? dx : 0);
1589 int dyy = (i & 2 ? dy : 0);
1592 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1595 /* check if neighbour field is of same crumble type */
1596 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1597 graphic_info[graphic].class ==
1598 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1600 /* return if check prevents inner corner */
1601 if (same == (dxx == dx && dyy == dy))
1605 /* if we reach this point, we have an inner corner */
1607 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1609 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1610 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1611 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1612 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1614 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1615 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1618 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1623 int width, height, bx, by, cx, cy;
1624 int sx = SCREENX(x), sy = SCREENY(y);
1625 int crumbled_border_size = graphic_info[graphic].border_size;
1626 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1627 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1630 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1632 /* draw simple, sloppy, non-corner-accurate crumbled border */
1634 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1635 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1636 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1637 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1639 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1640 FX + sx * TILEX_VAR + cx,
1641 FY + sy * TILEY_VAR + cy);
1643 /* (remaining middle border part must be at least as big as corner part) */
1644 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1645 crumbled_border_size >= TILESIZE / 3)
1648 /* correct corners of crumbled border, if needed */
1650 for (i = -1; i <= 1; i += 2)
1652 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1653 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1654 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1657 /* check if neighbour field is of same crumble type */
1658 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1659 graphic_info[graphic].class ==
1660 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1662 /* no crumbled corner, but continued crumbled border */
1664 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1665 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1666 int b1 = (i == 1 ? crumbled_border_size_var :
1667 TILESIZE_VAR - 2 * crumbled_border_size_var);
1669 width = crumbled_border_size_var;
1670 height = crumbled_border_size_var;
1672 if (dir == 1 || dir == 2)
1687 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1689 FX + sx * TILEX_VAR + cx,
1690 FY + sy * TILEY_VAR + cy);
1695 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1697 int sx = SCREENX(x), sy = SCREENY(y);
1700 static int xy[4][2] =
1708 if (!IN_LEV_FIELD(x, y))
1711 element = TILE_GFX_ELEMENT(x, y);
1713 /* crumble field itself */
1714 if (IS_CRUMBLED_TILE(x, y, element))
1716 if (!IN_SCR_FIELD(sx, sy))
1719 for (i = 0; i < 4; i++)
1721 int xx = x + xy[i][0];
1722 int yy = y + xy[i][1];
1724 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1727 /* check if neighbour field is of same crumble type */
1728 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1729 graphic_info[graphic].class ==
1730 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1733 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1736 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1737 graphic_info[graphic].anim_frames == 2)
1739 for (i = 0; i < 4; i++)
1741 int dx = (i & 1 ? +1 : -1);
1742 int dy = (i & 2 ? +1 : -1);
1744 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1748 MarkTileDirty(sx, sy);
1750 else /* center field not crumbled -- crumble neighbour fields */
1752 for (i = 0; i < 4; i++)
1754 int xx = x + xy[i][0];
1755 int yy = y + xy[i][1];
1756 int sxx = sx + xy[i][0];
1757 int syy = sy + xy[i][1];
1759 if (!IN_LEV_FIELD(xx, yy) ||
1760 !IN_SCR_FIELD(sxx, syy))
1763 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1766 element = TILE_GFX_ELEMENT(xx, yy);
1768 if (!IS_CRUMBLED_TILE(xx, yy, element))
1771 graphic = el_act2crm(element, ACTION_DEFAULT);
1773 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1775 MarkTileDirty(sxx, syy);
1780 void DrawLevelFieldCrumbled(int x, int y)
1784 if (!IN_LEV_FIELD(x, y))
1787 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1788 GfxElement[x][y] != EL_UNDEFINED &&
1789 GFX_CRUMBLED(GfxElement[x][y]))
1791 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1796 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1798 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1801 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1804 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1805 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1806 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1807 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1808 int sx = SCREENX(x), sy = SCREENY(y);
1810 DrawGraphic(sx, sy, graphic1, frame1);
1811 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1814 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1816 int sx = SCREENX(x), sy = SCREENY(y);
1817 static int xy[4][2] =
1826 for (i = 0; i < 4; i++)
1828 int xx = x + xy[i][0];
1829 int yy = y + xy[i][1];
1830 int sxx = sx + xy[i][0];
1831 int syy = sy + xy[i][1];
1833 if (!IN_LEV_FIELD(xx, yy) ||
1834 !IN_SCR_FIELD(sxx, syy) ||
1835 !GFX_CRUMBLED(Feld[xx][yy]) ||
1839 DrawLevelField(xx, yy);
1843 static int getBorderElement(int x, int y)
1847 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1848 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1849 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1850 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1851 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1852 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1853 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1855 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1856 int steel_position = (x == -1 && y == -1 ? 0 :
1857 x == lev_fieldx && y == -1 ? 1 :
1858 x == -1 && y == lev_fieldy ? 2 :
1859 x == lev_fieldx && y == lev_fieldy ? 3 :
1860 x == -1 || x == lev_fieldx ? 4 :
1861 y == -1 || y == lev_fieldy ? 5 : 6);
1863 return border[steel_position][steel_type];
1866 void DrawScreenElement(int x, int y, int element)
1868 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1869 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1872 void DrawLevelElement(int x, int y, int element)
1874 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1875 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1878 void DrawScreenField(int x, int y)
1880 int lx = LEVELX(x), ly = LEVELY(y);
1881 int element, content;
1883 if (!IN_LEV_FIELD(lx, ly))
1885 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1888 element = getBorderElement(lx, ly);
1890 DrawScreenElement(x, y, element);
1895 element = Feld[lx][ly];
1896 content = Store[lx][ly];
1898 if (IS_MOVING(lx, ly))
1900 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1901 boolean cut_mode = NO_CUTTING;
1903 if (element == EL_QUICKSAND_EMPTYING ||
1904 element == EL_QUICKSAND_FAST_EMPTYING ||
1905 element == EL_MAGIC_WALL_EMPTYING ||
1906 element == EL_BD_MAGIC_WALL_EMPTYING ||
1907 element == EL_DC_MAGIC_WALL_EMPTYING ||
1908 element == EL_AMOEBA_DROPPING)
1909 cut_mode = CUT_ABOVE;
1910 else if (element == EL_QUICKSAND_FILLING ||
1911 element == EL_QUICKSAND_FAST_FILLING ||
1912 element == EL_MAGIC_WALL_FILLING ||
1913 element == EL_BD_MAGIC_WALL_FILLING ||
1914 element == EL_DC_MAGIC_WALL_FILLING)
1915 cut_mode = CUT_BELOW;
1917 if (cut_mode == CUT_ABOVE)
1918 DrawScreenElement(x, y, element);
1920 DrawScreenElement(x, y, EL_EMPTY);
1923 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1924 else if (cut_mode == NO_CUTTING)
1925 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1928 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1930 if (cut_mode == CUT_BELOW &&
1931 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1932 DrawLevelElement(lx, ly + 1, element);
1935 if (content == EL_ACID)
1937 int dir = MovDir[lx][ly];
1938 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1939 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1941 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1944 else if (IS_BLOCKED(lx, ly))
1949 boolean cut_mode = NO_CUTTING;
1950 int element_old, content_old;
1952 Blocked2Moving(lx, ly, &oldx, &oldy);
1955 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1956 MovDir[oldx][oldy] == MV_RIGHT);
1958 element_old = Feld[oldx][oldy];
1959 content_old = Store[oldx][oldy];
1961 if (element_old == EL_QUICKSAND_EMPTYING ||
1962 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1963 element_old == EL_MAGIC_WALL_EMPTYING ||
1964 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1965 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1966 element_old == EL_AMOEBA_DROPPING)
1967 cut_mode = CUT_ABOVE;
1969 DrawScreenElement(x, y, EL_EMPTY);
1972 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1974 else if (cut_mode == NO_CUTTING)
1975 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1978 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1981 else if (IS_DRAWABLE(element))
1982 DrawScreenElement(x, y, element);
1984 DrawScreenElement(x, y, EL_EMPTY);
1987 void DrawLevelField(int x, int y)
1989 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1990 DrawScreenField(SCREENX(x), SCREENY(y));
1991 else if (IS_MOVING(x, y))
1995 Moving2Blocked(x, y, &newx, &newy);
1996 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1997 DrawScreenField(SCREENX(newx), SCREENY(newy));
1999 else if (IS_BLOCKED(x, y))
2003 Blocked2Moving(x, y, &oldx, &oldy);
2004 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2005 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2009 void DrawSizedElement(int x, int y, int element, int tilesize)
2013 graphic = el2edimg(element);
2014 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2017 void DrawMiniElement(int x, int y, int element)
2021 graphic = el2edimg(element);
2022 DrawMiniGraphic(x, y, graphic);
2025 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2028 int x = sx + scroll_x, y = sy + scroll_y;
2030 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2031 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2032 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2033 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2035 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2038 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2040 int x = sx + scroll_x, y = sy + scroll_y;
2042 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2043 DrawMiniElement(sx, sy, EL_EMPTY);
2044 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2045 DrawMiniElement(sx, sy, Feld[x][y]);
2047 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2050 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2051 int x, int y, int xsize, int ysize,
2052 int tile_width, int tile_height)
2056 int dst_x = startx + x * tile_width;
2057 int dst_y = starty + y * tile_height;
2058 int width = graphic_info[graphic].width;
2059 int height = graphic_info[graphic].height;
2060 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2061 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2062 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2063 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2064 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2065 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2066 boolean draw_masked = graphic_info[graphic].draw_masked;
2068 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2070 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2072 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2076 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2077 inner_sx + (x - 1) * tile_width % inner_width);
2078 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2079 inner_sy + (y - 1) * tile_height % inner_height);
2082 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2085 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2089 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2090 int x, int y, int xsize, int ysize, int font_nr)
2092 int font_width = getFontWidth(font_nr);
2093 int font_height = getFontHeight(font_nr);
2095 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2096 font_width, font_height);
2099 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2101 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2102 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2103 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2104 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2105 boolean no_delay = (tape.warp_forward);
2106 unsigned int anim_delay = 0;
2107 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2108 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2109 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2110 int font_width = getFontWidth(font_nr);
2111 int font_height = getFontHeight(font_nr);
2112 int max_xsize = level.envelope[envelope_nr].xsize;
2113 int max_ysize = level.envelope[envelope_nr].ysize;
2114 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2115 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2116 int xend = max_xsize;
2117 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2118 int xstep = (xstart < xend ? 1 : 0);
2119 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2121 int end = MAX(xend - xstart, yend - ystart);
2124 for (i = start; i <= end; i++)
2126 int last_frame = end; // last frame of this "for" loop
2127 int x = xstart + i * xstep;
2128 int y = ystart + i * ystep;
2129 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2130 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2131 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2132 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2135 SetDrawtoField(DRAW_FIELDBUFFER);
2137 BlitScreenToBitmap(backbuffer);
2139 SetDrawtoField(DRAW_BACKBUFFER);
2141 for (yy = 0; yy < ysize; yy++)
2142 for (xx = 0; xx < xsize; xx++)
2143 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2145 DrawTextBuffer(sx + font_width, sy + font_height,
2146 level.envelope[envelope_nr].text, font_nr, max_xsize,
2147 xsize - 2, ysize - 2, 0, mask_mode,
2148 level.envelope[envelope_nr].autowrap,
2149 level.envelope[envelope_nr].centered, FALSE);
2151 redraw_mask |= REDRAW_FIELD;
2154 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2158 void ShowEnvelope(int envelope_nr)
2160 int element = EL_ENVELOPE_1 + envelope_nr;
2161 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2162 int sound_opening = element_info[element].sound[ACTION_OPENING];
2163 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2164 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2165 boolean no_delay = (tape.warp_forward);
2166 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2167 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2168 int anim_mode = graphic_info[graphic].anim_mode;
2169 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2170 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2172 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2174 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2176 if (anim_mode == ANIM_DEFAULT)
2177 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2179 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2182 Delay(wait_delay_value);
2184 WaitForEventToContinue();
2186 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2188 if (anim_mode != ANIM_NONE)
2189 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2191 if (anim_mode == ANIM_DEFAULT)
2192 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2194 game.envelope_active = FALSE;
2196 SetDrawtoField(DRAW_FIELDBUFFER);
2198 redraw_mask |= REDRAW_FIELD;
2202 static void setRequestBasePosition(int *x, int *y)
2204 int sx_base, sy_base;
2206 if (request.x != -1)
2207 sx_base = request.x;
2208 else if (request.align == ALIGN_LEFT)
2210 else if (request.align == ALIGN_RIGHT)
2211 sx_base = SX + SXSIZE;
2213 sx_base = SX + SXSIZE / 2;
2215 if (request.y != -1)
2216 sy_base = request.y;
2217 else if (request.valign == VALIGN_TOP)
2219 else if (request.valign == VALIGN_BOTTOM)
2220 sy_base = SY + SYSIZE;
2222 sy_base = SY + SYSIZE / 2;
2228 static void setRequestPositionExt(int *x, int *y, int width, int height,
2229 boolean add_border_size)
2231 int border_size = request.border_size;
2232 int sx_base, sy_base;
2235 setRequestBasePosition(&sx_base, &sy_base);
2237 if (request.align == ALIGN_LEFT)
2239 else if (request.align == ALIGN_RIGHT)
2240 sx = sx_base - width;
2242 sx = sx_base - width / 2;
2244 if (request.valign == VALIGN_TOP)
2246 else if (request.valign == VALIGN_BOTTOM)
2247 sy = sy_base - height;
2249 sy = sy_base - height / 2;
2251 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2252 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2254 if (add_border_size)
2264 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2266 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2269 void DrawEnvelopeRequest(char *text)
2271 int last_game_status = game_status; /* save current game status */
2272 char *text_final = text;
2273 char *text_door_style = NULL;
2274 int graphic = IMG_BACKGROUND_REQUEST;
2275 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2276 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2277 int font_nr = FONT_REQUEST;
2278 int font_width = getFontWidth(font_nr);
2279 int font_height = getFontHeight(font_nr);
2280 int border_size = request.border_size;
2281 int line_spacing = request.line_spacing;
2282 int line_height = font_height + line_spacing;
2283 int max_text_width = request.width - 2 * border_size;
2284 int max_text_height = request.height - 2 * border_size;
2285 int line_length = max_text_width / font_width;
2286 int max_lines = max_text_height / line_height;
2287 int text_width = line_length * font_width;
2288 int width = request.width;
2289 int height = request.height;
2290 int tile_size = MAX(request.step_offset, 1);
2291 int x_steps = width / tile_size;
2292 int y_steps = height / tile_size;
2293 int sx_offset = border_size;
2294 int sy_offset = border_size;
2298 if (request.centered)
2299 sx_offset = (request.width - text_width) / 2;
2301 if (request.wrap_single_words && !request.autowrap)
2303 char *src_text_ptr, *dst_text_ptr;
2305 text_door_style = checked_malloc(2 * strlen(text) + 1);
2307 src_text_ptr = text;
2308 dst_text_ptr = text_door_style;
2310 while (*src_text_ptr)
2312 if (*src_text_ptr == ' ' ||
2313 *src_text_ptr == '?' ||
2314 *src_text_ptr == '!')
2315 *dst_text_ptr++ = '\n';
2317 if (*src_text_ptr != ' ')
2318 *dst_text_ptr++ = *src_text_ptr;
2323 *dst_text_ptr = '\0';
2325 text_final = text_door_style;
2328 setRequestPosition(&sx, &sy, FALSE);
2330 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2332 for (y = 0; y < y_steps; y++)
2333 for (x = 0; x < x_steps; x++)
2334 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2335 x, y, x_steps, y_steps,
2336 tile_size, tile_size);
2338 /* force DOOR font inside door area */
2339 game_status = GAME_MODE_PSEUDO_DOOR;
2341 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2342 line_length, -1, max_lines, line_spacing, mask_mode,
2343 request.autowrap, request.centered, FALSE);
2345 game_status = last_game_status; /* restore current game status */
2347 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2348 RedrawGadget(tool_gadget[i]);
2350 // store readily prepared envelope request for later use when animating
2351 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2353 if (text_door_style)
2354 free(text_door_style);
2357 void AnimateEnvelopeRequest(int anim_mode, int action)
2359 int graphic = IMG_BACKGROUND_REQUEST;
2360 boolean draw_masked = graphic_info[graphic].draw_masked;
2361 int delay_value_normal = request.step_delay;
2362 int delay_value_fast = delay_value_normal / 2;
2363 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2364 boolean no_delay = (tape.warp_forward);
2365 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2366 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2367 unsigned int anim_delay = 0;
2369 int tile_size = MAX(request.step_offset, 1);
2370 int max_xsize = request.width / tile_size;
2371 int max_ysize = request.height / tile_size;
2372 int max_xsize_inner = max_xsize - 2;
2373 int max_ysize_inner = max_ysize - 2;
2375 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2376 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2377 int xend = max_xsize_inner;
2378 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2379 int xstep = (xstart < xend ? 1 : 0);
2380 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2382 int end = MAX(xend - xstart, yend - ystart);
2385 if (setup.quick_doors)
2393 if (action == ACTION_OPENING)
2394 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2395 else if (action == ACTION_CLOSING)
2396 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2399 for (i = start; i <= end; i++)
2401 int last_frame = end; // last frame of this "for" loop
2402 int x = xstart + i * xstep;
2403 int y = ystart + i * ystep;
2404 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2405 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2406 int xsize_size_left = (xsize - 1) * tile_size;
2407 int ysize_size_top = (ysize - 1) * tile_size;
2408 int max_xsize_pos = (max_xsize - 1) * tile_size;
2409 int max_ysize_pos = (max_ysize - 1) * tile_size;
2410 int width = xsize * tile_size;
2411 int height = ysize * tile_size;
2416 setRequestPosition(&src_x, &src_y, FALSE);
2417 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2419 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2421 for (yy = 0; yy < 2; yy++)
2423 for (xx = 0; xx < 2; xx++)
2425 int src_xx = src_x + xx * max_xsize_pos;
2426 int src_yy = src_y + yy * max_ysize_pos;
2427 int dst_xx = dst_x + xx * xsize_size_left;
2428 int dst_yy = dst_y + yy * ysize_size_top;
2429 int xx_size = (xx ? tile_size : xsize_size_left);
2430 int yy_size = (yy ? tile_size : ysize_size_top);
2433 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2434 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2436 BlitBitmap(bitmap_db_cross, backbuffer,
2437 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2441 redraw_mask |= REDRAW_FIELD;
2446 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2450 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2452 int graphic = IMG_BACKGROUND_REQUEST;
2453 int sound_opening = SND_REQUEST_OPENING;
2454 int sound_closing = SND_REQUEST_CLOSING;
2455 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2456 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2457 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2458 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2459 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2461 if (game_status == GAME_MODE_PLAYING)
2462 BlitScreenToBitmap(backbuffer);
2464 SetDrawtoField(DRAW_BACKBUFFER);
2466 // SetDrawBackgroundMask(REDRAW_NONE);
2468 if (action == ACTION_OPENING)
2470 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2472 if (req_state & REQ_ASK)
2474 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2475 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2477 else if (req_state & REQ_CONFIRM)
2479 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2481 else if (req_state & REQ_PLAYER)
2483 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2484 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2485 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2486 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2489 DrawEnvelopeRequest(text);
2491 if (game_status != GAME_MODE_MAIN)
2495 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2497 if (action == ACTION_OPENING)
2499 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2501 if (anim_mode == ANIM_DEFAULT)
2502 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2504 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2508 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2510 if (anim_mode != ANIM_NONE)
2511 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2513 if (anim_mode == ANIM_DEFAULT)
2514 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2517 game.envelope_active = FALSE;
2519 if (action == ACTION_CLOSING)
2521 if (game_status != GAME_MODE_MAIN)
2524 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2527 // SetDrawBackgroundMask(last_draw_background_mask);
2529 redraw_mask |= REDRAW_FIELD;
2531 if (game_status == GAME_MODE_MAIN)
2536 if (action == ACTION_CLOSING &&
2537 game_status == GAME_MODE_PLAYING &&
2538 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2539 SetDrawtoField(DRAW_FIELDBUFFER);
2542 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2546 int graphic = el2preimg(element);
2548 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2549 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2552 void DrawLevel(int draw_background_mask)
2556 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2557 SetDrawBackgroundMask(draw_background_mask);
2561 for (x = BX1; x <= BX2; x++)
2562 for (y = BY1; y <= BY2; y++)
2563 DrawScreenField(x, y);
2565 redraw_mask |= REDRAW_FIELD;
2568 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2573 for (x = 0; x < size_x; x++)
2574 for (y = 0; y < size_y; y++)
2575 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2577 redraw_mask |= REDRAW_FIELD;
2580 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2584 for (x = 0; x < size_x; x++)
2585 for (y = 0; y < size_y; y++)
2586 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2588 redraw_mask |= REDRAW_FIELD;
2591 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2593 boolean show_level_border = (BorderElement != EL_EMPTY);
2594 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2595 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2596 int tile_size = preview.tile_size;
2597 int preview_width = preview.xsize * tile_size;
2598 int preview_height = preview.ysize * tile_size;
2599 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2600 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2601 int real_preview_width = real_preview_xsize * tile_size;
2602 int real_preview_height = real_preview_ysize * tile_size;
2603 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2604 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2607 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2610 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2612 dst_x += (preview_width - real_preview_width) / 2;
2613 dst_y += (preview_height - real_preview_height) / 2;
2615 for (x = 0; x < real_preview_xsize; x++)
2617 for (y = 0; y < real_preview_ysize; y++)
2619 int lx = from_x + x + (show_level_border ? -1 : 0);
2620 int ly = from_y + y + (show_level_border ? -1 : 0);
2621 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2622 getBorderElement(lx, ly));
2624 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2625 element, tile_size);
2629 redraw_mask |= REDRAW_FIELD;
2632 #define MICROLABEL_EMPTY 0
2633 #define MICROLABEL_LEVEL_NAME 1
2634 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2635 #define MICROLABEL_LEVEL_AUTHOR 3
2636 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2637 #define MICROLABEL_IMPORTED_FROM 5
2638 #define MICROLABEL_IMPORTED_BY_HEAD 6
2639 #define MICROLABEL_IMPORTED_BY 7
2641 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2643 int max_text_width = SXSIZE;
2644 int font_width = getFontWidth(font_nr);
2646 if (pos->align == ALIGN_CENTER)
2647 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2648 else if (pos->align == ALIGN_RIGHT)
2649 max_text_width = pos->x;
2651 max_text_width = SXSIZE - pos->x;
2653 return max_text_width / font_width;
2656 static void DrawPreviewLevelLabelExt(int mode)
2658 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2659 char label_text[MAX_OUTPUT_LINESIZE + 1];
2660 int max_len_label_text;
2661 int font_nr = pos->font;
2664 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2667 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2668 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2669 mode == MICROLABEL_IMPORTED_BY_HEAD)
2670 font_nr = pos->font_alt;
2672 max_len_label_text = getMaxTextLength(pos, font_nr);
2674 if (pos->size != -1)
2675 max_len_label_text = pos->size;
2677 for (i = 0; i < max_len_label_text; i++)
2678 label_text[i] = ' ';
2679 label_text[max_len_label_text] = '\0';
2681 if (strlen(label_text) > 0)
2682 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2685 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2686 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2687 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2688 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2689 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2690 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2691 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2692 max_len_label_text);
2693 label_text[max_len_label_text] = '\0';
2695 if (strlen(label_text) > 0)
2696 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2698 redraw_mask |= REDRAW_FIELD;
2701 static void DrawPreviewLevelExt(boolean restart)
2703 static unsigned int scroll_delay = 0;
2704 static unsigned int label_delay = 0;
2705 static int from_x, from_y, scroll_direction;
2706 static int label_state, label_counter;
2707 unsigned int scroll_delay_value = preview.step_delay;
2708 boolean show_level_border = (BorderElement != EL_EMPTY);
2709 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2710 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2711 int last_game_status = game_status; /* save current game status */
2718 if (preview.anim_mode == ANIM_CENTERED)
2720 if (level_xsize > preview.xsize)
2721 from_x = (level_xsize - preview.xsize) / 2;
2722 if (level_ysize > preview.ysize)
2723 from_y = (level_ysize - preview.ysize) / 2;
2726 from_x += preview.xoffset;
2727 from_y += preview.yoffset;
2729 scroll_direction = MV_RIGHT;
2733 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2734 DrawPreviewLevelLabelExt(label_state);
2736 /* initialize delay counters */
2737 DelayReached(&scroll_delay, 0);
2738 DelayReached(&label_delay, 0);
2740 if (leveldir_current->name)
2742 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2743 char label_text[MAX_OUTPUT_LINESIZE + 1];
2744 int font_nr = pos->font;
2745 int max_len_label_text = getMaxTextLength(pos, font_nr);
2747 if (pos->size != -1)
2748 max_len_label_text = pos->size;
2750 strncpy(label_text, leveldir_current->name, max_len_label_text);
2751 label_text[max_len_label_text] = '\0';
2753 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2754 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2757 game_status = last_game_status; /* restore current game status */
2762 /* scroll preview level, if needed */
2763 if (preview.anim_mode != ANIM_NONE &&
2764 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2765 DelayReached(&scroll_delay, scroll_delay_value))
2767 switch (scroll_direction)
2772 from_x -= preview.step_offset;
2773 from_x = (from_x < 0 ? 0 : from_x);
2776 scroll_direction = MV_UP;
2780 if (from_x < level_xsize - preview.xsize)
2782 from_x += preview.step_offset;
2783 from_x = (from_x > level_xsize - preview.xsize ?
2784 level_xsize - preview.xsize : from_x);
2787 scroll_direction = MV_DOWN;
2793 from_y -= preview.step_offset;
2794 from_y = (from_y < 0 ? 0 : from_y);
2797 scroll_direction = MV_RIGHT;
2801 if (from_y < level_ysize - preview.ysize)
2803 from_y += preview.step_offset;
2804 from_y = (from_y > level_ysize - preview.ysize ?
2805 level_ysize - preview.ysize : from_y);
2808 scroll_direction = MV_LEFT;
2815 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2818 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2819 /* redraw micro level label, if needed */
2820 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2821 !strEqual(level.author, ANONYMOUS_NAME) &&
2822 !strEqual(level.author, leveldir_current->name) &&
2823 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2825 int max_label_counter = 23;
2827 if (leveldir_current->imported_from != NULL &&
2828 strlen(leveldir_current->imported_from) > 0)
2829 max_label_counter += 14;
2830 if (leveldir_current->imported_by != NULL &&
2831 strlen(leveldir_current->imported_by) > 0)
2832 max_label_counter += 14;
2834 label_counter = (label_counter + 1) % max_label_counter;
2835 label_state = (label_counter >= 0 && label_counter <= 7 ?
2836 MICROLABEL_LEVEL_NAME :
2837 label_counter >= 9 && label_counter <= 12 ?
2838 MICROLABEL_LEVEL_AUTHOR_HEAD :
2839 label_counter >= 14 && label_counter <= 21 ?
2840 MICROLABEL_LEVEL_AUTHOR :
2841 label_counter >= 23 && label_counter <= 26 ?
2842 MICROLABEL_IMPORTED_FROM_HEAD :
2843 label_counter >= 28 && label_counter <= 35 ?
2844 MICROLABEL_IMPORTED_FROM :
2845 label_counter >= 37 && label_counter <= 40 ?
2846 MICROLABEL_IMPORTED_BY_HEAD :
2847 label_counter >= 42 && label_counter <= 49 ?
2848 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2850 if (leveldir_current->imported_from == NULL &&
2851 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2852 label_state == MICROLABEL_IMPORTED_FROM))
2853 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2854 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2856 DrawPreviewLevelLabelExt(label_state);
2859 game_status = last_game_status; /* restore current game status */
2862 void DrawPreviewLevelInitial()
2864 DrawPreviewLevelExt(TRUE);
2867 void DrawPreviewLevelAnimation()
2869 DrawPreviewLevelExt(FALSE);
2872 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2873 int graphic, int sync_frame,
2876 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2878 if (mask_mode == USE_MASKING)
2879 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2881 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2884 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2885 int graphic, int sync_frame, int mask_mode)
2887 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2889 if (mask_mode == USE_MASKING)
2890 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2892 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2895 inline static void DrawGraphicAnimation(int x, int y, int graphic)
2897 int lx = LEVELX(x), ly = LEVELY(y);
2899 if (!IN_SCR_FIELD(x, y))
2902 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2903 graphic, GfxFrame[lx][ly], NO_MASKING);
2905 MarkTileDirty(x, y);
2908 void DrawFixedGraphicAnimation(int x, int y, int graphic)
2910 int lx = LEVELX(x), ly = LEVELY(y);
2912 if (!IN_SCR_FIELD(x, y))
2915 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2916 graphic, GfxFrame[lx][ly], NO_MASKING);
2917 MarkTileDirty(x, y);
2920 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2922 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2925 void DrawLevelElementAnimation(int x, int y, int element)
2927 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2929 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2932 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2934 int sx = SCREENX(x), sy = SCREENY(y);
2936 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2939 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2942 DrawGraphicAnimation(sx, sy, graphic);
2945 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2946 DrawLevelFieldCrumbled(x, y);
2948 if (GFX_CRUMBLED(Feld[x][y]))
2949 DrawLevelFieldCrumbled(x, y);
2953 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2955 int sx = SCREENX(x), sy = SCREENY(y);
2958 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2961 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2963 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2966 DrawGraphicAnimation(sx, sy, graphic);
2968 if (GFX_CRUMBLED(element))
2969 DrawLevelFieldCrumbled(x, y);
2972 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2974 if (player->use_murphy)
2976 /* this works only because currently only one player can be "murphy" ... */
2977 static int last_horizontal_dir = MV_LEFT;
2978 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2980 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2981 last_horizontal_dir = move_dir;
2983 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2985 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2987 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2993 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2996 static boolean equalGraphics(int graphic1, int graphic2)
2998 struct GraphicInfo *g1 = &graphic_info[graphic1];
2999 struct GraphicInfo *g2 = &graphic_info[graphic2];
3001 return (g1->bitmap == g2->bitmap &&
3002 g1->src_x == g2->src_x &&
3003 g1->src_y == g2->src_y &&
3004 g1->anim_frames == g2->anim_frames &&
3005 g1->anim_delay == g2->anim_delay &&
3006 g1->anim_mode == g2->anim_mode);
3009 void DrawAllPlayers()
3013 for (i = 0; i < MAX_PLAYERS; i++)
3014 if (stored_player[i].active)
3015 DrawPlayer(&stored_player[i]);
3018 void DrawPlayerField(int x, int y)
3020 if (!IS_PLAYER(x, y))
3023 DrawPlayer(PLAYERINFO(x, y));
3026 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3028 void DrawPlayer(struct PlayerInfo *player)
3030 int jx = player->jx;
3031 int jy = player->jy;
3032 int move_dir = player->MovDir;
3033 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3034 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3035 int last_jx = (player->is_moving ? jx - dx : jx);
3036 int last_jy = (player->is_moving ? jy - dy : jy);
3037 int next_jx = jx + dx;
3038 int next_jy = jy + dy;
3039 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3040 boolean player_is_opaque = FALSE;
3041 int sx = SCREENX(jx), sy = SCREENY(jy);
3042 int sxx = 0, syy = 0;
3043 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3045 int action = ACTION_DEFAULT;
3046 int last_player_graphic = getPlayerGraphic(player, move_dir);
3047 int last_player_frame = player->Frame;
3050 /* GfxElement[][] is set to the element the player is digging or collecting;
3051 remove also for off-screen player if the player is not moving anymore */
3052 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3053 GfxElement[jx][jy] = EL_UNDEFINED;
3055 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3059 if (!IN_LEV_FIELD(jx, jy))
3061 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3062 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3063 printf("DrawPlayerField(): This should never happen!\n");
3068 if (element == EL_EXPLOSION)
3071 action = (player->is_pushing ? ACTION_PUSHING :
3072 player->is_digging ? ACTION_DIGGING :
3073 player->is_collecting ? ACTION_COLLECTING :
3074 player->is_moving ? ACTION_MOVING :
3075 player->is_snapping ? ACTION_SNAPPING :
3076 player->is_dropping ? ACTION_DROPPING :
3077 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3079 if (player->is_waiting)
3080 move_dir = player->dir_waiting;
3082 InitPlayerGfxAnimation(player, action, move_dir);
3084 /* ----------------------------------------------------------------------- */
3085 /* draw things in the field the player is leaving, if needed */
3086 /* ----------------------------------------------------------------------- */
3088 if (player->is_moving)
3090 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3092 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3094 if (last_element == EL_DYNAMITE_ACTIVE ||
3095 last_element == EL_EM_DYNAMITE_ACTIVE ||
3096 last_element == EL_SP_DISK_RED_ACTIVE)
3097 DrawDynamite(last_jx, last_jy);
3099 DrawLevelFieldThruMask(last_jx, last_jy);
3101 else if (last_element == EL_DYNAMITE_ACTIVE ||
3102 last_element == EL_EM_DYNAMITE_ACTIVE ||
3103 last_element == EL_SP_DISK_RED_ACTIVE)
3104 DrawDynamite(last_jx, last_jy);
3106 /* !!! this is not enough to prevent flickering of players which are
3107 moving next to each others without a free tile between them -- this
3108 can only be solved by drawing all players layer by layer (first the
3109 background, then the foreground etc.) !!! => TODO */
3110 else if (!IS_PLAYER(last_jx, last_jy))
3111 DrawLevelField(last_jx, last_jy);
3114 DrawLevelField(last_jx, last_jy);
3117 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3118 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3121 if (!IN_SCR_FIELD(sx, sy))
3124 /* ----------------------------------------------------------------------- */
3125 /* draw things behind the player, if needed */
3126 /* ----------------------------------------------------------------------- */
3129 DrawLevelElement(jx, jy, Back[jx][jy]);
3130 else if (IS_ACTIVE_BOMB(element))
3131 DrawLevelElement(jx, jy, EL_EMPTY);
3134 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3136 int old_element = GfxElement[jx][jy];
3137 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3138 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3140 if (GFX_CRUMBLED(old_element))
3141 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3143 DrawGraphic(sx, sy, old_graphic, frame);
3145 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3146 player_is_opaque = TRUE;
3150 GfxElement[jx][jy] = EL_UNDEFINED;
3152 /* make sure that pushed elements are drawn with correct frame rate */
3153 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3155 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3156 GfxFrame[jx][jy] = player->StepFrame;
3158 DrawLevelField(jx, jy);
3162 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3163 /* ----------------------------------------------------------------------- */
3164 /* draw player himself */
3165 /* ----------------------------------------------------------------------- */
3167 graphic = getPlayerGraphic(player, move_dir);
3169 /* in the case of changed player action or direction, prevent the current
3170 animation frame from being restarted for identical animations */
3171 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3172 player->Frame = last_player_frame;
3174 frame = getGraphicAnimationFrame(graphic, player->Frame);
3178 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3179 sxx = player->GfxPos;
3181 syy = player->GfxPos;
3184 if (player_is_opaque)
3185 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3187 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3189 if (SHIELD_ON(player))
3191 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3192 IMG_SHIELD_NORMAL_ACTIVE);
3193 int frame = getGraphicAnimationFrame(graphic, -1);
3195 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3199 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3202 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3203 sxx = player->GfxPos;
3205 syy = player->GfxPos;
3209 /* ----------------------------------------------------------------------- */
3210 /* draw things the player is pushing, if needed */
3211 /* ----------------------------------------------------------------------- */
3213 if (player->is_pushing && player->is_moving)
3215 int px = SCREENX(jx), py = SCREENY(jy);
3216 int pxx = (TILEX - ABS(sxx)) * dx;
3217 int pyy = (TILEY - ABS(syy)) * dy;
3218 int gfx_frame = GfxFrame[jx][jy];
3224 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3226 element = Feld[next_jx][next_jy];
3227 gfx_frame = GfxFrame[next_jx][next_jy];
3230 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3232 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3233 frame = getGraphicAnimationFrame(graphic, sync_frame);
3235 /* draw background element under pushed element (like the Sokoban field) */
3236 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3238 /* this allows transparent pushing animation over non-black background */
3241 DrawLevelElement(jx, jy, Back[jx][jy]);
3243 DrawLevelElement(jx, jy, EL_EMPTY);
3245 if (Back[next_jx][next_jy])
3246 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3248 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3250 else if (Back[next_jx][next_jy])
3251 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3254 /* do not draw (EM style) pushing animation when pushing is finished */
3255 /* (two-tile animations usually do not contain start and end frame) */
3256 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3257 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3259 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3261 /* masked drawing is needed for EMC style (double) movement graphics */
3262 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3263 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3267 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3268 /* ----------------------------------------------------------------------- */
3269 /* draw player himself */
3270 /* ----------------------------------------------------------------------- */
3272 graphic = getPlayerGraphic(player, move_dir);
3274 /* in the case of changed player action or direction, prevent the current
3275 animation frame from being restarted for identical animations */
3276 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3277 player->Frame = last_player_frame;
3279 frame = getGraphicAnimationFrame(graphic, player->Frame);
3283 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3284 sxx = player->GfxPos;
3286 syy = player->GfxPos;
3289 if (player_is_opaque)
3290 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3292 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3294 if (SHIELD_ON(player))
3296 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3297 IMG_SHIELD_NORMAL_ACTIVE);
3298 int frame = getGraphicAnimationFrame(graphic, -1);
3300 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3304 /* ----------------------------------------------------------------------- */
3305 /* draw things in front of player (active dynamite or dynabombs) */
3306 /* ----------------------------------------------------------------------- */
3308 if (IS_ACTIVE_BOMB(element))
3310 graphic = el2img(element);
3311 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3313 if (game.emulation == EMU_SUPAPLEX)
3314 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3316 DrawGraphicThruMask(sx, sy, graphic, frame);
3319 if (player_is_moving && last_element == EL_EXPLOSION)
3321 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3322 GfxElement[last_jx][last_jy] : EL_EMPTY);
3323 int graphic = el_act2img(element, ACTION_EXPLODING);
3324 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3325 int phase = ExplodePhase[last_jx][last_jy] - 1;
3326 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3329 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3332 /* ----------------------------------------------------------------------- */
3333 /* draw elements the player is just walking/passing through/under */
3334 /* ----------------------------------------------------------------------- */
3336 if (player_is_moving)
3338 /* handle the field the player is leaving ... */
3339 if (IS_ACCESSIBLE_INSIDE(last_element))
3340 DrawLevelField(last_jx, last_jy);
3341 else if (IS_ACCESSIBLE_UNDER(last_element))
3342 DrawLevelFieldThruMask(last_jx, last_jy);
3345 /* do not redraw accessible elements if the player is just pushing them */
3346 if (!player_is_moving || !player->is_pushing)
3348 /* ... and the field the player is entering */
3349 if (IS_ACCESSIBLE_INSIDE(element))
3350 DrawLevelField(jx, jy);
3351 else if (IS_ACCESSIBLE_UNDER(element))
3352 DrawLevelFieldThruMask(jx, jy);
3355 MarkTileDirty(sx, sy);
3358 /* ------------------------------------------------------------------------- */
3360 void WaitForEventToContinue()
3362 boolean still_wait = TRUE;
3364 /* simulate releasing mouse button over last gadget, if still pressed */
3366 HandleGadgets(-1, -1, 0);
3368 button_status = MB_RELEASED;
3382 case EVENT_BUTTONPRESS:
3383 case EVENT_KEYPRESS:
3387 case EVENT_KEYRELEASE:
3388 ClearPlayerAction();
3392 HandleOtherEvents(&event);
3396 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3403 /* don't eat all CPU time */
3408 #define MAX_REQUEST_LINES 13
3409 #define MAX_REQUEST_LINE_FONT1_LEN 7
3410 #define MAX_REQUEST_LINE_FONT2_LEN 10
3412 static int RequestHandleEvents(unsigned int req_state)
3414 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3415 local_player->LevelSolved_GameEnd);
3416 int width = request.width;
3417 int height = request.height;
3421 setRequestPosition(&sx, &sy, FALSE);
3423 button_status = MB_RELEASED;
3425 request_gadget_id = -1;
3432 SetDrawtoField(DRAW_FIELDBUFFER);
3434 HandleGameActions();
3436 SetDrawtoField(DRAW_BACKBUFFER);
3438 if (global.use_envelope_request)
3440 /* copy current state of request area to middle of playfield area */
3441 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3449 while (NextValidEvent(&event))
3453 case EVENT_BUTTONPRESS:
3454 case EVENT_BUTTONRELEASE:
3455 case EVENT_MOTIONNOTIFY:
3459 if (event.type == EVENT_MOTIONNOTIFY)
3464 motion_status = TRUE;
3465 mx = ((MotionEvent *) &event)->x;
3466 my = ((MotionEvent *) &event)->y;
3470 motion_status = FALSE;
3471 mx = ((ButtonEvent *) &event)->x;
3472 my = ((ButtonEvent *) &event)->y;
3473 if (event.type == EVENT_BUTTONPRESS)
3474 button_status = ((ButtonEvent *) &event)->button;
3476 button_status = MB_RELEASED;
3479 /* this sets 'request_gadget_id' */
3480 HandleGadgets(mx, my, button_status);
3482 switch (request_gadget_id)
3484 case TOOL_CTRL_ID_YES:
3487 case TOOL_CTRL_ID_NO:
3490 case TOOL_CTRL_ID_CONFIRM:
3491 result = TRUE | FALSE;
3494 case TOOL_CTRL_ID_PLAYER_1:
3497 case TOOL_CTRL_ID_PLAYER_2:
3500 case TOOL_CTRL_ID_PLAYER_3:
3503 case TOOL_CTRL_ID_PLAYER_4:
3514 case EVENT_KEYPRESS:
3515 switch (GetEventKey((KeyEvent *)&event, TRUE))
3518 if (req_state & REQ_CONFIRM)
3523 #if defined(TARGET_SDL2)
3530 #if defined(TARGET_SDL2)
3540 if (req_state & REQ_PLAYER)
3544 case EVENT_KEYRELEASE:
3545 ClearPlayerAction();
3549 HandleOtherEvents(&event);
3554 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3556 int joy = AnyJoystick();
3558 if (joy & JOY_BUTTON_1)
3560 else if (joy & JOY_BUTTON_2)
3566 if (global.use_envelope_request)
3568 /* copy back current state of pressed buttons inside request area */
3569 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3576 if (!PendingEvent()) /* delay only if no pending events */
3586 static boolean RequestDoor(char *text, unsigned int req_state)
3588 unsigned int old_door_state;
3589 int last_game_status = game_status; /* save current game status */
3590 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3591 int font_nr = FONT_TEXT_2;
3596 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3598 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3599 font_nr = FONT_TEXT_1;
3602 if (game_status == GAME_MODE_PLAYING)
3603 BlitScreenToBitmap(backbuffer);
3605 /* disable deactivated drawing when quick-loading level tape recording */
3606 if (tape.playing && tape.deactivate_display)
3607 TapeDeactivateDisplayOff(TRUE);
3609 SetMouseCursor(CURSOR_DEFAULT);
3611 #if defined(NETWORK_AVALIABLE)
3612 /* pause network game while waiting for request to answer */
3613 if (options.network &&
3614 game_status == GAME_MODE_PLAYING &&
3615 req_state & REQUEST_WAIT_FOR_INPUT)
3616 SendToServer_PausePlaying();
3619 old_door_state = GetDoorState();
3621 /* simulate releasing mouse button over last gadget, if still pressed */
3623 HandleGadgets(-1, -1, 0);
3627 /* draw released gadget before proceeding */
3630 if (old_door_state & DOOR_OPEN_1)
3632 CloseDoor(DOOR_CLOSE_1);
3634 /* save old door content */
3635 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3636 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3639 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3640 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3642 /* clear door drawing field */
3643 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3645 /* force DOOR font inside door area */
3646 game_status = GAME_MODE_PSEUDO_DOOR;
3648 /* write text for request */
3649 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3651 char text_line[max_request_line_len + 1];
3657 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3659 tc = *(text_ptr + tx);
3660 // if (!tc || tc == ' ')
3661 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3665 if ((tc == '?' || tc == '!') && tl == 0)
3675 strncpy(text_line, text_ptr, tl);
3678 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3679 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3680 text_line, font_nr);
3682 text_ptr += tl + (tc == ' ' ? 1 : 0);
3683 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3686 game_status = last_game_status; /* restore current game status */
3688 if (req_state & REQ_ASK)
3690 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3691 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3693 else if (req_state & REQ_CONFIRM)
3695 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3697 else if (req_state & REQ_PLAYER)
3699 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3700 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3701 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3702 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3705 /* copy request gadgets to door backbuffer */
3706 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3708 OpenDoor(DOOR_OPEN_1);
3710 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3712 if (game_status == GAME_MODE_PLAYING)
3714 SetPanelBackground();
3715 SetDrawBackgroundMask(REDRAW_DOOR_1);
3719 SetDrawBackgroundMask(REDRAW_FIELD);
3725 if (game_status != GAME_MODE_MAIN)
3728 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3730 // ---------- handle request buttons ----------
3731 result = RequestHandleEvents(req_state);
3733 if (game_status != GAME_MODE_MAIN)
3738 if (!(req_state & REQ_STAY_OPEN))
3740 CloseDoor(DOOR_CLOSE_1);
3742 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3743 (req_state & REQ_REOPEN))
3744 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3749 if (game_status == GAME_MODE_PLAYING)
3751 SetPanelBackground();
3752 SetDrawBackgroundMask(REDRAW_DOOR_1);
3756 SetDrawBackgroundMask(REDRAW_FIELD);
3759 #if defined(NETWORK_AVALIABLE)
3760 /* continue network game after request */
3761 if (options.network &&
3762 game_status == GAME_MODE_PLAYING &&
3763 req_state & REQUEST_WAIT_FOR_INPUT)
3764 SendToServer_ContinuePlaying();
3767 /* restore deactivated drawing when quick-loading level tape recording */
3768 if (tape.playing && tape.deactivate_display)
3769 TapeDeactivateDisplayOn();
3774 static boolean RequestEnvelope(char *text, unsigned int req_state)
3778 if (game_status == GAME_MODE_PLAYING)
3779 BlitScreenToBitmap(backbuffer);
3781 /* disable deactivated drawing when quick-loading level tape recording */
3782 if (tape.playing && tape.deactivate_display)
3783 TapeDeactivateDisplayOff(TRUE);
3785 SetMouseCursor(CURSOR_DEFAULT);
3787 #if defined(NETWORK_AVALIABLE)
3788 /* pause network game while waiting for request to answer */
3789 if (options.network &&
3790 game_status == GAME_MODE_PLAYING &&
3791 req_state & REQUEST_WAIT_FOR_INPUT)
3792 SendToServer_PausePlaying();
3795 /* simulate releasing mouse button over last gadget, if still pressed */
3797 HandleGadgets(-1, -1, 0);
3801 // (replace with setting corresponding request background)
3802 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3803 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3805 /* clear door drawing field */
3806 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3808 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3810 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3812 if (game_status == GAME_MODE_PLAYING)
3814 SetPanelBackground();
3815 SetDrawBackgroundMask(REDRAW_DOOR_1);
3819 SetDrawBackgroundMask(REDRAW_FIELD);
3825 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3827 // ---------- handle request buttons ----------
3828 result = RequestHandleEvents(req_state);
3830 if (game_status != GAME_MODE_MAIN)
3835 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3839 if (game_status == GAME_MODE_PLAYING)
3841 SetPanelBackground();
3842 SetDrawBackgroundMask(REDRAW_DOOR_1);
3846 SetDrawBackgroundMask(REDRAW_FIELD);
3849 #if defined(NETWORK_AVALIABLE)
3850 /* continue network game after request */
3851 if (options.network &&
3852 game_status == GAME_MODE_PLAYING &&
3853 req_state & REQUEST_WAIT_FOR_INPUT)
3854 SendToServer_ContinuePlaying();
3857 /* restore deactivated drawing when quick-loading level tape recording */
3858 if (tape.playing && tape.deactivate_display)
3859 TapeDeactivateDisplayOn();
3864 boolean Request(char *text, unsigned int req_state)
3866 if (global.use_envelope_request)
3867 return RequestEnvelope(text, req_state);
3869 return RequestDoor(text, req_state);
3872 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3874 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3875 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3878 if (dpo1->sort_priority != dpo2->sort_priority)
3879 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3881 compare_result = dpo1->nr - dpo2->nr;
3883 return compare_result;
3886 void InitGraphicCompatibilityInfo_Doors()
3892 struct DoorInfo *door;
3896 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3897 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3899 { -1, -1, -1, NULL }
3901 struct Rect door_rect_list[] =
3903 { DX, DY, DXSIZE, DYSIZE },
3904 { VX, VY, VXSIZE, VYSIZE }
3908 for (i = 0; doors[i].door_token != -1; i++)
3910 int door_token = doors[i].door_token;
3911 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3912 int part_1 = doors[i].part_1;
3913 int part_8 = doors[i].part_8;
3914 int part_2 = part_1 + 1;
3915 int part_3 = part_1 + 2;
3916 struct DoorInfo *door = doors[i].door;
3917 struct Rect *door_rect = &door_rect_list[door_index];
3918 boolean door_gfx_redefined = FALSE;
3920 /* check if any door part graphic definitions have been redefined */
3922 for (j = 0; door_part_controls[j].door_token != -1; j++)
3924 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3925 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3927 if (dpc->door_token == door_token && fi->redefined)
3928 door_gfx_redefined = TRUE;
3931 /* check for old-style door graphic/animation modifications */
3933 if (!door_gfx_redefined)
3935 if (door->anim_mode & ANIM_STATIC_PANEL)
3937 door->panel.step_xoffset = 0;
3938 door->panel.step_yoffset = 0;
3941 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3943 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3944 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3945 int num_door_steps, num_panel_steps;
3947 /* remove door part graphics other than the two default wings */
3949 for (j = 0; door_part_controls[j].door_token != -1; j++)
3951 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3952 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3954 if (dpc->graphic >= part_3 &&
3955 dpc->graphic <= part_8)
3959 /* set graphics and screen positions of the default wings */
3961 g_part_1->width = door_rect->width;
3962 g_part_1->height = door_rect->height;
3963 g_part_2->width = door_rect->width;
3964 g_part_2->height = door_rect->height;
3965 g_part_2->src_x = door_rect->width;
3966 g_part_2->src_y = g_part_1->src_y;
3968 door->part_2.x = door->part_1.x;
3969 door->part_2.y = door->part_1.y;
3971 if (door->width != -1)
3973 g_part_1->width = door->width;
3974 g_part_2->width = door->width;
3976 // special treatment for graphics and screen position of right wing
3977 g_part_2->src_x += door_rect->width - door->width;
3978 door->part_2.x += door_rect->width - door->width;
3981 if (door->height != -1)
3983 g_part_1->height = door->height;
3984 g_part_2->height = door->height;
3986 // special treatment for graphics and screen position of bottom wing
3987 g_part_2->src_y += door_rect->height - door->height;
3988 door->part_2.y += door_rect->height - door->height;
3991 /* set animation delays for the default wings and panels */
3993 door->part_1.step_delay = door->step_delay;
3994 door->part_2.step_delay = door->step_delay;
3995 door->panel.step_delay = door->step_delay;
3997 /* set animation draw order for the default wings */
3999 door->part_1.sort_priority = 2; /* draw left wing over ... */
4000 door->part_2.sort_priority = 1; /* ... right wing */
4002 /* set animation draw offset for the default wings */
4004 if (door->anim_mode & ANIM_HORIZONTAL)
4006 door->part_1.step_xoffset = door->step_offset;
4007 door->part_1.step_yoffset = 0;
4008 door->part_2.step_xoffset = door->step_offset * -1;
4009 door->part_2.step_yoffset = 0;
4011 num_door_steps = g_part_1->width / door->step_offset;
4013 else // ANIM_VERTICAL
4015 door->part_1.step_xoffset = 0;
4016 door->part_1.step_yoffset = door->step_offset;
4017 door->part_2.step_xoffset = 0;
4018 door->part_2.step_yoffset = door->step_offset * -1;
4020 num_door_steps = g_part_1->height / door->step_offset;
4023 /* set animation draw offset for the default panels */
4025 if (door->step_offset > 1)
4027 num_panel_steps = 2 * door_rect->height / door->step_offset;
4028 door->panel.start_step = num_panel_steps - num_door_steps;
4029 door->panel.start_step_closing = door->panel.start_step;
4033 num_panel_steps = door_rect->height / door->step_offset;
4034 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4035 door->panel.start_step_closing = door->panel.start_step;
4036 door->panel.step_delay *= 2;
4047 for (i = 0; door_part_controls[i].door_token != -1; i++)
4049 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4050 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4052 /* initialize "start_step_opening" and "start_step_closing", if needed */
4053 if (dpc->pos->start_step_opening == 0 &&
4054 dpc->pos->start_step_closing == 0)
4056 // dpc->pos->start_step_opening = dpc->pos->start_step;
4057 dpc->pos->start_step_closing = dpc->pos->start_step;
4060 /* fill structure for door part draw order (sorted below) */
4062 dpo->sort_priority = dpc->pos->sort_priority;
4065 /* sort door part controls according to sort_priority and graphic number */
4066 qsort(door_part_order, MAX_DOOR_PARTS,
4067 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4070 unsigned int OpenDoor(unsigned int door_state)
4072 if (door_state & DOOR_COPY_BACK)
4074 if (door_state & DOOR_OPEN_1)
4075 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4076 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4078 if (door_state & DOOR_OPEN_2)
4079 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4080 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4082 door_state &= ~DOOR_COPY_BACK;
4085 return MoveDoor(door_state);
4088 unsigned int CloseDoor(unsigned int door_state)
4090 unsigned int old_door_state = GetDoorState();
4092 if (!(door_state & DOOR_NO_COPY_BACK))
4094 if (old_door_state & DOOR_OPEN_1)
4095 BlitBitmap(backbuffer, bitmap_db_door_1,
4096 DX, DY, DXSIZE, DYSIZE, 0, 0);
4098 if (old_door_state & DOOR_OPEN_2)
4099 BlitBitmap(backbuffer, bitmap_db_door_2,
4100 VX, VY, VXSIZE, VYSIZE, 0, 0);
4102 door_state &= ~DOOR_NO_COPY_BACK;
4105 return MoveDoor(door_state);
4108 unsigned int GetDoorState()
4110 return MoveDoor(DOOR_GET_STATE);
4113 unsigned int SetDoorState(unsigned int door_state)
4115 return MoveDoor(door_state | DOOR_SET_STATE);
4118 int euclid(int a, int b)
4120 return (b ? euclid(b, a % b) : a);
4123 unsigned int MoveDoor(unsigned int door_state)
4125 struct Rect door_rect_list[] =
4127 { DX, DY, DXSIZE, DYSIZE },
4128 { VX, VY, VXSIZE, VYSIZE }
4130 static int door1 = DOOR_OPEN_1;
4131 static int door2 = DOOR_CLOSE_2;
4132 unsigned int door_delay = 0;
4133 unsigned int door_delay_value;
4136 if (door_state == DOOR_GET_STATE)
4137 return (door1 | door2);
4139 if (door_state & DOOR_SET_STATE)
4141 if (door_state & DOOR_ACTION_1)
4142 door1 = door_state & DOOR_ACTION_1;
4143 if (door_state & DOOR_ACTION_2)
4144 door2 = door_state & DOOR_ACTION_2;
4146 return (door1 | door2);
4149 if (!(door_state & DOOR_FORCE_REDRAW))
4151 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4152 door_state &= ~DOOR_OPEN_1;
4153 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4154 door_state &= ~DOOR_CLOSE_1;
4155 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4156 door_state &= ~DOOR_OPEN_2;
4157 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4158 door_state &= ~DOOR_CLOSE_2;
4161 if (global.autoplay_leveldir)
4163 door_state |= DOOR_NO_DELAY;
4164 door_state &= ~DOOR_CLOSE_ALL;
4167 if (game_status == GAME_MODE_EDITOR)
4168 door_state |= DOOR_NO_DELAY;
4170 if (door_state & DOOR_ACTION)
4172 boolean door_panel_drawn[NUM_DOORS];
4173 boolean panel_has_doors[NUM_DOORS];
4174 boolean door_part_skip[MAX_DOOR_PARTS];
4175 boolean door_part_done[MAX_DOOR_PARTS];
4176 boolean door_part_done_all;
4177 int num_steps[MAX_DOOR_PARTS];
4178 int max_move_delay = 0; // delay for complete animations of all doors
4179 int max_step_delay = 0; // delay (ms) between two animation frames
4180 int num_move_steps = 0; // number of animation steps for all doors
4181 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4182 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4183 int current_move_delay = 0;
4187 for (i = 0; i < NUM_DOORS; i++)
4188 panel_has_doors[i] = FALSE;
4190 for (i = 0; i < MAX_DOOR_PARTS; i++)
4192 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4193 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4194 int door_token = dpc->door_token;
4196 door_part_done[i] = FALSE;
4197 door_part_skip[i] = (!(door_state & door_token) ||
4201 for (i = 0; i < MAX_DOOR_PARTS; i++)
4203 int nr = door_part_order[i].nr;
4204 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4205 struct DoorPartPosInfo *pos = dpc->pos;
4206 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4207 int door_token = dpc->door_token;
4208 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4209 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4210 int step_xoffset = ABS(pos->step_xoffset);
4211 int step_yoffset = ABS(pos->step_yoffset);
4212 int step_delay = pos->step_delay;
4213 int current_door_state = door_state & door_token;
4214 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4215 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4216 boolean part_opening = (is_panel ? door_closing : door_opening);
4217 int start_step = (part_opening ? pos->start_step_opening :
4218 pos->start_step_closing);
4219 float move_xsize = (step_xoffset ? g->width : 0);
4220 float move_ysize = (step_yoffset ? g->height : 0);
4221 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4222 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4223 int move_steps = (move_xsteps && move_ysteps ?
4224 MIN(move_xsteps, move_ysteps) :
4225 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4226 int move_delay = move_steps * step_delay;
4228 if (door_part_skip[nr])
4231 max_move_delay = MAX(max_move_delay, move_delay);
4232 max_step_delay = (max_step_delay == 0 ? step_delay :
4233 euclid(max_step_delay, step_delay));
4234 num_steps[nr] = move_steps;
4238 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4240 panel_has_doors[door_index] = TRUE;
4244 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4246 num_move_steps = max_move_delay / max_step_delay;
4247 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4249 door_delay_value = max_step_delay;
4251 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4253 start = num_move_steps - 1;
4257 /* opening door sound has priority over simultaneously closing door */
4258 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4259 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4260 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4261 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4264 for (k = start; k < num_move_steps; k++)
4266 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4268 door_part_done_all = TRUE;
4270 for (i = 0; i < NUM_DOORS; i++)
4271 door_panel_drawn[i] = FALSE;
4273 for (i = 0; i < MAX_DOOR_PARTS; i++)
4275 int nr = door_part_order[i].nr;
4276 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4277 struct DoorPartPosInfo *pos = dpc->pos;
4278 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4279 int door_token = dpc->door_token;
4280 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4281 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4282 boolean is_panel_and_door_has_closed = FALSE;
4283 struct Rect *door_rect = &door_rect_list[door_index];
4284 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4286 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4287 int current_door_state = door_state & door_token;
4288 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4289 boolean door_closing = !door_opening;
4290 boolean part_opening = (is_panel ? door_closing : door_opening);
4291 boolean part_closing = !part_opening;
4292 int start_step = (part_opening ? pos->start_step_opening :
4293 pos->start_step_closing);
4294 int step_delay = pos->step_delay;
4295 int step_factor = step_delay / max_step_delay;
4296 int k1 = (step_factor ? k / step_factor + 1 : k);
4297 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4298 int kk = MAX(0, k2);
4301 int src_x, src_y, src_xx, src_yy;
4302 int dst_x, dst_y, dst_xx, dst_yy;
4305 if (door_part_skip[nr])
4308 if (!(door_state & door_token))
4316 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4317 int kk_door = MAX(0, k2_door);
4318 int sync_frame = kk_door * door_delay_value;
4319 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4321 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4326 if (!door_panel_drawn[door_index])
4328 ClearRectangle(drawto, door_rect->x, door_rect->y,
4329 door_rect->width, door_rect->height);
4331 door_panel_drawn[door_index] = TRUE;
4334 // draw opening or closing door parts
4336 if (pos->step_xoffset < 0) // door part on right side
4339 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4342 if (dst_xx + width > door_rect->width)
4343 width = door_rect->width - dst_xx;
4345 else // door part on left side
4348 dst_xx = pos->x - kk * pos->step_xoffset;
4352 src_xx = ABS(dst_xx);
4356 width = g->width - src_xx;
4358 if (width > door_rect->width)
4359 width = door_rect->width;
4361 // printf("::: k == %d [%d] \n", k, start_step);
4364 if (pos->step_yoffset < 0) // door part on bottom side
4367 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4370 if (dst_yy + height > door_rect->height)
4371 height = door_rect->height - dst_yy;
4373 else // door part on top side
4376 dst_yy = pos->y - kk * pos->step_yoffset;
4380 src_yy = ABS(dst_yy);
4384 height = g->height - src_yy;
4387 src_x = g_src_x + src_xx;
4388 src_y = g_src_y + src_yy;
4390 dst_x = door_rect->x + dst_xx;
4391 dst_y = door_rect->y + dst_yy;
4393 is_panel_and_door_has_closed =
4396 panel_has_doors[door_index] &&
4397 k >= num_move_steps_doors_only - 1);
4399 if (width >= 0 && width <= g->width &&
4400 height >= 0 && height <= g->height &&
4401 !is_panel_and_door_has_closed)
4403 if (is_panel || !pos->draw_masked)
4404 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4407 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4411 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4413 if ((part_opening && (width < 0 || height < 0)) ||
4414 (part_closing && (width >= g->width && height >= g->height)))
4415 door_part_done[nr] = TRUE;
4417 // continue door part animations, but not panel after door has closed
4418 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4419 door_part_done_all = FALSE;
4422 if (!(door_state & DOOR_NO_DELAY))
4426 if (game_status == GAME_MODE_MAIN)
4429 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4431 current_move_delay += max_step_delay;
4434 if (door_part_done_all)
4439 if (door_state & DOOR_ACTION_1)
4440 door1 = door_state & DOOR_ACTION_1;
4441 if (door_state & DOOR_ACTION_2)
4442 door2 = door_state & DOOR_ACTION_2;
4444 return (door1 | door2);
4447 static boolean useSpecialEditorDoor()
4449 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4450 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4452 // do not draw special editor door if editor border defined or redefined
4453 if (graphic_info[graphic].bitmap != NULL || redefined)
4456 // do not draw special editor door if global border defined to be empty
4457 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4460 // do not draw special editor door if viewport definitions do not match
4464 EY + EYSIZE != VY + VYSIZE)
4470 void DrawSpecialEditorDoor()
4472 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4473 int top_border_width = gfx1->width;
4474 int top_border_height = gfx1->height;
4475 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4476 int ex = EX - outer_border;
4477 int ey = EY - outer_border;
4478 int vy = VY - outer_border;
4479 int exsize = EXSIZE + 2 * outer_border;
4481 if (!useSpecialEditorDoor())
4484 /* draw bigger level editor toolbox window */
4485 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4486 top_border_width, top_border_height, ex, ey - top_border_height);
4487 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4488 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4490 redraw_mask |= REDRAW_ALL;
4493 void UndrawSpecialEditorDoor()
4495 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4496 int top_border_width = gfx1->width;
4497 int top_border_height = gfx1->height;
4498 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4499 int ex = EX - outer_border;
4500 int ey = EY - outer_border;
4501 int ey_top = ey - top_border_height;
4502 int exsize = EXSIZE + 2 * outer_border;
4503 int eysize = EYSIZE + 2 * outer_border;
4505 if (!useSpecialEditorDoor())
4508 /* draw normal tape recorder window */
4509 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4511 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4512 ex, ey_top, top_border_width, top_border_height,
4514 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4515 ex, ey, exsize, eysize, ex, ey);
4519 // if screen background is set to "[NONE]", clear editor toolbox window
4520 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4521 ClearRectangle(drawto, ex, ey, exsize, eysize);
4524 redraw_mask |= REDRAW_ALL;
4528 /* ---------- new tool button stuff ---------------------------------------- */
4533 struct TextPosInfo *pos;
4536 } toolbutton_info[NUM_TOOL_BUTTONS] =
4539 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4540 TOOL_CTRL_ID_YES, "yes"
4543 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4544 TOOL_CTRL_ID_NO, "no"
4547 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4548 TOOL_CTRL_ID_CONFIRM, "confirm"
4551 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4552 TOOL_CTRL_ID_PLAYER_1, "player 1"
4555 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4556 TOOL_CTRL_ID_PLAYER_2, "player 2"
4559 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4560 TOOL_CTRL_ID_PLAYER_3, "player 3"
4563 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4564 TOOL_CTRL_ID_PLAYER_4, "player 4"
4568 void CreateToolButtons()
4572 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4574 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4575 struct TextPosInfo *pos = toolbutton_info[i].pos;
4576 struct GadgetInfo *gi;
4577 Bitmap *deco_bitmap = None;
4578 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4579 unsigned int event_mask = GD_EVENT_RELEASED;
4582 int gd_x = gfx->src_x;
4583 int gd_y = gfx->src_y;
4584 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4585 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4588 if (global.use_envelope_request)
4589 setRequestPosition(&dx, &dy, TRUE);
4591 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4593 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4595 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4596 pos->size, &deco_bitmap, &deco_x, &deco_y);
4597 deco_xpos = (gfx->width - pos->size) / 2;
4598 deco_ypos = (gfx->height - pos->size) / 2;
4601 gi = CreateGadget(GDI_CUSTOM_ID, id,
4602 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4603 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4604 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4605 GDI_WIDTH, gfx->width,
4606 GDI_HEIGHT, gfx->height,
4607 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4608 GDI_STATE, GD_BUTTON_UNPRESSED,
4609 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4610 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4611 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4612 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4613 GDI_DECORATION_SIZE, pos->size, pos->size,
4614 GDI_DECORATION_SHIFTING, 1, 1,
4615 GDI_DIRECT_DRAW, FALSE,
4616 GDI_EVENT_MASK, event_mask,
4617 GDI_CALLBACK_ACTION, HandleToolButtons,
4621 Error(ERR_EXIT, "cannot create gadget");
4623 tool_gadget[id] = gi;
4627 void FreeToolButtons()
4631 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4632 FreeGadget(tool_gadget[i]);
4635 static void UnmapToolButtons()
4639 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4640 UnmapGadget(tool_gadget[i]);
4643 static void HandleToolButtons(struct GadgetInfo *gi)
4645 request_gadget_id = gi->custom_id;
4648 static struct Mapping_EM_to_RND_object
4651 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4652 boolean is_backside; /* backside of moving element */
4658 em_object_mapping_list[] =
4661 Xblank, TRUE, FALSE,
4665 Yacid_splash_eB, FALSE, FALSE,
4666 EL_ACID_SPLASH_RIGHT, -1, -1
4669 Yacid_splash_wB, FALSE, FALSE,
4670 EL_ACID_SPLASH_LEFT, -1, -1
4673 #ifdef EM_ENGINE_BAD_ROLL
4675 Xstone_force_e, FALSE, FALSE,
4676 EL_ROCK, -1, MV_BIT_RIGHT
4679 Xstone_force_w, FALSE, FALSE,
4680 EL_ROCK, -1, MV_BIT_LEFT
4683 Xnut_force_e, FALSE, FALSE,
4684 EL_NUT, -1, MV_BIT_RIGHT
4687 Xnut_force_w, FALSE, FALSE,
4688 EL_NUT, -1, MV_BIT_LEFT
4691 Xspring_force_e, FALSE, FALSE,
4692 EL_SPRING, -1, MV_BIT_RIGHT
4695 Xspring_force_w, FALSE, FALSE,
4696 EL_SPRING, -1, MV_BIT_LEFT
4699 Xemerald_force_e, FALSE, FALSE,
4700 EL_EMERALD, -1, MV_BIT_RIGHT
4703 Xemerald_force_w, FALSE, FALSE,
4704 EL_EMERALD, -1, MV_BIT_LEFT
4707 Xdiamond_force_e, FALSE, FALSE,
4708 EL_DIAMOND, -1, MV_BIT_RIGHT
4711 Xdiamond_force_w, FALSE, FALSE,
4712 EL_DIAMOND, -1, MV_BIT_LEFT
4715 Xbomb_force_e, FALSE, FALSE,
4716 EL_BOMB, -1, MV_BIT_RIGHT
4719 Xbomb_force_w, FALSE, FALSE,
4720 EL_BOMB, -1, MV_BIT_LEFT
4722 #endif /* EM_ENGINE_BAD_ROLL */
4725 Xstone, TRUE, FALSE,
4729 Xstone_pause, FALSE, FALSE,
4733 Xstone_fall, FALSE, FALSE,
4737 Ystone_s, FALSE, FALSE,
4738 EL_ROCK, ACTION_FALLING, -1
4741 Ystone_sB, FALSE, TRUE,
4742 EL_ROCK, ACTION_FALLING, -1
4745 Ystone_e, FALSE, FALSE,
4746 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4749 Ystone_eB, FALSE, TRUE,
4750 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4753 Ystone_w, FALSE, FALSE,
4754 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4757 Ystone_wB, FALSE, TRUE,
4758 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4765 Xnut_pause, FALSE, FALSE,
4769 Xnut_fall, FALSE, FALSE,
4773 Ynut_s, FALSE, FALSE,
4774 EL_NUT, ACTION_FALLING, -1
4777 Ynut_sB, FALSE, TRUE,
4778 EL_NUT, ACTION_FALLING, -1
4781 Ynut_e, FALSE, FALSE,
4782 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4785 Ynut_eB, FALSE, TRUE,
4786 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4789 Ynut_w, FALSE, FALSE,
4790 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4793 Ynut_wB, FALSE, TRUE,
4794 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4797 Xbug_n, TRUE, FALSE,
4801 Xbug_e, TRUE, FALSE,
4802 EL_BUG_RIGHT, -1, -1
4805 Xbug_s, TRUE, FALSE,
4809 Xbug_w, TRUE, FALSE,
4813 Xbug_gon, FALSE, FALSE,
4817 Xbug_goe, FALSE, FALSE,
4818 EL_BUG_RIGHT, -1, -1
4821 Xbug_gos, FALSE, FALSE,
4825 Xbug_gow, FALSE, FALSE,
4829 Ybug_n, FALSE, FALSE,
4830 EL_BUG, ACTION_MOVING, MV_BIT_UP
4833 Ybug_nB, FALSE, TRUE,
4834 EL_BUG, ACTION_MOVING, MV_BIT_UP
4837 Ybug_e, FALSE, FALSE,
4838 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4841 Ybug_eB, FALSE, TRUE,
4842 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4845 Ybug_s, FALSE, FALSE,
4846 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4849 Ybug_sB, FALSE, TRUE,
4850 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4853 Ybug_w, FALSE, FALSE,
4854 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4857 Ybug_wB, FALSE, TRUE,
4858 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4861 Ybug_w_n, FALSE, FALSE,
4862 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4865 Ybug_n_e, FALSE, FALSE,
4866 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4869 Ybug_e_s, FALSE, FALSE,
4870 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4873 Ybug_s_w, FALSE, FALSE,
4874 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4877 Ybug_e_n, FALSE, FALSE,
4878 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4881 Ybug_s_e, FALSE, FALSE,
4882 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4885 Ybug_w_s, FALSE, FALSE,
4886 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4889 Ybug_n_w, FALSE, FALSE,
4890 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4893 Ybug_stone, FALSE, FALSE,
4894 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4897 Ybug_spring, FALSE, FALSE,
4898 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4901 Xtank_n, TRUE, FALSE,
4902 EL_SPACESHIP_UP, -1, -1
4905 Xtank_e, TRUE, FALSE,
4906 EL_SPACESHIP_RIGHT, -1, -1
4909 Xtank_s, TRUE, FALSE,
4910 EL_SPACESHIP_DOWN, -1, -1
4913 Xtank_w, TRUE, FALSE,
4914 EL_SPACESHIP_LEFT, -1, -1
4917 Xtank_gon, FALSE, FALSE,
4918 EL_SPACESHIP_UP, -1, -1
4921 Xtank_goe, FALSE, FALSE,
4922 EL_SPACESHIP_RIGHT, -1, -1
4925 Xtank_gos, FALSE, FALSE,
4926 EL_SPACESHIP_DOWN, -1, -1
4929 Xtank_gow, FALSE, FALSE,
4930 EL_SPACESHIP_LEFT, -1, -1
4933 Ytank_n, FALSE, FALSE,
4934 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4937 Ytank_nB, FALSE, TRUE,
4938 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4941 Ytank_e, FALSE, FALSE,
4942 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4945 Ytank_eB, FALSE, TRUE,
4946 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4949 Ytank_s, FALSE, FALSE,
4950 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4953 Ytank_sB, FALSE, TRUE,
4954 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4957 Ytank_w, FALSE, FALSE,
4958 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4961 Ytank_wB, FALSE, TRUE,
4962 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4965 Ytank_w_n, FALSE, FALSE,
4966 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4969 Ytank_n_e, FALSE, FALSE,
4970 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4973 Ytank_e_s, FALSE, FALSE,
4974 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4977 Ytank_s_w, FALSE, FALSE,
4978 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4981 Ytank_e_n, FALSE, FALSE,
4982 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4985 Ytank_s_e, FALSE, FALSE,
4986 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4989 Ytank_w_s, FALSE, FALSE,
4990 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4993 Ytank_n_w, FALSE, FALSE,
4994 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4997 Ytank_stone, FALSE, FALSE,
4998 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5001 Ytank_spring, FALSE, FALSE,
5002 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5005 Xandroid, TRUE, FALSE,
5006 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5009 Xandroid_1_n, FALSE, FALSE,
5010 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5013 Xandroid_2_n, FALSE, FALSE,
5014 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5017 Xandroid_1_e, FALSE, FALSE,
5018 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5021 Xandroid_2_e, FALSE, FALSE,
5022 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5025 Xandroid_1_w, FALSE, FALSE,
5026 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5029 Xandroid_2_w, FALSE, FALSE,
5030 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5033 Xandroid_1_s, FALSE, FALSE,
5034 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5037 Xandroid_2_s, FALSE, FALSE,
5038 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5041 Yandroid_n, FALSE, FALSE,
5042 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5045 Yandroid_nB, FALSE, TRUE,
5046 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5049 Yandroid_ne, FALSE, FALSE,
5050 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5053 Yandroid_neB, FALSE, TRUE,
5054 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5057 Yandroid_e, FALSE, FALSE,
5058 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5061 Yandroid_eB, FALSE, TRUE,
5062 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5065 Yandroid_se, FALSE, FALSE,
5066 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5069 Yandroid_seB, FALSE, TRUE,
5070 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5073 Yandroid_s, FALSE, FALSE,
5074 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5077 Yandroid_sB, FALSE, TRUE,
5078 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5081 Yandroid_sw, FALSE, FALSE,
5082 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5085 Yandroid_swB, FALSE, TRUE,
5086 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5089 Yandroid_w, FALSE, FALSE,
5090 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5093 Yandroid_wB, FALSE, TRUE,
5094 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5097 Yandroid_nw, FALSE, FALSE,
5098 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5101 Yandroid_nwB, FALSE, TRUE,
5102 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5105 Xspring, TRUE, FALSE,
5109 Xspring_pause, FALSE, FALSE,
5113 Xspring_e, FALSE, FALSE,
5117 Xspring_w, FALSE, FALSE,
5121 Xspring_fall, FALSE, FALSE,
5125 Yspring_s, FALSE, FALSE,
5126 EL_SPRING, ACTION_FALLING, -1
5129 Yspring_sB, FALSE, TRUE,
5130 EL_SPRING, ACTION_FALLING, -1
5133 Yspring_e, FALSE, FALSE,
5134 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5137 Yspring_eB, FALSE, TRUE,
5138 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5141 Yspring_w, FALSE, FALSE,
5142 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5145 Yspring_wB, FALSE, TRUE,
5146 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5149 Yspring_kill_e, FALSE, FALSE,
5150 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5153 Yspring_kill_eB, FALSE, TRUE,
5154 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5157 Yspring_kill_w, FALSE, FALSE,
5158 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5161 Yspring_kill_wB, FALSE, TRUE,
5162 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5165 Xeater_n, TRUE, FALSE,
5166 EL_YAMYAM_UP, -1, -1
5169 Xeater_e, TRUE, FALSE,
5170 EL_YAMYAM_RIGHT, -1, -1
5173 Xeater_w, TRUE, FALSE,
5174 EL_YAMYAM_LEFT, -1, -1
5177 Xeater_s, TRUE, FALSE,
5178 EL_YAMYAM_DOWN, -1, -1
5181 Yeater_n, FALSE, FALSE,
5182 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5185 Yeater_nB, FALSE, TRUE,
5186 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5189 Yeater_e, FALSE, FALSE,
5190 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5193 Yeater_eB, FALSE, TRUE,
5194 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5197 Yeater_s, FALSE, FALSE,
5198 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5201 Yeater_sB, FALSE, TRUE,
5202 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5205 Yeater_w, FALSE, FALSE,
5206 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5209 Yeater_wB, FALSE, TRUE,
5210 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5213 Yeater_stone, FALSE, FALSE,
5214 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5217 Yeater_spring, FALSE, FALSE,
5218 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5221 Xalien, TRUE, FALSE,
5225 Xalien_pause, FALSE, FALSE,
5229 Yalien_n, FALSE, FALSE,
5230 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5233 Yalien_nB, FALSE, TRUE,
5234 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5237 Yalien_e, FALSE, FALSE,
5238 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5241 Yalien_eB, FALSE, TRUE,
5242 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5245 Yalien_s, FALSE, FALSE,
5246 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5249 Yalien_sB, FALSE, TRUE,
5250 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5253 Yalien_w, FALSE, FALSE,
5254 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5257 Yalien_wB, FALSE, TRUE,
5258 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5261 Yalien_stone, FALSE, FALSE,
5262 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5265 Yalien_spring, FALSE, FALSE,
5266 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5269 Xemerald, TRUE, FALSE,
5273 Xemerald_pause, FALSE, FALSE,
5277 Xemerald_fall, FALSE, FALSE,
5281 Xemerald_shine, FALSE, FALSE,
5282 EL_EMERALD, ACTION_TWINKLING, -1
5285 Yemerald_s, FALSE, FALSE,
5286 EL_EMERALD, ACTION_FALLING, -1
5289 Yemerald_sB, FALSE, TRUE,
5290 EL_EMERALD, ACTION_FALLING, -1
5293 Yemerald_e, FALSE, FALSE,
5294 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5297 Yemerald_eB, FALSE, TRUE,
5298 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5301 Yemerald_w, FALSE, FALSE,
5302 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5305 Yemerald_wB, FALSE, TRUE,
5306 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5309 Yemerald_eat, FALSE, FALSE,
5310 EL_EMERALD, ACTION_COLLECTING, -1
5313 Yemerald_stone, FALSE, FALSE,
5314 EL_NUT, ACTION_BREAKING, -1
5317 Xdiamond, TRUE, FALSE,
5321 Xdiamond_pause, FALSE, FALSE,
5325 Xdiamond_fall, FALSE, FALSE,
5329 Xdiamond_shine, FALSE, FALSE,
5330 EL_DIAMOND, ACTION_TWINKLING, -1
5333 Ydiamond_s, FALSE, FALSE,
5334 EL_DIAMOND, ACTION_FALLING, -1
5337 Ydiamond_sB, FALSE, TRUE,
5338 EL_DIAMOND, ACTION_FALLING, -1
5341 Ydiamond_e, FALSE, FALSE,
5342 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5345 Ydiamond_eB, FALSE, TRUE,
5346 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5349 Ydiamond_w, FALSE, FALSE,
5350 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5353 Ydiamond_wB, FALSE, TRUE,
5354 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5357 Ydiamond_eat, FALSE, FALSE,
5358 EL_DIAMOND, ACTION_COLLECTING, -1
5361 Ydiamond_stone, FALSE, FALSE,
5362 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5365 Xdrip_fall, TRUE, FALSE,
5366 EL_AMOEBA_DROP, -1, -1
5369 Xdrip_stretch, FALSE, FALSE,
5370 EL_AMOEBA_DROP, ACTION_FALLING, -1
5373 Xdrip_stretchB, FALSE, TRUE,
5374 EL_AMOEBA_DROP, ACTION_FALLING, -1
5377 Xdrip_eat, FALSE, FALSE,
5378 EL_AMOEBA_DROP, ACTION_GROWING, -1
5381 Ydrip_s1, FALSE, FALSE,
5382 EL_AMOEBA_DROP, ACTION_FALLING, -1
5385 Ydrip_s1B, FALSE, TRUE,
5386 EL_AMOEBA_DROP, ACTION_FALLING, -1
5389 Ydrip_s2, FALSE, FALSE,
5390 EL_AMOEBA_DROP, ACTION_FALLING, -1
5393 Ydrip_s2B, FALSE, TRUE,
5394 EL_AMOEBA_DROP, ACTION_FALLING, -1
5401 Xbomb_pause, FALSE, FALSE,
5405 Xbomb_fall, FALSE, FALSE,
5409 Ybomb_s, FALSE, FALSE,
5410 EL_BOMB, ACTION_FALLING, -1
5413 Ybomb_sB, FALSE, TRUE,
5414 EL_BOMB, ACTION_FALLING, -1
5417 Ybomb_e, FALSE, FALSE,
5418 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5421 Ybomb_eB, FALSE, TRUE,
5422 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5425 Ybomb_w, FALSE, FALSE,
5426 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5429 Ybomb_wB, FALSE, TRUE,
5430 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5433 Ybomb_eat, FALSE, FALSE,
5434 EL_BOMB, ACTION_ACTIVATING, -1
5437 Xballoon, TRUE, FALSE,
5441 Yballoon_n, FALSE, FALSE,
5442 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5445 Yballoon_nB, FALSE, TRUE,
5446 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5449 Yballoon_e, FALSE, FALSE,
5450 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5453 Yballoon_eB, FALSE, TRUE,
5454 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5457 Yballoon_s, FALSE, FALSE,
5458 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5461 Yballoon_sB, FALSE, TRUE,
5462 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5465 Yballoon_w, FALSE, FALSE,
5466 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5469 Yballoon_wB, FALSE, TRUE,
5470 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5473 Xgrass, TRUE, FALSE,
5474 EL_EMC_GRASS, -1, -1
5477 Ygrass_nB, FALSE, FALSE,
5478 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5481 Ygrass_eB, FALSE, FALSE,
5482 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5485 Ygrass_sB, FALSE, FALSE,
5486 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5489 Ygrass_wB, FALSE, FALSE,
5490 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5497 Ydirt_nB, FALSE, FALSE,
5498 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5501 Ydirt_eB, FALSE, FALSE,
5502 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5505 Ydirt_sB, FALSE, FALSE,
5506 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5509 Ydirt_wB, FALSE, FALSE,
5510 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5513 Xacid_ne, TRUE, FALSE,
5514 EL_ACID_POOL_TOPRIGHT, -1, -1
5517 Xacid_se, TRUE, FALSE,
5518 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5521 Xacid_s, TRUE, FALSE,
5522 EL_ACID_POOL_BOTTOM, -1, -1
5525 Xacid_sw, TRUE, FALSE,
5526 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5529 Xacid_nw, TRUE, FALSE,
5530 EL_ACID_POOL_TOPLEFT, -1, -1
5533 Xacid_1, TRUE, FALSE,
5537 Xacid_2, FALSE, FALSE,
5541 Xacid_3, FALSE, FALSE,
5545 Xacid_4, FALSE, FALSE,
5549 Xacid_5, FALSE, FALSE,
5553 Xacid_6, FALSE, FALSE,
5557 Xacid_7, FALSE, FALSE,
5561 Xacid_8, FALSE, FALSE,
5565 Xball_1, TRUE, FALSE,
5566 EL_EMC_MAGIC_BALL, -1, -1
5569 Xball_1B, FALSE, FALSE,
5570 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5573 Xball_2, FALSE, FALSE,
5574 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5577 Xball_2B, FALSE, FALSE,
5578 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5581 Yball_eat, FALSE, FALSE,
5582 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5585 Ykey_1_eat, FALSE, FALSE,
5586 EL_EM_KEY_1, ACTION_COLLECTING, -1
5589 Ykey_2_eat, FALSE, FALSE,
5590 EL_EM_KEY_2, ACTION_COLLECTING, -1
5593 Ykey_3_eat, FALSE, FALSE,
5594 EL_EM_KEY_3, ACTION_COLLECTING, -1
5597 Ykey_4_eat, FALSE, FALSE,
5598 EL_EM_KEY_4, ACTION_COLLECTING, -1
5601 Ykey_5_eat, FALSE, FALSE,
5602 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5605 Ykey_6_eat, FALSE, FALSE,
5606 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5609 Ykey_7_eat, FALSE, FALSE,
5610 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5613 Ykey_8_eat, FALSE, FALSE,
5614 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5617 Ylenses_eat, FALSE, FALSE,
5618 EL_EMC_LENSES, ACTION_COLLECTING, -1
5621 Ymagnify_eat, FALSE, FALSE,
5622 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5625 Ygrass_eat, FALSE, FALSE,
5626 EL_EMC_GRASS, ACTION_SNAPPING, -1
5629 Ydirt_eat, FALSE, FALSE,
5630 EL_SAND, ACTION_SNAPPING, -1
5633 Xgrow_ns, TRUE, FALSE,
5634 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5637 Ygrow_ns_eat, FALSE, FALSE,
5638 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5641 Xgrow_ew, TRUE, FALSE,
5642 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5645 Ygrow_ew_eat, FALSE, FALSE,
5646 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5649 Xwonderwall, TRUE, FALSE,
5650 EL_MAGIC_WALL, -1, -1
5653 XwonderwallB, FALSE, FALSE,
5654 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5657 Xamoeba_1, TRUE, FALSE,
5658 EL_AMOEBA_DRY, ACTION_OTHER, -1
5661 Xamoeba_2, FALSE, FALSE,
5662 EL_AMOEBA_DRY, ACTION_OTHER, -1
5665 Xamoeba_3, FALSE, FALSE,
5666 EL_AMOEBA_DRY, ACTION_OTHER, -1
5669 Xamoeba_4, FALSE, FALSE,
5670 EL_AMOEBA_DRY, ACTION_OTHER, -1
5673 Xamoeba_5, TRUE, FALSE,
5674 EL_AMOEBA_WET, ACTION_OTHER, -1
5677 Xamoeba_6, FALSE, FALSE,
5678 EL_AMOEBA_WET, ACTION_OTHER, -1
5681 Xamoeba_7, FALSE, FALSE,
5682 EL_AMOEBA_WET, ACTION_OTHER, -1
5685 Xamoeba_8, FALSE, FALSE,
5686 EL_AMOEBA_WET, ACTION_OTHER, -1
5689 Xdoor_1, TRUE, FALSE,
5690 EL_EM_GATE_1, -1, -1
5693 Xdoor_2, TRUE, FALSE,
5694 EL_EM_GATE_2, -1, -1
5697 Xdoor_3, TRUE, FALSE,
5698 EL_EM_GATE_3, -1, -1
5701 Xdoor_4, TRUE, FALSE,
5702 EL_EM_GATE_4, -1, -1
5705 Xdoor_5, TRUE, FALSE,
5706 EL_EMC_GATE_5, -1, -1
5709 Xdoor_6, TRUE, FALSE,
5710 EL_EMC_GATE_6, -1, -1
5713 Xdoor_7, TRUE, FALSE,
5714 EL_EMC_GATE_7, -1, -1
5717 Xdoor_8, TRUE, FALSE,
5718 EL_EMC_GATE_8, -1, -1
5721 Xkey_1, TRUE, FALSE,
5725 Xkey_2, TRUE, FALSE,
5729 Xkey_3, TRUE, FALSE,
5733 Xkey_4, TRUE, FALSE,
5737 Xkey_5, TRUE, FALSE,
5738 EL_EMC_KEY_5, -1, -1
5741 Xkey_6, TRUE, FALSE,
5742 EL_EMC_KEY_6, -1, -1
5745 Xkey_7, TRUE, FALSE,
5746 EL_EMC_KEY_7, -1, -1
5749 Xkey_8, TRUE, FALSE,
5750 EL_EMC_KEY_8, -1, -1
5753 Xwind_n, TRUE, FALSE,
5754 EL_BALLOON_SWITCH_UP, -1, -1
5757 Xwind_e, TRUE, FALSE,
5758 EL_BALLOON_SWITCH_RIGHT, -1, -1
5761 Xwind_s, TRUE, FALSE,
5762 EL_BALLOON_SWITCH_DOWN, -1, -1
5765 Xwind_w, TRUE, FALSE,
5766 EL_BALLOON_SWITCH_LEFT, -1, -1
5769 Xwind_nesw, TRUE, FALSE,
5770 EL_BALLOON_SWITCH_ANY, -1, -1
5773 Xwind_stop, TRUE, FALSE,
5774 EL_BALLOON_SWITCH_NONE, -1, -1
5778 EL_EM_EXIT_CLOSED, -1, -1
5781 Xexit_1, TRUE, FALSE,
5782 EL_EM_EXIT_OPEN, -1, -1
5785 Xexit_2, FALSE, FALSE,
5786 EL_EM_EXIT_OPEN, -1, -1
5789 Xexit_3, FALSE, FALSE,
5790 EL_EM_EXIT_OPEN, -1, -1
5793 Xdynamite, TRUE, FALSE,
5794 EL_EM_DYNAMITE, -1, -1
5797 Ydynamite_eat, FALSE, FALSE,
5798 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5801 Xdynamite_1, TRUE, FALSE,
5802 EL_EM_DYNAMITE_ACTIVE, -1, -1
5805 Xdynamite_2, FALSE, FALSE,
5806 EL_EM_DYNAMITE_ACTIVE, -1, -1
5809 Xdynamite_3, FALSE, FALSE,
5810 EL_EM_DYNAMITE_ACTIVE, -1, -1
5813 Xdynamite_4, FALSE, FALSE,
5814 EL_EM_DYNAMITE_ACTIVE, -1, -1
5817 Xbumper, TRUE, FALSE,
5818 EL_EMC_SPRING_BUMPER, -1, -1
5821 XbumperB, FALSE, FALSE,
5822 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5825 Xwheel, TRUE, FALSE,
5826 EL_ROBOT_WHEEL, -1, -1
5829 XwheelB, FALSE, FALSE,
5830 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5833 Xswitch, TRUE, FALSE,
5834 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5837 XswitchB, FALSE, FALSE,
5838 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5842 EL_QUICKSAND_EMPTY, -1, -1
5845 Xsand_stone, TRUE, FALSE,
5846 EL_QUICKSAND_FULL, -1, -1
5849 Xsand_stonein_1, FALSE, TRUE,
5850 EL_ROCK, ACTION_FILLING, -1
5853 Xsand_stonein_2, FALSE, TRUE,
5854 EL_ROCK, ACTION_FILLING, -1
5857 Xsand_stonein_3, FALSE, TRUE,
5858 EL_ROCK, ACTION_FILLING, -1
5861 Xsand_stonein_4, FALSE, TRUE,
5862 EL_ROCK, ACTION_FILLING, -1
5865 Xsand_stonesand_1, FALSE, FALSE,
5866 EL_QUICKSAND_EMPTYING, -1, -1
5869 Xsand_stonesand_2, FALSE, FALSE,
5870 EL_QUICKSAND_EMPTYING, -1, -1
5873 Xsand_stonesand_3, FALSE, FALSE,
5874 EL_QUICKSAND_EMPTYING, -1, -1
5877 Xsand_stonesand_4, FALSE, FALSE,
5878 EL_QUICKSAND_EMPTYING, -1, -1
5881 Xsand_stonesand_quickout_1, FALSE, FALSE,
5882 EL_QUICKSAND_EMPTYING, -1, -1
5885 Xsand_stonesand_quickout_2, FALSE, FALSE,
5886 EL_QUICKSAND_EMPTYING, -1, -1
5889 Xsand_stoneout_1, FALSE, FALSE,
5890 EL_ROCK, ACTION_EMPTYING, -1
5893 Xsand_stoneout_2, FALSE, FALSE,
5894 EL_ROCK, ACTION_EMPTYING, -1
5897 Xsand_sandstone_1, FALSE, FALSE,
5898 EL_QUICKSAND_FILLING, -1, -1
5901 Xsand_sandstone_2, FALSE, FALSE,
5902 EL_QUICKSAND_FILLING, -1, -1
5905 Xsand_sandstone_3, FALSE, FALSE,
5906 EL_QUICKSAND_FILLING, -1, -1
5909 Xsand_sandstone_4, FALSE, FALSE,
5910 EL_QUICKSAND_FILLING, -1, -1
5913 Xplant, TRUE, FALSE,
5914 EL_EMC_PLANT, -1, -1
5917 Yplant, FALSE, FALSE,
5918 EL_EMC_PLANT, -1, -1
5921 Xlenses, TRUE, FALSE,
5922 EL_EMC_LENSES, -1, -1
5925 Xmagnify, TRUE, FALSE,
5926 EL_EMC_MAGNIFIER, -1, -1
5929 Xdripper, TRUE, FALSE,
5930 EL_EMC_DRIPPER, -1, -1
5933 XdripperB, FALSE, FALSE,
5934 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5937 Xfake_blank, TRUE, FALSE,
5938 EL_INVISIBLE_WALL, -1, -1
5941 Xfake_blankB, FALSE, FALSE,
5942 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5945 Xfake_grass, TRUE, FALSE,
5946 EL_EMC_FAKE_GRASS, -1, -1
5949 Xfake_grassB, FALSE, FALSE,
5950 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5953 Xfake_door_1, TRUE, FALSE,
5954 EL_EM_GATE_1_GRAY, -1, -1
5957 Xfake_door_2, TRUE, FALSE,
5958 EL_EM_GATE_2_GRAY, -1, -1
5961 Xfake_door_3, TRUE, FALSE,
5962 EL_EM_GATE_3_GRAY, -1, -1
5965 Xfake_door_4, TRUE, FALSE,
5966 EL_EM_GATE_4_GRAY, -1, -1
5969 Xfake_door_5, TRUE, FALSE,
5970 EL_EMC_GATE_5_GRAY, -1, -1
5973 Xfake_door_6, TRUE, FALSE,
5974 EL_EMC_GATE_6_GRAY, -1, -1
5977 Xfake_door_7, TRUE, FALSE,
5978 EL_EMC_GATE_7_GRAY, -1, -1
5981 Xfake_door_8, TRUE, FALSE,
5982 EL_EMC_GATE_8_GRAY, -1, -1
5985 Xfake_acid_1, TRUE, FALSE,
5986 EL_EMC_FAKE_ACID, -1, -1
5989 Xfake_acid_2, FALSE, FALSE,
5990 EL_EMC_FAKE_ACID, -1, -1
5993 Xfake_acid_3, FALSE, FALSE,
5994 EL_EMC_FAKE_ACID, -1, -1
5997 Xfake_acid_4, FALSE, FALSE,
5998 EL_EMC_FAKE_ACID, -1, -1
6001 Xfake_acid_5, FALSE, FALSE,
6002 EL_EMC_FAKE_ACID, -1, -1
6005 Xfake_acid_6, FALSE, FALSE,
6006 EL_EMC_FAKE_ACID, -1, -1
6009 Xfake_acid_7, FALSE, FALSE,
6010 EL_EMC_FAKE_ACID, -1, -1
6013 Xfake_acid_8, FALSE, FALSE,
6014 EL_EMC_FAKE_ACID, -1, -1
6017 Xsteel_1, TRUE, FALSE,
6018 EL_STEELWALL, -1, -1
6021 Xsteel_2, TRUE, FALSE,
6022 EL_EMC_STEELWALL_2, -1, -1
6025 Xsteel_3, TRUE, FALSE,
6026 EL_EMC_STEELWALL_3, -1, -1
6029 Xsteel_4, TRUE, FALSE,
6030 EL_EMC_STEELWALL_4, -1, -1
6033 Xwall_1, TRUE, FALSE,
6037 Xwall_2, TRUE, FALSE,
6038 EL_EMC_WALL_14, -1, -1
6041 Xwall_3, TRUE, FALSE,
6042 EL_EMC_WALL_15, -1, -1
6045 Xwall_4, TRUE, FALSE,
6046 EL_EMC_WALL_16, -1, -1
6049 Xround_wall_1, TRUE, FALSE,
6050 EL_WALL_SLIPPERY, -1, -1
6053 Xround_wall_2, TRUE, FALSE,
6054 EL_EMC_WALL_SLIPPERY_2, -1, -1
6057 Xround_wall_3, TRUE, FALSE,
6058 EL_EMC_WALL_SLIPPERY_3, -1, -1
6061 Xround_wall_4, TRUE, FALSE,
6062 EL_EMC_WALL_SLIPPERY_4, -1, -1
6065 Xdecor_1, TRUE, FALSE,
6066 EL_EMC_WALL_8, -1, -1
6069 Xdecor_2, TRUE, FALSE,
6070 EL_EMC_WALL_6, -1, -1
6073 Xdecor_3, TRUE, FALSE,
6074 EL_EMC_WALL_4, -1, -1
6077 Xdecor_4, TRUE, FALSE,
6078 EL_EMC_WALL_7, -1, -1
6081 Xdecor_5, TRUE, FALSE,
6082 EL_EMC_WALL_5, -1, -1
6085 Xdecor_6, TRUE, FALSE,
6086 EL_EMC_WALL_9, -1, -1
6089 Xdecor_7, TRUE, FALSE,
6090 EL_EMC_WALL_10, -1, -1
6093 Xdecor_8, TRUE, FALSE,
6094 EL_EMC_WALL_1, -1, -1
6097 Xdecor_9, TRUE, FALSE,
6098 EL_EMC_WALL_2, -1, -1
6101 Xdecor_10, TRUE, FALSE,
6102 EL_EMC_WALL_3, -1, -1
6105 Xdecor_11, TRUE, FALSE,
6106 EL_EMC_WALL_11, -1, -1
6109 Xdecor_12, TRUE, FALSE,
6110 EL_EMC_WALL_12, -1, -1
6113 Xalpha_0, TRUE, FALSE,
6114 EL_CHAR('0'), -1, -1
6117 Xalpha_1, TRUE, FALSE,
6118 EL_CHAR('1'), -1, -1
6121 Xalpha_2, TRUE, FALSE,
6122 EL_CHAR('2'), -1, -1
6125 Xalpha_3, TRUE, FALSE,
6126 EL_CHAR('3'), -1, -1
6129 Xalpha_4, TRUE, FALSE,
6130 EL_CHAR('4'), -1, -1
6133 Xalpha_5, TRUE, FALSE,
6134 EL_CHAR('5'), -1, -1
6137 Xalpha_6, TRUE, FALSE,
6138 EL_CHAR('6'), -1, -1
6141 Xalpha_7, TRUE, FALSE,
6142 EL_CHAR('7'), -1, -1
6145 Xalpha_8, TRUE, FALSE,
6146 EL_CHAR('8'), -1, -1
6149 Xalpha_9, TRUE, FALSE,
6150 EL_CHAR('9'), -1, -1
6153 Xalpha_excla, TRUE, FALSE,
6154 EL_CHAR('!'), -1, -1
6157 Xalpha_quote, TRUE, FALSE,
6158 EL_CHAR('"'), -1, -1
6161 Xalpha_comma, TRUE, FALSE,
6162 EL_CHAR(','), -1, -1
6165 Xalpha_minus, TRUE, FALSE,
6166 EL_CHAR('-'), -1, -1
6169 Xalpha_perio, TRUE, FALSE,
6170 EL_CHAR('.'), -1, -1
6173 Xalpha_colon, TRUE, FALSE,
6174 EL_CHAR(':'), -1, -1
6177 Xalpha_quest, TRUE, FALSE,
6178 EL_CHAR('?'), -1, -1
6181 Xalpha_a, TRUE, FALSE,
6182 EL_CHAR('A'), -1, -1
6185 Xalpha_b, TRUE, FALSE,
6186 EL_CHAR('B'), -1, -1
6189 Xalpha_c, TRUE, FALSE,
6190 EL_CHAR('C'), -1, -1
6193 Xalpha_d, TRUE, FALSE,
6194 EL_CHAR('D'), -1, -1
6197 Xalpha_e, TRUE, FALSE,
6198 EL_CHAR('E'), -1, -1
6201 Xalpha_f, TRUE, FALSE,
6202 EL_CHAR('F'), -1, -1
6205 Xalpha_g, TRUE, FALSE,
6206 EL_CHAR('G'), -1, -1
6209 Xalpha_h, TRUE, FALSE,
6210 EL_CHAR('H'), -1, -1
6213 Xalpha_i, TRUE, FALSE,
6214 EL_CHAR('I'), -1, -1
6217 Xalpha_j, TRUE, FALSE,
6218 EL_CHAR('J'), -1, -1
6221 Xalpha_k, TRUE, FALSE,
6222 EL_CHAR('K'), -1, -1
6225 Xalpha_l, TRUE, FALSE,
6226 EL_CHAR('L'), -1, -1
6229 Xalpha_m, TRUE, FALSE,
6230 EL_CHAR('M'), -1, -1
6233 Xalpha_n, TRUE, FALSE,
6234 EL_CHAR('N'), -1, -1
6237 Xalpha_o, TRUE, FALSE,
6238 EL_CHAR('O'), -1, -1
6241 Xalpha_p, TRUE, FALSE,
6242 EL_CHAR('P'), -1, -1
6245 Xalpha_q, TRUE, FALSE,
6246 EL_CHAR('Q'), -1, -1
6249 Xalpha_r, TRUE, FALSE,
6250 EL_CHAR('R'), -1, -1
6253 Xalpha_s, TRUE, FALSE,
6254 EL_CHAR('S'), -1, -1
6257 Xalpha_t, TRUE, FALSE,
6258 EL_CHAR('T'), -1, -1
6261 Xalpha_u, TRUE, FALSE,
6262 EL_CHAR('U'), -1, -1
6265 Xalpha_v, TRUE, FALSE,
6266 EL_CHAR('V'), -1, -1
6269 Xalpha_w, TRUE, FALSE,
6270 EL_CHAR('W'), -1, -1
6273 Xalpha_x, TRUE, FALSE,
6274 EL_CHAR('X'), -1, -1
6277 Xalpha_y, TRUE, FALSE,
6278 EL_CHAR('Y'), -1, -1
6281 Xalpha_z, TRUE, FALSE,
6282 EL_CHAR('Z'), -1, -1
6285 Xalpha_arrow_e, TRUE, FALSE,
6286 EL_CHAR('>'), -1, -1
6289 Xalpha_arrow_w, TRUE, FALSE,
6290 EL_CHAR('<'), -1, -1
6293 Xalpha_copyr, TRUE, FALSE,
6294 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6298 Xboom_bug, FALSE, FALSE,
6299 EL_BUG, ACTION_EXPLODING, -1
6302 Xboom_bomb, FALSE, FALSE,
6303 EL_BOMB, ACTION_EXPLODING, -1
6306 Xboom_android, FALSE, FALSE,
6307 EL_EMC_ANDROID, ACTION_OTHER, -1
6310 Xboom_1, FALSE, FALSE,
6311 EL_DEFAULT, ACTION_EXPLODING, -1
6314 Xboom_2, FALSE, FALSE,
6315 EL_DEFAULT, ACTION_EXPLODING, -1
6318 Znormal, FALSE, FALSE,
6322 Zdynamite, FALSE, FALSE,
6326 Zplayer, FALSE, FALSE,
6330 ZBORDER, FALSE, FALSE,
6340 static struct Mapping_EM_to_RND_player
6349 em_player_mapping_list[] =
6353 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6357 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6361 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6365 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6369 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6373 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6377 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6381 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6385 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6389 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6393 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6397 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6401 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6405 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6409 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6413 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6417 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6421 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6425 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6429 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6433 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6437 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6441 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6445 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6449 EL_PLAYER_1, ACTION_DEFAULT, -1,
6453 EL_PLAYER_2, ACTION_DEFAULT, -1,
6457 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6461 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6465 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6469 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6473 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6477 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6481 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6485 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6489 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6493 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6497 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6501 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6505 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6509 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6513 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6517 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6521 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6525 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6529 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6533 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6537 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6541 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6545 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6549 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6553 EL_PLAYER_3, ACTION_DEFAULT, -1,
6557 EL_PLAYER_4, ACTION_DEFAULT, -1,
6566 int map_element_RND_to_EM(int element_rnd)
6568 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6569 static boolean mapping_initialized = FALSE;
6571 if (!mapping_initialized)
6575 /* return "Xalpha_quest" for all undefined elements in mapping array */
6576 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6577 mapping_RND_to_EM[i] = Xalpha_quest;
6579 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6580 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6581 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6582 em_object_mapping_list[i].element_em;
6584 mapping_initialized = TRUE;
6587 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6588 return mapping_RND_to_EM[element_rnd];
6590 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6595 int map_element_EM_to_RND(int element_em)
6597 static unsigned short mapping_EM_to_RND[TILE_MAX];
6598 static boolean mapping_initialized = FALSE;
6600 if (!mapping_initialized)
6604 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6605 for (i = 0; i < TILE_MAX; i++)
6606 mapping_EM_to_RND[i] = EL_UNKNOWN;
6608 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6609 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6610 em_object_mapping_list[i].element_rnd;
6612 mapping_initialized = TRUE;
6615 if (element_em >= 0 && element_em < TILE_MAX)
6616 return mapping_EM_to_RND[element_em];
6618 Error(ERR_WARN, "invalid EM level element %d", element_em);
6623 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6625 struct LevelInfo_EM *level_em = level->native_em_level;
6626 struct LEVEL *lev = level_em->lev;
6629 for (i = 0; i < TILE_MAX; i++)
6630 lev->android_array[i] = Xblank;
6632 for (i = 0; i < level->num_android_clone_elements; i++)
6634 int element_rnd = level->android_clone_element[i];
6635 int element_em = map_element_RND_to_EM(element_rnd);
6637 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6638 if (em_object_mapping_list[j].element_rnd == element_rnd)
6639 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6643 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6645 struct LevelInfo_EM *level_em = level->native_em_level;
6646 struct LEVEL *lev = level_em->lev;
6649 level->num_android_clone_elements = 0;
6651 for (i = 0; i < TILE_MAX; i++)
6653 int element_em = lev->android_array[i];
6655 boolean element_found = FALSE;
6657 if (element_em == Xblank)
6660 element_rnd = map_element_EM_to_RND(element_em);
6662 for (j = 0; j < level->num_android_clone_elements; j++)
6663 if (level->android_clone_element[j] == element_rnd)
6664 element_found = TRUE;
6668 level->android_clone_element[level->num_android_clone_elements++] =
6671 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6676 if (level->num_android_clone_elements == 0)
6678 level->num_android_clone_elements = 1;
6679 level->android_clone_element[0] = EL_EMPTY;
6683 int map_direction_RND_to_EM(int direction)
6685 return (direction == MV_UP ? 0 :
6686 direction == MV_RIGHT ? 1 :
6687 direction == MV_DOWN ? 2 :
6688 direction == MV_LEFT ? 3 :
6692 int map_direction_EM_to_RND(int direction)
6694 return (direction == 0 ? MV_UP :
6695 direction == 1 ? MV_RIGHT :
6696 direction == 2 ? MV_DOWN :
6697 direction == 3 ? MV_LEFT :
6701 int map_element_RND_to_SP(int element_rnd)
6703 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6705 if (element_rnd >= EL_SP_START &&
6706 element_rnd <= EL_SP_END)
6707 element_sp = element_rnd - EL_SP_START;
6708 else if (element_rnd == EL_EMPTY_SPACE)
6710 else if (element_rnd == EL_INVISIBLE_WALL)
6716 int map_element_SP_to_RND(int element_sp)
6718 int element_rnd = EL_UNKNOWN;
6720 if (element_sp >= 0x00 &&
6722 element_rnd = EL_SP_START + element_sp;
6723 else if (element_sp == 0x28)
6724 element_rnd = EL_INVISIBLE_WALL;
6729 int map_action_SP_to_RND(int action_sp)
6733 case actActive: return ACTION_ACTIVE;
6734 case actImpact: return ACTION_IMPACT;
6735 case actExploding: return ACTION_EXPLODING;
6736 case actDigging: return ACTION_DIGGING;
6737 case actSnapping: return ACTION_SNAPPING;
6738 case actCollecting: return ACTION_COLLECTING;
6739 case actPassing: return ACTION_PASSING;
6740 case actPushing: return ACTION_PUSHING;
6741 case actDropping: return ACTION_DROPPING;
6743 default: return ACTION_DEFAULT;
6747 int get_next_element(int element)
6751 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6752 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6753 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6754 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6755 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6756 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6757 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6758 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6759 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6760 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6761 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6763 default: return element;
6767 int el_act_dir2img(int element, int action, int direction)
6769 element = GFX_ELEMENT(element);
6770 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6772 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6773 return element_info[element].direction_graphic[action][direction];
6776 static int el_act_dir2crm(int element, int action, int direction)
6778 element = GFX_ELEMENT(element);
6779 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6781 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6782 return element_info[element].direction_crumbled[action][direction];
6785 int el_act2img(int element, int action)
6787 element = GFX_ELEMENT(element);
6789 return element_info[element].graphic[action];
6792 int el_act2crm(int element, int action)
6794 element = GFX_ELEMENT(element);
6796 return element_info[element].crumbled[action];
6799 int el_dir2img(int element, int direction)
6801 element = GFX_ELEMENT(element);
6803 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6806 int el2baseimg(int element)
6808 return element_info[element].graphic[ACTION_DEFAULT];
6811 int el2img(int element)
6813 element = GFX_ELEMENT(element);
6815 return element_info[element].graphic[ACTION_DEFAULT];
6818 int el2edimg(int element)
6820 element = GFX_ELEMENT(element);
6822 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6825 int el2preimg(int element)
6827 element = GFX_ELEMENT(element);
6829 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6832 int el2panelimg(int element)
6834 element = GFX_ELEMENT(element);
6836 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6839 int font2baseimg(int font_nr)
6841 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6844 int getBeltNrFromBeltElement(int element)
6846 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6847 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6848 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6851 int getBeltNrFromBeltActiveElement(int element)
6853 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6854 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6855 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6858 int getBeltNrFromBeltSwitchElement(int element)
6860 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6861 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6862 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6865 int getBeltDirNrFromBeltElement(int element)
6867 static int belt_base_element[4] =
6869 EL_CONVEYOR_BELT_1_LEFT,
6870 EL_CONVEYOR_BELT_2_LEFT,
6871 EL_CONVEYOR_BELT_3_LEFT,
6872 EL_CONVEYOR_BELT_4_LEFT
6875 int belt_nr = getBeltNrFromBeltElement(element);
6876 int belt_dir_nr = element - belt_base_element[belt_nr];
6878 return (belt_dir_nr % 3);
6881 int getBeltDirNrFromBeltSwitchElement(int element)
6883 static int belt_base_element[4] =
6885 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6886 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6887 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6888 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6891 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6892 int belt_dir_nr = element - belt_base_element[belt_nr];
6894 return (belt_dir_nr % 3);
6897 int getBeltDirFromBeltElement(int element)
6899 static int belt_move_dir[3] =
6906 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6908 return belt_move_dir[belt_dir_nr];
6911 int getBeltDirFromBeltSwitchElement(int element)
6913 static int belt_move_dir[3] =
6920 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6922 return belt_move_dir[belt_dir_nr];
6925 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6927 static int belt_base_element[4] =
6929 EL_CONVEYOR_BELT_1_LEFT,
6930 EL_CONVEYOR_BELT_2_LEFT,
6931 EL_CONVEYOR_BELT_3_LEFT,
6932 EL_CONVEYOR_BELT_4_LEFT
6935 return belt_base_element[belt_nr] + belt_dir_nr;
6938 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6940 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6942 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6945 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6947 static int belt_base_element[4] =
6949 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6950 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6951 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6952 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6955 return belt_base_element[belt_nr] + belt_dir_nr;
6958 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6960 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6962 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6965 boolean getTeamMode_EM()
6967 return game.team_mode;
6970 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6972 int game_frame_delay_value;
6974 game_frame_delay_value =
6975 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6976 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6979 if (tape.playing && tape.warp_forward && !tape.pausing)
6980 game_frame_delay_value = 0;
6982 return game_frame_delay_value;
6985 unsigned int InitRND(int seed)
6987 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6988 return InitEngineRandom_EM(seed);
6989 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6990 return InitEngineRandom_SP(seed);
6992 return InitEngineRandom_RND(seed);
6995 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6996 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6998 inline static int get_effective_element_EM(int tile, int frame_em)
7000 int element = object_mapping[tile].element_rnd;
7001 int action = object_mapping[tile].action;
7002 boolean is_backside = object_mapping[tile].is_backside;
7003 boolean action_removing = (action == ACTION_DIGGING ||
7004 action == ACTION_SNAPPING ||
7005 action == ACTION_COLLECTING);
7011 case Yacid_splash_eB:
7012 case Yacid_splash_wB:
7013 return (frame_em > 5 ? EL_EMPTY : element);
7019 else /* frame_em == 7 */
7023 case Yacid_splash_eB:
7024 case Yacid_splash_wB:
7027 case Yemerald_stone:
7030 case Ydiamond_stone:
7034 case Xdrip_stretchB:
7053 case Xsand_stonein_1:
7054 case Xsand_stonein_2:
7055 case Xsand_stonein_3:
7056 case Xsand_stonein_4:
7060 return (is_backside || action_removing ? EL_EMPTY : element);
7065 inline static boolean check_linear_animation_EM(int tile)
7069 case Xsand_stonesand_1:
7070 case Xsand_stonesand_quickout_1:
7071 case Xsand_sandstone_1:
7072 case Xsand_stonein_1:
7073 case Xsand_stoneout_1:
7092 case Yacid_splash_eB:
7093 case Yacid_splash_wB:
7094 case Yemerald_stone:
7101 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7102 boolean has_crumbled_graphics,
7103 int crumbled, int sync_frame)
7105 /* if element can be crumbled, but certain action graphics are just empty
7106 space (like instantly snapping sand to empty space in 1 frame), do not
7107 treat these empty space graphics as crumbled graphics in EMC engine */
7108 if (crumbled == IMG_EMPTY_SPACE)
7109 has_crumbled_graphics = FALSE;
7111 if (has_crumbled_graphics)
7113 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7114 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7115 g_crumbled->anim_delay,
7116 g_crumbled->anim_mode,
7117 g_crumbled->anim_start_frame,
7120 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7121 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7123 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7125 g_em->has_crumbled_graphics = TRUE;
7129 g_em->crumbled_bitmap = NULL;
7130 g_em->crumbled_src_x = 0;
7131 g_em->crumbled_src_y = 0;
7132 g_em->crumbled_border_size = 0;
7134 g_em->has_crumbled_graphics = FALSE;
7138 void ResetGfxAnimation_EM(int x, int y, int tile)
7143 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7144 int tile, int frame_em, int x, int y)
7146 int action = object_mapping[tile].action;
7147 int direction = object_mapping[tile].direction;
7148 int effective_element = get_effective_element_EM(tile, frame_em);
7149 int graphic = (direction == MV_NONE ?
7150 el_act2img(effective_element, action) :
7151 el_act_dir2img(effective_element, action, direction));
7152 struct GraphicInfo *g = &graphic_info[graphic];
7154 boolean action_removing = (action == ACTION_DIGGING ||
7155 action == ACTION_SNAPPING ||
7156 action == ACTION_COLLECTING);
7157 boolean action_moving = (action == ACTION_FALLING ||
7158 action == ACTION_MOVING ||
7159 action == ACTION_PUSHING ||
7160 action == ACTION_EATING ||
7161 action == ACTION_FILLING ||
7162 action == ACTION_EMPTYING);
7163 boolean action_falling = (action == ACTION_FALLING ||
7164 action == ACTION_FILLING ||
7165 action == ACTION_EMPTYING);
7167 /* special case: graphic uses "2nd movement tile" and has defined
7168 7 frames for movement animation (or less) => use default graphic
7169 for last (8th) frame which ends the movement animation */
7170 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7172 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7173 graphic = (direction == MV_NONE ?
7174 el_act2img(effective_element, action) :
7175 el_act_dir2img(effective_element, action, direction));
7177 g = &graphic_info[graphic];
7180 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7184 else if (action_moving)
7186 boolean is_backside = object_mapping[tile].is_backside;
7190 int direction = object_mapping[tile].direction;
7191 int move_dir = (action_falling ? MV_DOWN : direction);
7196 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7197 if (g->double_movement && frame_em == 0)
7201 if (move_dir == MV_LEFT)
7202 GfxFrame[x - 1][y] = GfxFrame[x][y];
7203 else if (move_dir == MV_RIGHT)
7204 GfxFrame[x + 1][y] = GfxFrame[x][y];
7205 else if (move_dir == MV_UP)
7206 GfxFrame[x][y - 1] = GfxFrame[x][y];
7207 else if (move_dir == MV_DOWN)
7208 GfxFrame[x][y + 1] = GfxFrame[x][y];
7215 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7216 if (tile == Xsand_stonesand_quickout_1 ||
7217 tile == Xsand_stonesand_quickout_2)
7221 if (graphic_info[graphic].anim_global_sync)
7222 sync_frame = FrameCounter;
7223 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7224 sync_frame = GfxFrame[x][y];
7226 sync_frame = 0; /* playfield border (pseudo steel) */
7228 SetRandomAnimationValue(x, y);
7230 int frame = getAnimationFrame(g->anim_frames,
7233 g->anim_start_frame,
7236 g_em->unique_identifier =
7237 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7240 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7241 int tile, int frame_em, int x, int y)
7243 int action = object_mapping[tile].action;
7244 int direction = object_mapping[tile].direction;
7245 boolean is_backside = object_mapping[tile].is_backside;
7246 int effective_element = get_effective_element_EM(tile, frame_em);
7247 int effective_action = action;
7248 int graphic = (direction == MV_NONE ?
7249 el_act2img(effective_element, effective_action) :
7250 el_act_dir2img(effective_element, effective_action,
7252 int crumbled = (direction == MV_NONE ?
7253 el_act2crm(effective_element, effective_action) :
7254 el_act_dir2crm(effective_element, effective_action,
7256 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7257 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7258 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7259 struct GraphicInfo *g = &graphic_info[graphic];
7262 /* special case: graphic uses "2nd movement tile" and has defined
7263 7 frames for movement animation (or less) => use default graphic
7264 for last (8th) frame which ends the movement animation */
7265 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7267 effective_action = ACTION_DEFAULT;
7268 graphic = (direction == MV_NONE ?
7269 el_act2img(effective_element, effective_action) :
7270 el_act_dir2img(effective_element, effective_action,
7272 crumbled = (direction == MV_NONE ?
7273 el_act2crm(effective_element, effective_action) :
7274 el_act_dir2crm(effective_element, effective_action,
7277 g = &graphic_info[graphic];
7280 if (graphic_info[graphic].anim_global_sync)
7281 sync_frame = FrameCounter;
7282 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7283 sync_frame = GfxFrame[x][y];
7285 sync_frame = 0; /* playfield border (pseudo steel) */
7287 SetRandomAnimationValue(x, y);
7289 int frame = getAnimationFrame(g->anim_frames,
7292 g->anim_start_frame,
7295 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7296 g->double_movement && is_backside);
7298 /* (updating the "crumbled" graphic definitions is probably not really needed,
7299 as animations for crumbled graphics can't be longer than one EMC cycle) */
7300 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7304 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7305 int player_nr, int anim, int frame_em)
7307 int element = player_mapping[player_nr][anim].element_rnd;
7308 int action = player_mapping[player_nr][anim].action;
7309 int direction = player_mapping[player_nr][anim].direction;
7310 int graphic = (direction == MV_NONE ?
7311 el_act2img(element, action) :
7312 el_act_dir2img(element, action, direction));
7313 struct GraphicInfo *g = &graphic_info[graphic];
7316 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7318 stored_player[player_nr].StepFrame = frame_em;
7320 sync_frame = stored_player[player_nr].Frame;
7322 int frame = getAnimationFrame(g->anim_frames,
7325 g->anim_start_frame,
7328 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7329 &g_em->src_x, &g_em->src_y, FALSE);
7332 void InitGraphicInfo_EM(void)
7337 int num_em_gfx_errors = 0;
7339 if (graphic_info_em_object[0][0].bitmap == NULL)
7341 /* EM graphics not yet initialized in em_open_all() */
7346 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7349 /* always start with reliable default values */
7350 for (i = 0; i < TILE_MAX; i++)
7352 object_mapping[i].element_rnd = EL_UNKNOWN;
7353 object_mapping[i].is_backside = FALSE;
7354 object_mapping[i].action = ACTION_DEFAULT;
7355 object_mapping[i].direction = MV_NONE;
7358 /* always start with reliable default values */
7359 for (p = 0; p < MAX_PLAYERS; p++)
7361 for (i = 0; i < SPR_MAX; i++)
7363 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7364 player_mapping[p][i].action = ACTION_DEFAULT;
7365 player_mapping[p][i].direction = MV_NONE;
7369 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7371 int e = em_object_mapping_list[i].element_em;
7373 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7374 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7376 if (em_object_mapping_list[i].action != -1)
7377 object_mapping[e].action = em_object_mapping_list[i].action;
7379 if (em_object_mapping_list[i].direction != -1)
7380 object_mapping[e].direction =
7381 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7384 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7386 int a = em_player_mapping_list[i].action_em;
7387 int p = em_player_mapping_list[i].player_nr;
7389 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7391 if (em_player_mapping_list[i].action != -1)
7392 player_mapping[p][a].action = em_player_mapping_list[i].action;
7394 if (em_player_mapping_list[i].direction != -1)
7395 player_mapping[p][a].direction =
7396 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7399 for (i = 0; i < TILE_MAX; i++)
7401 int element = object_mapping[i].element_rnd;
7402 int action = object_mapping[i].action;
7403 int direction = object_mapping[i].direction;
7404 boolean is_backside = object_mapping[i].is_backside;
7405 boolean action_exploding = ((action == ACTION_EXPLODING ||
7406 action == ACTION_SMASHED_BY_ROCK ||
7407 action == ACTION_SMASHED_BY_SPRING) &&
7408 element != EL_DIAMOND);
7409 boolean action_active = (action == ACTION_ACTIVE);
7410 boolean action_other = (action == ACTION_OTHER);
7412 for (j = 0; j < 8; j++)
7414 int effective_element = get_effective_element_EM(i, j);
7415 int effective_action = (j < 7 ? action :
7416 i == Xdrip_stretch ? action :
7417 i == Xdrip_stretchB ? action :
7418 i == Ydrip_s1 ? action :
7419 i == Ydrip_s1B ? action :
7420 i == Xball_1B ? action :
7421 i == Xball_2 ? action :
7422 i == Xball_2B ? action :
7423 i == Yball_eat ? action :
7424 i == Ykey_1_eat ? action :
7425 i == Ykey_2_eat ? action :
7426 i == Ykey_3_eat ? action :
7427 i == Ykey_4_eat ? action :
7428 i == Ykey_5_eat ? action :
7429 i == Ykey_6_eat ? action :
7430 i == Ykey_7_eat ? action :
7431 i == Ykey_8_eat ? action :
7432 i == Ylenses_eat ? action :
7433 i == Ymagnify_eat ? action :
7434 i == Ygrass_eat ? action :
7435 i == Ydirt_eat ? action :
7436 i == Xsand_stonein_1 ? action :
7437 i == Xsand_stonein_2 ? action :
7438 i == Xsand_stonein_3 ? action :
7439 i == Xsand_stonein_4 ? action :
7440 i == Xsand_stoneout_1 ? action :
7441 i == Xsand_stoneout_2 ? action :
7442 i == Xboom_android ? ACTION_EXPLODING :
7443 action_exploding ? ACTION_EXPLODING :
7444 action_active ? action :
7445 action_other ? action :
7447 int graphic = (el_act_dir2img(effective_element, effective_action,
7449 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7451 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7452 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7453 boolean has_action_graphics = (graphic != base_graphic);
7454 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7455 struct GraphicInfo *g = &graphic_info[graphic];
7456 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7459 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7460 boolean special_animation = (action != ACTION_DEFAULT &&
7461 g->anim_frames == 3 &&
7462 g->anim_delay == 2 &&
7463 g->anim_mode & ANIM_LINEAR);
7464 int sync_frame = (i == Xdrip_stretch ? 7 :
7465 i == Xdrip_stretchB ? 7 :
7466 i == Ydrip_s2 ? j + 8 :
7467 i == Ydrip_s2B ? j + 8 :
7476 i == Xfake_acid_1 ? 0 :
7477 i == Xfake_acid_2 ? 10 :
7478 i == Xfake_acid_3 ? 20 :
7479 i == Xfake_acid_4 ? 30 :
7480 i == Xfake_acid_5 ? 40 :
7481 i == Xfake_acid_6 ? 50 :
7482 i == Xfake_acid_7 ? 60 :
7483 i == Xfake_acid_8 ? 70 :
7485 i == Xball_2B ? j + 8 :
7486 i == Yball_eat ? j + 1 :
7487 i == Ykey_1_eat ? j + 1 :
7488 i == Ykey_2_eat ? j + 1 :
7489 i == Ykey_3_eat ? j + 1 :
7490 i == Ykey_4_eat ? j + 1 :
7491 i == Ykey_5_eat ? j + 1 :
7492 i == Ykey_6_eat ? j + 1 :
7493 i == Ykey_7_eat ? j + 1 :
7494 i == Ykey_8_eat ? j + 1 :
7495 i == Ylenses_eat ? j + 1 :
7496 i == Ymagnify_eat ? j + 1 :
7497 i == Ygrass_eat ? j + 1 :
7498 i == Ydirt_eat ? j + 1 :
7499 i == Xamoeba_1 ? 0 :
7500 i == Xamoeba_2 ? 1 :
7501 i == Xamoeba_3 ? 2 :
7502 i == Xamoeba_4 ? 3 :
7503 i == Xamoeba_5 ? 0 :
7504 i == Xamoeba_6 ? 1 :
7505 i == Xamoeba_7 ? 2 :
7506 i == Xamoeba_8 ? 3 :
7507 i == Xexit_2 ? j + 8 :
7508 i == Xexit_3 ? j + 16 :
7509 i == Xdynamite_1 ? 0 :
7510 i == Xdynamite_2 ? 8 :
7511 i == Xdynamite_3 ? 16 :
7512 i == Xdynamite_4 ? 24 :
7513 i == Xsand_stonein_1 ? j + 1 :
7514 i == Xsand_stonein_2 ? j + 9 :
7515 i == Xsand_stonein_3 ? j + 17 :
7516 i == Xsand_stonein_4 ? j + 25 :
7517 i == Xsand_stoneout_1 && j == 0 ? 0 :
7518 i == Xsand_stoneout_1 && j == 1 ? 0 :
7519 i == Xsand_stoneout_1 && j == 2 ? 1 :
7520 i == Xsand_stoneout_1 && j == 3 ? 2 :
7521 i == Xsand_stoneout_1 && j == 4 ? 2 :
7522 i == Xsand_stoneout_1 && j == 5 ? 3 :
7523 i == Xsand_stoneout_1 && j == 6 ? 4 :
7524 i == Xsand_stoneout_1 && j == 7 ? 4 :
7525 i == Xsand_stoneout_2 && j == 0 ? 5 :
7526 i == Xsand_stoneout_2 && j == 1 ? 6 :
7527 i == Xsand_stoneout_2 && j == 2 ? 7 :
7528 i == Xsand_stoneout_2 && j == 3 ? 8 :
7529 i == Xsand_stoneout_2 && j == 4 ? 9 :
7530 i == Xsand_stoneout_2 && j == 5 ? 11 :
7531 i == Xsand_stoneout_2 && j == 6 ? 13 :
7532 i == Xsand_stoneout_2 && j == 7 ? 15 :
7533 i == Xboom_bug && j == 1 ? 2 :
7534 i == Xboom_bug && j == 2 ? 2 :
7535 i == Xboom_bug && j == 3 ? 4 :
7536 i == Xboom_bug && j == 4 ? 4 :
7537 i == Xboom_bug && j == 5 ? 2 :
7538 i == Xboom_bug && j == 6 ? 2 :
7539 i == Xboom_bug && j == 7 ? 0 :
7540 i == Xboom_bomb && j == 1 ? 2 :
7541 i == Xboom_bomb && j == 2 ? 2 :
7542 i == Xboom_bomb && j == 3 ? 4 :
7543 i == Xboom_bomb && j == 4 ? 4 :
7544 i == Xboom_bomb && j == 5 ? 2 :
7545 i == Xboom_bomb && j == 6 ? 2 :
7546 i == Xboom_bomb && j == 7 ? 0 :
7547 i == Xboom_android && j == 7 ? 6 :
7548 i == Xboom_1 && j == 1 ? 2 :
7549 i == Xboom_1 && j == 2 ? 2 :
7550 i == Xboom_1 && j == 3 ? 4 :
7551 i == Xboom_1 && j == 4 ? 4 :
7552 i == Xboom_1 && j == 5 ? 6 :
7553 i == Xboom_1 && j == 6 ? 6 :
7554 i == Xboom_1 && j == 7 ? 8 :
7555 i == Xboom_2 && j == 0 ? 8 :
7556 i == Xboom_2 && j == 1 ? 8 :
7557 i == Xboom_2 && j == 2 ? 10 :
7558 i == Xboom_2 && j == 3 ? 10 :
7559 i == Xboom_2 && j == 4 ? 10 :
7560 i == Xboom_2 && j == 5 ? 12 :
7561 i == Xboom_2 && j == 6 ? 12 :
7562 i == Xboom_2 && j == 7 ? 12 :
7563 special_animation && j == 4 ? 3 :
7564 effective_action != action ? 0 :
7568 Bitmap *debug_bitmap = g_em->bitmap;
7569 int debug_src_x = g_em->src_x;
7570 int debug_src_y = g_em->src_y;
7573 int frame = getAnimationFrame(g->anim_frames,
7576 g->anim_start_frame,
7579 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7580 g->double_movement && is_backside);
7582 g_em->bitmap = src_bitmap;
7583 g_em->src_x = src_x;
7584 g_em->src_y = src_y;
7585 g_em->src_offset_x = 0;
7586 g_em->src_offset_y = 0;
7587 g_em->dst_offset_x = 0;
7588 g_em->dst_offset_y = 0;
7589 g_em->width = TILEX;
7590 g_em->height = TILEY;
7592 g_em->preserve_background = FALSE;
7594 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7597 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7598 effective_action == ACTION_MOVING ||
7599 effective_action == ACTION_PUSHING ||
7600 effective_action == ACTION_EATING)) ||
7601 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7602 effective_action == ACTION_EMPTYING)))
7605 (effective_action == ACTION_FALLING ||
7606 effective_action == ACTION_FILLING ||
7607 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7608 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7609 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7610 int num_steps = (i == Ydrip_s1 ? 16 :
7611 i == Ydrip_s1B ? 16 :
7612 i == Ydrip_s2 ? 16 :
7613 i == Ydrip_s2B ? 16 :
7614 i == Xsand_stonein_1 ? 32 :
7615 i == Xsand_stonein_2 ? 32 :
7616 i == Xsand_stonein_3 ? 32 :
7617 i == Xsand_stonein_4 ? 32 :
7618 i == Xsand_stoneout_1 ? 16 :
7619 i == Xsand_stoneout_2 ? 16 : 8);
7620 int cx = ABS(dx) * (TILEX / num_steps);
7621 int cy = ABS(dy) * (TILEY / num_steps);
7622 int step_frame = (i == Ydrip_s2 ? j + 8 :
7623 i == Ydrip_s2B ? j + 8 :
7624 i == Xsand_stonein_2 ? j + 8 :
7625 i == Xsand_stonein_3 ? j + 16 :
7626 i == Xsand_stonein_4 ? j + 24 :
7627 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7628 int step = (is_backside ? step_frame : num_steps - step_frame);
7630 if (is_backside) /* tile where movement starts */
7632 if (dx < 0 || dy < 0)
7634 g_em->src_offset_x = cx * step;
7635 g_em->src_offset_y = cy * step;
7639 g_em->dst_offset_x = cx * step;
7640 g_em->dst_offset_y = cy * step;
7643 else /* tile where movement ends */
7645 if (dx < 0 || dy < 0)
7647 g_em->dst_offset_x = cx * step;
7648 g_em->dst_offset_y = cy * step;
7652 g_em->src_offset_x = cx * step;
7653 g_em->src_offset_y = cy * step;
7657 g_em->width = TILEX - cx * step;
7658 g_em->height = TILEY - cy * step;
7661 /* create unique graphic identifier to decide if tile must be redrawn */
7662 /* bit 31 - 16 (16 bit): EM style graphic
7663 bit 15 - 12 ( 4 bit): EM style frame
7664 bit 11 - 6 ( 6 bit): graphic width
7665 bit 5 - 0 ( 6 bit): graphic height */
7666 g_em->unique_identifier =
7667 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7671 /* skip check for EMC elements not contained in original EMC artwork */
7672 if (element == EL_EMC_FAKE_ACID)
7675 if (g_em->bitmap != debug_bitmap ||
7676 g_em->src_x != debug_src_x ||
7677 g_em->src_y != debug_src_y ||
7678 g_em->src_offset_x != 0 ||
7679 g_em->src_offset_y != 0 ||
7680 g_em->dst_offset_x != 0 ||
7681 g_em->dst_offset_y != 0 ||
7682 g_em->width != TILEX ||
7683 g_em->height != TILEY)
7685 static int last_i = -1;
7693 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7694 i, element, element_info[element].token_name,
7695 element_action_info[effective_action].suffix, direction);
7697 if (element != effective_element)
7698 printf(" [%d ('%s')]",
7700 element_info[effective_element].token_name);
7704 if (g_em->bitmap != debug_bitmap)
7705 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7706 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7708 if (g_em->src_x != debug_src_x ||
7709 g_em->src_y != debug_src_y)
7710 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7711 j, (is_backside ? 'B' : 'F'),
7712 g_em->src_x, g_em->src_y,
7713 g_em->src_x / 32, g_em->src_y / 32,
7714 debug_src_x, debug_src_y,
7715 debug_src_x / 32, debug_src_y / 32);
7717 if (g_em->src_offset_x != 0 ||
7718 g_em->src_offset_y != 0 ||
7719 g_em->dst_offset_x != 0 ||
7720 g_em->dst_offset_y != 0)
7721 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7723 g_em->src_offset_x, g_em->src_offset_y,
7724 g_em->dst_offset_x, g_em->dst_offset_y);
7726 if (g_em->width != TILEX ||
7727 g_em->height != TILEY)
7728 printf(" %d (%d): size %d,%d should be %d,%d\n",
7730 g_em->width, g_em->height, TILEX, TILEY);
7732 num_em_gfx_errors++;
7739 for (i = 0; i < TILE_MAX; i++)
7741 for (j = 0; j < 8; j++)
7743 int element = object_mapping[i].element_rnd;
7744 int action = object_mapping[i].action;
7745 int direction = object_mapping[i].direction;
7746 boolean is_backside = object_mapping[i].is_backside;
7747 int graphic_action = el_act_dir2img(element, action, direction);
7748 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7750 if ((action == ACTION_SMASHED_BY_ROCK ||
7751 action == ACTION_SMASHED_BY_SPRING ||
7752 action == ACTION_EATING) &&
7753 graphic_action == graphic_default)
7755 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7756 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7757 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7758 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7761 /* no separate animation for "smashed by rock" -- use rock instead */
7762 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7763 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7765 g_em->bitmap = g_xx->bitmap;
7766 g_em->src_x = g_xx->src_x;
7767 g_em->src_y = g_xx->src_y;
7768 g_em->src_offset_x = g_xx->src_offset_x;
7769 g_em->src_offset_y = g_xx->src_offset_y;
7770 g_em->dst_offset_x = g_xx->dst_offset_x;
7771 g_em->dst_offset_y = g_xx->dst_offset_y;
7772 g_em->width = g_xx->width;
7773 g_em->height = g_xx->height;
7774 g_em->unique_identifier = g_xx->unique_identifier;
7777 g_em->preserve_background = TRUE;
7782 for (p = 0; p < MAX_PLAYERS; p++)
7784 for (i = 0; i < SPR_MAX; i++)
7786 int element = player_mapping[p][i].element_rnd;
7787 int action = player_mapping[p][i].action;
7788 int direction = player_mapping[p][i].direction;
7790 for (j = 0; j < 8; j++)
7792 int effective_element = element;
7793 int effective_action = action;
7794 int graphic = (direction == MV_NONE ?
7795 el_act2img(effective_element, effective_action) :
7796 el_act_dir2img(effective_element, effective_action,
7798 struct GraphicInfo *g = &graphic_info[graphic];
7799 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7805 Bitmap *debug_bitmap = g_em->bitmap;
7806 int debug_src_x = g_em->src_x;
7807 int debug_src_y = g_em->src_y;
7810 int frame = getAnimationFrame(g->anim_frames,
7813 g->anim_start_frame,
7816 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7818 g_em->bitmap = src_bitmap;
7819 g_em->src_x = src_x;
7820 g_em->src_y = src_y;
7821 g_em->src_offset_x = 0;
7822 g_em->src_offset_y = 0;
7823 g_em->dst_offset_x = 0;
7824 g_em->dst_offset_y = 0;
7825 g_em->width = TILEX;
7826 g_em->height = TILEY;
7830 /* skip check for EMC elements not contained in original EMC artwork */
7831 if (element == EL_PLAYER_3 ||
7832 element == EL_PLAYER_4)
7835 if (g_em->bitmap != debug_bitmap ||
7836 g_em->src_x != debug_src_x ||
7837 g_em->src_y != debug_src_y)
7839 static int last_i = -1;
7847 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7848 p, i, element, element_info[element].token_name,
7849 element_action_info[effective_action].suffix, direction);
7851 if (element != effective_element)
7852 printf(" [%d ('%s')]",
7854 element_info[effective_element].token_name);
7858 if (g_em->bitmap != debug_bitmap)
7859 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7860 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7862 if (g_em->src_x != debug_src_x ||
7863 g_em->src_y != debug_src_y)
7864 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7866 g_em->src_x, g_em->src_y,
7867 g_em->src_x / 32, g_em->src_y / 32,
7868 debug_src_x, debug_src_y,
7869 debug_src_x / 32, debug_src_y / 32);
7871 num_em_gfx_errors++;
7881 printf("::: [%d errors found]\n", num_em_gfx_errors);
7887 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7888 boolean any_player_moving,
7889 boolean any_player_snapping,
7890 boolean any_player_dropping)
7892 static boolean player_was_waiting = TRUE;
7894 if (frame == 0 && !any_player_dropping)
7896 if (!player_was_waiting)
7898 if (!SaveEngineSnapshotToList())
7901 player_was_waiting = TRUE;
7904 else if (any_player_moving || any_player_snapping || any_player_dropping)
7906 player_was_waiting = FALSE;
7910 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7911 boolean murphy_is_dropping)
7913 static boolean player_was_waiting = TRUE;
7915 if (murphy_is_waiting)
7917 if (!player_was_waiting)
7919 if (!SaveEngineSnapshotToList())
7922 player_was_waiting = TRUE;
7927 player_was_waiting = FALSE;
7931 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7932 boolean any_player_moving,
7933 boolean any_player_snapping,
7934 boolean any_player_dropping)
7936 if (tape.single_step && tape.recording && !tape.pausing)
7937 if (frame == 0 && !any_player_dropping)
7938 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7940 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7941 any_player_snapping, any_player_dropping);
7944 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7945 boolean murphy_is_dropping)
7947 if (tape.single_step && tape.recording && !tape.pausing)
7948 if (murphy_is_waiting)
7949 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7951 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7954 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7955 int graphic, int sync_frame, int x, int y)
7957 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7959 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7962 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7964 return (IS_NEXT_FRAME(sync_frame, graphic));
7967 int getGraphicInfo_Delay(int graphic)
7969 return graphic_info[graphic].anim_delay;
7972 void PlayMenuSoundExt(int sound)
7974 if (sound == SND_UNDEFINED)
7977 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7978 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7981 if (IS_LOOP_SOUND(sound))
7982 PlaySoundLoop(sound);
7987 void PlayMenuSound()
7989 PlayMenuSoundExt(menu.sound[game_status]);
7992 void PlayMenuSoundStereo(int sound, int stereo_position)
7994 if (sound == SND_UNDEFINED)
7997 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7998 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8001 if (IS_LOOP_SOUND(sound))
8002 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8004 PlaySoundStereo(sound, stereo_position);
8007 void PlayMenuSoundIfLoopExt(int sound)
8009 if (sound == SND_UNDEFINED)
8012 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8013 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8016 if (IS_LOOP_SOUND(sound))
8017 PlaySoundLoop(sound);
8020 void PlayMenuSoundIfLoop()
8022 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8025 void PlayMenuMusicExt(int music)
8027 if (music == MUS_UNDEFINED)
8030 if (!setup.sound_music)
8036 void PlayMenuMusic()
8038 PlayMenuMusicExt(menu.music[game_status]);
8041 void PlaySoundActivating()
8044 PlaySound(SND_MENU_ITEM_ACTIVATING);
8048 void PlaySoundSelecting()
8051 PlaySound(SND_MENU_ITEM_SELECTING);
8055 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8057 boolean change_fullscreen = (setup.fullscreen !=
8058 video.fullscreen_enabled);
8059 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8060 !strEqual(setup.fullscreen_mode,
8061 video.fullscreen_mode_current));
8062 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8063 setup.window_scaling_percent !=
8064 video.window_scaling_percent);
8066 if (change_window_scaling_percent && video.fullscreen_enabled)
8069 if (!change_window_scaling_percent && !video.fullscreen_available)
8072 #if defined(TARGET_SDL2)
8073 if (change_window_scaling_percent)
8075 SDLSetWindowScaling(setup.window_scaling_percent);
8079 else if (change_fullscreen)
8081 SDLSetWindowFullscreen(setup.fullscreen);
8083 /* set setup value according to successfully changed fullscreen mode */
8084 setup.fullscreen = video.fullscreen_enabled;
8090 if (change_fullscreen ||
8091 change_fullscreen_mode ||
8092 change_window_scaling_percent)
8094 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8096 /* save backbuffer content which gets lost when toggling fullscreen mode */
8097 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8099 if (change_fullscreen_mode)
8101 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8102 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8105 if (change_window_scaling_percent)
8107 /* keep window mode, but change window scaling */
8108 video.fullscreen_enabled = TRUE; /* force new window scaling */
8111 /* toggle fullscreen */
8112 ChangeVideoModeIfNeeded(setup.fullscreen);
8114 /* set setup value according to successfully changed fullscreen mode */
8115 setup.fullscreen = video.fullscreen_enabled;
8117 /* restore backbuffer content from temporary backbuffer backup bitmap */
8118 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8120 FreeBitmap(tmp_backbuffer);
8122 /* update visible window/screen */
8123 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8127 void JoinRectangles(int *x, int *y, int *width, int *height,
8128 int x2, int y2, int width2, int height2)
8130 // do not join with "off-screen" rectangle
8131 if (x2 == -1 || y2 == -1)
8136 *width = MAX(*width, width2);
8137 *height = MAX(*height, height2);
8140 void ChangeViewportPropertiesIfNeeded()
8142 int gfx_game_mode = game_status;
8143 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8145 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8146 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8147 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8148 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8149 int border_size = vp_playfield->border_size;
8150 int new_sx = vp_playfield->x + border_size;
8151 int new_sy = vp_playfield->y + border_size;
8152 int new_sxsize = vp_playfield->width - 2 * border_size;
8153 int new_sysize = vp_playfield->height - 2 * border_size;
8154 int new_real_sx = vp_playfield->x;
8155 int new_real_sy = vp_playfield->y;
8156 int new_full_sxsize = vp_playfield->width;
8157 int new_full_sysize = vp_playfield->height;
8158 int new_dx = vp_door_1->x;
8159 int new_dy = vp_door_1->y;
8160 int new_dxsize = vp_door_1->width;
8161 int new_dysize = vp_door_1->height;
8162 int new_vx = vp_door_2->x;
8163 int new_vy = vp_door_2->y;
8164 int new_vxsize = vp_door_2->width;
8165 int new_vysize = vp_door_2->height;
8166 int new_ex = vp_door_3->x;
8167 int new_ey = vp_door_3->y;
8168 int new_exsize = vp_door_3->width;
8169 int new_eysize = vp_door_3->height;
8170 int new_tilesize_var =
8171 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8173 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8174 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8175 int new_scr_fieldx = new_sxsize / tilesize;
8176 int new_scr_fieldy = new_sysize / tilesize;
8177 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8178 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8179 boolean init_gfx_buffers = FALSE;
8180 boolean init_video_buffer = FALSE;
8181 boolean init_gadgets_and_toons = FALSE;
8182 boolean init_em_graphics = FALSE;
8184 if (viewport.window.width != WIN_XSIZE ||
8185 viewport.window.height != WIN_YSIZE)
8187 WIN_XSIZE = viewport.window.width;
8188 WIN_YSIZE = viewport.window.height;
8190 init_video_buffer = TRUE;
8191 init_gfx_buffers = TRUE;
8193 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8196 if (new_scr_fieldx != SCR_FIELDX ||
8197 new_scr_fieldy != SCR_FIELDY)
8199 /* this always toggles between MAIN and GAME when using small tile size */
8201 SCR_FIELDX = new_scr_fieldx;
8202 SCR_FIELDY = new_scr_fieldy;
8204 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8215 new_sxsize != SXSIZE ||
8216 new_sysize != SYSIZE ||
8217 new_dxsize != DXSIZE ||
8218 new_dysize != DYSIZE ||
8219 new_vxsize != VXSIZE ||
8220 new_vysize != VYSIZE ||
8221 new_exsize != EXSIZE ||
8222 new_eysize != EYSIZE ||
8223 new_real_sx != REAL_SX ||
8224 new_real_sy != REAL_SY ||
8225 new_full_sxsize != FULL_SXSIZE ||
8226 new_full_sysize != FULL_SYSIZE ||
8227 new_tilesize_var != TILESIZE_VAR
8230 // ------------------------------------------------------------------------
8231 // determine next fading area for changed viewport definitions
8232 // ------------------------------------------------------------------------
8234 // start with current playfield area (default fading area)
8237 FADE_SXSIZE = FULL_SXSIZE;
8238 FADE_SYSIZE = FULL_SYSIZE;
8240 // add new playfield area if position or size has changed
8241 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8242 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8244 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8245 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8248 // add current and new door 1 area if position or size has changed
8249 if (new_dx != DX || new_dy != DY ||
8250 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8252 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8253 DX, DY, DXSIZE, DYSIZE);
8254 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8255 new_dx, new_dy, new_dxsize, new_dysize);
8258 // add current and new door 2 area if position or size has changed
8259 if (new_dx != VX || new_dy != VY ||
8260 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8262 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8263 VX, VY, VXSIZE, VYSIZE);
8264 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8265 new_vx, new_vy, new_vxsize, new_vysize);
8268 // ------------------------------------------------------------------------
8269 // handle changed tile size
8270 // ------------------------------------------------------------------------
8272 if (new_tilesize_var != TILESIZE_VAR)
8274 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8276 // changing tile size invalidates scroll values of engine snapshots
8277 FreeEngineSnapshotSingle();
8279 // changing tile size requires update of graphic mapping for EM engine
8280 init_em_graphics = TRUE;
8291 SXSIZE = new_sxsize;
8292 SYSIZE = new_sysize;
8293 DXSIZE = new_dxsize;
8294 DYSIZE = new_dysize;
8295 VXSIZE = new_vxsize;
8296 VYSIZE = new_vysize;
8297 EXSIZE = new_exsize;
8298 EYSIZE = new_eysize;
8299 REAL_SX = new_real_sx;
8300 REAL_SY = new_real_sy;
8301 FULL_SXSIZE = new_full_sxsize;
8302 FULL_SYSIZE = new_full_sysize;
8303 TILESIZE_VAR = new_tilesize_var;
8305 init_gfx_buffers = TRUE;
8306 init_gadgets_and_toons = TRUE;
8308 // printf("::: viewports: init_gfx_buffers\n");
8309 // printf("::: viewports: init_gadgets_and_toons\n");
8312 if (init_gfx_buffers)
8314 // printf("::: init_gfx_buffers\n");
8316 SCR_FIELDX = new_scr_fieldx_buffers;
8317 SCR_FIELDY = new_scr_fieldy_buffers;
8321 SCR_FIELDX = new_scr_fieldx;
8322 SCR_FIELDY = new_scr_fieldy;
8324 SetDrawDeactivationMask(REDRAW_NONE);
8325 SetDrawBackgroundMask(REDRAW_FIELD);
8328 if (init_video_buffer)
8330 // printf("::: init_video_buffer\n");
8332 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8335 if (init_gadgets_and_toons)
8337 // printf("::: init_gadgets_and_toons\n");
8343 if (init_em_graphics)
8345 InitGraphicInfo_EM();