1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static char *print_if_not_empty(int element)
178 static char *s = NULL;
179 char *token_name = element_info[element].token_name;
184 s = checked_malloc(strlen(token_name) + 10 + 1);
186 if (element != EL_EMPTY)
187 sprintf(s, "%d\t['%s']", element, token_name);
189 sprintf(s, "%d", element);
194 void DumpTile(int x, int y)
199 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
205 printf_line("-", 79);
206 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
207 printf_line("-", 79);
209 if (!IN_LEV_FIELD(x, y))
211 printf("(not in level field)\n");
217 printf(" Feld: %d\t['%s']\n", Feld[x][y],
218 element_info[Feld[x][y]].token_name);
219 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
220 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
221 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
222 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
223 printf(" MovPos: %d\n", MovPos[x][y]);
224 printf(" MovDir: %d\n", MovDir[x][y]);
225 printf(" MovDelay: %d\n", MovDelay[x][y]);
226 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
227 printf(" CustomValue: %d\n", CustomValue[x][y]);
228 printf(" GfxElement: %d\n", GfxElement[x][y]);
229 printf(" GfxAction: %d\n", GfxAction[x][y]);
230 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
234 void SetDrawtoField(int mode)
236 if (mode == DRAW_FIELDBUFFER)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
245 drawto_field = fieldbuffer;
247 else /* DRAW_BACKBUFFER */
253 BX2 = SCR_FIELDX - 1;
254 BY2 = SCR_FIELDY - 1;
256 drawto_field = backbuffer;
260 static void RedrawPlayfield_RND()
262 if (game.envelope_active)
265 DrawLevel(REDRAW_ALL);
269 void RedrawPlayfield()
271 if (game_status != GAME_MODE_PLAYING)
274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
275 RedrawPlayfield_EM(TRUE);
276 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
277 RedrawPlayfield_SP(TRUE);
278 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
279 RedrawPlayfield_RND();
281 BlitScreenToBitmap(backbuffer);
283 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
287 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
289 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
291 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
294 void DrawMaskedBorder_FIELD()
296 if (global.border_status >= GAME_MODE_TITLE &&
297 global.border_status <= GAME_MODE_PLAYING &&
298 border.draw_masked[global.border_status])
299 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
302 void DrawMaskedBorder_DOOR_1()
304 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
305 (global.border_status != GAME_MODE_EDITOR ||
306 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
307 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
310 void DrawMaskedBorder_DOOR_2()
312 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
313 global.border_status != GAME_MODE_EDITOR)
314 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
317 void DrawMaskedBorder_DOOR_3()
319 /* currently not available */
322 void DrawMaskedBorder_ALL()
324 DrawMaskedBorder_FIELD();
325 DrawMaskedBorder_DOOR_1();
326 DrawMaskedBorder_DOOR_2();
327 DrawMaskedBorder_DOOR_3();
330 void DrawMaskedBorder(int redraw_mask)
332 /* never draw masked screen borders on borderless screens */
333 if (game_status == GAME_MODE_LOADING ||
334 game_status == GAME_MODE_TITLE)
337 if (redraw_mask & REDRAW_ALL)
338 DrawMaskedBorder_ALL();
341 if (redraw_mask & REDRAW_FIELD)
342 DrawMaskedBorder_FIELD();
343 if (redraw_mask & REDRAW_DOOR_1)
344 DrawMaskedBorder_DOOR_1();
345 if (redraw_mask & REDRAW_DOOR_2)
346 DrawMaskedBorder_DOOR_2();
347 if (redraw_mask & REDRAW_DOOR_3)
348 DrawMaskedBorder_DOOR_3();
352 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
354 int fx = FX, fy = FY;
355 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
356 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
358 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
359 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
360 int dx_var = dx * TILESIZE_VAR / TILESIZE;
361 int dy_var = dy * TILESIZE_VAR / TILESIZE;
364 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
365 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
367 if (EVEN(SCR_FIELDX))
369 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
370 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
372 fx += (dx_var > 0 ? TILEX_VAR : 0);
379 if (EVEN(SCR_FIELDY))
381 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
382 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
384 fy += (dy_var > 0 ? TILEY_VAR : 0);
391 if (full_lev_fieldx <= SCR_FIELDX)
393 if (EVEN(SCR_FIELDX))
394 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
396 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
399 if (full_lev_fieldy <= SCR_FIELDY)
401 if (EVEN(SCR_FIELDY))
402 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
404 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
407 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
410 void BlitScreenToBitmap(Bitmap *target_bitmap)
412 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
413 BlitScreenToBitmap_EM(target_bitmap);
414 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
415 BlitScreenToBitmap_SP(target_bitmap);
416 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
417 BlitScreenToBitmap_RND(target_bitmap);
419 redraw_mask |= REDRAW_FIELD;
422 void DrawFramesPerSecond()
425 int font_nr = FONT_TEXT_2;
426 int font_width = getFontWidth(font_nr);
428 sprintf(text, "%04.1f fps", global.frames_per_second);
430 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
431 font_nr, BLIT_OPAQUE);
436 if (redraw_mask == REDRAW_NONE)
439 // draw masked border to all viewports, if defined
440 DrawMaskedBorder(redraw_mask);
442 // draw frames per second (only if debug mode is enabled)
443 if (redraw_mask & REDRAW_FPS)
444 DrawFramesPerSecond();
446 // redraw complete window if both playfield and (some) doors need redraw
447 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
448 redraw_mask = REDRAW_ALL;
450 /* although redrawing the whole window would be fine for normal gameplay,
451 being able to only redraw the playfield is required for deactivating
452 certain drawing areas (mainly playfield) to work, which is needed for
453 warp-forward to be fast enough (by skipping redraw of most frames) */
455 if (redraw_mask & REDRAW_ALL)
457 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
459 else if (redraw_mask & REDRAW_FIELD)
461 BlitBitmap(backbuffer, window,
462 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
464 else if (redraw_mask & REDRAW_DOORS)
466 if (redraw_mask & REDRAW_DOOR_1)
467 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
469 if (redraw_mask & REDRAW_DOOR_2)
470 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
472 if (redraw_mask & REDRAW_DOOR_3)
473 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
476 redraw_mask = REDRAW_NONE;
479 static void FadeCrossSaveBackbuffer()
481 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
484 static void FadeCrossRestoreBackbuffer()
486 int redraw_mask_last = redraw_mask;
488 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
490 // do not change redraw mask when restoring backbuffer after cross-fading
491 redraw_mask = redraw_mask_last;
494 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
496 static int fade_type_skip = FADE_TYPE_NONE;
497 void (*draw_border_function)(void) = NULL;
498 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
499 int x, y, width, height;
500 int fade_delay, post_delay;
502 if (fade_type == FADE_TYPE_FADE_OUT)
504 if (fade_type_skip != FADE_TYPE_NONE)
506 /* skip all fade operations until specified fade operation */
507 if (fade_type & fade_type_skip)
508 fade_type_skip = FADE_TYPE_NONE;
514 FadeCrossSaveBackbuffer();
517 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
520 FadeCrossSaveBackbuffer();
527 redraw_mask |= fade_mask;
529 if (fade_type == FADE_TYPE_SKIP)
531 fade_type_skip = fade_mode;
536 fade_delay = fading.fade_delay;
537 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
539 if (fade_type_skip != FADE_TYPE_NONE)
541 /* skip all fade operations until specified fade operation */
542 if (fade_type & fade_type_skip)
543 fade_type_skip = FADE_TYPE_NONE;
548 if (global.autoplay_leveldir)
553 if (fade_mask == REDRAW_FIELD)
558 height = FADE_SYSIZE;
560 if (border.draw_masked_when_fading)
561 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
563 DrawMaskedBorder_FIELD(); /* draw once */
565 else /* REDRAW_ALL */
573 if (!setup.fade_screens ||
575 fading.fade_mode == FADE_MODE_NONE)
577 if (fade_mode == FADE_MODE_FADE_OUT)
580 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
582 redraw_mask &= ~fade_mask;
587 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
588 draw_border_function);
590 if (fade_type == FADE_TYPE_FADE_OUT)
591 FadeCrossRestoreBackbuffer();
593 redraw_mask &= ~fade_mask;
596 void FadeIn(int fade_mask)
598 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
599 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
601 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
605 FADE_SXSIZE = FULL_SXSIZE;
606 FADE_SYSIZE = FULL_SYSIZE;
609 void FadeOut(int fade_mask)
611 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
612 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
614 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
616 global.border_status = game_status;
619 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
621 static struct TitleFadingInfo fading_leave_stored;
624 fading_leave_stored = fading_leave;
626 fading = fading_leave_stored;
629 void FadeSetEnterMenu()
631 fading = menu.enter_menu;
633 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
636 void FadeSetLeaveMenu()
638 fading = menu.leave_menu;
640 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
643 void FadeSetEnterScreen()
645 fading = menu.enter_screen[game_status];
647 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
650 void FadeSetNextScreen()
652 fading = menu.next_screen[game_status];
654 // (do not overwrite fade mode set by FadeSetEnterScreen)
655 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
658 void FadeSetLeaveScreen()
660 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
663 void FadeSetFromType(int type)
665 if (type & TYPE_ENTER_SCREEN)
666 FadeSetEnterScreen();
667 else if (type & TYPE_ENTER)
669 else if (type & TYPE_LEAVE)
673 void FadeSetDisabled()
675 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
677 fading = fading_none;
680 void FadeSkipNextFadeIn()
682 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
685 void FadeSkipNextFadeOut()
687 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
690 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
692 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
694 return (graphic == IMG_UNDEFINED ? NULL :
695 graphic_info[graphic].bitmap != NULL || redefined ?
696 graphic_info[graphic].bitmap :
697 graphic_info[default_graphic].bitmap);
700 Bitmap *getBackgroundBitmap(int graphic)
702 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
705 Bitmap *getGlobalBorderBitmap(int graphic)
707 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
710 Bitmap *getGlobalBorderBitmapFromGameStatus()
713 (game_status == GAME_MODE_MAIN ||
714 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
715 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
716 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
717 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
720 return getGlobalBorderBitmap(graphic);
723 void SetWindowBackgroundImageIfDefined(int graphic)
725 if (graphic_info[graphic].bitmap)
726 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
729 void SetMainBackgroundImageIfDefined(int graphic)
731 if (graphic_info[graphic].bitmap)
732 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
735 void SetDoorBackgroundImageIfDefined(int graphic)
737 if (graphic_info[graphic].bitmap)
738 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
741 void SetWindowBackgroundImage(int graphic)
743 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
746 void SetMainBackgroundImage(int graphic)
748 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
751 void SetDoorBackgroundImage(int graphic)
753 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
756 void SetPanelBackground()
758 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
760 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
761 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
763 SetDoorBackgroundBitmap(bitmap_db_panel);
766 void DrawBackground(int x, int y, int width, int height)
768 /* "drawto" might still point to playfield buffer here (hall of fame) */
769 ClearRectangleOnBackground(backbuffer, x, y, width, height);
771 if (IN_GFX_FIELD_FULL(x, y))
772 redraw_mask |= REDRAW_FIELD;
773 else if (IN_GFX_DOOR_1(x, y))
774 redraw_mask |= REDRAW_DOOR_1;
775 else if (IN_GFX_DOOR_2(x, y))
776 redraw_mask |= REDRAW_DOOR_2;
777 else if (IN_GFX_DOOR_3(x, y))
778 redraw_mask |= REDRAW_DOOR_3;
781 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
783 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
785 if (font->bitmap == NULL)
788 DrawBackground(x, y, width, height);
791 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
793 struct GraphicInfo *g = &graphic_info[graphic];
795 if (g->bitmap == NULL)
798 DrawBackground(x, y, width, height);
801 static int game_status_last = -1;
802 static Bitmap *global_border_bitmap_last = NULL;
803 static Bitmap *global_border_bitmap = NULL;
804 static int real_sx_last = -1, real_sy_last = -1;
805 static int full_sxsize_last = -1, full_sysize_last = -1;
806 static int dx_last = -1, dy_last = -1;
807 static int dxsize_last = -1, dysize_last = -1;
808 static int vx_last = -1, vy_last = -1;
809 static int vxsize_last = -1, vysize_last = -1;
811 boolean CheckIfGlobalBorderHasChanged()
813 // if game status has not changed, global border has not changed either
814 if (game_status == game_status_last)
817 // determine and store new global border bitmap for current game status
818 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
820 return (global_border_bitmap_last != global_border_bitmap);
823 boolean CheckIfGlobalBorderRedrawIsNeeded()
825 // if game status has not changed, nothing has to be redrawn
826 if (game_status == game_status_last)
829 // redraw if last screen was title screen
830 if (game_status_last == GAME_MODE_TITLE)
833 // redraw if global screen border has changed
834 if (CheckIfGlobalBorderHasChanged())
837 // redraw if position or size of playfield area has changed
838 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
839 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
842 // redraw if position or size of door area has changed
843 if (dx_last != DX || dy_last != DY ||
844 dxsize_last != DXSIZE || dysize_last != DYSIZE)
847 // redraw if position or size of tape area has changed
848 if (vx_last != VX || vy_last != VY ||
849 vxsize_last != VXSIZE || vysize_last != VYSIZE)
855 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
858 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
860 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
863 void RedrawGlobalBorder()
865 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
867 RedrawGlobalBorderFromBitmap(bitmap);
869 redraw_mask = REDRAW_ALL;
872 static void RedrawGlobalBorderIfNeeded()
874 if (game_status == game_status_last)
877 // copy current draw buffer to later copy back areas that have not changed
878 if (game_status_last != GAME_MODE_TITLE)
879 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
881 if (CheckIfGlobalBorderRedrawIsNeeded())
883 // redraw global screen border (or clear, if defined to be empty)
884 RedrawGlobalBorderFromBitmap(global_border_bitmap);
886 // copy previous playfield and door areas, if they are defined on both
887 // previous and current screen and if they still have the same size
889 if (real_sx_last != -1 && real_sy_last != -1 &&
890 REAL_SX != -1 && REAL_SY != -1 &&
891 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
892 BlitBitmap(bitmap_db_store, backbuffer,
893 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
896 if (dx_last != -1 && dy_last != -1 &&
897 DX != -1 && DY != -1 &&
898 dxsize_last == DXSIZE && dysize_last == DYSIZE)
899 BlitBitmap(bitmap_db_store, backbuffer,
900 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
902 if (vx_last != -1 && vy_last != -1 &&
903 VX != -1 && VY != -1 &&
904 vxsize_last == VXSIZE && vysize_last == VYSIZE)
905 BlitBitmap(bitmap_db_store, backbuffer,
906 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
908 redraw_mask = REDRAW_ALL;
911 game_status_last = game_status;
913 global_border_bitmap_last = global_border_bitmap;
915 real_sx_last = REAL_SX;
916 real_sy_last = REAL_SY;
917 full_sxsize_last = FULL_SXSIZE;
918 full_sysize_last = FULL_SYSIZE;
921 dxsize_last = DXSIZE;
922 dysize_last = DYSIZE;
925 vxsize_last = VXSIZE;
926 vysize_last = VYSIZE;
931 RedrawGlobalBorderIfNeeded();
933 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
934 /* (when entering hall of fame after playing) */
935 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
937 /* !!! maybe this should be done before clearing the background !!! */
938 if (game_status == GAME_MODE_PLAYING)
940 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
941 SetDrawtoField(DRAW_FIELDBUFFER);
945 SetDrawtoField(DRAW_BACKBUFFER);
949 void MarkTileDirty(int x, int y)
951 redraw_mask |= REDRAW_FIELD;
954 void SetBorderElement()
958 BorderElement = EL_EMPTY;
960 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
962 for (x = 0; x < lev_fieldx; x++)
964 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
965 BorderElement = EL_STEELWALL;
967 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
973 void FloodFillLevel(int from_x, int from_y, int fill_element,
974 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
975 int max_fieldx, int max_fieldy)
979 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
980 static int safety = 0;
982 /* check if starting field still has the desired content */
983 if (field[from_x][from_y] == fill_element)
988 if (safety > max_fieldx * max_fieldy)
989 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
991 old_element = field[from_x][from_y];
992 field[from_x][from_y] = fill_element;
994 for (i = 0; i < 4; i++)
996 x = from_x + check[i][0];
997 y = from_y + check[i][1];
999 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1000 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1006 void SetRandomAnimationValue(int x, int y)
1008 gfx.anim_random_frame = GfxRandom[x][y];
1011 int getGraphicAnimationFrame(int graphic, int sync_frame)
1013 /* animation synchronized with global frame counter, not move position */
1014 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1015 sync_frame = FrameCounter;
1017 return getAnimationFrame(graphic_info[graphic].anim_frames,
1018 graphic_info[graphic].anim_delay,
1019 graphic_info[graphic].anim_mode,
1020 graphic_info[graphic].anim_start_frame,
1024 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1025 Bitmap **bitmap, int *x, int *y,
1026 boolean get_backside)
1028 struct GraphicInfo *g = &graphic_info[graphic];
1029 Bitmap *src_bitmap = g->bitmap;
1030 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1031 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1032 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1034 // if no in-game graphics defined, always use standard graphic size
1035 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1036 tilesize = TILESIZE;
1038 if (tilesize == gfx.standard_tile_size)
1039 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1040 else if (tilesize == game.tile_size)
1041 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1043 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1045 if (g->offset_y == 0) /* frames are ordered horizontally */
1047 int max_width = g->anim_frames_per_line * g->width;
1048 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1050 src_x = pos % max_width;
1051 src_y = src_y % g->height + pos / max_width * g->height;
1053 else if (g->offset_x == 0) /* frames are ordered vertically */
1055 int max_height = g->anim_frames_per_line * g->height;
1056 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1058 src_x = src_x % g->width + pos / max_height * g->width;
1059 src_y = pos % max_height;
1061 else /* frames are ordered diagonally */
1063 src_x = src_x + frame * g->offset_x;
1064 src_y = src_y + frame * g->offset_y;
1067 *bitmap = src_bitmap;
1068 *x = src_x * tilesize / g->tile_size;
1069 *y = src_y * tilesize / g->tile_size;
1072 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1073 int *x, int *y, boolean get_backside)
1075 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1079 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1080 Bitmap **bitmap, int *x, int *y)
1082 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1085 void getFixedGraphicSource(int graphic, int frame,
1086 Bitmap **bitmap, int *x, int *y)
1088 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1091 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1093 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1096 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1097 int *x, int *y, boolean get_backside)
1099 struct GraphicInfo *g = &graphic_info[graphic];
1100 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1101 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1103 if (TILESIZE_VAR != TILESIZE)
1104 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1107 *bitmap = g->bitmap;
1109 if (g->offset_y == 0) /* frames are ordered horizontally */
1111 int max_width = g->anim_frames_per_line * g->width;
1112 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1114 *x = pos % max_width;
1115 *y = src_y % g->height + pos / max_width * g->height;
1117 else if (g->offset_x == 0) /* frames are ordered vertically */
1119 int max_height = g->anim_frames_per_line * g->height;
1120 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1122 *x = src_x % g->width + pos / max_height * g->width;
1123 *y = pos % max_height;
1125 else /* frames are ordered diagonally */
1127 *x = src_x + frame * g->offset_x;
1128 *y = src_y + frame * g->offset_y;
1131 *x = *x * TILESIZE_VAR / g->tile_size;
1132 *y = *y * TILESIZE_VAR / g->tile_size;
1135 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1137 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1140 void DrawGraphic(int x, int y, int graphic, int frame)
1143 if (!IN_SCR_FIELD(x, y))
1145 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1146 printf("DrawGraphic(): This should never happen!\n");
1151 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1154 MarkTileDirty(x, y);
1157 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1160 if (!IN_SCR_FIELD(x, y))
1162 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1163 printf("DrawGraphic(): This should never happen!\n");
1168 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1170 MarkTileDirty(x, y);
1173 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1179 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1181 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1184 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1190 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1191 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1194 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1197 if (!IN_SCR_FIELD(x, y))
1199 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1200 printf("DrawGraphicThruMask(): This should never happen!\n");
1205 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1208 MarkTileDirty(x, y);
1211 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1214 if (!IN_SCR_FIELD(x, y))
1216 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1217 printf("DrawGraphicThruMask(): This should never happen!\n");
1222 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1224 MarkTileDirty(x, y);
1227 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1233 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1235 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1239 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1240 int graphic, int frame)
1245 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1247 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1251 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1253 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1255 MarkTileDirty(x / tilesize, y / tilesize);
1258 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1264 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1265 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1268 void DrawMiniGraphic(int x, int y, int graphic)
1270 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1271 MarkTileDirty(x / 2, y / 2);
1274 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1279 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1280 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1283 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1284 int graphic, int frame,
1285 int cut_mode, int mask_mode)
1290 int width = TILEX, height = TILEY;
1293 if (dx || dy) /* shifted graphic */
1295 if (x < BX1) /* object enters playfield from the left */
1302 else if (x > BX2) /* object enters playfield from the right */
1308 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1314 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1316 else if (dx) /* general horizontal movement */
1317 MarkTileDirty(x + SIGN(dx), y);
1319 if (y < BY1) /* object enters playfield from the top */
1321 if (cut_mode == CUT_BELOW) /* object completely above top border */
1329 else if (y > BY2) /* object enters playfield from the bottom */
1335 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1341 else if (dy > 0 && cut_mode == CUT_ABOVE)
1343 if (y == BY2) /* object completely above bottom border */
1349 MarkTileDirty(x, y + 1);
1350 } /* object leaves playfield to the bottom */
1351 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1353 else if (dy) /* general vertical movement */
1354 MarkTileDirty(x, y + SIGN(dy));
1358 if (!IN_SCR_FIELD(x, y))
1360 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1361 printf("DrawGraphicShifted(): This should never happen!\n");
1366 width = width * TILESIZE_VAR / TILESIZE;
1367 height = height * TILESIZE_VAR / TILESIZE;
1368 cx = cx * TILESIZE_VAR / TILESIZE;
1369 cy = cy * TILESIZE_VAR / TILESIZE;
1370 dx = dx * TILESIZE_VAR / TILESIZE;
1371 dy = dy * TILESIZE_VAR / TILESIZE;
1373 if (width > 0 && height > 0)
1375 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1380 dst_x = FX + x * TILEX_VAR + dx;
1381 dst_y = FY + y * TILEY_VAR + dy;
1383 if (mask_mode == USE_MASKING)
1384 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1387 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1390 MarkTileDirty(x, y);
1394 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1395 int graphic, int frame,
1396 int cut_mode, int mask_mode)
1401 int width = TILEX_VAR, height = TILEY_VAR;
1404 int x2 = x + SIGN(dx);
1405 int y2 = y + SIGN(dy);
1407 /* movement with two-tile animations must be sync'ed with movement position,
1408 not with current GfxFrame (which can be higher when using slow movement) */
1409 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1410 int anim_frames = graphic_info[graphic].anim_frames;
1412 /* (we also need anim_delay here for movement animations with less frames) */
1413 int anim_delay = graphic_info[graphic].anim_delay;
1414 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1416 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1417 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1419 /* re-calculate animation frame for two-tile movement animation */
1420 frame = getGraphicAnimationFrame(graphic, sync_frame);
1422 /* check if movement start graphic inside screen area and should be drawn */
1423 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1425 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1427 dst_x = FX + x1 * TILEX_VAR;
1428 dst_y = FY + y1 * TILEY_VAR;
1430 if (mask_mode == USE_MASKING)
1431 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1434 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1437 MarkTileDirty(x1, y1);
1440 /* check if movement end graphic inside screen area and should be drawn */
1441 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1443 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1445 dst_x = FX + x2 * TILEX_VAR;
1446 dst_y = FY + y2 * TILEY_VAR;
1448 if (mask_mode == USE_MASKING)
1449 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1452 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1455 MarkTileDirty(x2, y2);
1459 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1460 int graphic, int frame,
1461 int cut_mode, int mask_mode)
1465 DrawGraphic(x, y, graphic, frame);
1470 if (graphic_info[graphic].double_movement) /* EM style movement images */
1471 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1473 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1476 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1477 int frame, int cut_mode)
1479 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1482 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1483 int cut_mode, int mask_mode)
1485 int lx = LEVELX(x), ly = LEVELY(y);
1489 if (IN_LEV_FIELD(lx, ly))
1491 SetRandomAnimationValue(lx, ly);
1493 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1494 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1496 /* do not use double (EM style) movement graphic when not moving */
1497 if (graphic_info[graphic].double_movement && !dx && !dy)
1499 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1500 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1503 else /* border element */
1505 graphic = el2img(element);
1506 frame = getGraphicAnimationFrame(graphic, -1);
1509 if (element == EL_EXPANDABLE_WALL)
1511 boolean left_stopped = FALSE, right_stopped = FALSE;
1513 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1514 left_stopped = TRUE;
1515 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1516 right_stopped = TRUE;
1518 if (left_stopped && right_stopped)
1520 else if (left_stopped)
1522 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1523 frame = graphic_info[graphic].anim_frames - 1;
1525 else if (right_stopped)
1527 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1528 frame = graphic_info[graphic].anim_frames - 1;
1533 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1534 else if (mask_mode == USE_MASKING)
1535 DrawGraphicThruMask(x, y, graphic, frame);
1537 DrawGraphic(x, y, graphic, frame);
1540 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1541 int cut_mode, int mask_mode)
1543 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1544 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1545 cut_mode, mask_mode);
1548 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1551 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1554 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1557 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1560 void DrawLevelElementThruMask(int x, int y, int element)
1562 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1565 void DrawLevelFieldThruMask(int x, int y)
1567 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1570 /* !!! implementation of quicksand is totally broken !!! */
1571 #define IS_CRUMBLED_TILE(x, y, e) \
1572 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1573 !IS_MOVING(x, y) || \
1574 (e) == EL_QUICKSAND_EMPTYING || \
1575 (e) == EL_QUICKSAND_FAST_EMPTYING))
1577 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1582 int width, height, cx, cy;
1583 int sx = SCREENX(x), sy = SCREENY(y);
1584 int crumbled_border_size = graphic_info[graphic].border_size;
1587 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1589 for (i = 1; i < 4; i++)
1591 int dxx = (i & 1 ? dx : 0);
1592 int dyy = (i & 2 ? dy : 0);
1595 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1598 /* check if neighbour field is of same crumble type */
1599 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1600 graphic_info[graphic].class ==
1601 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1603 /* return if check prevents inner corner */
1604 if (same == (dxx == dx && dyy == dy))
1608 /* if we reach this point, we have an inner corner */
1610 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1612 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1613 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1614 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1615 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1617 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1618 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1621 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1626 int width, height, bx, by, cx, cy;
1627 int sx = SCREENX(x), sy = SCREENY(y);
1628 int crumbled_border_size = graphic_info[graphic].border_size;
1629 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1630 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1633 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1635 /* draw simple, sloppy, non-corner-accurate crumbled border */
1637 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1638 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1639 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1640 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1642 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1643 FX + sx * TILEX_VAR + cx,
1644 FY + sy * TILEY_VAR + cy);
1646 /* (remaining middle border part must be at least as big as corner part) */
1647 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1648 crumbled_border_size >= TILESIZE / 3)
1651 /* correct corners of crumbled border, if needed */
1653 for (i = -1; i <= 1; i += 2)
1655 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1656 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1657 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1660 /* check if neighbour field is of same crumble type */
1661 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1662 graphic_info[graphic].class ==
1663 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1665 /* no crumbled corner, but continued crumbled border */
1667 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1668 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1669 int b1 = (i == 1 ? crumbled_border_size_var :
1670 TILESIZE_VAR - 2 * crumbled_border_size_var);
1672 width = crumbled_border_size_var;
1673 height = crumbled_border_size_var;
1675 if (dir == 1 || dir == 2)
1690 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1692 FX + sx * TILEX_VAR + cx,
1693 FY + sy * TILEY_VAR + cy);
1698 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1700 int sx = SCREENX(x), sy = SCREENY(y);
1703 static int xy[4][2] =
1711 if (!IN_LEV_FIELD(x, y))
1714 element = TILE_GFX_ELEMENT(x, y);
1716 /* crumble field itself */
1717 if (IS_CRUMBLED_TILE(x, y, element))
1719 if (!IN_SCR_FIELD(sx, sy))
1722 for (i = 0; i < 4; i++)
1724 int xx = x + xy[i][0];
1725 int yy = y + xy[i][1];
1727 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1730 /* check if neighbour field is of same crumble type */
1731 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1732 graphic_info[graphic].class ==
1733 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1736 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1739 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1740 graphic_info[graphic].anim_frames == 2)
1742 for (i = 0; i < 4; i++)
1744 int dx = (i & 1 ? +1 : -1);
1745 int dy = (i & 2 ? +1 : -1);
1747 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1751 MarkTileDirty(sx, sy);
1753 else /* center field not crumbled -- crumble neighbour fields */
1755 for (i = 0; i < 4; i++)
1757 int xx = x + xy[i][0];
1758 int yy = y + xy[i][1];
1759 int sxx = sx + xy[i][0];
1760 int syy = sy + xy[i][1];
1762 if (!IN_LEV_FIELD(xx, yy) ||
1763 !IN_SCR_FIELD(sxx, syy))
1766 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1769 element = TILE_GFX_ELEMENT(xx, yy);
1771 if (!IS_CRUMBLED_TILE(xx, yy, element))
1774 graphic = el_act2crm(element, ACTION_DEFAULT);
1776 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1778 MarkTileDirty(sxx, syy);
1783 void DrawLevelFieldCrumbled(int x, int y)
1787 if (!IN_LEV_FIELD(x, y))
1790 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1791 GfxElement[x][y] != EL_UNDEFINED &&
1792 GFX_CRUMBLED(GfxElement[x][y]))
1794 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1799 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1801 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1804 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1807 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1808 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1809 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1810 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1811 int sx = SCREENX(x), sy = SCREENY(y);
1813 DrawGraphic(sx, sy, graphic1, frame1);
1814 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1817 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1819 int sx = SCREENX(x), sy = SCREENY(y);
1820 static int xy[4][2] =
1829 for (i = 0; i < 4; i++)
1831 int xx = x + xy[i][0];
1832 int yy = y + xy[i][1];
1833 int sxx = sx + xy[i][0];
1834 int syy = sy + xy[i][1];
1836 if (!IN_LEV_FIELD(xx, yy) ||
1837 !IN_SCR_FIELD(sxx, syy) ||
1838 !GFX_CRUMBLED(Feld[xx][yy]) ||
1842 DrawLevelField(xx, yy);
1846 static int getBorderElement(int x, int y)
1850 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1851 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1852 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1853 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1854 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1855 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1856 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1858 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1859 int steel_position = (x == -1 && y == -1 ? 0 :
1860 x == lev_fieldx && y == -1 ? 1 :
1861 x == -1 && y == lev_fieldy ? 2 :
1862 x == lev_fieldx && y == lev_fieldy ? 3 :
1863 x == -1 || x == lev_fieldx ? 4 :
1864 y == -1 || y == lev_fieldy ? 5 : 6);
1866 return border[steel_position][steel_type];
1869 void DrawScreenElement(int x, int y, int element)
1871 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1872 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1875 void DrawLevelElement(int x, int y, int element)
1877 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1878 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1881 void DrawScreenField(int x, int y)
1883 int lx = LEVELX(x), ly = LEVELY(y);
1884 int element, content;
1886 if (!IN_LEV_FIELD(lx, ly))
1888 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1891 element = getBorderElement(lx, ly);
1893 DrawScreenElement(x, y, element);
1898 element = Feld[lx][ly];
1899 content = Store[lx][ly];
1901 if (IS_MOVING(lx, ly))
1903 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1904 boolean cut_mode = NO_CUTTING;
1906 if (element == EL_QUICKSAND_EMPTYING ||
1907 element == EL_QUICKSAND_FAST_EMPTYING ||
1908 element == EL_MAGIC_WALL_EMPTYING ||
1909 element == EL_BD_MAGIC_WALL_EMPTYING ||
1910 element == EL_DC_MAGIC_WALL_EMPTYING ||
1911 element == EL_AMOEBA_DROPPING)
1912 cut_mode = CUT_ABOVE;
1913 else if (element == EL_QUICKSAND_FILLING ||
1914 element == EL_QUICKSAND_FAST_FILLING ||
1915 element == EL_MAGIC_WALL_FILLING ||
1916 element == EL_BD_MAGIC_WALL_FILLING ||
1917 element == EL_DC_MAGIC_WALL_FILLING)
1918 cut_mode = CUT_BELOW;
1920 if (cut_mode == CUT_ABOVE)
1921 DrawScreenElement(x, y, element);
1923 DrawScreenElement(x, y, EL_EMPTY);
1926 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1927 else if (cut_mode == NO_CUTTING)
1928 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1931 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1933 if (cut_mode == CUT_BELOW &&
1934 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1935 DrawLevelElement(lx, ly + 1, element);
1938 if (content == EL_ACID)
1940 int dir = MovDir[lx][ly];
1941 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1942 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1944 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1947 else if (IS_BLOCKED(lx, ly))
1952 boolean cut_mode = NO_CUTTING;
1953 int element_old, content_old;
1955 Blocked2Moving(lx, ly, &oldx, &oldy);
1958 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1959 MovDir[oldx][oldy] == MV_RIGHT);
1961 element_old = Feld[oldx][oldy];
1962 content_old = Store[oldx][oldy];
1964 if (element_old == EL_QUICKSAND_EMPTYING ||
1965 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1966 element_old == EL_MAGIC_WALL_EMPTYING ||
1967 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1968 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1969 element_old == EL_AMOEBA_DROPPING)
1970 cut_mode = CUT_ABOVE;
1972 DrawScreenElement(x, y, EL_EMPTY);
1975 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1977 else if (cut_mode == NO_CUTTING)
1978 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1981 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1984 else if (IS_DRAWABLE(element))
1985 DrawScreenElement(x, y, element);
1987 DrawScreenElement(x, y, EL_EMPTY);
1990 void DrawLevelField(int x, int y)
1992 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1993 DrawScreenField(SCREENX(x), SCREENY(y));
1994 else if (IS_MOVING(x, y))
1998 Moving2Blocked(x, y, &newx, &newy);
1999 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2000 DrawScreenField(SCREENX(newx), SCREENY(newy));
2002 else if (IS_BLOCKED(x, y))
2006 Blocked2Moving(x, y, &oldx, &oldy);
2007 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2008 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2012 void DrawSizedElement(int x, int y, int element, int tilesize)
2016 graphic = el2edimg(element);
2017 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2020 void DrawMiniElement(int x, int y, int element)
2024 graphic = el2edimg(element);
2025 DrawMiniGraphic(x, y, graphic);
2028 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2031 int x = sx + scroll_x, y = sy + scroll_y;
2033 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2034 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2035 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2036 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2038 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2041 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2043 int x = sx + scroll_x, y = sy + scroll_y;
2045 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2046 DrawMiniElement(sx, sy, EL_EMPTY);
2047 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2048 DrawMiniElement(sx, sy, Feld[x][y]);
2050 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2053 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2054 int x, int y, int xsize, int ysize,
2055 int tile_width, int tile_height)
2059 int dst_x = startx + x * tile_width;
2060 int dst_y = starty + y * tile_height;
2061 int width = graphic_info[graphic].width;
2062 int height = graphic_info[graphic].height;
2063 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2064 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2065 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2066 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2067 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2068 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2069 boolean draw_masked = graphic_info[graphic].draw_masked;
2071 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2073 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2075 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2079 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2080 inner_sx + (x - 1) * tile_width % inner_width);
2081 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2082 inner_sy + (y - 1) * tile_height % inner_height);
2085 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2088 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2092 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2093 int x, int y, int xsize, int ysize, int font_nr)
2095 int font_width = getFontWidth(font_nr);
2096 int font_height = getFontHeight(font_nr);
2098 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2099 font_width, font_height);
2102 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2104 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2105 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2106 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2107 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2108 boolean no_delay = (tape.warp_forward);
2109 unsigned int anim_delay = 0;
2110 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2111 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2112 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2113 int font_width = getFontWidth(font_nr);
2114 int font_height = getFontHeight(font_nr);
2115 int max_xsize = level.envelope[envelope_nr].xsize;
2116 int max_ysize = level.envelope[envelope_nr].ysize;
2117 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2118 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2119 int xend = max_xsize;
2120 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2121 int xstep = (xstart < xend ? 1 : 0);
2122 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2124 int end = MAX(xend - xstart, yend - ystart);
2127 for (i = start; i <= end; i++)
2129 int last_frame = end; // last frame of this "for" loop
2130 int x = xstart + i * xstep;
2131 int y = ystart + i * ystep;
2132 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2133 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2134 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2135 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2138 SetDrawtoField(DRAW_FIELDBUFFER);
2140 BlitScreenToBitmap(backbuffer);
2142 SetDrawtoField(DRAW_BACKBUFFER);
2144 for (yy = 0; yy < ysize; yy++)
2145 for (xx = 0; xx < xsize; xx++)
2146 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2148 DrawTextBuffer(sx + font_width, sy + font_height,
2149 level.envelope[envelope_nr].text, font_nr, max_xsize,
2150 xsize - 2, ysize - 2, 0, mask_mode,
2151 level.envelope[envelope_nr].autowrap,
2152 level.envelope[envelope_nr].centered, FALSE);
2154 redraw_mask |= REDRAW_FIELD;
2157 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2161 void ShowEnvelope(int envelope_nr)
2163 int element = EL_ENVELOPE_1 + envelope_nr;
2164 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2165 int sound_opening = element_info[element].sound[ACTION_OPENING];
2166 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2167 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2168 boolean no_delay = (tape.warp_forward);
2169 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2170 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2171 int anim_mode = graphic_info[graphic].anim_mode;
2172 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2173 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2175 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2177 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2179 if (anim_mode == ANIM_DEFAULT)
2180 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2182 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2185 Delay(wait_delay_value);
2187 WaitForEventToContinue();
2189 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2191 if (anim_mode != ANIM_NONE)
2192 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2194 if (anim_mode == ANIM_DEFAULT)
2195 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2197 game.envelope_active = FALSE;
2199 SetDrawtoField(DRAW_FIELDBUFFER);
2201 redraw_mask |= REDRAW_FIELD;
2205 static void setRequestBasePosition(int *x, int *y)
2207 int sx_base, sy_base;
2209 if (request.x != -1)
2210 sx_base = request.x;
2211 else if (request.align == ALIGN_LEFT)
2213 else if (request.align == ALIGN_RIGHT)
2214 sx_base = SX + SXSIZE;
2216 sx_base = SX + SXSIZE / 2;
2218 if (request.y != -1)
2219 sy_base = request.y;
2220 else if (request.valign == VALIGN_TOP)
2222 else if (request.valign == VALIGN_BOTTOM)
2223 sy_base = SY + SYSIZE;
2225 sy_base = SY + SYSIZE / 2;
2231 static void setRequestPositionExt(int *x, int *y, int width, int height,
2232 boolean add_border_size)
2234 int border_size = request.border_size;
2235 int sx_base, sy_base;
2238 setRequestBasePosition(&sx_base, &sy_base);
2240 if (request.align == ALIGN_LEFT)
2242 else if (request.align == ALIGN_RIGHT)
2243 sx = sx_base - width;
2245 sx = sx_base - width / 2;
2247 if (request.valign == VALIGN_TOP)
2249 else if (request.valign == VALIGN_BOTTOM)
2250 sy = sy_base - height;
2252 sy = sy_base - height / 2;
2254 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2255 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2257 if (add_border_size)
2267 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2269 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2272 void DrawEnvelopeRequest(char *text)
2274 int last_game_status = game_status; /* save current game status */
2275 char *text_final = text;
2276 char *text_door_style = NULL;
2277 int graphic = IMG_BACKGROUND_REQUEST;
2278 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2279 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2280 int font_nr = FONT_REQUEST;
2281 int font_width = getFontWidth(font_nr);
2282 int font_height = getFontHeight(font_nr);
2283 int border_size = request.border_size;
2284 int line_spacing = request.line_spacing;
2285 int line_height = font_height + line_spacing;
2286 int max_text_width = request.width - 2 * border_size;
2287 int max_text_height = request.height - 2 * border_size;
2288 int line_length = max_text_width / font_width;
2289 int max_lines = max_text_height / line_height;
2290 int text_width = line_length * font_width;
2291 int width = request.width;
2292 int height = request.height;
2293 int tile_size = MAX(request.step_offset, 1);
2294 int x_steps = width / tile_size;
2295 int y_steps = height / tile_size;
2296 int sx_offset = border_size;
2297 int sy_offset = border_size;
2301 if (request.centered)
2302 sx_offset = (request.width - text_width) / 2;
2304 if (request.wrap_single_words && !request.autowrap)
2306 char *src_text_ptr, *dst_text_ptr;
2308 text_door_style = checked_malloc(2 * strlen(text) + 1);
2310 src_text_ptr = text;
2311 dst_text_ptr = text_door_style;
2313 while (*src_text_ptr)
2315 if (*src_text_ptr == ' ' ||
2316 *src_text_ptr == '?' ||
2317 *src_text_ptr == '!')
2318 *dst_text_ptr++ = '\n';
2320 if (*src_text_ptr != ' ')
2321 *dst_text_ptr++ = *src_text_ptr;
2326 *dst_text_ptr = '\0';
2328 text_final = text_door_style;
2331 setRequestPosition(&sx, &sy, FALSE);
2333 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2335 for (y = 0; y < y_steps; y++)
2336 for (x = 0; x < x_steps; x++)
2337 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2338 x, y, x_steps, y_steps,
2339 tile_size, tile_size);
2341 /* force DOOR font inside door area */
2342 game_status = GAME_MODE_PSEUDO_DOOR;
2344 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2345 line_length, -1, max_lines, line_spacing, mask_mode,
2346 request.autowrap, request.centered, FALSE);
2348 game_status = last_game_status; /* restore current game status */
2350 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2351 RedrawGadget(tool_gadget[i]);
2353 // store readily prepared envelope request for later use when animating
2354 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2356 if (text_door_style)
2357 free(text_door_style);
2360 void AnimateEnvelopeRequest(int anim_mode, int action)
2362 int graphic = IMG_BACKGROUND_REQUEST;
2363 boolean draw_masked = graphic_info[graphic].draw_masked;
2364 int delay_value_normal = request.step_delay;
2365 int delay_value_fast = delay_value_normal / 2;
2366 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2367 boolean no_delay = (tape.warp_forward);
2368 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2369 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2370 unsigned int anim_delay = 0;
2372 int tile_size = MAX(request.step_offset, 1);
2373 int max_xsize = request.width / tile_size;
2374 int max_ysize = request.height / tile_size;
2375 int max_xsize_inner = max_xsize - 2;
2376 int max_ysize_inner = max_ysize - 2;
2378 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2379 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2380 int xend = max_xsize_inner;
2381 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2382 int xstep = (xstart < xend ? 1 : 0);
2383 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2385 int end = MAX(xend - xstart, yend - ystart);
2388 if (setup.quick_doors)
2396 if (action == ACTION_OPENING)
2397 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2398 else if (action == ACTION_CLOSING)
2399 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2402 for (i = start; i <= end; i++)
2404 int last_frame = end; // last frame of this "for" loop
2405 int x = xstart + i * xstep;
2406 int y = ystart + i * ystep;
2407 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2408 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2409 int xsize_size_left = (xsize - 1) * tile_size;
2410 int ysize_size_top = (ysize - 1) * tile_size;
2411 int max_xsize_pos = (max_xsize - 1) * tile_size;
2412 int max_ysize_pos = (max_ysize - 1) * tile_size;
2413 int width = xsize * tile_size;
2414 int height = ysize * tile_size;
2419 setRequestPosition(&src_x, &src_y, FALSE);
2420 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2422 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2424 for (yy = 0; yy < 2; yy++)
2426 for (xx = 0; xx < 2; xx++)
2428 int src_xx = src_x + xx * max_xsize_pos;
2429 int src_yy = src_y + yy * max_ysize_pos;
2430 int dst_xx = dst_x + xx * xsize_size_left;
2431 int dst_yy = dst_y + yy * ysize_size_top;
2432 int xx_size = (xx ? tile_size : xsize_size_left);
2433 int yy_size = (yy ? tile_size : ysize_size_top);
2436 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2437 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2439 BlitBitmap(bitmap_db_cross, backbuffer,
2440 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2444 redraw_mask |= REDRAW_FIELD;
2449 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2453 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2455 int graphic = IMG_BACKGROUND_REQUEST;
2456 int sound_opening = SND_REQUEST_OPENING;
2457 int sound_closing = SND_REQUEST_CLOSING;
2458 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2459 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2460 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2461 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2462 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2464 if (game_status == GAME_MODE_PLAYING)
2465 BlitScreenToBitmap(backbuffer);
2467 SetDrawtoField(DRAW_BACKBUFFER);
2469 // SetDrawBackgroundMask(REDRAW_NONE);
2471 if (action == ACTION_OPENING)
2473 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2475 if (req_state & REQ_ASK)
2477 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2478 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2480 else if (req_state & REQ_CONFIRM)
2482 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2484 else if (req_state & REQ_PLAYER)
2486 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2487 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2488 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2489 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2492 DrawEnvelopeRequest(text);
2494 if (game_status != GAME_MODE_MAIN)
2498 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2500 if (action == ACTION_OPENING)
2502 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2504 if (anim_mode == ANIM_DEFAULT)
2505 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2507 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2511 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2513 if (anim_mode != ANIM_NONE)
2514 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2516 if (anim_mode == ANIM_DEFAULT)
2517 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2520 game.envelope_active = FALSE;
2522 if (action == ACTION_CLOSING)
2524 if (game_status != GAME_MODE_MAIN)
2527 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2530 // SetDrawBackgroundMask(last_draw_background_mask);
2532 redraw_mask |= REDRAW_FIELD;
2534 if (game_status == GAME_MODE_MAIN)
2539 if (action == ACTION_CLOSING &&
2540 game_status == GAME_MODE_PLAYING &&
2541 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2542 SetDrawtoField(DRAW_FIELDBUFFER);
2545 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2549 int graphic = el2preimg(element);
2551 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2552 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2555 void DrawLevel(int draw_background_mask)
2559 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2560 SetDrawBackgroundMask(draw_background_mask);
2564 for (x = BX1; x <= BX2; x++)
2565 for (y = BY1; y <= BY2; y++)
2566 DrawScreenField(x, y);
2568 redraw_mask |= REDRAW_FIELD;
2571 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2576 for (x = 0; x < size_x; x++)
2577 for (y = 0; y < size_y; y++)
2578 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2580 redraw_mask |= REDRAW_FIELD;
2583 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2587 for (x = 0; x < size_x; x++)
2588 for (y = 0; y < size_y; y++)
2589 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2591 redraw_mask |= REDRAW_FIELD;
2594 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2596 boolean show_level_border = (BorderElement != EL_EMPTY);
2597 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2598 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2599 int tile_size = preview.tile_size;
2600 int preview_width = preview.xsize * tile_size;
2601 int preview_height = preview.ysize * tile_size;
2602 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2603 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2604 int real_preview_width = real_preview_xsize * tile_size;
2605 int real_preview_height = real_preview_ysize * tile_size;
2606 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2607 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2610 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2613 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2615 dst_x += (preview_width - real_preview_width) / 2;
2616 dst_y += (preview_height - real_preview_height) / 2;
2618 for (x = 0; x < real_preview_xsize; x++)
2620 for (y = 0; y < real_preview_ysize; y++)
2622 int lx = from_x + x + (show_level_border ? -1 : 0);
2623 int ly = from_y + y + (show_level_border ? -1 : 0);
2624 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2625 getBorderElement(lx, ly));
2627 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2628 element, tile_size);
2632 redraw_mask |= REDRAW_FIELD;
2635 #define MICROLABEL_EMPTY 0
2636 #define MICROLABEL_LEVEL_NAME 1
2637 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2638 #define MICROLABEL_LEVEL_AUTHOR 3
2639 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2640 #define MICROLABEL_IMPORTED_FROM 5
2641 #define MICROLABEL_IMPORTED_BY_HEAD 6
2642 #define MICROLABEL_IMPORTED_BY 7
2644 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2646 int max_text_width = SXSIZE;
2647 int font_width = getFontWidth(font_nr);
2649 if (pos->align == ALIGN_CENTER)
2650 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2651 else if (pos->align == ALIGN_RIGHT)
2652 max_text_width = pos->x;
2654 max_text_width = SXSIZE - pos->x;
2656 return max_text_width / font_width;
2659 static void DrawPreviewLevelLabelExt(int mode)
2661 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2662 char label_text[MAX_OUTPUT_LINESIZE + 1];
2663 int max_len_label_text;
2664 int font_nr = pos->font;
2667 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2670 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2671 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2672 mode == MICROLABEL_IMPORTED_BY_HEAD)
2673 font_nr = pos->font_alt;
2675 max_len_label_text = getMaxTextLength(pos, font_nr);
2677 if (pos->size != -1)
2678 max_len_label_text = pos->size;
2680 for (i = 0; i < max_len_label_text; i++)
2681 label_text[i] = ' ';
2682 label_text[max_len_label_text] = '\0';
2684 if (strlen(label_text) > 0)
2685 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2688 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2689 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2690 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2691 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2692 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2693 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2694 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2695 max_len_label_text);
2696 label_text[max_len_label_text] = '\0';
2698 if (strlen(label_text) > 0)
2699 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2701 redraw_mask |= REDRAW_FIELD;
2704 static void DrawPreviewLevelExt(boolean restart)
2706 static unsigned int scroll_delay = 0;
2707 static unsigned int label_delay = 0;
2708 static int from_x, from_y, scroll_direction;
2709 static int label_state, label_counter;
2710 unsigned int scroll_delay_value = preview.step_delay;
2711 boolean show_level_border = (BorderElement != EL_EMPTY);
2712 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2713 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2714 int last_game_status = game_status; /* save current game status */
2721 if (preview.anim_mode == ANIM_CENTERED)
2723 if (level_xsize > preview.xsize)
2724 from_x = (level_xsize - preview.xsize) / 2;
2725 if (level_ysize > preview.ysize)
2726 from_y = (level_ysize - preview.ysize) / 2;
2729 from_x += preview.xoffset;
2730 from_y += preview.yoffset;
2732 scroll_direction = MV_RIGHT;
2736 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2737 DrawPreviewLevelLabelExt(label_state);
2739 /* initialize delay counters */
2740 DelayReached(&scroll_delay, 0);
2741 DelayReached(&label_delay, 0);
2743 if (leveldir_current->name)
2745 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2746 char label_text[MAX_OUTPUT_LINESIZE + 1];
2747 int font_nr = pos->font;
2748 int max_len_label_text = getMaxTextLength(pos, font_nr);
2750 if (pos->size != -1)
2751 max_len_label_text = pos->size;
2753 strncpy(label_text, leveldir_current->name, max_len_label_text);
2754 label_text[max_len_label_text] = '\0';
2756 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2757 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2760 game_status = last_game_status; /* restore current game status */
2765 /* scroll preview level, if needed */
2766 if (preview.anim_mode != ANIM_NONE &&
2767 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2768 DelayReached(&scroll_delay, scroll_delay_value))
2770 switch (scroll_direction)
2775 from_x -= preview.step_offset;
2776 from_x = (from_x < 0 ? 0 : from_x);
2779 scroll_direction = MV_UP;
2783 if (from_x < level_xsize - preview.xsize)
2785 from_x += preview.step_offset;
2786 from_x = (from_x > level_xsize - preview.xsize ?
2787 level_xsize - preview.xsize : from_x);
2790 scroll_direction = MV_DOWN;
2796 from_y -= preview.step_offset;
2797 from_y = (from_y < 0 ? 0 : from_y);
2800 scroll_direction = MV_RIGHT;
2804 if (from_y < level_ysize - preview.ysize)
2806 from_y += preview.step_offset;
2807 from_y = (from_y > level_ysize - preview.ysize ?
2808 level_ysize - preview.ysize : from_y);
2811 scroll_direction = MV_LEFT;
2818 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2821 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2822 /* redraw micro level label, if needed */
2823 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2824 !strEqual(level.author, ANONYMOUS_NAME) &&
2825 !strEqual(level.author, leveldir_current->name) &&
2826 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2828 int max_label_counter = 23;
2830 if (leveldir_current->imported_from != NULL &&
2831 strlen(leveldir_current->imported_from) > 0)
2832 max_label_counter += 14;
2833 if (leveldir_current->imported_by != NULL &&
2834 strlen(leveldir_current->imported_by) > 0)
2835 max_label_counter += 14;
2837 label_counter = (label_counter + 1) % max_label_counter;
2838 label_state = (label_counter >= 0 && label_counter <= 7 ?
2839 MICROLABEL_LEVEL_NAME :
2840 label_counter >= 9 && label_counter <= 12 ?
2841 MICROLABEL_LEVEL_AUTHOR_HEAD :
2842 label_counter >= 14 && label_counter <= 21 ?
2843 MICROLABEL_LEVEL_AUTHOR :
2844 label_counter >= 23 && label_counter <= 26 ?
2845 MICROLABEL_IMPORTED_FROM_HEAD :
2846 label_counter >= 28 && label_counter <= 35 ?
2847 MICROLABEL_IMPORTED_FROM :
2848 label_counter >= 37 && label_counter <= 40 ?
2849 MICROLABEL_IMPORTED_BY_HEAD :
2850 label_counter >= 42 && label_counter <= 49 ?
2851 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2853 if (leveldir_current->imported_from == NULL &&
2854 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2855 label_state == MICROLABEL_IMPORTED_FROM))
2856 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2857 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2859 DrawPreviewLevelLabelExt(label_state);
2862 game_status = last_game_status; /* restore current game status */
2865 void DrawPreviewLevelInitial()
2867 DrawPreviewLevelExt(TRUE);
2870 void DrawPreviewLevelAnimation()
2872 DrawPreviewLevelExt(FALSE);
2875 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2876 int graphic, int sync_frame,
2879 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2881 if (mask_mode == USE_MASKING)
2882 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2884 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2887 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2888 int graphic, int sync_frame, int mask_mode)
2890 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2892 if (mask_mode == USE_MASKING)
2893 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2895 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2898 inline static void DrawGraphicAnimation(int x, int y, int graphic)
2900 int lx = LEVELX(x), ly = LEVELY(y);
2902 if (!IN_SCR_FIELD(x, y))
2905 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2906 graphic, GfxFrame[lx][ly], NO_MASKING);
2908 MarkTileDirty(x, y);
2911 void DrawFixedGraphicAnimation(int x, int y, int graphic)
2913 int lx = LEVELX(x), ly = LEVELY(y);
2915 if (!IN_SCR_FIELD(x, y))
2918 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2919 graphic, GfxFrame[lx][ly], NO_MASKING);
2920 MarkTileDirty(x, y);
2923 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2925 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2928 void DrawLevelElementAnimation(int x, int y, int element)
2930 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2932 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2935 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2937 int sx = SCREENX(x), sy = SCREENY(y);
2939 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2942 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2945 DrawGraphicAnimation(sx, sy, graphic);
2948 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2949 DrawLevelFieldCrumbled(x, y);
2951 if (GFX_CRUMBLED(Feld[x][y]))
2952 DrawLevelFieldCrumbled(x, y);
2956 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2958 int sx = SCREENX(x), sy = SCREENY(y);
2961 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2964 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2966 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2969 DrawGraphicAnimation(sx, sy, graphic);
2971 if (GFX_CRUMBLED(element))
2972 DrawLevelFieldCrumbled(x, y);
2975 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2977 if (player->use_murphy)
2979 /* this works only because currently only one player can be "murphy" ... */
2980 static int last_horizontal_dir = MV_LEFT;
2981 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2983 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2984 last_horizontal_dir = move_dir;
2986 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2988 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2990 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2996 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2999 static boolean equalGraphics(int graphic1, int graphic2)
3001 struct GraphicInfo *g1 = &graphic_info[graphic1];
3002 struct GraphicInfo *g2 = &graphic_info[graphic2];
3004 return (g1->bitmap == g2->bitmap &&
3005 g1->src_x == g2->src_x &&
3006 g1->src_y == g2->src_y &&
3007 g1->anim_frames == g2->anim_frames &&
3008 g1->anim_delay == g2->anim_delay &&
3009 g1->anim_mode == g2->anim_mode);
3012 void DrawAllPlayers()
3016 for (i = 0; i < MAX_PLAYERS; i++)
3017 if (stored_player[i].active)
3018 DrawPlayer(&stored_player[i]);
3021 void DrawPlayerField(int x, int y)
3023 if (!IS_PLAYER(x, y))
3026 DrawPlayer(PLAYERINFO(x, y));
3029 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3031 void DrawPlayer(struct PlayerInfo *player)
3033 int jx = player->jx;
3034 int jy = player->jy;
3035 int move_dir = player->MovDir;
3036 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3037 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3038 int last_jx = (player->is_moving ? jx - dx : jx);
3039 int last_jy = (player->is_moving ? jy - dy : jy);
3040 int next_jx = jx + dx;
3041 int next_jy = jy + dy;
3042 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3043 boolean player_is_opaque = FALSE;
3044 int sx = SCREENX(jx), sy = SCREENY(jy);
3045 int sxx = 0, syy = 0;
3046 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3048 int action = ACTION_DEFAULT;
3049 int last_player_graphic = getPlayerGraphic(player, move_dir);
3050 int last_player_frame = player->Frame;
3053 /* GfxElement[][] is set to the element the player is digging or collecting;
3054 remove also for off-screen player if the player is not moving anymore */
3055 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3056 GfxElement[jx][jy] = EL_UNDEFINED;
3058 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3062 if (!IN_LEV_FIELD(jx, jy))
3064 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3065 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3066 printf("DrawPlayerField(): This should never happen!\n");
3071 if (element == EL_EXPLOSION)
3074 action = (player->is_pushing ? ACTION_PUSHING :
3075 player->is_digging ? ACTION_DIGGING :
3076 player->is_collecting ? ACTION_COLLECTING :
3077 player->is_moving ? ACTION_MOVING :
3078 player->is_snapping ? ACTION_SNAPPING :
3079 player->is_dropping ? ACTION_DROPPING :
3080 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3082 if (player->is_waiting)
3083 move_dir = player->dir_waiting;
3085 InitPlayerGfxAnimation(player, action, move_dir);
3087 /* ----------------------------------------------------------------------- */
3088 /* draw things in the field the player is leaving, if needed */
3089 /* ----------------------------------------------------------------------- */
3091 if (player->is_moving)
3093 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3095 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3097 if (last_element == EL_DYNAMITE_ACTIVE ||
3098 last_element == EL_EM_DYNAMITE_ACTIVE ||
3099 last_element == EL_SP_DISK_RED_ACTIVE)
3100 DrawDynamite(last_jx, last_jy);
3102 DrawLevelFieldThruMask(last_jx, last_jy);
3104 else if (last_element == EL_DYNAMITE_ACTIVE ||
3105 last_element == EL_EM_DYNAMITE_ACTIVE ||
3106 last_element == EL_SP_DISK_RED_ACTIVE)
3107 DrawDynamite(last_jx, last_jy);
3109 /* !!! this is not enough to prevent flickering of players which are
3110 moving next to each others without a free tile between them -- this
3111 can only be solved by drawing all players layer by layer (first the
3112 background, then the foreground etc.) !!! => TODO */
3113 else if (!IS_PLAYER(last_jx, last_jy))
3114 DrawLevelField(last_jx, last_jy);
3117 DrawLevelField(last_jx, last_jy);
3120 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3121 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3124 if (!IN_SCR_FIELD(sx, sy))
3127 /* ----------------------------------------------------------------------- */
3128 /* draw things behind the player, if needed */
3129 /* ----------------------------------------------------------------------- */
3132 DrawLevelElement(jx, jy, Back[jx][jy]);
3133 else if (IS_ACTIVE_BOMB(element))
3134 DrawLevelElement(jx, jy, EL_EMPTY);
3137 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3139 int old_element = GfxElement[jx][jy];
3140 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3141 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3143 if (GFX_CRUMBLED(old_element))
3144 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3146 DrawGraphic(sx, sy, old_graphic, frame);
3148 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3149 player_is_opaque = TRUE;
3153 GfxElement[jx][jy] = EL_UNDEFINED;
3155 /* make sure that pushed elements are drawn with correct frame rate */
3156 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3158 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3159 GfxFrame[jx][jy] = player->StepFrame;
3161 DrawLevelField(jx, jy);
3165 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3166 /* ----------------------------------------------------------------------- */
3167 /* draw player himself */
3168 /* ----------------------------------------------------------------------- */
3170 graphic = getPlayerGraphic(player, move_dir);
3172 /* in the case of changed player action or direction, prevent the current
3173 animation frame from being restarted for identical animations */
3174 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3175 player->Frame = last_player_frame;
3177 frame = getGraphicAnimationFrame(graphic, player->Frame);
3181 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3182 sxx = player->GfxPos;
3184 syy = player->GfxPos;
3187 if (player_is_opaque)
3188 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3190 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3192 if (SHIELD_ON(player))
3194 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3195 IMG_SHIELD_NORMAL_ACTIVE);
3196 int frame = getGraphicAnimationFrame(graphic, -1);
3198 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3202 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3205 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3206 sxx = player->GfxPos;
3208 syy = player->GfxPos;
3212 /* ----------------------------------------------------------------------- */
3213 /* draw things the player is pushing, if needed */
3214 /* ----------------------------------------------------------------------- */
3216 if (player->is_pushing && player->is_moving)
3218 int px = SCREENX(jx), py = SCREENY(jy);
3219 int pxx = (TILEX - ABS(sxx)) * dx;
3220 int pyy = (TILEY - ABS(syy)) * dy;
3221 int gfx_frame = GfxFrame[jx][jy];
3227 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3229 element = Feld[next_jx][next_jy];
3230 gfx_frame = GfxFrame[next_jx][next_jy];
3233 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3235 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3236 frame = getGraphicAnimationFrame(graphic, sync_frame);
3238 /* draw background element under pushed element (like the Sokoban field) */
3239 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3241 /* this allows transparent pushing animation over non-black background */
3244 DrawLevelElement(jx, jy, Back[jx][jy]);
3246 DrawLevelElement(jx, jy, EL_EMPTY);
3248 if (Back[next_jx][next_jy])
3249 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3251 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3253 else if (Back[next_jx][next_jy])
3254 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3257 /* do not draw (EM style) pushing animation when pushing is finished */
3258 /* (two-tile animations usually do not contain start and end frame) */
3259 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3260 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3262 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3264 /* masked drawing is needed for EMC style (double) movement graphics */
3265 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3266 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3270 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3271 /* ----------------------------------------------------------------------- */
3272 /* draw player himself */
3273 /* ----------------------------------------------------------------------- */
3275 graphic = getPlayerGraphic(player, move_dir);
3277 /* in the case of changed player action or direction, prevent the current
3278 animation frame from being restarted for identical animations */
3279 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3280 player->Frame = last_player_frame;
3282 frame = getGraphicAnimationFrame(graphic, player->Frame);
3286 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3287 sxx = player->GfxPos;
3289 syy = player->GfxPos;
3292 if (player_is_opaque)
3293 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3295 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3297 if (SHIELD_ON(player))
3299 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3300 IMG_SHIELD_NORMAL_ACTIVE);
3301 int frame = getGraphicAnimationFrame(graphic, -1);
3303 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3307 /* ----------------------------------------------------------------------- */
3308 /* draw things in front of player (active dynamite or dynabombs) */
3309 /* ----------------------------------------------------------------------- */
3311 if (IS_ACTIVE_BOMB(element))
3313 graphic = el2img(element);
3314 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3316 if (game.emulation == EMU_SUPAPLEX)
3317 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3319 DrawGraphicThruMask(sx, sy, graphic, frame);
3322 if (player_is_moving && last_element == EL_EXPLOSION)
3324 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3325 GfxElement[last_jx][last_jy] : EL_EMPTY);
3326 int graphic = el_act2img(element, ACTION_EXPLODING);
3327 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3328 int phase = ExplodePhase[last_jx][last_jy] - 1;
3329 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3332 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3335 /* ----------------------------------------------------------------------- */
3336 /* draw elements the player is just walking/passing through/under */
3337 /* ----------------------------------------------------------------------- */
3339 if (player_is_moving)
3341 /* handle the field the player is leaving ... */
3342 if (IS_ACCESSIBLE_INSIDE(last_element))
3343 DrawLevelField(last_jx, last_jy);
3344 else if (IS_ACCESSIBLE_UNDER(last_element))
3345 DrawLevelFieldThruMask(last_jx, last_jy);
3348 /* do not redraw accessible elements if the player is just pushing them */
3349 if (!player_is_moving || !player->is_pushing)
3351 /* ... and the field the player is entering */
3352 if (IS_ACCESSIBLE_INSIDE(element))
3353 DrawLevelField(jx, jy);
3354 else if (IS_ACCESSIBLE_UNDER(element))
3355 DrawLevelFieldThruMask(jx, jy);
3358 MarkTileDirty(sx, sy);
3361 /* ------------------------------------------------------------------------- */
3363 void WaitForEventToContinue()
3365 boolean still_wait = TRUE;
3367 /* simulate releasing mouse button over last gadget, if still pressed */
3369 HandleGadgets(-1, -1, 0);
3371 button_status = MB_RELEASED;
3385 case EVENT_BUTTONPRESS:
3386 case EVENT_KEYPRESS:
3390 case EVENT_KEYRELEASE:
3391 ClearPlayerAction();
3395 HandleOtherEvents(&event);
3399 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3406 /* don't eat all CPU time */
3411 #define MAX_REQUEST_LINES 13
3412 #define MAX_REQUEST_LINE_FONT1_LEN 7
3413 #define MAX_REQUEST_LINE_FONT2_LEN 10
3415 static int RequestHandleEvents(unsigned int req_state)
3417 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3418 local_player->LevelSolved_GameEnd);
3419 int width = request.width;
3420 int height = request.height;
3424 setRequestPosition(&sx, &sy, FALSE);
3426 button_status = MB_RELEASED;
3428 request_gadget_id = -1;
3435 SetDrawtoField(DRAW_FIELDBUFFER);
3437 HandleGameActions();
3439 SetDrawtoField(DRAW_BACKBUFFER);
3441 if (global.use_envelope_request)
3443 /* copy current state of request area to middle of playfield area */
3444 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3452 while (NextValidEvent(&event))
3456 case EVENT_BUTTONPRESS:
3457 case EVENT_BUTTONRELEASE:
3458 case EVENT_MOTIONNOTIFY:
3462 if (event.type == EVENT_MOTIONNOTIFY)
3467 motion_status = TRUE;
3468 mx = ((MotionEvent *) &event)->x;
3469 my = ((MotionEvent *) &event)->y;
3473 motion_status = FALSE;
3474 mx = ((ButtonEvent *) &event)->x;
3475 my = ((ButtonEvent *) &event)->y;
3476 if (event.type == EVENT_BUTTONPRESS)
3477 button_status = ((ButtonEvent *) &event)->button;
3479 button_status = MB_RELEASED;
3482 /* this sets 'request_gadget_id' */
3483 HandleGadgets(mx, my, button_status);
3485 switch (request_gadget_id)
3487 case TOOL_CTRL_ID_YES:
3490 case TOOL_CTRL_ID_NO:
3493 case TOOL_CTRL_ID_CONFIRM:
3494 result = TRUE | FALSE;
3497 case TOOL_CTRL_ID_PLAYER_1:
3500 case TOOL_CTRL_ID_PLAYER_2:
3503 case TOOL_CTRL_ID_PLAYER_3:
3506 case TOOL_CTRL_ID_PLAYER_4:
3517 case EVENT_KEYPRESS:
3518 switch (GetEventKey((KeyEvent *)&event, TRUE))
3521 if (req_state & REQ_CONFIRM)
3526 #if defined(TARGET_SDL2)
3533 #if defined(TARGET_SDL2)
3543 if (req_state & REQ_PLAYER)
3547 case EVENT_KEYRELEASE:
3548 ClearPlayerAction();
3552 HandleOtherEvents(&event);
3557 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3559 int joy = AnyJoystick();
3561 if (joy & JOY_BUTTON_1)
3563 else if (joy & JOY_BUTTON_2)
3569 if (global.use_envelope_request)
3571 /* copy back current state of pressed buttons inside request area */
3572 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3579 if (!PendingEvent()) /* delay only if no pending events */
3589 static boolean RequestDoor(char *text, unsigned int req_state)
3591 unsigned int old_door_state;
3592 int last_game_status = game_status; /* save current game status */
3593 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3594 int font_nr = FONT_TEXT_2;
3599 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3601 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3602 font_nr = FONT_TEXT_1;
3605 if (game_status == GAME_MODE_PLAYING)
3606 BlitScreenToBitmap(backbuffer);
3608 /* disable deactivated drawing when quick-loading level tape recording */
3609 if (tape.playing && tape.deactivate_display)
3610 TapeDeactivateDisplayOff(TRUE);
3612 SetMouseCursor(CURSOR_DEFAULT);
3614 #if defined(NETWORK_AVALIABLE)
3615 /* pause network game while waiting for request to answer */
3616 if (options.network &&
3617 game_status == GAME_MODE_PLAYING &&
3618 req_state & REQUEST_WAIT_FOR_INPUT)
3619 SendToServer_PausePlaying();
3622 old_door_state = GetDoorState();
3624 /* simulate releasing mouse button over last gadget, if still pressed */
3626 HandleGadgets(-1, -1, 0);
3630 /* draw released gadget before proceeding */
3633 if (old_door_state & DOOR_OPEN_1)
3635 CloseDoor(DOOR_CLOSE_1);
3637 /* save old door content */
3638 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3639 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3642 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3643 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3645 /* clear door drawing field */
3646 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3648 /* force DOOR font inside door area */
3649 game_status = GAME_MODE_PSEUDO_DOOR;
3651 /* write text for request */
3652 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3654 char text_line[max_request_line_len + 1];
3660 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3662 tc = *(text_ptr + tx);
3663 // if (!tc || tc == ' ')
3664 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3668 if ((tc == '?' || tc == '!') && tl == 0)
3678 strncpy(text_line, text_ptr, tl);
3681 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3682 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3683 text_line, font_nr);
3685 text_ptr += tl + (tc == ' ' ? 1 : 0);
3686 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3689 game_status = last_game_status; /* restore current game status */
3691 if (req_state & REQ_ASK)
3693 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3694 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3696 else if (req_state & REQ_CONFIRM)
3698 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3700 else if (req_state & REQ_PLAYER)
3702 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3703 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3704 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3705 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3708 /* copy request gadgets to door backbuffer */
3709 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3711 OpenDoor(DOOR_OPEN_1);
3713 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3715 if (game_status == GAME_MODE_PLAYING)
3717 SetPanelBackground();
3718 SetDrawBackgroundMask(REDRAW_DOOR_1);
3722 SetDrawBackgroundMask(REDRAW_FIELD);
3728 if (game_status != GAME_MODE_MAIN)
3731 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3733 // ---------- handle request buttons ----------
3734 result = RequestHandleEvents(req_state);
3736 if (game_status != GAME_MODE_MAIN)
3741 if (!(req_state & REQ_STAY_OPEN))
3743 CloseDoor(DOOR_CLOSE_1);
3745 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3746 (req_state & REQ_REOPEN))
3747 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3752 if (game_status == GAME_MODE_PLAYING)
3754 SetPanelBackground();
3755 SetDrawBackgroundMask(REDRAW_DOOR_1);
3759 SetDrawBackgroundMask(REDRAW_FIELD);
3762 #if defined(NETWORK_AVALIABLE)
3763 /* continue network game after request */
3764 if (options.network &&
3765 game_status == GAME_MODE_PLAYING &&
3766 req_state & REQUEST_WAIT_FOR_INPUT)
3767 SendToServer_ContinuePlaying();
3770 /* restore deactivated drawing when quick-loading level tape recording */
3771 if (tape.playing && tape.deactivate_display)
3772 TapeDeactivateDisplayOn();
3777 static boolean RequestEnvelope(char *text, unsigned int req_state)
3781 if (game_status == GAME_MODE_PLAYING)
3782 BlitScreenToBitmap(backbuffer);
3784 /* disable deactivated drawing when quick-loading level tape recording */
3785 if (tape.playing && tape.deactivate_display)
3786 TapeDeactivateDisplayOff(TRUE);
3788 SetMouseCursor(CURSOR_DEFAULT);
3790 #if defined(NETWORK_AVALIABLE)
3791 /* pause network game while waiting for request to answer */
3792 if (options.network &&
3793 game_status == GAME_MODE_PLAYING &&
3794 req_state & REQUEST_WAIT_FOR_INPUT)
3795 SendToServer_PausePlaying();
3798 /* simulate releasing mouse button over last gadget, if still pressed */
3800 HandleGadgets(-1, -1, 0);
3804 // (replace with setting corresponding request background)
3805 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3806 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3808 /* clear door drawing field */
3809 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3811 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3813 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3815 if (game_status == GAME_MODE_PLAYING)
3817 SetPanelBackground();
3818 SetDrawBackgroundMask(REDRAW_DOOR_1);
3822 SetDrawBackgroundMask(REDRAW_FIELD);
3828 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3830 // ---------- handle request buttons ----------
3831 result = RequestHandleEvents(req_state);
3833 if (game_status != GAME_MODE_MAIN)
3838 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3842 if (game_status == GAME_MODE_PLAYING)
3844 SetPanelBackground();
3845 SetDrawBackgroundMask(REDRAW_DOOR_1);
3849 SetDrawBackgroundMask(REDRAW_FIELD);
3852 #if defined(NETWORK_AVALIABLE)
3853 /* continue network game after request */
3854 if (options.network &&
3855 game_status == GAME_MODE_PLAYING &&
3856 req_state & REQUEST_WAIT_FOR_INPUT)
3857 SendToServer_ContinuePlaying();
3860 /* restore deactivated drawing when quick-loading level tape recording */
3861 if (tape.playing && tape.deactivate_display)
3862 TapeDeactivateDisplayOn();
3867 boolean Request(char *text, unsigned int req_state)
3869 if (global.use_envelope_request)
3870 return RequestEnvelope(text, req_state);
3872 return RequestDoor(text, req_state);
3875 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3877 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3878 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3881 if (dpo1->sort_priority != dpo2->sort_priority)
3882 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3884 compare_result = dpo1->nr - dpo2->nr;
3886 return compare_result;
3889 void InitGraphicCompatibilityInfo_Doors()
3895 struct DoorInfo *door;
3899 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3900 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3902 { -1, -1, -1, NULL }
3904 struct Rect door_rect_list[] =
3906 { DX, DY, DXSIZE, DYSIZE },
3907 { VX, VY, VXSIZE, VYSIZE }
3911 for (i = 0; doors[i].door_token != -1; i++)
3913 int door_token = doors[i].door_token;
3914 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3915 int part_1 = doors[i].part_1;
3916 int part_8 = doors[i].part_8;
3917 int part_2 = part_1 + 1;
3918 int part_3 = part_1 + 2;
3919 struct DoorInfo *door = doors[i].door;
3920 struct Rect *door_rect = &door_rect_list[door_index];
3921 boolean door_gfx_redefined = FALSE;
3923 /* check if any door part graphic definitions have been redefined */
3925 for (j = 0; door_part_controls[j].door_token != -1; j++)
3927 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3928 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3930 if (dpc->door_token == door_token && fi->redefined)
3931 door_gfx_redefined = TRUE;
3934 /* check for old-style door graphic/animation modifications */
3936 if (!door_gfx_redefined)
3938 if (door->anim_mode & ANIM_STATIC_PANEL)
3940 door->panel.step_xoffset = 0;
3941 door->panel.step_yoffset = 0;
3944 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3946 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3947 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3948 int num_door_steps, num_panel_steps;
3950 /* remove door part graphics other than the two default wings */
3952 for (j = 0; door_part_controls[j].door_token != -1; j++)
3954 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3955 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3957 if (dpc->graphic >= part_3 &&
3958 dpc->graphic <= part_8)
3962 /* set graphics and screen positions of the default wings */
3964 g_part_1->width = door_rect->width;
3965 g_part_1->height = door_rect->height;
3966 g_part_2->width = door_rect->width;
3967 g_part_2->height = door_rect->height;
3968 g_part_2->src_x = door_rect->width;
3969 g_part_2->src_y = g_part_1->src_y;
3971 door->part_2.x = door->part_1.x;
3972 door->part_2.y = door->part_1.y;
3974 if (door->width != -1)
3976 g_part_1->width = door->width;
3977 g_part_2->width = door->width;
3979 // special treatment for graphics and screen position of right wing
3980 g_part_2->src_x += door_rect->width - door->width;
3981 door->part_2.x += door_rect->width - door->width;
3984 if (door->height != -1)
3986 g_part_1->height = door->height;
3987 g_part_2->height = door->height;
3989 // special treatment for graphics and screen position of bottom wing
3990 g_part_2->src_y += door_rect->height - door->height;
3991 door->part_2.y += door_rect->height - door->height;
3994 /* set animation delays for the default wings and panels */
3996 door->part_1.step_delay = door->step_delay;
3997 door->part_2.step_delay = door->step_delay;
3998 door->panel.step_delay = door->step_delay;
4000 /* set animation draw order for the default wings */
4002 door->part_1.sort_priority = 2; /* draw left wing over ... */
4003 door->part_2.sort_priority = 1; /* ... right wing */
4005 /* set animation draw offset for the default wings */
4007 if (door->anim_mode & ANIM_HORIZONTAL)
4009 door->part_1.step_xoffset = door->step_offset;
4010 door->part_1.step_yoffset = 0;
4011 door->part_2.step_xoffset = door->step_offset * -1;
4012 door->part_2.step_yoffset = 0;
4014 num_door_steps = g_part_1->width / door->step_offset;
4016 else // ANIM_VERTICAL
4018 door->part_1.step_xoffset = 0;
4019 door->part_1.step_yoffset = door->step_offset;
4020 door->part_2.step_xoffset = 0;
4021 door->part_2.step_yoffset = door->step_offset * -1;
4023 num_door_steps = g_part_1->height / door->step_offset;
4026 /* set animation draw offset for the default panels */
4028 if (door->step_offset > 1)
4030 num_panel_steps = 2 * door_rect->height / door->step_offset;
4031 door->panel.start_step = num_panel_steps - num_door_steps;
4032 door->panel.start_step_closing = door->panel.start_step;
4036 num_panel_steps = door_rect->height / door->step_offset;
4037 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4038 door->panel.start_step_closing = door->panel.start_step;
4039 door->panel.step_delay *= 2;
4050 for (i = 0; door_part_controls[i].door_token != -1; i++)
4052 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4053 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4055 /* initialize "start_step_opening" and "start_step_closing", if needed */
4056 if (dpc->pos->start_step_opening == 0 &&
4057 dpc->pos->start_step_closing == 0)
4059 // dpc->pos->start_step_opening = dpc->pos->start_step;
4060 dpc->pos->start_step_closing = dpc->pos->start_step;
4063 /* fill structure for door part draw order (sorted below) */
4065 dpo->sort_priority = dpc->pos->sort_priority;
4068 /* sort door part controls according to sort_priority and graphic number */
4069 qsort(door_part_order, MAX_DOOR_PARTS,
4070 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4073 unsigned int OpenDoor(unsigned int door_state)
4075 if (door_state & DOOR_COPY_BACK)
4077 if (door_state & DOOR_OPEN_1)
4078 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4079 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4081 if (door_state & DOOR_OPEN_2)
4082 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4083 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4085 door_state &= ~DOOR_COPY_BACK;
4088 return MoveDoor(door_state);
4091 unsigned int CloseDoor(unsigned int door_state)
4093 unsigned int old_door_state = GetDoorState();
4095 if (!(door_state & DOOR_NO_COPY_BACK))
4097 if (old_door_state & DOOR_OPEN_1)
4098 BlitBitmap(backbuffer, bitmap_db_door_1,
4099 DX, DY, DXSIZE, DYSIZE, 0, 0);
4101 if (old_door_state & DOOR_OPEN_2)
4102 BlitBitmap(backbuffer, bitmap_db_door_2,
4103 VX, VY, VXSIZE, VYSIZE, 0, 0);
4105 door_state &= ~DOOR_NO_COPY_BACK;
4108 return MoveDoor(door_state);
4111 unsigned int GetDoorState()
4113 return MoveDoor(DOOR_GET_STATE);
4116 unsigned int SetDoorState(unsigned int door_state)
4118 return MoveDoor(door_state | DOOR_SET_STATE);
4121 int euclid(int a, int b)
4123 return (b ? euclid(b, a % b) : a);
4126 unsigned int MoveDoor(unsigned int door_state)
4128 struct Rect door_rect_list[] =
4130 { DX, DY, DXSIZE, DYSIZE },
4131 { VX, VY, VXSIZE, VYSIZE }
4133 static int door1 = DOOR_OPEN_1;
4134 static int door2 = DOOR_CLOSE_2;
4135 unsigned int door_delay = 0;
4136 unsigned int door_delay_value;
4139 if (door_state == DOOR_GET_STATE)
4140 return (door1 | door2);
4142 if (door_state & DOOR_SET_STATE)
4144 if (door_state & DOOR_ACTION_1)
4145 door1 = door_state & DOOR_ACTION_1;
4146 if (door_state & DOOR_ACTION_2)
4147 door2 = door_state & DOOR_ACTION_2;
4149 return (door1 | door2);
4152 if (!(door_state & DOOR_FORCE_REDRAW))
4154 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4155 door_state &= ~DOOR_OPEN_1;
4156 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4157 door_state &= ~DOOR_CLOSE_1;
4158 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4159 door_state &= ~DOOR_OPEN_2;
4160 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4161 door_state &= ~DOOR_CLOSE_2;
4164 if (global.autoplay_leveldir)
4166 door_state |= DOOR_NO_DELAY;
4167 door_state &= ~DOOR_CLOSE_ALL;
4170 if (game_status == GAME_MODE_EDITOR)
4171 door_state |= DOOR_NO_DELAY;
4173 if (door_state & DOOR_ACTION)
4175 boolean door_panel_drawn[NUM_DOORS];
4176 boolean panel_has_doors[NUM_DOORS];
4177 boolean door_part_skip[MAX_DOOR_PARTS];
4178 boolean door_part_done[MAX_DOOR_PARTS];
4179 boolean door_part_done_all;
4180 int num_steps[MAX_DOOR_PARTS];
4181 int max_move_delay = 0; // delay for complete animations of all doors
4182 int max_step_delay = 0; // delay (ms) between two animation frames
4183 int num_move_steps = 0; // number of animation steps for all doors
4184 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4185 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4186 int current_move_delay = 0;
4190 for (i = 0; i < NUM_DOORS; i++)
4191 panel_has_doors[i] = FALSE;
4193 for (i = 0; i < MAX_DOOR_PARTS; i++)
4195 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4196 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4197 int door_token = dpc->door_token;
4199 door_part_done[i] = FALSE;
4200 door_part_skip[i] = (!(door_state & door_token) ||
4204 for (i = 0; i < MAX_DOOR_PARTS; i++)
4206 int nr = door_part_order[i].nr;
4207 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4208 struct DoorPartPosInfo *pos = dpc->pos;
4209 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4210 int door_token = dpc->door_token;
4211 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4212 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4213 int step_xoffset = ABS(pos->step_xoffset);
4214 int step_yoffset = ABS(pos->step_yoffset);
4215 int step_delay = pos->step_delay;
4216 int current_door_state = door_state & door_token;
4217 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4218 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4219 boolean part_opening = (is_panel ? door_closing : door_opening);
4220 int start_step = (part_opening ? pos->start_step_opening :
4221 pos->start_step_closing);
4222 float move_xsize = (step_xoffset ? g->width : 0);
4223 float move_ysize = (step_yoffset ? g->height : 0);
4224 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4225 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4226 int move_steps = (move_xsteps && move_ysteps ?
4227 MIN(move_xsteps, move_ysteps) :
4228 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4229 int move_delay = move_steps * step_delay;
4231 if (door_part_skip[nr])
4234 max_move_delay = MAX(max_move_delay, move_delay);
4235 max_step_delay = (max_step_delay == 0 ? step_delay :
4236 euclid(max_step_delay, step_delay));
4237 num_steps[nr] = move_steps;
4241 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4243 panel_has_doors[door_index] = TRUE;
4247 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4249 num_move_steps = max_move_delay / max_step_delay;
4250 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4252 door_delay_value = max_step_delay;
4254 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4256 start = num_move_steps - 1;
4260 /* opening door sound has priority over simultaneously closing door */
4261 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4262 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4263 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4264 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4267 for (k = start; k < num_move_steps; k++)
4269 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4271 door_part_done_all = TRUE;
4273 for (i = 0; i < NUM_DOORS; i++)
4274 door_panel_drawn[i] = FALSE;
4276 for (i = 0; i < MAX_DOOR_PARTS; i++)
4278 int nr = door_part_order[i].nr;
4279 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4280 struct DoorPartPosInfo *pos = dpc->pos;
4281 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4282 int door_token = dpc->door_token;
4283 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4284 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4285 boolean is_panel_and_door_has_closed = FALSE;
4286 struct Rect *door_rect = &door_rect_list[door_index];
4287 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4289 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4290 int current_door_state = door_state & door_token;
4291 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4292 boolean door_closing = !door_opening;
4293 boolean part_opening = (is_panel ? door_closing : door_opening);
4294 boolean part_closing = !part_opening;
4295 int start_step = (part_opening ? pos->start_step_opening :
4296 pos->start_step_closing);
4297 int step_delay = pos->step_delay;
4298 int step_factor = step_delay / max_step_delay;
4299 int k1 = (step_factor ? k / step_factor + 1 : k);
4300 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4301 int kk = MAX(0, k2);
4304 int src_x, src_y, src_xx, src_yy;
4305 int dst_x, dst_y, dst_xx, dst_yy;
4308 if (door_part_skip[nr])
4311 if (!(door_state & door_token))
4319 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4320 int kk_door = MAX(0, k2_door);
4321 int sync_frame = kk_door * door_delay_value;
4322 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4324 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4329 if (!door_panel_drawn[door_index])
4331 ClearRectangle(drawto, door_rect->x, door_rect->y,
4332 door_rect->width, door_rect->height);
4334 door_panel_drawn[door_index] = TRUE;
4337 // draw opening or closing door parts
4339 if (pos->step_xoffset < 0) // door part on right side
4342 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4345 if (dst_xx + width > door_rect->width)
4346 width = door_rect->width - dst_xx;
4348 else // door part on left side
4351 dst_xx = pos->x - kk * pos->step_xoffset;
4355 src_xx = ABS(dst_xx);
4359 width = g->width - src_xx;
4361 if (width > door_rect->width)
4362 width = door_rect->width;
4364 // printf("::: k == %d [%d] \n", k, start_step);
4367 if (pos->step_yoffset < 0) // door part on bottom side
4370 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4373 if (dst_yy + height > door_rect->height)
4374 height = door_rect->height - dst_yy;
4376 else // door part on top side
4379 dst_yy = pos->y - kk * pos->step_yoffset;
4383 src_yy = ABS(dst_yy);
4387 height = g->height - src_yy;
4390 src_x = g_src_x + src_xx;
4391 src_y = g_src_y + src_yy;
4393 dst_x = door_rect->x + dst_xx;
4394 dst_y = door_rect->y + dst_yy;
4396 is_panel_and_door_has_closed =
4399 panel_has_doors[door_index] &&
4400 k >= num_move_steps_doors_only - 1);
4402 if (width >= 0 && width <= g->width &&
4403 height >= 0 && height <= g->height &&
4404 !is_panel_and_door_has_closed)
4406 if (is_panel || !pos->draw_masked)
4407 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4410 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4414 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4416 if ((part_opening && (width < 0 || height < 0)) ||
4417 (part_closing && (width >= g->width && height >= g->height)))
4418 door_part_done[nr] = TRUE;
4420 // continue door part animations, but not panel after door has closed
4421 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4422 door_part_done_all = FALSE;
4425 if (!(door_state & DOOR_NO_DELAY))
4429 if (game_status == GAME_MODE_MAIN)
4432 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4434 current_move_delay += max_step_delay;
4437 if (door_part_done_all)
4442 if (door_state & DOOR_ACTION_1)
4443 door1 = door_state & DOOR_ACTION_1;
4444 if (door_state & DOOR_ACTION_2)
4445 door2 = door_state & DOOR_ACTION_2;
4447 return (door1 | door2);
4450 static boolean useSpecialEditorDoor()
4452 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4453 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4455 // do not draw special editor door if editor border defined or redefined
4456 if (graphic_info[graphic].bitmap != NULL || redefined)
4459 // do not draw special editor door if global border defined to be empty
4460 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4463 // do not draw special editor door if viewport definitions do not match
4467 EY + EYSIZE != VY + VYSIZE)
4473 void DrawSpecialEditorDoor()
4475 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4476 int top_border_width = gfx1->width;
4477 int top_border_height = gfx1->height;
4478 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4479 int ex = EX - outer_border;
4480 int ey = EY - outer_border;
4481 int vy = VY - outer_border;
4482 int exsize = EXSIZE + 2 * outer_border;
4484 if (!useSpecialEditorDoor())
4487 /* draw bigger level editor toolbox window */
4488 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4489 top_border_width, top_border_height, ex, ey - top_border_height);
4490 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4491 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4493 redraw_mask |= REDRAW_ALL;
4496 void UndrawSpecialEditorDoor()
4498 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4499 int top_border_width = gfx1->width;
4500 int top_border_height = gfx1->height;
4501 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4502 int ex = EX - outer_border;
4503 int ey = EY - outer_border;
4504 int ey_top = ey - top_border_height;
4505 int exsize = EXSIZE + 2 * outer_border;
4506 int eysize = EYSIZE + 2 * outer_border;
4508 if (!useSpecialEditorDoor())
4511 /* draw normal tape recorder window */
4512 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4514 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4515 ex, ey_top, top_border_width, top_border_height,
4517 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4518 ex, ey, exsize, eysize, ex, ey);
4522 // if screen background is set to "[NONE]", clear editor toolbox window
4523 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4524 ClearRectangle(drawto, ex, ey, exsize, eysize);
4527 redraw_mask |= REDRAW_ALL;
4531 /* ---------- new tool button stuff ---------------------------------------- */
4536 struct TextPosInfo *pos;
4539 } toolbutton_info[NUM_TOOL_BUTTONS] =
4542 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4543 TOOL_CTRL_ID_YES, "yes"
4546 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4547 TOOL_CTRL_ID_NO, "no"
4550 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4551 TOOL_CTRL_ID_CONFIRM, "confirm"
4554 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4555 TOOL_CTRL_ID_PLAYER_1, "player 1"
4558 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4559 TOOL_CTRL_ID_PLAYER_2, "player 2"
4562 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4563 TOOL_CTRL_ID_PLAYER_3, "player 3"
4566 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4567 TOOL_CTRL_ID_PLAYER_4, "player 4"
4571 void CreateToolButtons()
4575 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4577 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4578 struct TextPosInfo *pos = toolbutton_info[i].pos;
4579 struct GadgetInfo *gi;
4580 Bitmap *deco_bitmap = None;
4581 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4582 unsigned int event_mask = GD_EVENT_RELEASED;
4585 int gd_x = gfx->src_x;
4586 int gd_y = gfx->src_y;
4587 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4588 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4591 if (global.use_envelope_request)
4592 setRequestPosition(&dx, &dy, TRUE);
4594 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4596 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4598 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4599 pos->size, &deco_bitmap, &deco_x, &deco_y);
4600 deco_xpos = (gfx->width - pos->size) / 2;
4601 deco_ypos = (gfx->height - pos->size) / 2;
4604 gi = CreateGadget(GDI_CUSTOM_ID, id,
4605 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4606 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4607 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4608 GDI_WIDTH, gfx->width,
4609 GDI_HEIGHT, gfx->height,
4610 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4611 GDI_STATE, GD_BUTTON_UNPRESSED,
4612 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4613 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4614 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4615 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4616 GDI_DECORATION_SIZE, pos->size, pos->size,
4617 GDI_DECORATION_SHIFTING, 1, 1,
4618 GDI_DIRECT_DRAW, FALSE,
4619 GDI_EVENT_MASK, event_mask,
4620 GDI_CALLBACK_ACTION, HandleToolButtons,
4624 Error(ERR_EXIT, "cannot create gadget");
4626 tool_gadget[id] = gi;
4630 void FreeToolButtons()
4634 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4635 FreeGadget(tool_gadget[i]);
4638 static void UnmapToolButtons()
4642 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4643 UnmapGadget(tool_gadget[i]);
4646 static void HandleToolButtons(struct GadgetInfo *gi)
4648 request_gadget_id = gi->custom_id;
4651 static struct Mapping_EM_to_RND_object
4654 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4655 boolean is_backside; /* backside of moving element */
4661 em_object_mapping_list[] =
4664 Xblank, TRUE, FALSE,
4668 Yacid_splash_eB, FALSE, FALSE,
4669 EL_ACID_SPLASH_RIGHT, -1, -1
4672 Yacid_splash_wB, FALSE, FALSE,
4673 EL_ACID_SPLASH_LEFT, -1, -1
4676 #ifdef EM_ENGINE_BAD_ROLL
4678 Xstone_force_e, FALSE, FALSE,
4679 EL_ROCK, -1, MV_BIT_RIGHT
4682 Xstone_force_w, FALSE, FALSE,
4683 EL_ROCK, -1, MV_BIT_LEFT
4686 Xnut_force_e, FALSE, FALSE,
4687 EL_NUT, -1, MV_BIT_RIGHT
4690 Xnut_force_w, FALSE, FALSE,
4691 EL_NUT, -1, MV_BIT_LEFT
4694 Xspring_force_e, FALSE, FALSE,
4695 EL_SPRING, -1, MV_BIT_RIGHT
4698 Xspring_force_w, FALSE, FALSE,
4699 EL_SPRING, -1, MV_BIT_LEFT
4702 Xemerald_force_e, FALSE, FALSE,
4703 EL_EMERALD, -1, MV_BIT_RIGHT
4706 Xemerald_force_w, FALSE, FALSE,
4707 EL_EMERALD, -1, MV_BIT_LEFT
4710 Xdiamond_force_e, FALSE, FALSE,
4711 EL_DIAMOND, -1, MV_BIT_RIGHT
4714 Xdiamond_force_w, FALSE, FALSE,
4715 EL_DIAMOND, -1, MV_BIT_LEFT
4718 Xbomb_force_e, FALSE, FALSE,
4719 EL_BOMB, -1, MV_BIT_RIGHT
4722 Xbomb_force_w, FALSE, FALSE,
4723 EL_BOMB, -1, MV_BIT_LEFT
4725 #endif /* EM_ENGINE_BAD_ROLL */
4728 Xstone, TRUE, FALSE,
4732 Xstone_pause, FALSE, FALSE,
4736 Xstone_fall, FALSE, FALSE,
4740 Ystone_s, FALSE, FALSE,
4741 EL_ROCK, ACTION_FALLING, -1
4744 Ystone_sB, FALSE, TRUE,
4745 EL_ROCK, ACTION_FALLING, -1
4748 Ystone_e, FALSE, FALSE,
4749 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4752 Ystone_eB, FALSE, TRUE,
4753 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4756 Ystone_w, FALSE, FALSE,
4757 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4760 Ystone_wB, FALSE, TRUE,
4761 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4768 Xnut_pause, FALSE, FALSE,
4772 Xnut_fall, FALSE, FALSE,
4776 Ynut_s, FALSE, FALSE,
4777 EL_NUT, ACTION_FALLING, -1
4780 Ynut_sB, FALSE, TRUE,
4781 EL_NUT, ACTION_FALLING, -1
4784 Ynut_e, FALSE, FALSE,
4785 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4788 Ynut_eB, FALSE, TRUE,
4789 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4792 Ynut_w, FALSE, FALSE,
4793 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4796 Ynut_wB, FALSE, TRUE,
4797 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4800 Xbug_n, TRUE, FALSE,
4804 Xbug_e, TRUE, FALSE,
4805 EL_BUG_RIGHT, -1, -1
4808 Xbug_s, TRUE, FALSE,
4812 Xbug_w, TRUE, FALSE,
4816 Xbug_gon, FALSE, FALSE,
4820 Xbug_goe, FALSE, FALSE,
4821 EL_BUG_RIGHT, -1, -1
4824 Xbug_gos, FALSE, FALSE,
4828 Xbug_gow, FALSE, FALSE,
4832 Ybug_n, FALSE, FALSE,
4833 EL_BUG, ACTION_MOVING, MV_BIT_UP
4836 Ybug_nB, FALSE, TRUE,
4837 EL_BUG, ACTION_MOVING, MV_BIT_UP
4840 Ybug_e, FALSE, FALSE,
4841 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4844 Ybug_eB, FALSE, TRUE,
4845 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4848 Ybug_s, FALSE, FALSE,
4849 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4852 Ybug_sB, FALSE, TRUE,
4853 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4856 Ybug_w, FALSE, FALSE,
4857 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4860 Ybug_wB, FALSE, TRUE,
4861 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4864 Ybug_w_n, FALSE, FALSE,
4865 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4868 Ybug_n_e, FALSE, FALSE,
4869 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4872 Ybug_e_s, FALSE, FALSE,
4873 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4876 Ybug_s_w, FALSE, FALSE,
4877 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4880 Ybug_e_n, FALSE, FALSE,
4881 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4884 Ybug_s_e, FALSE, FALSE,
4885 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4888 Ybug_w_s, FALSE, FALSE,
4889 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4892 Ybug_n_w, FALSE, FALSE,
4893 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4896 Ybug_stone, FALSE, FALSE,
4897 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4900 Ybug_spring, FALSE, FALSE,
4901 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4904 Xtank_n, TRUE, FALSE,
4905 EL_SPACESHIP_UP, -1, -1
4908 Xtank_e, TRUE, FALSE,
4909 EL_SPACESHIP_RIGHT, -1, -1
4912 Xtank_s, TRUE, FALSE,
4913 EL_SPACESHIP_DOWN, -1, -1
4916 Xtank_w, TRUE, FALSE,
4917 EL_SPACESHIP_LEFT, -1, -1
4920 Xtank_gon, FALSE, FALSE,
4921 EL_SPACESHIP_UP, -1, -1
4924 Xtank_goe, FALSE, FALSE,
4925 EL_SPACESHIP_RIGHT, -1, -1
4928 Xtank_gos, FALSE, FALSE,
4929 EL_SPACESHIP_DOWN, -1, -1
4932 Xtank_gow, FALSE, FALSE,
4933 EL_SPACESHIP_LEFT, -1, -1
4936 Ytank_n, FALSE, FALSE,
4937 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4940 Ytank_nB, FALSE, TRUE,
4941 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4944 Ytank_e, FALSE, FALSE,
4945 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4948 Ytank_eB, FALSE, TRUE,
4949 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4952 Ytank_s, FALSE, FALSE,
4953 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4956 Ytank_sB, FALSE, TRUE,
4957 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4960 Ytank_w, FALSE, FALSE,
4961 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4964 Ytank_wB, FALSE, TRUE,
4965 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4968 Ytank_w_n, FALSE, FALSE,
4969 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4972 Ytank_n_e, FALSE, FALSE,
4973 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4976 Ytank_e_s, FALSE, FALSE,
4977 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4980 Ytank_s_w, FALSE, FALSE,
4981 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4984 Ytank_e_n, FALSE, FALSE,
4985 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4988 Ytank_s_e, FALSE, FALSE,
4989 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4992 Ytank_w_s, FALSE, FALSE,
4993 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4996 Ytank_n_w, FALSE, FALSE,
4997 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5000 Ytank_stone, FALSE, FALSE,
5001 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5004 Ytank_spring, FALSE, FALSE,
5005 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5008 Xandroid, TRUE, FALSE,
5009 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5012 Xandroid_1_n, FALSE, FALSE,
5013 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5016 Xandroid_2_n, FALSE, FALSE,
5017 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5020 Xandroid_1_e, FALSE, FALSE,
5021 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5024 Xandroid_2_e, FALSE, FALSE,
5025 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5028 Xandroid_1_w, FALSE, FALSE,
5029 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5032 Xandroid_2_w, FALSE, FALSE,
5033 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5036 Xandroid_1_s, FALSE, FALSE,
5037 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5040 Xandroid_2_s, FALSE, FALSE,
5041 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5044 Yandroid_n, FALSE, FALSE,
5045 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5048 Yandroid_nB, FALSE, TRUE,
5049 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5052 Yandroid_ne, FALSE, FALSE,
5053 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5056 Yandroid_neB, FALSE, TRUE,
5057 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5060 Yandroid_e, FALSE, FALSE,
5061 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5064 Yandroid_eB, FALSE, TRUE,
5065 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5068 Yandroid_se, FALSE, FALSE,
5069 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5072 Yandroid_seB, FALSE, TRUE,
5073 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5076 Yandroid_s, FALSE, FALSE,
5077 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5080 Yandroid_sB, FALSE, TRUE,
5081 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5084 Yandroid_sw, FALSE, FALSE,
5085 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5088 Yandroid_swB, FALSE, TRUE,
5089 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5092 Yandroid_w, FALSE, FALSE,
5093 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5096 Yandroid_wB, FALSE, TRUE,
5097 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5100 Yandroid_nw, FALSE, FALSE,
5101 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5104 Yandroid_nwB, FALSE, TRUE,
5105 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5108 Xspring, TRUE, FALSE,
5112 Xspring_pause, FALSE, FALSE,
5116 Xspring_e, FALSE, FALSE,
5120 Xspring_w, FALSE, FALSE,
5124 Xspring_fall, FALSE, FALSE,
5128 Yspring_s, FALSE, FALSE,
5129 EL_SPRING, ACTION_FALLING, -1
5132 Yspring_sB, FALSE, TRUE,
5133 EL_SPRING, ACTION_FALLING, -1
5136 Yspring_e, FALSE, FALSE,
5137 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5140 Yspring_eB, FALSE, TRUE,
5141 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5144 Yspring_w, FALSE, FALSE,
5145 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5148 Yspring_wB, FALSE, TRUE,
5149 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5152 Yspring_kill_e, FALSE, FALSE,
5153 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5156 Yspring_kill_eB, FALSE, TRUE,
5157 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5160 Yspring_kill_w, FALSE, FALSE,
5161 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5164 Yspring_kill_wB, FALSE, TRUE,
5165 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5168 Xeater_n, TRUE, FALSE,
5169 EL_YAMYAM_UP, -1, -1
5172 Xeater_e, TRUE, FALSE,
5173 EL_YAMYAM_RIGHT, -1, -1
5176 Xeater_w, TRUE, FALSE,
5177 EL_YAMYAM_LEFT, -1, -1
5180 Xeater_s, TRUE, FALSE,
5181 EL_YAMYAM_DOWN, -1, -1
5184 Yeater_n, FALSE, FALSE,
5185 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5188 Yeater_nB, FALSE, TRUE,
5189 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5192 Yeater_e, FALSE, FALSE,
5193 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5196 Yeater_eB, FALSE, TRUE,
5197 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5200 Yeater_s, FALSE, FALSE,
5201 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5204 Yeater_sB, FALSE, TRUE,
5205 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5208 Yeater_w, FALSE, FALSE,
5209 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5212 Yeater_wB, FALSE, TRUE,
5213 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5216 Yeater_stone, FALSE, FALSE,
5217 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5220 Yeater_spring, FALSE, FALSE,
5221 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5224 Xalien, TRUE, FALSE,
5228 Xalien_pause, FALSE, FALSE,
5232 Yalien_n, FALSE, FALSE,
5233 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5236 Yalien_nB, FALSE, TRUE,
5237 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5240 Yalien_e, FALSE, FALSE,
5241 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5244 Yalien_eB, FALSE, TRUE,
5245 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5248 Yalien_s, FALSE, FALSE,
5249 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5252 Yalien_sB, FALSE, TRUE,
5253 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5256 Yalien_w, FALSE, FALSE,
5257 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5260 Yalien_wB, FALSE, TRUE,
5261 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5264 Yalien_stone, FALSE, FALSE,
5265 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5268 Yalien_spring, FALSE, FALSE,
5269 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5272 Xemerald, TRUE, FALSE,
5276 Xemerald_pause, FALSE, FALSE,
5280 Xemerald_fall, FALSE, FALSE,
5284 Xemerald_shine, FALSE, FALSE,
5285 EL_EMERALD, ACTION_TWINKLING, -1
5288 Yemerald_s, FALSE, FALSE,
5289 EL_EMERALD, ACTION_FALLING, -1
5292 Yemerald_sB, FALSE, TRUE,
5293 EL_EMERALD, ACTION_FALLING, -1
5296 Yemerald_e, FALSE, FALSE,
5297 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5300 Yemerald_eB, FALSE, TRUE,
5301 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5304 Yemerald_w, FALSE, FALSE,
5305 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5308 Yemerald_wB, FALSE, TRUE,
5309 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5312 Yemerald_eat, FALSE, FALSE,
5313 EL_EMERALD, ACTION_COLLECTING, -1
5316 Yemerald_stone, FALSE, FALSE,
5317 EL_NUT, ACTION_BREAKING, -1
5320 Xdiamond, TRUE, FALSE,
5324 Xdiamond_pause, FALSE, FALSE,
5328 Xdiamond_fall, FALSE, FALSE,
5332 Xdiamond_shine, FALSE, FALSE,
5333 EL_DIAMOND, ACTION_TWINKLING, -1
5336 Ydiamond_s, FALSE, FALSE,
5337 EL_DIAMOND, ACTION_FALLING, -1
5340 Ydiamond_sB, FALSE, TRUE,
5341 EL_DIAMOND, ACTION_FALLING, -1
5344 Ydiamond_e, FALSE, FALSE,
5345 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5348 Ydiamond_eB, FALSE, TRUE,
5349 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5352 Ydiamond_w, FALSE, FALSE,
5353 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5356 Ydiamond_wB, FALSE, TRUE,
5357 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5360 Ydiamond_eat, FALSE, FALSE,
5361 EL_DIAMOND, ACTION_COLLECTING, -1
5364 Ydiamond_stone, FALSE, FALSE,
5365 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5368 Xdrip_fall, TRUE, FALSE,
5369 EL_AMOEBA_DROP, -1, -1
5372 Xdrip_stretch, FALSE, FALSE,
5373 EL_AMOEBA_DROP, ACTION_FALLING, -1
5376 Xdrip_stretchB, FALSE, TRUE,
5377 EL_AMOEBA_DROP, ACTION_FALLING, -1
5380 Xdrip_eat, FALSE, FALSE,
5381 EL_AMOEBA_DROP, ACTION_GROWING, -1
5384 Ydrip_s1, FALSE, FALSE,
5385 EL_AMOEBA_DROP, ACTION_FALLING, -1
5388 Ydrip_s1B, FALSE, TRUE,
5389 EL_AMOEBA_DROP, ACTION_FALLING, -1
5392 Ydrip_s2, FALSE, FALSE,
5393 EL_AMOEBA_DROP, ACTION_FALLING, -1
5396 Ydrip_s2B, FALSE, TRUE,
5397 EL_AMOEBA_DROP, ACTION_FALLING, -1
5404 Xbomb_pause, FALSE, FALSE,
5408 Xbomb_fall, FALSE, FALSE,
5412 Ybomb_s, FALSE, FALSE,
5413 EL_BOMB, ACTION_FALLING, -1
5416 Ybomb_sB, FALSE, TRUE,
5417 EL_BOMB, ACTION_FALLING, -1
5420 Ybomb_e, FALSE, FALSE,
5421 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5424 Ybomb_eB, FALSE, TRUE,
5425 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5428 Ybomb_w, FALSE, FALSE,
5429 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5432 Ybomb_wB, FALSE, TRUE,
5433 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5436 Ybomb_eat, FALSE, FALSE,
5437 EL_BOMB, ACTION_ACTIVATING, -1
5440 Xballoon, TRUE, FALSE,
5444 Yballoon_n, FALSE, FALSE,
5445 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5448 Yballoon_nB, FALSE, TRUE,
5449 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5452 Yballoon_e, FALSE, FALSE,
5453 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5456 Yballoon_eB, FALSE, TRUE,
5457 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5460 Yballoon_s, FALSE, FALSE,
5461 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5464 Yballoon_sB, FALSE, TRUE,
5465 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5468 Yballoon_w, FALSE, FALSE,
5469 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5472 Yballoon_wB, FALSE, TRUE,
5473 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5476 Xgrass, TRUE, FALSE,
5477 EL_EMC_GRASS, -1, -1
5480 Ygrass_nB, FALSE, FALSE,
5481 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5484 Ygrass_eB, FALSE, FALSE,
5485 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5488 Ygrass_sB, FALSE, FALSE,
5489 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5492 Ygrass_wB, FALSE, FALSE,
5493 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5500 Ydirt_nB, FALSE, FALSE,
5501 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5504 Ydirt_eB, FALSE, FALSE,
5505 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5508 Ydirt_sB, FALSE, FALSE,
5509 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5512 Ydirt_wB, FALSE, FALSE,
5513 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5516 Xacid_ne, TRUE, FALSE,
5517 EL_ACID_POOL_TOPRIGHT, -1, -1
5520 Xacid_se, TRUE, FALSE,
5521 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5524 Xacid_s, TRUE, FALSE,
5525 EL_ACID_POOL_BOTTOM, -1, -1
5528 Xacid_sw, TRUE, FALSE,
5529 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5532 Xacid_nw, TRUE, FALSE,
5533 EL_ACID_POOL_TOPLEFT, -1, -1
5536 Xacid_1, TRUE, FALSE,
5540 Xacid_2, FALSE, FALSE,
5544 Xacid_3, FALSE, FALSE,
5548 Xacid_4, FALSE, FALSE,
5552 Xacid_5, FALSE, FALSE,
5556 Xacid_6, FALSE, FALSE,
5560 Xacid_7, FALSE, FALSE,
5564 Xacid_8, FALSE, FALSE,
5568 Xball_1, TRUE, FALSE,
5569 EL_EMC_MAGIC_BALL, -1, -1
5572 Xball_1B, FALSE, FALSE,
5573 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5576 Xball_2, FALSE, FALSE,
5577 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5580 Xball_2B, FALSE, FALSE,
5581 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5584 Yball_eat, FALSE, FALSE,
5585 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5588 Ykey_1_eat, FALSE, FALSE,
5589 EL_EM_KEY_1, ACTION_COLLECTING, -1
5592 Ykey_2_eat, FALSE, FALSE,
5593 EL_EM_KEY_2, ACTION_COLLECTING, -1
5596 Ykey_3_eat, FALSE, FALSE,
5597 EL_EM_KEY_3, ACTION_COLLECTING, -1
5600 Ykey_4_eat, FALSE, FALSE,
5601 EL_EM_KEY_4, ACTION_COLLECTING, -1
5604 Ykey_5_eat, FALSE, FALSE,
5605 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5608 Ykey_6_eat, FALSE, FALSE,
5609 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5612 Ykey_7_eat, FALSE, FALSE,
5613 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5616 Ykey_8_eat, FALSE, FALSE,
5617 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5620 Ylenses_eat, FALSE, FALSE,
5621 EL_EMC_LENSES, ACTION_COLLECTING, -1
5624 Ymagnify_eat, FALSE, FALSE,
5625 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5628 Ygrass_eat, FALSE, FALSE,
5629 EL_EMC_GRASS, ACTION_SNAPPING, -1
5632 Ydirt_eat, FALSE, FALSE,
5633 EL_SAND, ACTION_SNAPPING, -1
5636 Xgrow_ns, TRUE, FALSE,
5637 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5640 Ygrow_ns_eat, FALSE, FALSE,
5641 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5644 Xgrow_ew, TRUE, FALSE,
5645 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5648 Ygrow_ew_eat, FALSE, FALSE,
5649 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5652 Xwonderwall, TRUE, FALSE,
5653 EL_MAGIC_WALL, -1, -1
5656 XwonderwallB, FALSE, FALSE,
5657 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5660 Xamoeba_1, TRUE, FALSE,
5661 EL_AMOEBA_DRY, ACTION_OTHER, -1
5664 Xamoeba_2, FALSE, FALSE,
5665 EL_AMOEBA_DRY, ACTION_OTHER, -1
5668 Xamoeba_3, FALSE, FALSE,
5669 EL_AMOEBA_DRY, ACTION_OTHER, -1
5672 Xamoeba_4, FALSE, FALSE,
5673 EL_AMOEBA_DRY, ACTION_OTHER, -1
5676 Xamoeba_5, TRUE, FALSE,
5677 EL_AMOEBA_WET, ACTION_OTHER, -1
5680 Xamoeba_6, FALSE, FALSE,
5681 EL_AMOEBA_WET, ACTION_OTHER, -1
5684 Xamoeba_7, FALSE, FALSE,
5685 EL_AMOEBA_WET, ACTION_OTHER, -1
5688 Xamoeba_8, FALSE, FALSE,
5689 EL_AMOEBA_WET, ACTION_OTHER, -1
5692 Xdoor_1, TRUE, FALSE,
5693 EL_EM_GATE_1, -1, -1
5696 Xdoor_2, TRUE, FALSE,
5697 EL_EM_GATE_2, -1, -1
5700 Xdoor_3, TRUE, FALSE,
5701 EL_EM_GATE_3, -1, -1
5704 Xdoor_4, TRUE, FALSE,
5705 EL_EM_GATE_4, -1, -1
5708 Xdoor_5, TRUE, FALSE,
5709 EL_EMC_GATE_5, -1, -1
5712 Xdoor_6, TRUE, FALSE,
5713 EL_EMC_GATE_6, -1, -1
5716 Xdoor_7, TRUE, FALSE,
5717 EL_EMC_GATE_7, -1, -1
5720 Xdoor_8, TRUE, FALSE,
5721 EL_EMC_GATE_8, -1, -1
5724 Xkey_1, TRUE, FALSE,
5728 Xkey_2, TRUE, FALSE,
5732 Xkey_3, TRUE, FALSE,
5736 Xkey_4, TRUE, FALSE,
5740 Xkey_5, TRUE, FALSE,
5741 EL_EMC_KEY_5, -1, -1
5744 Xkey_6, TRUE, FALSE,
5745 EL_EMC_KEY_6, -1, -1
5748 Xkey_7, TRUE, FALSE,
5749 EL_EMC_KEY_7, -1, -1
5752 Xkey_8, TRUE, FALSE,
5753 EL_EMC_KEY_8, -1, -1
5756 Xwind_n, TRUE, FALSE,
5757 EL_BALLOON_SWITCH_UP, -1, -1
5760 Xwind_e, TRUE, FALSE,
5761 EL_BALLOON_SWITCH_RIGHT, -1, -1
5764 Xwind_s, TRUE, FALSE,
5765 EL_BALLOON_SWITCH_DOWN, -1, -1
5768 Xwind_w, TRUE, FALSE,
5769 EL_BALLOON_SWITCH_LEFT, -1, -1
5772 Xwind_nesw, TRUE, FALSE,
5773 EL_BALLOON_SWITCH_ANY, -1, -1
5776 Xwind_stop, TRUE, FALSE,
5777 EL_BALLOON_SWITCH_NONE, -1, -1
5781 EL_EM_EXIT_CLOSED, -1, -1
5784 Xexit_1, TRUE, FALSE,
5785 EL_EM_EXIT_OPEN, -1, -1
5788 Xexit_2, FALSE, FALSE,
5789 EL_EM_EXIT_OPEN, -1, -1
5792 Xexit_3, FALSE, FALSE,
5793 EL_EM_EXIT_OPEN, -1, -1
5796 Xdynamite, TRUE, FALSE,
5797 EL_EM_DYNAMITE, -1, -1
5800 Ydynamite_eat, FALSE, FALSE,
5801 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5804 Xdynamite_1, TRUE, FALSE,
5805 EL_EM_DYNAMITE_ACTIVE, -1, -1
5808 Xdynamite_2, FALSE, FALSE,
5809 EL_EM_DYNAMITE_ACTIVE, -1, -1
5812 Xdynamite_3, FALSE, FALSE,
5813 EL_EM_DYNAMITE_ACTIVE, -1, -1
5816 Xdynamite_4, FALSE, FALSE,
5817 EL_EM_DYNAMITE_ACTIVE, -1, -1
5820 Xbumper, TRUE, FALSE,
5821 EL_EMC_SPRING_BUMPER, -1, -1
5824 XbumperB, FALSE, FALSE,
5825 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5828 Xwheel, TRUE, FALSE,
5829 EL_ROBOT_WHEEL, -1, -1
5832 XwheelB, FALSE, FALSE,
5833 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5836 Xswitch, TRUE, FALSE,
5837 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5840 XswitchB, FALSE, FALSE,
5841 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5845 EL_QUICKSAND_EMPTY, -1, -1
5848 Xsand_stone, TRUE, FALSE,
5849 EL_QUICKSAND_FULL, -1, -1
5852 Xsand_stonein_1, FALSE, TRUE,
5853 EL_ROCK, ACTION_FILLING, -1
5856 Xsand_stonein_2, FALSE, TRUE,
5857 EL_ROCK, ACTION_FILLING, -1
5860 Xsand_stonein_3, FALSE, TRUE,
5861 EL_ROCK, ACTION_FILLING, -1
5864 Xsand_stonein_4, FALSE, TRUE,
5865 EL_ROCK, ACTION_FILLING, -1
5868 Xsand_stonesand_1, FALSE, FALSE,
5869 EL_QUICKSAND_EMPTYING, -1, -1
5872 Xsand_stonesand_2, FALSE, FALSE,
5873 EL_QUICKSAND_EMPTYING, -1, -1
5876 Xsand_stonesand_3, FALSE, FALSE,
5877 EL_QUICKSAND_EMPTYING, -1, -1
5880 Xsand_stonesand_4, FALSE, FALSE,
5881 EL_QUICKSAND_EMPTYING, -1, -1
5884 Xsand_stonesand_quickout_1, FALSE, FALSE,
5885 EL_QUICKSAND_EMPTYING, -1, -1
5888 Xsand_stonesand_quickout_2, FALSE, FALSE,
5889 EL_QUICKSAND_EMPTYING, -1, -1
5892 Xsand_stoneout_1, FALSE, FALSE,
5893 EL_ROCK, ACTION_EMPTYING, -1
5896 Xsand_stoneout_2, FALSE, FALSE,
5897 EL_ROCK, ACTION_EMPTYING, -1
5900 Xsand_sandstone_1, FALSE, FALSE,
5901 EL_QUICKSAND_FILLING, -1, -1
5904 Xsand_sandstone_2, FALSE, FALSE,
5905 EL_QUICKSAND_FILLING, -1, -1
5908 Xsand_sandstone_3, FALSE, FALSE,
5909 EL_QUICKSAND_FILLING, -1, -1
5912 Xsand_sandstone_4, FALSE, FALSE,
5913 EL_QUICKSAND_FILLING, -1, -1
5916 Xplant, TRUE, FALSE,
5917 EL_EMC_PLANT, -1, -1
5920 Yplant, FALSE, FALSE,
5921 EL_EMC_PLANT, -1, -1
5924 Xlenses, TRUE, FALSE,
5925 EL_EMC_LENSES, -1, -1
5928 Xmagnify, TRUE, FALSE,
5929 EL_EMC_MAGNIFIER, -1, -1
5932 Xdripper, TRUE, FALSE,
5933 EL_EMC_DRIPPER, -1, -1
5936 XdripperB, FALSE, FALSE,
5937 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5940 Xfake_blank, TRUE, FALSE,
5941 EL_INVISIBLE_WALL, -1, -1
5944 Xfake_blankB, FALSE, FALSE,
5945 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5948 Xfake_grass, TRUE, FALSE,
5949 EL_EMC_FAKE_GRASS, -1, -1
5952 Xfake_grassB, FALSE, FALSE,
5953 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5956 Xfake_door_1, TRUE, FALSE,
5957 EL_EM_GATE_1_GRAY, -1, -1
5960 Xfake_door_2, TRUE, FALSE,
5961 EL_EM_GATE_2_GRAY, -1, -1
5964 Xfake_door_3, TRUE, FALSE,
5965 EL_EM_GATE_3_GRAY, -1, -1
5968 Xfake_door_4, TRUE, FALSE,
5969 EL_EM_GATE_4_GRAY, -1, -1
5972 Xfake_door_5, TRUE, FALSE,
5973 EL_EMC_GATE_5_GRAY, -1, -1
5976 Xfake_door_6, TRUE, FALSE,
5977 EL_EMC_GATE_6_GRAY, -1, -1
5980 Xfake_door_7, TRUE, FALSE,
5981 EL_EMC_GATE_7_GRAY, -1, -1
5984 Xfake_door_8, TRUE, FALSE,
5985 EL_EMC_GATE_8_GRAY, -1, -1
5988 Xfake_acid_1, TRUE, FALSE,
5989 EL_EMC_FAKE_ACID, -1, -1
5992 Xfake_acid_2, FALSE, FALSE,
5993 EL_EMC_FAKE_ACID, -1, -1
5996 Xfake_acid_3, FALSE, FALSE,
5997 EL_EMC_FAKE_ACID, -1, -1
6000 Xfake_acid_4, FALSE, FALSE,
6001 EL_EMC_FAKE_ACID, -1, -1
6004 Xfake_acid_5, FALSE, FALSE,
6005 EL_EMC_FAKE_ACID, -1, -1
6008 Xfake_acid_6, FALSE, FALSE,
6009 EL_EMC_FAKE_ACID, -1, -1
6012 Xfake_acid_7, FALSE, FALSE,
6013 EL_EMC_FAKE_ACID, -1, -1
6016 Xfake_acid_8, FALSE, FALSE,
6017 EL_EMC_FAKE_ACID, -1, -1
6020 Xsteel_1, TRUE, FALSE,
6021 EL_STEELWALL, -1, -1
6024 Xsteel_2, TRUE, FALSE,
6025 EL_EMC_STEELWALL_2, -1, -1
6028 Xsteel_3, TRUE, FALSE,
6029 EL_EMC_STEELWALL_3, -1, -1
6032 Xsteel_4, TRUE, FALSE,
6033 EL_EMC_STEELWALL_4, -1, -1
6036 Xwall_1, TRUE, FALSE,
6040 Xwall_2, TRUE, FALSE,
6041 EL_EMC_WALL_14, -1, -1
6044 Xwall_3, TRUE, FALSE,
6045 EL_EMC_WALL_15, -1, -1
6048 Xwall_4, TRUE, FALSE,
6049 EL_EMC_WALL_16, -1, -1
6052 Xround_wall_1, TRUE, FALSE,
6053 EL_WALL_SLIPPERY, -1, -1
6056 Xround_wall_2, TRUE, FALSE,
6057 EL_EMC_WALL_SLIPPERY_2, -1, -1
6060 Xround_wall_3, TRUE, FALSE,
6061 EL_EMC_WALL_SLIPPERY_3, -1, -1
6064 Xround_wall_4, TRUE, FALSE,
6065 EL_EMC_WALL_SLIPPERY_4, -1, -1
6068 Xdecor_1, TRUE, FALSE,
6069 EL_EMC_WALL_8, -1, -1
6072 Xdecor_2, TRUE, FALSE,
6073 EL_EMC_WALL_6, -1, -1
6076 Xdecor_3, TRUE, FALSE,
6077 EL_EMC_WALL_4, -1, -1
6080 Xdecor_4, TRUE, FALSE,
6081 EL_EMC_WALL_7, -1, -1
6084 Xdecor_5, TRUE, FALSE,
6085 EL_EMC_WALL_5, -1, -1
6088 Xdecor_6, TRUE, FALSE,
6089 EL_EMC_WALL_9, -1, -1
6092 Xdecor_7, TRUE, FALSE,
6093 EL_EMC_WALL_10, -1, -1
6096 Xdecor_8, TRUE, FALSE,
6097 EL_EMC_WALL_1, -1, -1
6100 Xdecor_9, TRUE, FALSE,
6101 EL_EMC_WALL_2, -1, -1
6104 Xdecor_10, TRUE, FALSE,
6105 EL_EMC_WALL_3, -1, -1
6108 Xdecor_11, TRUE, FALSE,
6109 EL_EMC_WALL_11, -1, -1
6112 Xdecor_12, TRUE, FALSE,
6113 EL_EMC_WALL_12, -1, -1
6116 Xalpha_0, TRUE, FALSE,
6117 EL_CHAR('0'), -1, -1
6120 Xalpha_1, TRUE, FALSE,
6121 EL_CHAR('1'), -1, -1
6124 Xalpha_2, TRUE, FALSE,
6125 EL_CHAR('2'), -1, -1
6128 Xalpha_3, TRUE, FALSE,
6129 EL_CHAR('3'), -1, -1
6132 Xalpha_4, TRUE, FALSE,
6133 EL_CHAR('4'), -1, -1
6136 Xalpha_5, TRUE, FALSE,
6137 EL_CHAR('5'), -1, -1
6140 Xalpha_6, TRUE, FALSE,
6141 EL_CHAR('6'), -1, -1
6144 Xalpha_7, TRUE, FALSE,
6145 EL_CHAR('7'), -1, -1
6148 Xalpha_8, TRUE, FALSE,
6149 EL_CHAR('8'), -1, -1
6152 Xalpha_9, TRUE, FALSE,
6153 EL_CHAR('9'), -1, -1
6156 Xalpha_excla, TRUE, FALSE,
6157 EL_CHAR('!'), -1, -1
6160 Xalpha_quote, TRUE, FALSE,
6161 EL_CHAR('"'), -1, -1
6164 Xalpha_comma, TRUE, FALSE,
6165 EL_CHAR(','), -1, -1
6168 Xalpha_minus, TRUE, FALSE,
6169 EL_CHAR('-'), -1, -1
6172 Xalpha_perio, TRUE, FALSE,
6173 EL_CHAR('.'), -1, -1
6176 Xalpha_colon, TRUE, FALSE,
6177 EL_CHAR(':'), -1, -1
6180 Xalpha_quest, TRUE, FALSE,
6181 EL_CHAR('?'), -1, -1
6184 Xalpha_a, TRUE, FALSE,
6185 EL_CHAR('A'), -1, -1
6188 Xalpha_b, TRUE, FALSE,
6189 EL_CHAR('B'), -1, -1
6192 Xalpha_c, TRUE, FALSE,
6193 EL_CHAR('C'), -1, -1
6196 Xalpha_d, TRUE, FALSE,
6197 EL_CHAR('D'), -1, -1
6200 Xalpha_e, TRUE, FALSE,
6201 EL_CHAR('E'), -1, -1
6204 Xalpha_f, TRUE, FALSE,
6205 EL_CHAR('F'), -1, -1
6208 Xalpha_g, TRUE, FALSE,
6209 EL_CHAR('G'), -1, -1
6212 Xalpha_h, TRUE, FALSE,
6213 EL_CHAR('H'), -1, -1
6216 Xalpha_i, TRUE, FALSE,
6217 EL_CHAR('I'), -1, -1
6220 Xalpha_j, TRUE, FALSE,
6221 EL_CHAR('J'), -1, -1
6224 Xalpha_k, TRUE, FALSE,
6225 EL_CHAR('K'), -1, -1
6228 Xalpha_l, TRUE, FALSE,
6229 EL_CHAR('L'), -1, -1
6232 Xalpha_m, TRUE, FALSE,
6233 EL_CHAR('M'), -1, -1
6236 Xalpha_n, TRUE, FALSE,
6237 EL_CHAR('N'), -1, -1
6240 Xalpha_o, TRUE, FALSE,
6241 EL_CHAR('O'), -1, -1
6244 Xalpha_p, TRUE, FALSE,
6245 EL_CHAR('P'), -1, -1
6248 Xalpha_q, TRUE, FALSE,
6249 EL_CHAR('Q'), -1, -1
6252 Xalpha_r, TRUE, FALSE,
6253 EL_CHAR('R'), -1, -1
6256 Xalpha_s, TRUE, FALSE,
6257 EL_CHAR('S'), -1, -1
6260 Xalpha_t, TRUE, FALSE,
6261 EL_CHAR('T'), -1, -1
6264 Xalpha_u, TRUE, FALSE,
6265 EL_CHAR('U'), -1, -1
6268 Xalpha_v, TRUE, FALSE,
6269 EL_CHAR('V'), -1, -1
6272 Xalpha_w, TRUE, FALSE,
6273 EL_CHAR('W'), -1, -1
6276 Xalpha_x, TRUE, FALSE,
6277 EL_CHAR('X'), -1, -1
6280 Xalpha_y, TRUE, FALSE,
6281 EL_CHAR('Y'), -1, -1
6284 Xalpha_z, TRUE, FALSE,
6285 EL_CHAR('Z'), -1, -1
6288 Xalpha_arrow_e, TRUE, FALSE,
6289 EL_CHAR('>'), -1, -1
6292 Xalpha_arrow_w, TRUE, FALSE,
6293 EL_CHAR('<'), -1, -1
6296 Xalpha_copyr, TRUE, FALSE,
6297 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6301 Xboom_bug, FALSE, FALSE,
6302 EL_BUG, ACTION_EXPLODING, -1
6305 Xboom_bomb, FALSE, FALSE,
6306 EL_BOMB, ACTION_EXPLODING, -1
6309 Xboom_android, FALSE, FALSE,
6310 EL_EMC_ANDROID, ACTION_OTHER, -1
6313 Xboom_1, FALSE, FALSE,
6314 EL_DEFAULT, ACTION_EXPLODING, -1
6317 Xboom_2, FALSE, FALSE,
6318 EL_DEFAULT, ACTION_EXPLODING, -1
6321 Znormal, FALSE, FALSE,
6325 Zdynamite, FALSE, FALSE,
6329 Zplayer, FALSE, FALSE,
6333 ZBORDER, FALSE, FALSE,
6343 static struct Mapping_EM_to_RND_player
6352 em_player_mapping_list[] =
6356 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6360 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6364 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6368 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6372 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6376 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6380 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6384 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6388 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6392 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6396 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6400 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6404 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6408 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6412 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6416 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6420 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6424 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6428 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6432 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6436 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6440 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6444 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6448 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6452 EL_PLAYER_1, ACTION_DEFAULT, -1,
6456 EL_PLAYER_2, ACTION_DEFAULT, -1,
6460 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6464 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6468 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6472 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6476 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6480 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6484 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6488 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6492 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6496 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6500 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6504 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6508 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6512 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6516 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6520 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6524 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6528 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6532 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6536 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6540 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6544 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6548 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6552 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6556 EL_PLAYER_3, ACTION_DEFAULT, -1,
6560 EL_PLAYER_4, ACTION_DEFAULT, -1,
6569 int map_element_RND_to_EM(int element_rnd)
6571 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6572 static boolean mapping_initialized = FALSE;
6574 if (!mapping_initialized)
6578 /* return "Xalpha_quest" for all undefined elements in mapping array */
6579 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6580 mapping_RND_to_EM[i] = Xalpha_quest;
6582 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6583 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6584 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6585 em_object_mapping_list[i].element_em;
6587 mapping_initialized = TRUE;
6590 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6591 return mapping_RND_to_EM[element_rnd];
6593 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6598 int map_element_EM_to_RND(int element_em)
6600 static unsigned short mapping_EM_to_RND[TILE_MAX];
6601 static boolean mapping_initialized = FALSE;
6603 if (!mapping_initialized)
6607 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6608 for (i = 0; i < TILE_MAX; i++)
6609 mapping_EM_to_RND[i] = EL_UNKNOWN;
6611 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6612 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6613 em_object_mapping_list[i].element_rnd;
6615 mapping_initialized = TRUE;
6618 if (element_em >= 0 && element_em < TILE_MAX)
6619 return mapping_EM_to_RND[element_em];
6621 Error(ERR_WARN, "invalid EM level element %d", element_em);
6626 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6628 struct LevelInfo_EM *level_em = level->native_em_level;
6629 struct LEVEL *lev = level_em->lev;
6632 for (i = 0; i < TILE_MAX; i++)
6633 lev->android_array[i] = Xblank;
6635 for (i = 0; i < level->num_android_clone_elements; i++)
6637 int element_rnd = level->android_clone_element[i];
6638 int element_em = map_element_RND_to_EM(element_rnd);
6640 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6641 if (em_object_mapping_list[j].element_rnd == element_rnd)
6642 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6646 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6648 struct LevelInfo_EM *level_em = level->native_em_level;
6649 struct LEVEL *lev = level_em->lev;
6652 level->num_android_clone_elements = 0;
6654 for (i = 0; i < TILE_MAX; i++)
6656 int element_em = lev->android_array[i];
6658 boolean element_found = FALSE;
6660 if (element_em == Xblank)
6663 element_rnd = map_element_EM_to_RND(element_em);
6665 for (j = 0; j < level->num_android_clone_elements; j++)
6666 if (level->android_clone_element[j] == element_rnd)
6667 element_found = TRUE;
6671 level->android_clone_element[level->num_android_clone_elements++] =
6674 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6679 if (level->num_android_clone_elements == 0)
6681 level->num_android_clone_elements = 1;
6682 level->android_clone_element[0] = EL_EMPTY;
6686 int map_direction_RND_to_EM(int direction)
6688 return (direction == MV_UP ? 0 :
6689 direction == MV_RIGHT ? 1 :
6690 direction == MV_DOWN ? 2 :
6691 direction == MV_LEFT ? 3 :
6695 int map_direction_EM_to_RND(int direction)
6697 return (direction == 0 ? MV_UP :
6698 direction == 1 ? MV_RIGHT :
6699 direction == 2 ? MV_DOWN :
6700 direction == 3 ? MV_LEFT :
6704 int map_element_RND_to_SP(int element_rnd)
6706 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6708 if (element_rnd >= EL_SP_START &&
6709 element_rnd <= EL_SP_END)
6710 element_sp = element_rnd - EL_SP_START;
6711 else if (element_rnd == EL_EMPTY_SPACE)
6713 else if (element_rnd == EL_INVISIBLE_WALL)
6719 int map_element_SP_to_RND(int element_sp)
6721 int element_rnd = EL_UNKNOWN;
6723 if (element_sp >= 0x00 &&
6725 element_rnd = EL_SP_START + element_sp;
6726 else if (element_sp == 0x28)
6727 element_rnd = EL_INVISIBLE_WALL;
6732 int map_action_SP_to_RND(int action_sp)
6736 case actActive: return ACTION_ACTIVE;
6737 case actImpact: return ACTION_IMPACT;
6738 case actExploding: return ACTION_EXPLODING;
6739 case actDigging: return ACTION_DIGGING;
6740 case actSnapping: return ACTION_SNAPPING;
6741 case actCollecting: return ACTION_COLLECTING;
6742 case actPassing: return ACTION_PASSING;
6743 case actPushing: return ACTION_PUSHING;
6744 case actDropping: return ACTION_DROPPING;
6746 default: return ACTION_DEFAULT;
6750 int get_next_element(int element)
6754 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6755 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6756 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6757 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6758 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6759 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6760 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6761 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6762 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6763 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6764 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6766 default: return element;
6770 int el_act_dir2img(int element, int action, int direction)
6772 element = GFX_ELEMENT(element);
6773 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6775 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6776 return element_info[element].direction_graphic[action][direction];
6779 static int el_act_dir2crm(int element, int action, int direction)
6781 element = GFX_ELEMENT(element);
6782 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6784 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6785 return element_info[element].direction_crumbled[action][direction];
6788 int el_act2img(int element, int action)
6790 element = GFX_ELEMENT(element);
6792 return element_info[element].graphic[action];
6795 int el_act2crm(int element, int action)
6797 element = GFX_ELEMENT(element);
6799 return element_info[element].crumbled[action];
6802 int el_dir2img(int element, int direction)
6804 element = GFX_ELEMENT(element);
6806 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6809 int el2baseimg(int element)
6811 return element_info[element].graphic[ACTION_DEFAULT];
6814 int el2img(int element)
6816 element = GFX_ELEMENT(element);
6818 return element_info[element].graphic[ACTION_DEFAULT];
6821 int el2edimg(int element)
6823 element = GFX_ELEMENT(element);
6825 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6828 int el2preimg(int element)
6830 element = GFX_ELEMENT(element);
6832 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6835 int el2panelimg(int element)
6837 element = GFX_ELEMENT(element);
6839 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6842 int font2baseimg(int font_nr)
6844 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6847 int getBeltNrFromBeltElement(int element)
6849 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6850 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6851 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6854 int getBeltNrFromBeltActiveElement(int element)
6856 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6857 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6858 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6861 int getBeltNrFromBeltSwitchElement(int element)
6863 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6864 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6865 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6868 int getBeltDirNrFromBeltElement(int element)
6870 static int belt_base_element[4] =
6872 EL_CONVEYOR_BELT_1_LEFT,
6873 EL_CONVEYOR_BELT_2_LEFT,
6874 EL_CONVEYOR_BELT_3_LEFT,
6875 EL_CONVEYOR_BELT_4_LEFT
6878 int belt_nr = getBeltNrFromBeltElement(element);
6879 int belt_dir_nr = element - belt_base_element[belt_nr];
6881 return (belt_dir_nr % 3);
6884 int getBeltDirNrFromBeltSwitchElement(int element)
6886 static int belt_base_element[4] =
6888 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6889 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6890 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6891 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6894 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6895 int belt_dir_nr = element - belt_base_element[belt_nr];
6897 return (belt_dir_nr % 3);
6900 int getBeltDirFromBeltElement(int element)
6902 static int belt_move_dir[3] =
6909 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6911 return belt_move_dir[belt_dir_nr];
6914 int getBeltDirFromBeltSwitchElement(int element)
6916 static int belt_move_dir[3] =
6923 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6925 return belt_move_dir[belt_dir_nr];
6928 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6930 static int belt_base_element[4] =
6932 EL_CONVEYOR_BELT_1_LEFT,
6933 EL_CONVEYOR_BELT_2_LEFT,
6934 EL_CONVEYOR_BELT_3_LEFT,
6935 EL_CONVEYOR_BELT_4_LEFT
6938 return belt_base_element[belt_nr] + belt_dir_nr;
6941 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6943 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6945 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6948 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6950 static int belt_base_element[4] =
6952 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6953 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6954 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6955 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6958 return belt_base_element[belt_nr] + belt_dir_nr;
6961 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6963 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6965 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6968 boolean getTeamMode_EM()
6970 return game.team_mode;
6973 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6975 int game_frame_delay_value;
6977 game_frame_delay_value =
6978 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6979 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6982 if (tape.playing && tape.warp_forward && !tape.pausing)
6983 game_frame_delay_value = 0;
6985 return game_frame_delay_value;
6988 unsigned int InitRND(int seed)
6990 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6991 return InitEngineRandom_EM(seed);
6992 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6993 return InitEngineRandom_SP(seed);
6995 return InitEngineRandom_RND(seed);
6998 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6999 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7001 inline static int get_effective_element_EM(int tile, int frame_em)
7003 int element = object_mapping[tile].element_rnd;
7004 int action = object_mapping[tile].action;
7005 boolean is_backside = object_mapping[tile].is_backside;
7006 boolean action_removing = (action == ACTION_DIGGING ||
7007 action == ACTION_SNAPPING ||
7008 action == ACTION_COLLECTING);
7014 case Yacid_splash_eB:
7015 case Yacid_splash_wB:
7016 return (frame_em > 5 ? EL_EMPTY : element);
7022 else /* frame_em == 7 */
7026 case Yacid_splash_eB:
7027 case Yacid_splash_wB:
7030 case Yemerald_stone:
7033 case Ydiamond_stone:
7037 case Xdrip_stretchB:
7056 case Xsand_stonein_1:
7057 case Xsand_stonein_2:
7058 case Xsand_stonein_3:
7059 case Xsand_stonein_4:
7063 return (is_backside || action_removing ? EL_EMPTY : element);
7068 inline static boolean check_linear_animation_EM(int tile)
7072 case Xsand_stonesand_1:
7073 case Xsand_stonesand_quickout_1:
7074 case Xsand_sandstone_1:
7075 case Xsand_stonein_1:
7076 case Xsand_stoneout_1:
7095 case Yacid_splash_eB:
7096 case Yacid_splash_wB:
7097 case Yemerald_stone:
7104 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7105 boolean has_crumbled_graphics,
7106 int crumbled, int sync_frame)
7108 /* if element can be crumbled, but certain action graphics are just empty
7109 space (like instantly snapping sand to empty space in 1 frame), do not
7110 treat these empty space graphics as crumbled graphics in EMC engine */
7111 if (crumbled == IMG_EMPTY_SPACE)
7112 has_crumbled_graphics = FALSE;
7114 if (has_crumbled_graphics)
7116 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7117 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7118 g_crumbled->anim_delay,
7119 g_crumbled->anim_mode,
7120 g_crumbled->anim_start_frame,
7123 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7124 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7126 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7128 g_em->has_crumbled_graphics = TRUE;
7132 g_em->crumbled_bitmap = NULL;
7133 g_em->crumbled_src_x = 0;
7134 g_em->crumbled_src_y = 0;
7135 g_em->crumbled_border_size = 0;
7137 g_em->has_crumbled_graphics = FALSE;
7141 void ResetGfxAnimation_EM(int x, int y, int tile)
7146 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7147 int tile, int frame_em, int x, int y)
7149 int action = object_mapping[tile].action;
7150 int direction = object_mapping[tile].direction;
7151 int effective_element = get_effective_element_EM(tile, frame_em);
7152 int graphic = (direction == MV_NONE ?
7153 el_act2img(effective_element, action) :
7154 el_act_dir2img(effective_element, action, direction));
7155 struct GraphicInfo *g = &graphic_info[graphic];
7157 boolean action_removing = (action == ACTION_DIGGING ||
7158 action == ACTION_SNAPPING ||
7159 action == ACTION_COLLECTING);
7160 boolean action_moving = (action == ACTION_FALLING ||
7161 action == ACTION_MOVING ||
7162 action == ACTION_PUSHING ||
7163 action == ACTION_EATING ||
7164 action == ACTION_FILLING ||
7165 action == ACTION_EMPTYING);
7166 boolean action_falling = (action == ACTION_FALLING ||
7167 action == ACTION_FILLING ||
7168 action == ACTION_EMPTYING);
7170 /* special case: graphic uses "2nd movement tile" and has defined
7171 7 frames for movement animation (or less) => use default graphic
7172 for last (8th) frame which ends the movement animation */
7173 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7175 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7176 graphic = (direction == MV_NONE ?
7177 el_act2img(effective_element, action) :
7178 el_act_dir2img(effective_element, action, direction));
7180 g = &graphic_info[graphic];
7183 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7187 else if (action_moving)
7189 boolean is_backside = object_mapping[tile].is_backside;
7193 int direction = object_mapping[tile].direction;
7194 int move_dir = (action_falling ? MV_DOWN : direction);
7199 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7200 if (g->double_movement && frame_em == 0)
7204 if (move_dir == MV_LEFT)
7205 GfxFrame[x - 1][y] = GfxFrame[x][y];
7206 else if (move_dir == MV_RIGHT)
7207 GfxFrame[x + 1][y] = GfxFrame[x][y];
7208 else if (move_dir == MV_UP)
7209 GfxFrame[x][y - 1] = GfxFrame[x][y];
7210 else if (move_dir == MV_DOWN)
7211 GfxFrame[x][y + 1] = GfxFrame[x][y];
7218 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7219 if (tile == Xsand_stonesand_quickout_1 ||
7220 tile == Xsand_stonesand_quickout_2)
7224 if (graphic_info[graphic].anim_global_sync)
7225 sync_frame = FrameCounter;
7226 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7227 sync_frame = GfxFrame[x][y];
7229 sync_frame = 0; /* playfield border (pseudo steel) */
7231 SetRandomAnimationValue(x, y);
7233 int frame = getAnimationFrame(g->anim_frames,
7236 g->anim_start_frame,
7239 g_em->unique_identifier =
7240 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7243 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7244 int tile, int frame_em, int x, int y)
7246 int action = object_mapping[tile].action;
7247 int direction = object_mapping[tile].direction;
7248 boolean is_backside = object_mapping[tile].is_backside;
7249 int effective_element = get_effective_element_EM(tile, frame_em);
7250 int effective_action = action;
7251 int graphic = (direction == MV_NONE ?
7252 el_act2img(effective_element, effective_action) :
7253 el_act_dir2img(effective_element, effective_action,
7255 int crumbled = (direction == MV_NONE ?
7256 el_act2crm(effective_element, effective_action) :
7257 el_act_dir2crm(effective_element, effective_action,
7259 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7260 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7261 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7262 struct GraphicInfo *g = &graphic_info[graphic];
7265 /* special case: graphic uses "2nd movement tile" and has defined
7266 7 frames for movement animation (or less) => use default graphic
7267 for last (8th) frame which ends the movement animation */
7268 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7270 effective_action = ACTION_DEFAULT;
7271 graphic = (direction == MV_NONE ?
7272 el_act2img(effective_element, effective_action) :
7273 el_act_dir2img(effective_element, effective_action,
7275 crumbled = (direction == MV_NONE ?
7276 el_act2crm(effective_element, effective_action) :
7277 el_act_dir2crm(effective_element, effective_action,
7280 g = &graphic_info[graphic];
7283 if (graphic_info[graphic].anim_global_sync)
7284 sync_frame = FrameCounter;
7285 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7286 sync_frame = GfxFrame[x][y];
7288 sync_frame = 0; /* playfield border (pseudo steel) */
7290 SetRandomAnimationValue(x, y);
7292 int frame = getAnimationFrame(g->anim_frames,
7295 g->anim_start_frame,
7298 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7299 g->double_movement && is_backside);
7301 /* (updating the "crumbled" graphic definitions is probably not really needed,
7302 as animations for crumbled graphics can't be longer than one EMC cycle) */
7303 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7307 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7308 int player_nr, int anim, int frame_em)
7310 int element = player_mapping[player_nr][anim].element_rnd;
7311 int action = player_mapping[player_nr][anim].action;
7312 int direction = player_mapping[player_nr][anim].direction;
7313 int graphic = (direction == MV_NONE ?
7314 el_act2img(element, action) :
7315 el_act_dir2img(element, action, direction));
7316 struct GraphicInfo *g = &graphic_info[graphic];
7319 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7321 stored_player[player_nr].StepFrame = frame_em;
7323 sync_frame = stored_player[player_nr].Frame;
7325 int frame = getAnimationFrame(g->anim_frames,
7328 g->anim_start_frame,
7331 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7332 &g_em->src_x, &g_em->src_y, FALSE);
7335 void InitGraphicInfo_EM(void)
7340 int num_em_gfx_errors = 0;
7342 if (graphic_info_em_object[0][0].bitmap == NULL)
7344 /* EM graphics not yet initialized in em_open_all() */
7349 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7352 /* always start with reliable default values */
7353 for (i = 0; i < TILE_MAX; i++)
7355 object_mapping[i].element_rnd = EL_UNKNOWN;
7356 object_mapping[i].is_backside = FALSE;
7357 object_mapping[i].action = ACTION_DEFAULT;
7358 object_mapping[i].direction = MV_NONE;
7361 /* always start with reliable default values */
7362 for (p = 0; p < MAX_PLAYERS; p++)
7364 for (i = 0; i < SPR_MAX; i++)
7366 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7367 player_mapping[p][i].action = ACTION_DEFAULT;
7368 player_mapping[p][i].direction = MV_NONE;
7372 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7374 int e = em_object_mapping_list[i].element_em;
7376 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7377 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7379 if (em_object_mapping_list[i].action != -1)
7380 object_mapping[e].action = em_object_mapping_list[i].action;
7382 if (em_object_mapping_list[i].direction != -1)
7383 object_mapping[e].direction =
7384 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7387 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7389 int a = em_player_mapping_list[i].action_em;
7390 int p = em_player_mapping_list[i].player_nr;
7392 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7394 if (em_player_mapping_list[i].action != -1)
7395 player_mapping[p][a].action = em_player_mapping_list[i].action;
7397 if (em_player_mapping_list[i].direction != -1)
7398 player_mapping[p][a].direction =
7399 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7402 for (i = 0; i < TILE_MAX; i++)
7404 int element = object_mapping[i].element_rnd;
7405 int action = object_mapping[i].action;
7406 int direction = object_mapping[i].direction;
7407 boolean is_backside = object_mapping[i].is_backside;
7408 boolean action_exploding = ((action == ACTION_EXPLODING ||
7409 action == ACTION_SMASHED_BY_ROCK ||
7410 action == ACTION_SMASHED_BY_SPRING) &&
7411 element != EL_DIAMOND);
7412 boolean action_active = (action == ACTION_ACTIVE);
7413 boolean action_other = (action == ACTION_OTHER);
7415 for (j = 0; j < 8; j++)
7417 int effective_element = get_effective_element_EM(i, j);
7418 int effective_action = (j < 7 ? action :
7419 i == Xdrip_stretch ? action :
7420 i == Xdrip_stretchB ? action :
7421 i == Ydrip_s1 ? action :
7422 i == Ydrip_s1B ? action :
7423 i == Xball_1B ? action :
7424 i == Xball_2 ? action :
7425 i == Xball_2B ? action :
7426 i == Yball_eat ? action :
7427 i == Ykey_1_eat ? action :
7428 i == Ykey_2_eat ? action :
7429 i == Ykey_3_eat ? action :
7430 i == Ykey_4_eat ? action :
7431 i == Ykey_5_eat ? action :
7432 i == Ykey_6_eat ? action :
7433 i == Ykey_7_eat ? action :
7434 i == Ykey_8_eat ? action :
7435 i == Ylenses_eat ? action :
7436 i == Ymagnify_eat ? action :
7437 i == Ygrass_eat ? action :
7438 i == Ydirt_eat ? action :
7439 i == Xsand_stonein_1 ? action :
7440 i == Xsand_stonein_2 ? action :
7441 i == Xsand_stonein_3 ? action :
7442 i == Xsand_stonein_4 ? action :
7443 i == Xsand_stoneout_1 ? action :
7444 i == Xsand_stoneout_2 ? action :
7445 i == Xboom_android ? ACTION_EXPLODING :
7446 action_exploding ? ACTION_EXPLODING :
7447 action_active ? action :
7448 action_other ? action :
7450 int graphic = (el_act_dir2img(effective_element, effective_action,
7452 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7454 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7455 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7456 boolean has_action_graphics = (graphic != base_graphic);
7457 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7458 struct GraphicInfo *g = &graphic_info[graphic];
7459 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7462 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7463 boolean special_animation = (action != ACTION_DEFAULT &&
7464 g->anim_frames == 3 &&
7465 g->anim_delay == 2 &&
7466 g->anim_mode & ANIM_LINEAR);
7467 int sync_frame = (i == Xdrip_stretch ? 7 :
7468 i == Xdrip_stretchB ? 7 :
7469 i == Ydrip_s2 ? j + 8 :
7470 i == Ydrip_s2B ? j + 8 :
7479 i == Xfake_acid_1 ? 0 :
7480 i == Xfake_acid_2 ? 10 :
7481 i == Xfake_acid_3 ? 20 :
7482 i == Xfake_acid_4 ? 30 :
7483 i == Xfake_acid_5 ? 40 :
7484 i == Xfake_acid_6 ? 50 :
7485 i == Xfake_acid_7 ? 60 :
7486 i == Xfake_acid_8 ? 70 :
7488 i == Xball_2B ? j + 8 :
7489 i == Yball_eat ? j + 1 :
7490 i == Ykey_1_eat ? j + 1 :
7491 i == Ykey_2_eat ? j + 1 :
7492 i == Ykey_3_eat ? j + 1 :
7493 i == Ykey_4_eat ? j + 1 :
7494 i == Ykey_5_eat ? j + 1 :
7495 i == Ykey_6_eat ? j + 1 :
7496 i == Ykey_7_eat ? j + 1 :
7497 i == Ykey_8_eat ? j + 1 :
7498 i == Ylenses_eat ? j + 1 :
7499 i == Ymagnify_eat ? j + 1 :
7500 i == Ygrass_eat ? j + 1 :
7501 i == Ydirt_eat ? j + 1 :
7502 i == Xamoeba_1 ? 0 :
7503 i == Xamoeba_2 ? 1 :
7504 i == Xamoeba_3 ? 2 :
7505 i == Xamoeba_4 ? 3 :
7506 i == Xamoeba_5 ? 0 :
7507 i == Xamoeba_6 ? 1 :
7508 i == Xamoeba_7 ? 2 :
7509 i == Xamoeba_8 ? 3 :
7510 i == Xexit_2 ? j + 8 :
7511 i == Xexit_3 ? j + 16 :
7512 i == Xdynamite_1 ? 0 :
7513 i == Xdynamite_2 ? 8 :
7514 i == Xdynamite_3 ? 16 :
7515 i == Xdynamite_4 ? 24 :
7516 i == Xsand_stonein_1 ? j + 1 :
7517 i == Xsand_stonein_2 ? j + 9 :
7518 i == Xsand_stonein_3 ? j + 17 :
7519 i == Xsand_stonein_4 ? j + 25 :
7520 i == Xsand_stoneout_1 && j == 0 ? 0 :
7521 i == Xsand_stoneout_1 && j == 1 ? 0 :
7522 i == Xsand_stoneout_1 && j == 2 ? 1 :
7523 i == Xsand_stoneout_1 && j == 3 ? 2 :
7524 i == Xsand_stoneout_1 && j == 4 ? 2 :
7525 i == Xsand_stoneout_1 && j == 5 ? 3 :
7526 i == Xsand_stoneout_1 && j == 6 ? 4 :
7527 i == Xsand_stoneout_1 && j == 7 ? 4 :
7528 i == Xsand_stoneout_2 && j == 0 ? 5 :
7529 i == Xsand_stoneout_2 && j == 1 ? 6 :
7530 i == Xsand_stoneout_2 && j == 2 ? 7 :
7531 i == Xsand_stoneout_2 && j == 3 ? 8 :
7532 i == Xsand_stoneout_2 && j == 4 ? 9 :
7533 i == Xsand_stoneout_2 && j == 5 ? 11 :
7534 i == Xsand_stoneout_2 && j == 6 ? 13 :
7535 i == Xsand_stoneout_2 && j == 7 ? 15 :
7536 i == Xboom_bug && j == 1 ? 2 :
7537 i == Xboom_bug && j == 2 ? 2 :
7538 i == Xboom_bug && j == 3 ? 4 :
7539 i == Xboom_bug && j == 4 ? 4 :
7540 i == Xboom_bug && j == 5 ? 2 :
7541 i == Xboom_bug && j == 6 ? 2 :
7542 i == Xboom_bug && j == 7 ? 0 :
7543 i == Xboom_bomb && j == 1 ? 2 :
7544 i == Xboom_bomb && j == 2 ? 2 :
7545 i == Xboom_bomb && j == 3 ? 4 :
7546 i == Xboom_bomb && j == 4 ? 4 :
7547 i == Xboom_bomb && j == 5 ? 2 :
7548 i == Xboom_bomb && j == 6 ? 2 :
7549 i == Xboom_bomb && j == 7 ? 0 :
7550 i == Xboom_android && j == 7 ? 6 :
7551 i == Xboom_1 && j == 1 ? 2 :
7552 i == Xboom_1 && j == 2 ? 2 :
7553 i == Xboom_1 && j == 3 ? 4 :
7554 i == Xboom_1 && j == 4 ? 4 :
7555 i == Xboom_1 && j == 5 ? 6 :
7556 i == Xboom_1 && j == 6 ? 6 :
7557 i == Xboom_1 && j == 7 ? 8 :
7558 i == Xboom_2 && j == 0 ? 8 :
7559 i == Xboom_2 && j == 1 ? 8 :
7560 i == Xboom_2 && j == 2 ? 10 :
7561 i == Xboom_2 && j == 3 ? 10 :
7562 i == Xboom_2 && j == 4 ? 10 :
7563 i == Xboom_2 && j == 5 ? 12 :
7564 i == Xboom_2 && j == 6 ? 12 :
7565 i == Xboom_2 && j == 7 ? 12 :
7566 special_animation && j == 4 ? 3 :
7567 effective_action != action ? 0 :
7571 Bitmap *debug_bitmap = g_em->bitmap;
7572 int debug_src_x = g_em->src_x;
7573 int debug_src_y = g_em->src_y;
7576 int frame = getAnimationFrame(g->anim_frames,
7579 g->anim_start_frame,
7582 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7583 g->double_movement && is_backside);
7585 g_em->bitmap = src_bitmap;
7586 g_em->src_x = src_x;
7587 g_em->src_y = src_y;
7588 g_em->src_offset_x = 0;
7589 g_em->src_offset_y = 0;
7590 g_em->dst_offset_x = 0;
7591 g_em->dst_offset_y = 0;
7592 g_em->width = TILEX;
7593 g_em->height = TILEY;
7595 g_em->preserve_background = FALSE;
7597 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7600 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7601 effective_action == ACTION_MOVING ||
7602 effective_action == ACTION_PUSHING ||
7603 effective_action == ACTION_EATING)) ||
7604 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7605 effective_action == ACTION_EMPTYING)))
7608 (effective_action == ACTION_FALLING ||
7609 effective_action == ACTION_FILLING ||
7610 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7611 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7612 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7613 int num_steps = (i == Ydrip_s1 ? 16 :
7614 i == Ydrip_s1B ? 16 :
7615 i == Ydrip_s2 ? 16 :
7616 i == Ydrip_s2B ? 16 :
7617 i == Xsand_stonein_1 ? 32 :
7618 i == Xsand_stonein_2 ? 32 :
7619 i == Xsand_stonein_3 ? 32 :
7620 i == Xsand_stonein_4 ? 32 :
7621 i == Xsand_stoneout_1 ? 16 :
7622 i == Xsand_stoneout_2 ? 16 : 8);
7623 int cx = ABS(dx) * (TILEX / num_steps);
7624 int cy = ABS(dy) * (TILEY / num_steps);
7625 int step_frame = (i == Ydrip_s2 ? j + 8 :
7626 i == Ydrip_s2B ? j + 8 :
7627 i == Xsand_stonein_2 ? j + 8 :
7628 i == Xsand_stonein_3 ? j + 16 :
7629 i == Xsand_stonein_4 ? j + 24 :
7630 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7631 int step = (is_backside ? step_frame : num_steps - step_frame);
7633 if (is_backside) /* tile where movement starts */
7635 if (dx < 0 || dy < 0)
7637 g_em->src_offset_x = cx * step;
7638 g_em->src_offset_y = cy * step;
7642 g_em->dst_offset_x = cx * step;
7643 g_em->dst_offset_y = cy * step;
7646 else /* tile where movement ends */
7648 if (dx < 0 || dy < 0)
7650 g_em->dst_offset_x = cx * step;
7651 g_em->dst_offset_y = cy * step;
7655 g_em->src_offset_x = cx * step;
7656 g_em->src_offset_y = cy * step;
7660 g_em->width = TILEX - cx * step;
7661 g_em->height = TILEY - cy * step;
7664 /* create unique graphic identifier to decide if tile must be redrawn */
7665 /* bit 31 - 16 (16 bit): EM style graphic
7666 bit 15 - 12 ( 4 bit): EM style frame
7667 bit 11 - 6 ( 6 bit): graphic width
7668 bit 5 - 0 ( 6 bit): graphic height */
7669 g_em->unique_identifier =
7670 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7674 /* skip check for EMC elements not contained in original EMC artwork */
7675 if (element == EL_EMC_FAKE_ACID)
7678 if (g_em->bitmap != debug_bitmap ||
7679 g_em->src_x != debug_src_x ||
7680 g_em->src_y != debug_src_y ||
7681 g_em->src_offset_x != 0 ||
7682 g_em->src_offset_y != 0 ||
7683 g_em->dst_offset_x != 0 ||
7684 g_em->dst_offset_y != 0 ||
7685 g_em->width != TILEX ||
7686 g_em->height != TILEY)
7688 static int last_i = -1;
7696 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7697 i, element, element_info[element].token_name,
7698 element_action_info[effective_action].suffix, direction);
7700 if (element != effective_element)
7701 printf(" [%d ('%s')]",
7703 element_info[effective_element].token_name);
7707 if (g_em->bitmap != debug_bitmap)
7708 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7709 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7711 if (g_em->src_x != debug_src_x ||
7712 g_em->src_y != debug_src_y)
7713 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7714 j, (is_backside ? 'B' : 'F'),
7715 g_em->src_x, g_em->src_y,
7716 g_em->src_x / 32, g_em->src_y / 32,
7717 debug_src_x, debug_src_y,
7718 debug_src_x / 32, debug_src_y / 32);
7720 if (g_em->src_offset_x != 0 ||
7721 g_em->src_offset_y != 0 ||
7722 g_em->dst_offset_x != 0 ||
7723 g_em->dst_offset_y != 0)
7724 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7726 g_em->src_offset_x, g_em->src_offset_y,
7727 g_em->dst_offset_x, g_em->dst_offset_y);
7729 if (g_em->width != TILEX ||
7730 g_em->height != TILEY)
7731 printf(" %d (%d): size %d,%d should be %d,%d\n",
7733 g_em->width, g_em->height, TILEX, TILEY);
7735 num_em_gfx_errors++;
7742 for (i = 0; i < TILE_MAX; i++)
7744 for (j = 0; j < 8; j++)
7746 int element = object_mapping[i].element_rnd;
7747 int action = object_mapping[i].action;
7748 int direction = object_mapping[i].direction;
7749 boolean is_backside = object_mapping[i].is_backside;
7750 int graphic_action = el_act_dir2img(element, action, direction);
7751 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7753 if ((action == ACTION_SMASHED_BY_ROCK ||
7754 action == ACTION_SMASHED_BY_SPRING ||
7755 action == ACTION_EATING) &&
7756 graphic_action == graphic_default)
7758 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7759 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7760 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7761 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7764 /* no separate animation for "smashed by rock" -- use rock instead */
7765 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7766 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7768 g_em->bitmap = g_xx->bitmap;
7769 g_em->src_x = g_xx->src_x;
7770 g_em->src_y = g_xx->src_y;
7771 g_em->src_offset_x = g_xx->src_offset_x;
7772 g_em->src_offset_y = g_xx->src_offset_y;
7773 g_em->dst_offset_x = g_xx->dst_offset_x;
7774 g_em->dst_offset_y = g_xx->dst_offset_y;
7775 g_em->width = g_xx->width;
7776 g_em->height = g_xx->height;
7777 g_em->unique_identifier = g_xx->unique_identifier;
7780 g_em->preserve_background = TRUE;
7785 for (p = 0; p < MAX_PLAYERS; p++)
7787 for (i = 0; i < SPR_MAX; i++)
7789 int element = player_mapping[p][i].element_rnd;
7790 int action = player_mapping[p][i].action;
7791 int direction = player_mapping[p][i].direction;
7793 for (j = 0; j < 8; j++)
7795 int effective_element = element;
7796 int effective_action = action;
7797 int graphic = (direction == MV_NONE ?
7798 el_act2img(effective_element, effective_action) :
7799 el_act_dir2img(effective_element, effective_action,
7801 struct GraphicInfo *g = &graphic_info[graphic];
7802 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7808 Bitmap *debug_bitmap = g_em->bitmap;
7809 int debug_src_x = g_em->src_x;
7810 int debug_src_y = g_em->src_y;
7813 int frame = getAnimationFrame(g->anim_frames,
7816 g->anim_start_frame,
7819 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7821 g_em->bitmap = src_bitmap;
7822 g_em->src_x = src_x;
7823 g_em->src_y = src_y;
7824 g_em->src_offset_x = 0;
7825 g_em->src_offset_y = 0;
7826 g_em->dst_offset_x = 0;
7827 g_em->dst_offset_y = 0;
7828 g_em->width = TILEX;
7829 g_em->height = TILEY;
7833 /* skip check for EMC elements not contained in original EMC artwork */
7834 if (element == EL_PLAYER_3 ||
7835 element == EL_PLAYER_4)
7838 if (g_em->bitmap != debug_bitmap ||
7839 g_em->src_x != debug_src_x ||
7840 g_em->src_y != debug_src_y)
7842 static int last_i = -1;
7850 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7851 p, i, element, element_info[element].token_name,
7852 element_action_info[effective_action].suffix, direction);
7854 if (element != effective_element)
7855 printf(" [%d ('%s')]",
7857 element_info[effective_element].token_name);
7861 if (g_em->bitmap != debug_bitmap)
7862 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7863 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7865 if (g_em->src_x != debug_src_x ||
7866 g_em->src_y != debug_src_y)
7867 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7869 g_em->src_x, g_em->src_y,
7870 g_em->src_x / 32, g_em->src_y / 32,
7871 debug_src_x, debug_src_y,
7872 debug_src_x / 32, debug_src_y / 32);
7874 num_em_gfx_errors++;
7884 printf("::: [%d errors found]\n", num_em_gfx_errors);
7890 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7891 boolean any_player_moving,
7892 boolean any_player_snapping,
7893 boolean any_player_dropping)
7895 static boolean player_was_waiting = TRUE;
7897 if (frame == 0 && !any_player_dropping)
7899 if (!player_was_waiting)
7901 if (!SaveEngineSnapshotToList())
7904 player_was_waiting = TRUE;
7907 else if (any_player_moving || any_player_snapping || any_player_dropping)
7909 player_was_waiting = FALSE;
7913 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7914 boolean murphy_is_dropping)
7916 static boolean player_was_waiting = TRUE;
7918 if (murphy_is_waiting)
7920 if (!player_was_waiting)
7922 if (!SaveEngineSnapshotToList())
7925 player_was_waiting = TRUE;
7930 player_was_waiting = FALSE;
7934 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7935 boolean any_player_moving,
7936 boolean any_player_snapping,
7937 boolean any_player_dropping)
7939 if (tape.single_step && tape.recording && !tape.pausing)
7940 if (frame == 0 && !any_player_dropping)
7941 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7943 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7944 any_player_snapping, any_player_dropping);
7947 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7948 boolean murphy_is_dropping)
7950 if (tape.single_step && tape.recording && !tape.pausing)
7951 if (murphy_is_waiting)
7952 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7954 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7957 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7958 int graphic, int sync_frame, int x, int y)
7960 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7962 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7965 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7967 return (IS_NEXT_FRAME(sync_frame, graphic));
7970 int getGraphicInfo_Delay(int graphic)
7972 return graphic_info[graphic].anim_delay;
7975 void PlayMenuSoundExt(int sound)
7977 if (sound == SND_UNDEFINED)
7980 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7981 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7984 if (IS_LOOP_SOUND(sound))
7985 PlaySoundLoop(sound);
7990 void PlayMenuSound()
7992 PlayMenuSoundExt(menu.sound[game_status]);
7995 void PlayMenuSoundStereo(int sound, int stereo_position)
7997 if (sound == SND_UNDEFINED)
8000 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8001 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8004 if (IS_LOOP_SOUND(sound))
8005 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8007 PlaySoundStereo(sound, stereo_position);
8010 void PlayMenuSoundIfLoopExt(int sound)
8012 if (sound == SND_UNDEFINED)
8015 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8016 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8019 if (IS_LOOP_SOUND(sound))
8020 PlaySoundLoop(sound);
8023 void PlayMenuSoundIfLoop()
8025 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8028 void PlayMenuMusicExt(int music)
8030 if (music == MUS_UNDEFINED)
8033 if (!setup.sound_music)
8039 void PlayMenuMusic()
8041 PlayMenuMusicExt(menu.music[game_status]);
8044 void PlaySoundActivating()
8047 PlaySound(SND_MENU_ITEM_ACTIVATING);
8051 void PlaySoundSelecting()
8054 PlaySound(SND_MENU_ITEM_SELECTING);
8058 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8060 boolean change_fullscreen = (setup.fullscreen !=
8061 video.fullscreen_enabled);
8062 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8063 !strEqual(setup.fullscreen_mode,
8064 video.fullscreen_mode_current));
8065 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8066 setup.window_scaling_percent !=
8067 video.window_scaling_percent);
8069 if (change_window_scaling_percent && video.fullscreen_enabled)
8072 if (!change_window_scaling_percent && !video.fullscreen_available)
8075 #if defined(TARGET_SDL2)
8076 if (change_window_scaling_percent)
8078 SDLSetWindowScaling(setup.window_scaling_percent);
8082 else if (change_fullscreen)
8084 SDLSetWindowFullscreen(setup.fullscreen);
8086 /* set setup value according to successfully changed fullscreen mode */
8087 setup.fullscreen = video.fullscreen_enabled;
8093 if (change_fullscreen ||
8094 change_fullscreen_mode ||
8095 change_window_scaling_percent)
8097 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8099 /* save backbuffer content which gets lost when toggling fullscreen mode */
8100 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8102 if (change_fullscreen_mode)
8104 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8105 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8108 if (change_window_scaling_percent)
8110 /* keep window mode, but change window scaling */
8111 video.fullscreen_enabled = TRUE; /* force new window scaling */
8114 /* toggle fullscreen */
8115 ChangeVideoModeIfNeeded(setup.fullscreen);
8117 /* set setup value according to successfully changed fullscreen mode */
8118 setup.fullscreen = video.fullscreen_enabled;
8120 /* restore backbuffer content from temporary backbuffer backup bitmap */
8121 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8123 FreeBitmap(tmp_backbuffer);
8125 /* update visible window/screen */
8126 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8130 void JoinRectangles(int *x, int *y, int *width, int *height,
8131 int x2, int y2, int width2, int height2)
8133 // do not join with "off-screen" rectangle
8134 if (x2 == -1 || y2 == -1)
8139 *width = MAX(*width, width2);
8140 *height = MAX(*height, height2);
8143 void ChangeViewportPropertiesIfNeeded()
8145 int gfx_game_mode = game_status;
8146 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8148 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8149 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8150 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8151 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8152 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8153 int new_win_xsize = vp_window->width;
8154 int new_win_ysize = vp_window->height;
8155 int border_size = vp_playfield->border_size;
8156 int new_sx = vp_playfield->x + border_size;
8157 int new_sy = vp_playfield->y + border_size;
8158 int new_sxsize = vp_playfield->width - 2 * border_size;
8159 int new_sysize = vp_playfield->height - 2 * border_size;
8160 int new_real_sx = vp_playfield->x;
8161 int new_real_sy = vp_playfield->y;
8162 int new_full_sxsize = vp_playfield->width;
8163 int new_full_sysize = vp_playfield->height;
8164 int new_dx = vp_door_1->x;
8165 int new_dy = vp_door_1->y;
8166 int new_dxsize = vp_door_1->width;
8167 int new_dysize = vp_door_1->height;
8168 int new_vx = vp_door_2->x;
8169 int new_vy = vp_door_2->y;
8170 int new_vxsize = vp_door_2->width;
8171 int new_vysize = vp_door_2->height;
8172 int new_ex = vp_door_3->x;
8173 int new_ey = vp_door_3->y;
8174 int new_exsize = vp_door_3->width;
8175 int new_eysize = vp_door_3->height;
8176 int new_tilesize_var =
8177 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8179 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8180 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8181 int new_scr_fieldx = new_sxsize / tilesize;
8182 int new_scr_fieldy = new_sysize / tilesize;
8183 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8184 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8185 boolean init_gfx_buffers = FALSE;
8186 boolean init_video_buffer = FALSE;
8187 boolean init_gadgets_and_toons = FALSE;
8188 boolean init_em_graphics = FALSE;
8190 if (new_win_xsize != WIN_XSIZE ||
8191 new_win_ysize != WIN_YSIZE)
8193 WIN_XSIZE = new_win_xsize;
8194 WIN_YSIZE = new_win_ysize;
8196 init_video_buffer = TRUE;
8197 init_gfx_buffers = TRUE;
8199 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8202 if (new_scr_fieldx != SCR_FIELDX ||
8203 new_scr_fieldy != SCR_FIELDY)
8205 /* this always toggles between MAIN and GAME when using small tile size */
8207 SCR_FIELDX = new_scr_fieldx;
8208 SCR_FIELDY = new_scr_fieldy;
8210 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8221 new_sxsize != SXSIZE ||
8222 new_sysize != SYSIZE ||
8223 new_dxsize != DXSIZE ||
8224 new_dysize != DYSIZE ||
8225 new_vxsize != VXSIZE ||
8226 new_vysize != VYSIZE ||
8227 new_exsize != EXSIZE ||
8228 new_eysize != EYSIZE ||
8229 new_real_sx != REAL_SX ||
8230 new_real_sy != REAL_SY ||
8231 new_full_sxsize != FULL_SXSIZE ||
8232 new_full_sysize != FULL_SYSIZE ||
8233 new_tilesize_var != TILESIZE_VAR
8236 // ------------------------------------------------------------------------
8237 // determine next fading area for changed viewport definitions
8238 // ------------------------------------------------------------------------
8240 // start with current playfield area (default fading area)
8243 FADE_SXSIZE = FULL_SXSIZE;
8244 FADE_SYSIZE = FULL_SYSIZE;
8246 // add new playfield area if position or size has changed
8247 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8248 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8250 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8251 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8254 // add current and new door 1 area if position or size has changed
8255 if (new_dx != DX || new_dy != DY ||
8256 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8258 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8259 DX, DY, DXSIZE, DYSIZE);
8260 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8261 new_dx, new_dy, new_dxsize, new_dysize);
8264 // add current and new door 2 area if position or size has changed
8265 if (new_dx != VX || new_dy != VY ||
8266 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8268 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8269 VX, VY, VXSIZE, VYSIZE);
8270 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8271 new_vx, new_vy, new_vxsize, new_vysize);
8274 // ------------------------------------------------------------------------
8275 // handle changed tile size
8276 // ------------------------------------------------------------------------
8278 if (new_tilesize_var != TILESIZE_VAR)
8280 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8282 // changing tile size invalidates scroll values of engine snapshots
8283 FreeEngineSnapshotSingle();
8285 // changing tile size requires update of graphic mapping for EM engine
8286 init_em_graphics = TRUE;
8297 SXSIZE = new_sxsize;
8298 SYSIZE = new_sysize;
8299 DXSIZE = new_dxsize;
8300 DYSIZE = new_dysize;
8301 VXSIZE = new_vxsize;
8302 VYSIZE = new_vysize;
8303 EXSIZE = new_exsize;
8304 EYSIZE = new_eysize;
8305 REAL_SX = new_real_sx;
8306 REAL_SY = new_real_sy;
8307 FULL_SXSIZE = new_full_sxsize;
8308 FULL_SYSIZE = new_full_sysize;
8309 TILESIZE_VAR = new_tilesize_var;
8311 init_gfx_buffers = TRUE;
8312 init_gadgets_and_toons = TRUE;
8314 // printf("::: viewports: init_gfx_buffers\n");
8315 // printf("::: viewports: init_gadgets_and_toons\n");
8318 if (init_gfx_buffers)
8320 // printf("::: init_gfx_buffers\n");
8322 SCR_FIELDX = new_scr_fieldx_buffers;
8323 SCR_FIELDY = new_scr_fieldy_buffers;
8327 SCR_FIELDX = new_scr_fieldx;
8328 SCR_FIELDY = new_scr_fieldy;
8330 SetDrawDeactivationMask(REDRAW_NONE);
8331 SetDrawBackgroundMask(REDRAW_FIELD);
8334 if (init_video_buffer)
8336 // printf("::: init_video_buffer\n");
8338 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8341 if (init_gadgets_and_toons)
8343 // printf("::: init_gadgets_and_toons\n");
8349 if (init_em_graphics)
8351 InitGraphicInfo_EM();