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)
1242 struct GraphicInfo *g = &graphic_info[graphic];
1246 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1248 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1252 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1254 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1256 MarkTileDirty(x / tilesize, y / tilesize);
1259 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1265 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1266 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1269 void DrawMiniGraphic(int x, int y, int graphic)
1271 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1272 MarkTileDirty(x / 2, y / 2);
1275 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1280 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1281 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1284 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1285 int graphic, int frame,
1286 int cut_mode, int mask_mode)
1291 int width = TILEX, height = TILEY;
1294 if (dx || dy) /* shifted graphic */
1296 if (x < BX1) /* object enters playfield from the left */
1303 else if (x > BX2) /* object enters playfield from the right */
1309 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1315 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1317 else if (dx) /* general horizontal movement */
1318 MarkTileDirty(x + SIGN(dx), y);
1320 if (y < BY1) /* object enters playfield from the top */
1322 if (cut_mode == CUT_BELOW) /* object completely above top border */
1330 else if (y > BY2) /* object enters playfield from the bottom */
1336 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1342 else if (dy > 0 && cut_mode == CUT_ABOVE)
1344 if (y == BY2) /* object completely above bottom border */
1350 MarkTileDirty(x, y + 1);
1351 } /* object leaves playfield to the bottom */
1352 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1354 else if (dy) /* general vertical movement */
1355 MarkTileDirty(x, y + SIGN(dy));
1359 if (!IN_SCR_FIELD(x, y))
1361 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1362 printf("DrawGraphicShifted(): This should never happen!\n");
1367 width = width * TILESIZE_VAR / TILESIZE;
1368 height = height * TILESIZE_VAR / TILESIZE;
1369 cx = cx * TILESIZE_VAR / TILESIZE;
1370 cy = cy * TILESIZE_VAR / TILESIZE;
1371 dx = dx * TILESIZE_VAR / TILESIZE;
1372 dy = dy * TILESIZE_VAR / TILESIZE;
1374 if (width > 0 && height > 0)
1376 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1381 dst_x = FX + x * TILEX_VAR + dx;
1382 dst_y = FY + y * TILEY_VAR + dy;
1384 if (mask_mode == USE_MASKING)
1385 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1388 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1391 MarkTileDirty(x, y);
1395 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1396 int graphic, int frame,
1397 int cut_mode, int mask_mode)
1402 int width = TILEX_VAR, height = TILEY_VAR;
1405 int x2 = x + SIGN(dx);
1406 int y2 = y + SIGN(dy);
1408 /* movement with two-tile animations must be sync'ed with movement position,
1409 not with current GfxFrame (which can be higher when using slow movement) */
1410 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1411 int anim_frames = graphic_info[graphic].anim_frames;
1413 /* (we also need anim_delay here for movement animations with less frames) */
1414 int anim_delay = graphic_info[graphic].anim_delay;
1415 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1417 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1418 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1420 /* re-calculate animation frame for two-tile movement animation */
1421 frame = getGraphicAnimationFrame(graphic, sync_frame);
1423 /* check if movement start graphic inside screen area and should be drawn */
1424 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1426 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1428 dst_x = FX + x1 * TILEX_VAR;
1429 dst_y = FY + y1 * TILEY_VAR;
1431 if (mask_mode == USE_MASKING)
1432 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1435 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1438 MarkTileDirty(x1, y1);
1441 /* check if movement end graphic inside screen area and should be drawn */
1442 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1444 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1446 dst_x = FX + x2 * TILEX_VAR;
1447 dst_y = FY + y2 * TILEY_VAR;
1449 if (mask_mode == USE_MASKING)
1450 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1453 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1456 MarkTileDirty(x2, y2);
1460 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1461 int graphic, int frame,
1462 int cut_mode, int mask_mode)
1466 DrawGraphic(x, y, graphic, frame);
1471 if (graphic_info[graphic].double_movement) /* EM style movement images */
1472 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1474 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1477 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1478 int frame, int cut_mode)
1480 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1483 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1484 int cut_mode, int mask_mode)
1486 int lx = LEVELX(x), ly = LEVELY(y);
1490 if (IN_LEV_FIELD(lx, ly))
1492 SetRandomAnimationValue(lx, ly);
1494 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1495 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1497 /* do not use double (EM style) movement graphic when not moving */
1498 if (graphic_info[graphic].double_movement && !dx && !dy)
1500 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1501 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1504 else /* border element */
1506 graphic = el2img(element);
1507 frame = getGraphicAnimationFrame(graphic, -1);
1510 if (element == EL_EXPANDABLE_WALL)
1512 boolean left_stopped = FALSE, right_stopped = FALSE;
1514 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1515 left_stopped = TRUE;
1516 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1517 right_stopped = TRUE;
1519 if (left_stopped && right_stopped)
1521 else if (left_stopped)
1523 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1524 frame = graphic_info[graphic].anim_frames - 1;
1526 else if (right_stopped)
1528 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1529 frame = graphic_info[graphic].anim_frames - 1;
1534 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1535 else if (mask_mode == USE_MASKING)
1536 DrawGraphicThruMask(x, y, graphic, frame);
1538 DrawGraphic(x, y, graphic, frame);
1541 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1542 int cut_mode, int mask_mode)
1544 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1545 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1546 cut_mode, mask_mode);
1549 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1552 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1555 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1558 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1561 void DrawLevelElementThruMask(int x, int y, int element)
1563 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1566 void DrawLevelFieldThruMask(int x, int y)
1568 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1571 /* !!! implementation of quicksand is totally broken !!! */
1572 #define IS_CRUMBLED_TILE(x, y, e) \
1573 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1574 !IS_MOVING(x, y) || \
1575 (e) == EL_QUICKSAND_EMPTYING || \
1576 (e) == EL_QUICKSAND_FAST_EMPTYING))
1578 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1583 int width, height, cx, cy;
1584 int sx = SCREENX(x), sy = SCREENY(y);
1585 int crumbled_border_size = graphic_info[graphic].border_size;
1588 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1590 for (i = 1; i < 4; i++)
1592 int dxx = (i & 1 ? dx : 0);
1593 int dyy = (i & 2 ? dy : 0);
1596 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1599 /* check if neighbour field is of same crumble type */
1600 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1601 graphic_info[graphic].class ==
1602 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1604 /* return if check prevents inner corner */
1605 if (same == (dxx == dx && dyy == dy))
1609 /* if we reach this point, we have an inner corner */
1611 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1613 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1614 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1615 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1616 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1618 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1619 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1622 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1627 int width, height, bx, by, cx, cy;
1628 int sx = SCREENX(x), sy = SCREENY(y);
1629 int crumbled_border_size = graphic_info[graphic].border_size;
1630 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1631 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1634 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1636 /* draw simple, sloppy, non-corner-accurate crumbled border */
1638 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1639 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1640 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1641 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1643 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1644 FX + sx * TILEX_VAR + cx,
1645 FY + sy * TILEY_VAR + cy);
1647 /* (remaining middle border part must be at least as big as corner part) */
1648 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1649 crumbled_border_size >= TILESIZE / 3)
1652 /* correct corners of crumbled border, if needed */
1654 for (i = -1; i <= 1; i += 2)
1656 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1657 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1658 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1661 /* check if neighbour field is of same crumble type */
1662 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1663 graphic_info[graphic].class ==
1664 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1666 /* no crumbled corner, but continued crumbled border */
1668 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1669 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1670 int b1 = (i == 1 ? crumbled_border_size_var :
1671 TILESIZE_VAR - 2 * crumbled_border_size_var);
1673 width = crumbled_border_size_var;
1674 height = crumbled_border_size_var;
1676 if (dir == 1 || dir == 2)
1691 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1693 FX + sx * TILEX_VAR + cx,
1694 FY + sy * TILEY_VAR + cy);
1699 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1701 int sx = SCREENX(x), sy = SCREENY(y);
1704 static int xy[4][2] =
1712 if (!IN_LEV_FIELD(x, y))
1715 element = TILE_GFX_ELEMENT(x, y);
1717 /* crumble field itself */
1718 if (IS_CRUMBLED_TILE(x, y, element))
1720 if (!IN_SCR_FIELD(sx, sy))
1723 for (i = 0; i < 4; i++)
1725 int xx = x + xy[i][0];
1726 int yy = y + xy[i][1];
1728 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1731 /* check if neighbour field is of same crumble type */
1732 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1733 graphic_info[graphic].class ==
1734 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1737 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1740 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1741 graphic_info[graphic].anim_frames == 2)
1743 for (i = 0; i < 4; i++)
1745 int dx = (i & 1 ? +1 : -1);
1746 int dy = (i & 2 ? +1 : -1);
1748 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1752 MarkTileDirty(sx, sy);
1754 else /* center field not crumbled -- crumble neighbour fields */
1756 for (i = 0; i < 4; i++)
1758 int xx = x + xy[i][0];
1759 int yy = y + xy[i][1];
1760 int sxx = sx + xy[i][0];
1761 int syy = sy + xy[i][1];
1763 if (!IN_LEV_FIELD(xx, yy) ||
1764 !IN_SCR_FIELD(sxx, syy))
1767 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1770 element = TILE_GFX_ELEMENT(xx, yy);
1772 if (!IS_CRUMBLED_TILE(xx, yy, element))
1775 graphic = el_act2crm(element, ACTION_DEFAULT);
1777 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1779 MarkTileDirty(sxx, syy);
1784 void DrawLevelFieldCrumbled(int x, int y)
1788 if (!IN_LEV_FIELD(x, y))
1791 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1792 GfxElement[x][y] != EL_UNDEFINED &&
1793 GFX_CRUMBLED(GfxElement[x][y]))
1795 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1800 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1802 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1805 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1808 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1809 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1810 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1811 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1812 int sx = SCREENX(x), sy = SCREENY(y);
1814 DrawGraphic(sx, sy, graphic1, frame1);
1815 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1818 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1820 int sx = SCREENX(x), sy = SCREENY(y);
1821 static int xy[4][2] =
1830 for (i = 0; i < 4; i++)
1832 int xx = x + xy[i][0];
1833 int yy = y + xy[i][1];
1834 int sxx = sx + xy[i][0];
1835 int syy = sy + xy[i][1];
1837 if (!IN_LEV_FIELD(xx, yy) ||
1838 !IN_SCR_FIELD(sxx, syy) ||
1839 !GFX_CRUMBLED(Feld[xx][yy]) ||
1843 DrawLevelField(xx, yy);
1847 static int getBorderElement(int x, int y)
1851 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1852 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1853 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1854 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1855 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1856 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1857 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1859 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1860 int steel_position = (x == -1 && y == -1 ? 0 :
1861 x == lev_fieldx && y == -1 ? 1 :
1862 x == -1 && y == lev_fieldy ? 2 :
1863 x == lev_fieldx && y == lev_fieldy ? 3 :
1864 x == -1 || x == lev_fieldx ? 4 :
1865 y == -1 || y == lev_fieldy ? 5 : 6);
1867 return border[steel_position][steel_type];
1870 void DrawScreenElement(int x, int y, int element)
1872 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1873 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1876 void DrawLevelElement(int x, int y, int element)
1878 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1879 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1882 void DrawScreenField(int x, int y)
1884 int lx = LEVELX(x), ly = LEVELY(y);
1885 int element, content;
1887 if (!IN_LEV_FIELD(lx, ly))
1889 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1892 element = getBorderElement(lx, ly);
1894 DrawScreenElement(x, y, element);
1899 element = Feld[lx][ly];
1900 content = Store[lx][ly];
1902 if (IS_MOVING(lx, ly))
1904 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1905 boolean cut_mode = NO_CUTTING;
1907 if (element == EL_QUICKSAND_EMPTYING ||
1908 element == EL_QUICKSAND_FAST_EMPTYING ||
1909 element == EL_MAGIC_WALL_EMPTYING ||
1910 element == EL_BD_MAGIC_WALL_EMPTYING ||
1911 element == EL_DC_MAGIC_WALL_EMPTYING ||
1912 element == EL_AMOEBA_DROPPING)
1913 cut_mode = CUT_ABOVE;
1914 else if (element == EL_QUICKSAND_FILLING ||
1915 element == EL_QUICKSAND_FAST_FILLING ||
1916 element == EL_MAGIC_WALL_FILLING ||
1917 element == EL_BD_MAGIC_WALL_FILLING ||
1918 element == EL_DC_MAGIC_WALL_FILLING)
1919 cut_mode = CUT_BELOW;
1921 if (cut_mode == CUT_ABOVE)
1922 DrawScreenElement(x, y, element);
1924 DrawScreenElement(x, y, EL_EMPTY);
1927 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1928 else if (cut_mode == NO_CUTTING)
1929 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1932 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1934 if (cut_mode == CUT_BELOW &&
1935 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1936 DrawLevelElement(lx, ly + 1, element);
1939 if (content == EL_ACID)
1941 int dir = MovDir[lx][ly];
1942 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1943 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1945 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1948 else if (IS_BLOCKED(lx, ly))
1953 boolean cut_mode = NO_CUTTING;
1954 int element_old, content_old;
1956 Blocked2Moving(lx, ly, &oldx, &oldy);
1959 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1960 MovDir[oldx][oldy] == MV_RIGHT);
1962 element_old = Feld[oldx][oldy];
1963 content_old = Store[oldx][oldy];
1965 if (element_old == EL_QUICKSAND_EMPTYING ||
1966 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1967 element_old == EL_MAGIC_WALL_EMPTYING ||
1968 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1969 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1970 element_old == EL_AMOEBA_DROPPING)
1971 cut_mode = CUT_ABOVE;
1973 DrawScreenElement(x, y, EL_EMPTY);
1976 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1978 else if (cut_mode == NO_CUTTING)
1979 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1982 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1985 else if (IS_DRAWABLE(element))
1986 DrawScreenElement(x, y, element);
1988 DrawScreenElement(x, y, EL_EMPTY);
1991 void DrawLevelField(int x, int y)
1993 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1994 DrawScreenField(SCREENX(x), SCREENY(y));
1995 else if (IS_MOVING(x, y))
1999 Moving2Blocked(x, y, &newx, &newy);
2000 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2001 DrawScreenField(SCREENX(newx), SCREENY(newy));
2003 else if (IS_BLOCKED(x, y))
2007 Blocked2Moving(x, y, &oldx, &oldy);
2008 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2009 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2013 void DrawSizedElement(int x, int y, int element, int tilesize)
2017 graphic = el2edimg(element);
2018 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2021 void DrawMiniElement(int x, int y, int element)
2025 graphic = el2edimg(element);
2026 DrawMiniGraphic(x, y, graphic);
2029 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2032 int x = sx + scroll_x, y = sy + scroll_y;
2034 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2035 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2036 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2037 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2039 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2042 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2044 int x = sx + scroll_x, y = sy + scroll_y;
2046 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2047 DrawMiniElement(sx, sy, EL_EMPTY);
2048 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2049 DrawMiniElement(sx, sy, Feld[x][y]);
2051 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2054 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2055 int x, int y, int xsize, int ysize,
2056 int tile_width, int tile_height)
2060 int dst_x = startx + x * tile_width;
2061 int dst_y = starty + y * tile_height;
2062 int width = graphic_info[graphic].width;
2063 int height = graphic_info[graphic].height;
2064 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2065 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2066 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2067 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2068 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2069 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2070 boolean draw_masked = graphic_info[graphic].draw_masked;
2072 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2074 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2076 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2080 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2081 inner_sx + (x - 1) * tile_width % inner_width);
2082 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2083 inner_sy + (y - 1) * tile_height % inner_height);
2086 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2089 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2093 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2094 int x, int y, int xsize, int ysize, int font_nr)
2096 int font_width = getFontWidth(font_nr);
2097 int font_height = getFontHeight(font_nr);
2099 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2100 font_width, font_height);
2103 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2105 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2106 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2107 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2108 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2109 boolean no_delay = (tape.warp_forward);
2110 unsigned int anim_delay = 0;
2111 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2112 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2113 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2114 int font_width = getFontWidth(font_nr);
2115 int font_height = getFontHeight(font_nr);
2116 int max_xsize = level.envelope[envelope_nr].xsize;
2117 int max_ysize = level.envelope[envelope_nr].ysize;
2118 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2119 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2120 int xend = max_xsize;
2121 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2122 int xstep = (xstart < xend ? 1 : 0);
2123 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2125 int end = MAX(xend - xstart, yend - ystart);
2128 for (i = start; i <= end; i++)
2130 int last_frame = end; // last frame of this "for" loop
2131 int x = xstart + i * xstep;
2132 int y = ystart + i * ystep;
2133 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2134 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2135 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2136 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2139 SetDrawtoField(DRAW_FIELDBUFFER);
2141 BlitScreenToBitmap(backbuffer);
2143 SetDrawtoField(DRAW_BACKBUFFER);
2145 for (yy = 0; yy < ysize; yy++)
2146 for (xx = 0; xx < xsize; xx++)
2147 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2149 DrawTextBuffer(sx + font_width, sy + font_height,
2150 level.envelope[envelope_nr].text, font_nr, max_xsize,
2151 xsize - 2, ysize - 2, 0, mask_mode,
2152 level.envelope[envelope_nr].autowrap,
2153 level.envelope[envelope_nr].centered, FALSE);
2155 redraw_mask |= REDRAW_FIELD;
2158 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2162 void ShowEnvelope(int envelope_nr)
2164 int element = EL_ENVELOPE_1 + envelope_nr;
2165 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2166 int sound_opening = element_info[element].sound[ACTION_OPENING];
2167 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2168 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2169 boolean no_delay = (tape.warp_forward);
2170 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2171 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2172 int anim_mode = graphic_info[graphic].anim_mode;
2173 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2174 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2176 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2178 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2180 if (anim_mode == ANIM_DEFAULT)
2181 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2183 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2186 Delay(wait_delay_value);
2188 WaitForEventToContinue();
2190 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2192 if (anim_mode != ANIM_NONE)
2193 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2195 if (anim_mode == ANIM_DEFAULT)
2196 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2198 game.envelope_active = FALSE;
2200 SetDrawtoField(DRAW_FIELDBUFFER);
2202 redraw_mask |= REDRAW_FIELD;
2206 static void setRequestBasePosition(int *x, int *y)
2208 int sx_base, sy_base;
2210 if (request.x != -1)
2211 sx_base = request.x;
2212 else if (request.align == ALIGN_LEFT)
2214 else if (request.align == ALIGN_RIGHT)
2215 sx_base = SX + SXSIZE;
2217 sx_base = SX + SXSIZE / 2;
2219 if (request.y != -1)
2220 sy_base = request.y;
2221 else if (request.valign == VALIGN_TOP)
2223 else if (request.valign == VALIGN_BOTTOM)
2224 sy_base = SY + SYSIZE;
2226 sy_base = SY + SYSIZE / 2;
2232 static void setRequestPositionExt(int *x, int *y, int width, int height,
2233 boolean add_border_size)
2235 int border_size = request.border_size;
2236 int sx_base, sy_base;
2239 setRequestBasePosition(&sx_base, &sy_base);
2241 if (request.align == ALIGN_LEFT)
2243 else if (request.align == ALIGN_RIGHT)
2244 sx = sx_base - width;
2246 sx = sx_base - width / 2;
2248 if (request.valign == VALIGN_TOP)
2250 else if (request.valign == VALIGN_BOTTOM)
2251 sy = sy_base - height;
2253 sy = sy_base - height / 2;
2255 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2256 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2258 if (add_border_size)
2268 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2270 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2273 void DrawEnvelopeRequest(char *text)
2275 int last_game_status = game_status; /* save current game status */
2276 char *text_final = text;
2277 char *text_door_style = NULL;
2278 int graphic = IMG_BACKGROUND_REQUEST;
2279 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2280 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2281 int font_nr = FONT_REQUEST;
2282 int font_width = getFontWidth(font_nr);
2283 int font_height = getFontHeight(font_nr);
2284 int border_size = request.border_size;
2285 int line_spacing = request.line_spacing;
2286 int line_height = font_height + line_spacing;
2287 int max_text_width = request.width - 2 * border_size;
2288 int max_text_height = request.height - 2 * border_size;
2289 int line_length = max_text_width / font_width;
2290 int max_lines = max_text_height / line_height;
2291 int text_width = line_length * font_width;
2292 int width = request.width;
2293 int height = request.height;
2294 int tile_size = MAX(request.step_offset, 1);
2295 int x_steps = width / tile_size;
2296 int y_steps = height / tile_size;
2297 int sx_offset = border_size;
2298 int sy_offset = border_size;
2302 if (request.centered)
2303 sx_offset = (request.width - text_width) / 2;
2305 if (request.wrap_single_words && !request.autowrap)
2307 char *src_text_ptr, *dst_text_ptr;
2309 text_door_style = checked_malloc(2 * strlen(text) + 1);
2311 src_text_ptr = text;
2312 dst_text_ptr = text_door_style;
2314 while (*src_text_ptr)
2316 if (*src_text_ptr == ' ' ||
2317 *src_text_ptr == '?' ||
2318 *src_text_ptr == '!')
2319 *dst_text_ptr++ = '\n';
2321 if (*src_text_ptr != ' ')
2322 *dst_text_ptr++ = *src_text_ptr;
2327 *dst_text_ptr = '\0';
2329 text_final = text_door_style;
2332 setRequestPosition(&sx, &sy, FALSE);
2334 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2336 for (y = 0; y < y_steps; y++)
2337 for (x = 0; x < x_steps; x++)
2338 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2339 x, y, x_steps, y_steps,
2340 tile_size, tile_size);
2342 /* force DOOR font inside door area */
2343 game_status = GAME_MODE_PSEUDO_DOOR;
2345 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2346 line_length, -1, max_lines, line_spacing, mask_mode,
2347 request.autowrap, request.centered, FALSE);
2349 game_status = last_game_status; /* restore current game status */
2351 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2352 RedrawGadget(tool_gadget[i]);
2354 // store readily prepared envelope request for later use when animating
2355 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2357 if (text_door_style)
2358 free(text_door_style);
2361 void AnimateEnvelopeRequest(int anim_mode, int action)
2363 int graphic = IMG_BACKGROUND_REQUEST;
2364 boolean draw_masked = graphic_info[graphic].draw_masked;
2365 int delay_value_normal = request.step_delay;
2366 int delay_value_fast = delay_value_normal / 2;
2367 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2368 boolean no_delay = (tape.warp_forward);
2369 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2370 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2371 unsigned int anim_delay = 0;
2373 int tile_size = MAX(request.step_offset, 1);
2374 int max_xsize = request.width / tile_size;
2375 int max_ysize = request.height / tile_size;
2376 int max_xsize_inner = max_xsize - 2;
2377 int max_ysize_inner = max_ysize - 2;
2379 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2380 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2381 int xend = max_xsize_inner;
2382 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2383 int xstep = (xstart < xend ? 1 : 0);
2384 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2386 int end = MAX(xend - xstart, yend - ystart);
2389 if (setup.quick_doors)
2397 if (action == ACTION_OPENING)
2398 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2399 else if (action == ACTION_CLOSING)
2400 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2403 for (i = start; i <= end; i++)
2405 int last_frame = end; // last frame of this "for" loop
2406 int x = xstart + i * xstep;
2407 int y = ystart + i * ystep;
2408 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2409 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2410 int xsize_size_left = (xsize - 1) * tile_size;
2411 int ysize_size_top = (ysize - 1) * tile_size;
2412 int max_xsize_pos = (max_xsize - 1) * tile_size;
2413 int max_ysize_pos = (max_ysize - 1) * tile_size;
2414 int width = xsize * tile_size;
2415 int height = ysize * tile_size;
2420 setRequestPosition(&src_x, &src_y, FALSE);
2421 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2423 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2425 for (yy = 0; yy < 2; yy++)
2427 for (xx = 0; xx < 2; xx++)
2429 int src_xx = src_x + xx * max_xsize_pos;
2430 int src_yy = src_y + yy * max_ysize_pos;
2431 int dst_xx = dst_x + xx * xsize_size_left;
2432 int dst_yy = dst_y + yy * ysize_size_top;
2433 int xx_size = (xx ? tile_size : xsize_size_left);
2434 int yy_size = (yy ? tile_size : ysize_size_top);
2437 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2438 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2440 BlitBitmap(bitmap_db_cross, backbuffer,
2441 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2445 redraw_mask |= REDRAW_FIELD;
2450 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2454 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2456 int graphic = IMG_BACKGROUND_REQUEST;
2457 int sound_opening = SND_REQUEST_OPENING;
2458 int sound_closing = SND_REQUEST_CLOSING;
2459 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2460 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2461 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2462 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2463 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2465 if (game_status == GAME_MODE_PLAYING)
2466 BlitScreenToBitmap(backbuffer);
2468 SetDrawtoField(DRAW_BACKBUFFER);
2470 // SetDrawBackgroundMask(REDRAW_NONE);
2472 if (action == ACTION_OPENING)
2474 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2476 if (req_state & REQ_ASK)
2478 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2479 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2481 else if (req_state & REQ_CONFIRM)
2483 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2485 else if (req_state & REQ_PLAYER)
2487 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2488 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2489 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2490 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2493 DrawEnvelopeRequest(text);
2495 if (game_status != GAME_MODE_MAIN)
2499 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2501 if (action == ACTION_OPENING)
2503 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2505 if (anim_mode == ANIM_DEFAULT)
2506 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2508 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2512 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2514 if (anim_mode != ANIM_NONE)
2515 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2517 if (anim_mode == ANIM_DEFAULT)
2518 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2521 game.envelope_active = FALSE;
2523 if (action == ACTION_CLOSING)
2525 if (game_status != GAME_MODE_MAIN)
2528 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2531 // SetDrawBackgroundMask(last_draw_background_mask);
2533 redraw_mask |= REDRAW_FIELD;
2535 if (game_status == GAME_MODE_MAIN)
2540 if (action == ACTION_CLOSING &&
2541 game_status == GAME_MODE_PLAYING &&
2542 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2543 SetDrawtoField(DRAW_FIELDBUFFER);
2546 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2550 int graphic = el2preimg(element);
2552 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2553 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2556 void DrawLevel(int draw_background_mask)
2560 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2561 SetDrawBackgroundMask(draw_background_mask);
2565 for (x = BX1; x <= BX2; x++)
2566 for (y = BY1; y <= BY2; y++)
2567 DrawScreenField(x, y);
2569 redraw_mask |= REDRAW_FIELD;
2572 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2577 for (x = 0; x < size_x; x++)
2578 for (y = 0; y < size_y; y++)
2579 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2581 redraw_mask |= REDRAW_FIELD;
2584 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2588 for (x = 0; x < size_x; x++)
2589 for (y = 0; y < size_y; y++)
2590 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2592 redraw_mask |= REDRAW_FIELD;
2595 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2597 boolean show_level_border = (BorderElement != EL_EMPTY);
2598 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2599 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2600 int tile_size = preview.tile_size;
2601 int preview_width = preview.xsize * tile_size;
2602 int preview_height = preview.ysize * tile_size;
2603 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2604 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2605 int real_preview_width = real_preview_xsize * tile_size;
2606 int real_preview_height = real_preview_ysize * tile_size;
2607 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2608 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2611 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2614 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2616 dst_x += (preview_width - real_preview_width) / 2;
2617 dst_y += (preview_height - real_preview_height) / 2;
2619 for (x = 0; x < real_preview_xsize; x++)
2621 for (y = 0; y < real_preview_ysize; y++)
2623 int lx = from_x + x + (show_level_border ? -1 : 0);
2624 int ly = from_y + y + (show_level_border ? -1 : 0);
2625 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2626 getBorderElement(lx, ly));
2628 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2629 element, tile_size);
2633 redraw_mask |= REDRAW_FIELD;
2636 #define MICROLABEL_EMPTY 0
2637 #define MICROLABEL_LEVEL_NAME 1
2638 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2639 #define MICROLABEL_LEVEL_AUTHOR 3
2640 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2641 #define MICROLABEL_IMPORTED_FROM 5
2642 #define MICROLABEL_IMPORTED_BY_HEAD 6
2643 #define MICROLABEL_IMPORTED_BY 7
2645 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2647 int max_text_width = SXSIZE;
2648 int font_width = getFontWidth(font_nr);
2650 if (pos->align == ALIGN_CENTER)
2651 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2652 else if (pos->align == ALIGN_RIGHT)
2653 max_text_width = pos->x;
2655 max_text_width = SXSIZE - pos->x;
2657 return max_text_width / font_width;
2660 static void DrawPreviewLevelLabelExt(int mode)
2662 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2663 char label_text[MAX_OUTPUT_LINESIZE + 1];
2664 int max_len_label_text;
2665 int font_nr = pos->font;
2668 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2671 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2672 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2673 mode == MICROLABEL_IMPORTED_BY_HEAD)
2674 font_nr = pos->font_alt;
2676 max_len_label_text = getMaxTextLength(pos, font_nr);
2678 if (pos->size != -1)
2679 max_len_label_text = pos->size;
2681 for (i = 0; i < max_len_label_text; i++)
2682 label_text[i] = ' ';
2683 label_text[max_len_label_text] = '\0';
2685 if (strlen(label_text) > 0)
2686 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2689 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2690 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2691 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2692 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2693 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2694 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2695 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2696 max_len_label_text);
2697 label_text[max_len_label_text] = '\0';
2699 if (strlen(label_text) > 0)
2700 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2702 redraw_mask |= REDRAW_FIELD;
2705 static void DrawPreviewLevelExt(boolean restart)
2707 static unsigned int scroll_delay = 0;
2708 static unsigned int label_delay = 0;
2709 static int from_x, from_y, scroll_direction;
2710 static int label_state, label_counter;
2711 unsigned int scroll_delay_value = preview.step_delay;
2712 boolean show_level_border = (BorderElement != EL_EMPTY);
2713 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2714 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2715 int last_game_status = game_status; /* save current game status */
2722 if (preview.anim_mode == ANIM_CENTERED)
2724 if (level_xsize > preview.xsize)
2725 from_x = (level_xsize - preview.xsize) / 2;
2726 if (level_ysize > preview.ysize)
2727 from_y = (level_ysize - preview.ysize) / 2;
2730 from_x += preview.xoffset;
2731 from_y += preview.yoffset;
2733 scroll_direction = MV_RIGHT;
2737 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2738 DrawPreviewLevelLabelExt(label_state);
2740 /* initialize delay counters */
2741 DelayReached(&scroll_delay, 0);
2742 DelayReached(&label_delay, 0);
2744 if (leveldir_current->name)
2746 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2747 char label_text[MAX_OUTPUT_LINESIZE + 1];
2748 int font_nr = pos->font;
2749 int max_len_label_text = getMaxTextLength(pos, font_nr);
2751 if (pos->size != -1)
2752 max_len_label_text = pos->size;
2754 strncpy(label_text, leveldir_current->name, max_len_label_text);
2755 label_text[max_len_label_text] = '\0';
2757 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2758 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2761 game_status = last_game_status; /* restore current game status */
2766 /* scroll preview level, if needed */
2767 if (preview.anim_mode != ANIM_NONE &&
2768 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2769 DelayReached(&scroll_delay, scroll_delay_value))
2771 switch (scroll_direction)
2776 from_x -= preview.step_offset;
2777 from_x = (from_x < 0 ? 0 : from_x);
2780 scroll_direction = MV_UP;
2784 if (from_x < level_xsize - preview.xsize)
2786 from_x += preview.step_offset;
2787 from_x = (from_x > level_xsize - preview.xsize ?
2788 level_xsize - preview.xsize : from_x);
2791 scroll_direction = MV_DOWN;
2797 from_y -= preview.step_offset;
2798 from_y = (from_y < 0 ? 0 : from_y);
2801 scroll_direction = MV_RIGHT;
2805 if (from_y < level_ysize - preview.ysize)
2807 from_y += preview.step_offset;
2808 from_y = (from_y > level_ysize - preview.ysize ?
2809 level_ysize - preview.ysize : from_y);
2812 scroll_direction = MV_LEFT;
2819 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2822 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2823 /* redraw micro level label, if needed */
2824 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2825 !strEqual(level.author, ANONYMOUS_NAME) &&
2826 !strEqual(level.author, leveldir_current->name) &&
2827 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2829 int max_label_counter = 23;
2831 if (leveldir_current->imported_from != NULL &&
2832 strlen(leveldir_current->imported_from) > 0)
2833 max_label_counter += 14;
2834 if (leveldir_current->imported_by != NULL &&
2835 strlen(leveldir_current->imported_by) > 0)
2836 max_label_counter += 14;
2838 label_counter = (label_counter + 1) % max_label_counter;
2839 label_state = (label_counter >= 0 && label_counter <= 7 ?
2840 MICROLABEL_LEVEL_NAME :
2841 label_counter >= 9 && label_counter <= 12 ?
2842 MICROLABEL_LEVEL_AUTHOR_HEAD :
2843 label_counter >= 14 && label_counter <= 21 ?
2844 MICROLABEL_LEVEL_AUTHOR :
2845 label_counter >= 23 && label_counter <= 26 ?
2846 MICROLABEL_IMPORTED_FROM_HEAD :
2847 label_counter >= 28 && label_counter <= 35 ?
2848 MICROLABEL_IMPORTED_FROM :
2849 label_counter >= 37 && label_counter <= 40 ?
2850 MICROLABEL_IMPORTED_BY_HEAD :
2851 label_counter >= 42 && label_counter <= 49 ?
2852 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2854 if (leveldir_current->imported_from == NULL &&
2855 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2856 label_state == MICROLABEL_IMPORTED_FROM))
2857 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2858 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2860 DrawPreviewLevelLabelExt(label_state);
2863 game_status = last_game_status; /* restore current game status */
2866 void DrawPreviewLevelInitial()
2868 DrawPreviewLevelExt(TRUE);
2871 void DrawPreviewLevelAnimation()
2873 DrawPreviewLevelExt(FALSE);
2876 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2877 int graphic, int sync_frame,
2880 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2882 if (mask_mode == USE_MASKING)
2883 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2885 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2888 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2889 int graphic, int sync_frame, int mask_mode)
2891 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2893 if (mask_mode == USE_MASKING)
2894 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2896 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2899 inline static void DrawGraphicAnimation(int x, int y, int graphic)
2901 int lx = LEVELX(x), ly = LEVELY(y);
2903 if (!IN_SCR_FIELD(x, y))
2906 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2907 graphic, GfxFrame[lx][ly], NO_MASKING);
2909 MarkTileDirty(x, y);
2912 void DrawFixedGraphicAnimation(int x, int y, int graphic)
2914 int lx = LEVELX(x), ly = LEVELY(y);
2916 if (!IN_SCR_FIELD(x, y))
2919 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2920 graphic, GfxFrame[lx][ly], NO_MASKING);
2921 MarkTileDirty(x, y);
2924 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2926 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2929 void DrawLevelElementAnimation(int x, int y, int element)
2931 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2933 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2936 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2938 int sx = SCREENX(x), sy = SCREENY(y);
2940 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2943 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2946 DrawGraphicAnimation(sx, sy, graphic);
2949 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2950 DrawLevelFieldCrumbled(x, y);
2952 if (GFX_CRUMBLED(Feld[x][y]))
2953 DrawLevelFieldCrumbled(x, y);
2957 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2959 int sx = SCREENX(x), sy = SCREENY(y);
2962 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2965 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2967 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2970 DrawGraphicAnimation(sx, sy, graphic);
2972 if (GFX_CRUMBLED(element))
2973 DrawLevelFieldCrumbled(x, y);
2976 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2978 if (player->use_murphy)
2980 /* this works only because currently only one player can be "murphy" ... */
2981 static int last_horizontal_dir = MV_LEFT;
2982 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2984 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2985 last_horizontal_dir = move_dir;
2987 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2989 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2991 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2997 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3000 static boolean equalGraphics(int graphic1, int graphic2)
3002 struct GraphicInfo *g1 = &graphic_info[graphic1];
3003 struct GraphicInfo *g2 = &graphic_info[graphic2];
3005 return (g1->bitmap == g2->bitmap &&
3006 g1->src_x == g2->src_x &&
3007 g1->src_y == g2->src_y &&
3008 g1->anim_frames == g2->anim_frames &&
3009 g1->anim_delay == g2->anim_delay &&
3010 g1->anim_mode == g2->anim_mode);
3013 void DrawAllPlayers()
3017 for (i = 0; i < MAX_PLAYERS; i++)
3018 if (stored_player[i].active)
3019 DrawPlayer(&stored_player[i]);
3022 void DrawPlayerField(int x, int y)
3024 if (!IS_PLAYER(x, y))
3027 DrawPlayer(PLAYERINFO(x, y));
3030 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3032 void DrawPlayer(struct PlayerInfo *player)
3034 int jx = player->jx;
3035 int jy = player->jy;
3036 int move_dir = player->MovDir;
3037 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3038 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3039 int last_jx = (player->is_moving ? jx - dx : jx);
3040 int last_jy = (player->is_moving ? jy - dy : jy);
3041 int next_jx = jx + dx;
3042 int next_jy = jy + dy;
3043 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3044 boolean player_is_opaque = FALSE;
3045 int sx = SCREENX(jx), sy = SCREENY(jy);
3046 int sxx = 0, syy = 0;
3047 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3049 int action = ACTION_DEFAULT;
3050 int last_player_graphic = getPlayerGraphic(player, move_dir);
3051 int last_player_frame = player->Frame;
3054 /* GfxElement[][] is set to the element the player is digging or collecting;
3055 remove also for off-screen player if the player is not moving anymore */
3056 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3057 GfxElement[jx][jy] = EL_UNDEFINED;
3059 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3063 if (!IN_LEV_FIELD(jx, jy))
3065 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3066 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3067 printf("DrawPlayerField(): This should never happen!\n");
3072 if (element == EL_EXPLOSION)
3075 action = (player->is_pushing ? ACTION_PUSHING :
3076 player->is_digging ? ACTION_DIGGING :
3077 player->is_collecting ? ACTION_COLLECTING :
3078 player->is_moving ? ACTION_MOVING :
3079 player->is_snapping ? ACTION_SNAPPING :
3080 player->is_dropping ? ACTION_DROPPING :
3081 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3083 if (player->is_waiting)
3084 move_dir = player->dir_waiting;
3086 InitPlayerGfxAnimation(player, action, move_dir);
3088 /* ----------------------------------------------------------------------- */
3089 /* draw things in the field the player is leaving, if needed */
3090 /* ----------------------------------------------------------------------- */
3092 if (player->is_moving)
3094 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3096 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3098 if (last_element == EL_DYNAMITE_ACTIVE ||
3099 last_element == EL_EM_DYNAMITE_ACTIVE ||
3100 last_element == EL_SP_DISK_RED_ACTIVE)
3101 DrawDynamite(last_jx, last_jy);
3103 DrawLevelFieldThruMask(last_jx, last_jy);
3105 else if (last_element == EL_DYNAMITE_ACTIVE ||
3106 last_element == EL_EM_DYNAMITE_ACTIVE ||
3107 last_element == EL_SP_DISK_RED_ACTIVE)
3108 DrawDynamite(last_jx, last_jy);
3110 /* !!! this is not enough to prevent flickering of players which are
3111 moving next to each others without a free tile between them -- this
3112 can only be solved by drawing all players layer by layer (first the
3113 background, then the foreground etc.) !!! => TODO */
3114 else if (!IS_PLAYER(last_jx, last_jy))
3115 DrawLevelField(last_jx, last_jy);
3118 DrawLevelField(last_jx, last_jy);
3121 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3122 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3125 if (!IN_SCR_FIELD(sx, sy))
3128 /* ----------------------------------------------------------------------- */
3129 /* draw things behind the player, if needed */
3130 /* ----------------------------------------------------------------------- */
3133 DrawLevelElement(jx, jy, Back[jx][jy]);
3134 else if (IS_ACTIVE_BOMB(element))
3135 DrawLevelElement(jx, jy, EL_EMPTY);
3138 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3140 int old_element = GfxElement[jx][jy];
3141 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3142 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3144 if (GFX_CRUMBLED(old_element))
3145 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3147 DrawGraphic(sx, sy, old_graphic, frame);
3149 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3150 player_is_opaque = TRUE;
3154 GfxElement[jx][jy] = EL_UNDEFINED;
3156 /* make sure that pushed elements are drawn with correct frame rate */
3157 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3159 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3160 GfxFrame[jx][jy] = player->StepFrame;
3162 DrawLevelField(jx, jy);
3166 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3167 /* ----------------------------------------------------------------------- */
3168 /* draw player himself */
3169 /* ----------------------------------------------------------------------- */
3171 graphic = getPlayerGraphic(player, move_dir);
3173 /* in the case of changed player action or direction, prevent the current
3174 animation frame from being restarted for identical animations */
3175 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3176 player->Frame = last_player_frame;
3178 frame = getGraphicAnimationFrame(graphic, player->Frame);
3182 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3183 sxx = player->GfxPos;
3185 syy = player->GfxPos;
3188 if (player_is_opaque)
3189 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3191 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3193 if (SHIELD_ON(player))
3195 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3196 IMG_SHIELD_NORMAL_ACTIVE);
3197 int frame = getGraphicAnimationFrame(graphic, -1);
3199 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3203 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3206 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3207 sxx = player->GfxPos;
3209 syy = player->GfxPos;
3213 /* ----------------------------------------------------------------------- */
3214 /* draw things the player is pushing, if needed */
3215 /* ----------------------------------------------------------------------- */
3217 if (player->is_pushing && player->is_moving)
3219 int px = SCREENX(jx), py = SCREENY(jy);
3220 int pxx = (TILEX - ABS(sxx)) * dx;
3221 int pyy = (TILEY - ABS(syy)) * dy;
3222 int gfx_frame = GfxFrame[jx][jy];
3228 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3230 element = Feld[next_jx][next_jy];
3231 gfx_frame = GfxFrame[next_jx][next_jy];
3234 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3236 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3237 frame = getGraphicAnimationFrame(graphic, sync_frame);
3239 /* draw background element under pushed element (like the Sokoban field) */
3240 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3242 /* this allows transparent pushing animation over non-black background */
3245 DrawLevelElement(jx, jy, Back[jx][jy]);
3247 DrawLevelElement(jx, jy, EL_EMPTY);
3249 if (Back[next_jx][next_jy])
3250 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3252 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3254 else if (Back[next_jx][next_jy])
3255 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3258 /* do not draw (EM style) pushing animation when pushing is finished */
3259 /* (two-tile animations usually do not contain start and end frame) */
3260 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3261 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3263 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3265 /* masked drawing is needed for EMC style (double) movement graphics */
3266 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3267 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3271 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3272 /* ----------------------------------------------------------------------- */
3273 /* draw player himself */
3274 /* ----------------------------------------------------------------------- */
3276 graphic = getPlayerGraphic(player, move_dir);
3278 /* in the case of changed player action or direction, prevent the current
3279 animation frame from being restarted for identical animations */
3280 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3281 player->Frame = last_player_frame;
3283 frame = getGraphicAnimationFrame(graphic, player->Frame);
3287 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3288 sxx = player->GfxPos;
3290 syy = player->GfxPos;
3293 if (player_is_opaque)
3294 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3296 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3298 if (SHIELD_ON(player))
3300 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3301 IMG_SHIELD_NORMAL_ACTIVE);
3302 int frame = getGraphicAnimationFrame(graphic, -1);
3304 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3308 /* ----------------------------------------------------------------------- */
3309 /* draw things in front of player (active dynamite or dynabombs) */
3310 /* ----------------------------------------------------------------------- */
3312 if (IS_ACTIVE_BOMB(element))
3314 graphic = el2img(element);
3315 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3317 if (game.emulation == EMU_SUPAPLEX)
3318 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3320 DrawGraphicThruMask(sx, sy, graphic, frame);
3323 if (player_is_moving && last_element == EL_EXPLOSION)
3325 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3326 GfxElement[last_jx][last_jy] : EL_EMPTY);
3327 int graphic = el_act2img(element, ACTION_EXPLODING);
3328 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3329 int phase = ExplodePhase[last_jx][last_jy] - 1;
3330 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3333 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3336 /* ----------------------------------------------------------------------- */
3337 /* draw elements the player is just walking/passing through/under */
3338 /* ----------------------------------------------------------------------- */
3340 if (player_is_moving)
3342 /* handle the field the player is leaving ... */
3343 if (IS_ACCESSIBLE_INSIDE(last_element))
3344 DrawLevelField(last_jx, last_jy);
3345 else if (IS_ACCESSIBLE_UNDER(last_element))
3346 DrawLevelFieldThruMask(last_jx, last_jy);
3349 /* do not redraw accessible elements if the player is just pushing them */
3350 if (!player_is_moving || !player->is_pushing)
3352 /* ... and the field the player is entering */
3353 if (IS_ACCESSIBLE_INSIDE(element))
3354 DrawLevelField(jx, jy);
3355 else if (IS_ACCESSIBLE_UNDER(element))
3356 DrawLevelFieldThruMask(jx, jy);
3359 MarkTileDirty(sx, sy);
3362 /* ------------------------------------------------------------------------- */
3364 void WaitForEventToContinue()
3366 boolean still_wait = TRUE;
3368 /* simulate releasing mouse button over last gadget, if still pressed */
3370 HandleGadgets(-1, -1, 0);
3372 button_status = MB_RELEASED;
3386 case EVENT_BUTTONPRESS:
3387 case EVENT_KEYPRESS:
3391 case EVENT_KEYRELEASE:
3392 ClearPlayerAction();
3396 HandleOtherEvents(&event);
3400 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3407 /* don't eat all CPU time */
3412 #define MAX_REQUEST_LINES 13
3413 #define MAX_REQUEST_LINE_FONT1_LEN 7
3414 #define MAX_REQUEST_LINE_FONT2_LEN 10
3416 static int RequestHandleEvents(unsigned int req_state)
3418 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3419 local_player->LevelSolved_GameEnd);
3420 int width = request.width;
3421 int height = request.height;
3425 setRequestPosition(&sx, &sy, FALSE);
3427 button_status = MB_RELEASED;
3429 request_gadget_id = -1;
3436 SetDrawtoField(DRAW_FIELDBUFFER);
3438 HandleGameActions();
3440 SetDrawtoField(DRAW_BACKBUFFER);
3442 if (global.use_envelope_request)
3444 /* copy current state of request area to middle of playfield area */
3445 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3453 while (NextValidEvent(&event))
3457 case EVENT_BUTTONPRESS:
3458 case EVENT_BUTTONRELEASE:
3459 case EVENT_MOTIONNOTIFY:
3463 if (event.type == EVENT_MOTIONNOTIFY)
3468 motion_status = TRUE;
3469 mx = ((MotionEvent *) &event)->x;
3470 my = ((MotionEvent *) &event)->y;
3474 motion_status = FALSE;
3475 mx = ((ButtonEvent *) &event)->x;
3476 my = ((ButtonEvent *) &event)->y;
3477 if (event.type == EVENT_BUTTONPRESS)
3478 button_status = ((ButtonEvent *) &event)->button;
3480 button_status = MB_RELEASED;
3483 /* this sets 'request_gadget_id' */
3484 HandleGadgets(mx, my, button_status);
3486 switch (request_gadget_id)
3488 case TOOL_CTRL_ID_YES:
3491 case TOOL_CTRL_ID_NO:
3494 case TOOL_CTRL_ID_CONFIRM:
3495 result = TRUE | FALSE;
3498 case TOOL_CTRL_ID_PLAYER_1:
3501 case TOOL_CTRL_ID_PLAYER_2:
3504 case TOOL_CTRL_ID_PLAYER_3:
3507 case TOOL_CTRL_ID_PLAYER_4:
3518 case EVENT_KEYPRESS:
3519 switch (GetEventKey((KeyEvent *)&event, TRUE))
3522 if (req_state & REQ_CONFIRM)
3527 #if defined(TARGET_SDL2)
3534 #if defined(TARGET_SDL2)
3544 if (req_state & REQ_PLAYER)
3548 case EVENT_KEYRELEASE:
3549 ClearPlayerAction();
3553 HandleOtherEvents(&event);
3558 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3560 int joy = AnyJoystick();
3562 if (joy & JOY_BUTTON_1)
3564 else if (joy & JOY_BUTTON_2)
3570 if (global.use_envelope_request)
3572 /* copy back current state of pressed buttons inside request area */
3573 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3580 if (!PendingEvent()) /* delay only if no pending events */
3590 static boolean RequestDoor(char *text, unsigned int req_state)
3592 unsigned int old_door_state;
3593 int last_game_status = game_status; /* save current game status */
3594 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3595 int font_nr = FONT_TEXT_2;
3600 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3602 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3603 font_nr = FONT_TEXT_1;
3606 if (game_status == GAME_MODE_PLAYING)
3607 BlitScreenToBitmap(backbuffer);
3609 /* disable deactivated drawing when quick-loading level tape recording */
3610 if (tape.playing && tape.deactivate_display)
3611 TapeDeactivateDisplayOff(TRUE);
3613 SetMouseCursor(CURSOR_DEFAULT);
3615 #if defined(NETWORK_AVALIABLE)
3616 /* pause network game while waiting for request to answer */
3617 if (options.network &&
3618 game_status == GAME_MODE_PLAYING &&
3619 req_state & REQUEST_WAIT_FOR_INPUT)
3620 SendToServer_PausePlaying();
3623 old_door_state = GetDoorState();
3625 /* simulate releasing mouse button over last gadget, if still pressed */
3627 HandleGadgets(-1, -1, 0);
3631 /* draw released gadget before proceeding */
3634 if (old_door_state & DOOR_OPEN_1)
3636 CloseDoor(DOOR_CLOSE_1);
3638 /* save old door content */
3639 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3640 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3643 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3644 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3646 /* clear door drawing field */
3647 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3649 /* force DOOR font inside door area */
3650 game_status = GAME_MODE_PSEUDO_DOOR;
3652 /* write text for request */
3653 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3655 char text_line[max_request_line_len + 1];
3661 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3663 tc = *(text_ptr + tx);
3664 // if (!tc || tc == ' ')
3665 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3669 if ((tc == '?' || tc == '!') && tl == 0)
3679 strncpy(text_line, text_ptr, tl);
3682 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3683 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3684 text_line, font_nr);
3686 text_ptr += tl + (tc == ' ' ? 1 : 0);
3687 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3690 game_status = last_game_status; /* restore current game status */
3692 if (req_state & REQ_ASK)
3694 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3695 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3697 else if (req_state & REQ_CONFIRM)
3699 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3701 else if (req_state & REQ_PLAYER)
3703 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3704 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3705 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3706 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3709 /* copy request gadgets to door backbuffer */
3710 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3712 OpenDoor(DOOR_OPEN_1);
3714 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3716 if (game_status == GAME_MODE_PLAYING)
3718 SetPanelBackground();
3719 SetDrawBackgroundMask(REDRAW_DOOR_1);
3723 SetDrawBackgroundMask(REDRAW_FIELD);
3729 if (game_status != GAME_MODE_MAIN)
3732 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3734 // ---------- handle request buttons ----------
3735 result = RequestHandleEvents(req_state);
3737 if (game_status != GAME_MODE_MAIN)
3742 if (!(req_state & REQ_STAY_OPEN))
3744 CloseDoor(DOOR_CLOSE_1);
3746 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3747 (req_state & REQ_REOPEN))
3748 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3753 if (game_status == GAME_MODE_PLAYING)
3755 SetPanelBackground();
3756 SetDrawBackgroundMask(REDRAW_DOOR_1);
3760 SetDrawBackgroundMask(REDRAW_FIELD);
3763 #if defined(NETWORK_AVALIABLE)
3764 /* continue network game after request */
3765 if (options.network &&
3766 game_status == GAME_MODE_PLAYING &&
3767 req_state & REQUEST_WAIT_FOR_INPUT)
3768 SendToServer_ContinuePlaying();
3771 /* restore deactivated drawing when quick-loading level tape recording */
3772 if (tape.playing && tape.deactivate_display)
3773 TapeDeactivateDisplayOn();
3778 static boolean RequestEnvelope(char *text, unsigned int req_state)
3782 if (game_status == GAME_MODE_PLAYING)
3783 BlitScreenToBitmap(backbuffer);
3785 /* disable deactivated drawing when quick-loading level tape recording */
3786 if (tape.playing && tape.deactivate_display)
3787 TapeDeactivateDisplayOff(TRUE);
3789 SetMouseCursor(CURSOR_DEFAULT);
3791 #if defined(NETWORK_AVALIABLE)
3792 /* pause network game while waiting for request to answer */
3793 if (options.network &&
3794 game_status == GAME_MODE_PLAYING &&
3795 req_state & REQUEST_WAIT_FOR_INPUT)
3796 SendToServer_PausePlaying();
3799 /* simulate releasing mouse button over last gadget, if still pressed */
3801 HandleGadgets(-1, -1, 0);
3805 // (replace with setting corresponding request background)
3806 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3807 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3809 /* clear door drawing field */
3810 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3812 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3814 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3816 if (game_status == GAME_MODE_PLAYING)
3818 SetPanelBackground();
3819 SetDrawBackgroundMask(REDRAW_DOOR_1);
3823 SetDrawBackgroundMask(REDRAW_FIELD);
3829 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3831 // ---------- handle request buttons ----------
3832 result = RequestHandleEvents(req_state);
3834 if (game_status != GAME_MODE_MAIN)
3839 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3843 if (game_status == GAME_MODE_PLAYING)
3845 SetPanelBackground();
3846 SetDrawBackgroundMask(REDRAW_DOOR_1);
3850 SetDrawBackgroundMask(REDRAW_FIELD);
3853 #if defined(NETWORK_AVALIABLE)
3854 /* continue network game after request */
3855 if (options.network &&
3856 game_status == GAME_MODE_PLAYING &&
3857 req_state & REQUEST_WAIT_FOR_INPUT)
3858 SendToServer_ContinuePlaying();
3861 /* restore deactivated drawing when quick-loading level tape recording */
3862 if (tape.playing && tape.deactivate_display)
3863 TapeDeactivateDisplayOn();
3868 boolean Request(char *text, unsigned int req_state)
3870 if (global.use_envelope_request)
3871 return RequestEnvelope(text, req_state);
3873 return RequestDoor(text, req_state);
3876 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3878 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3879 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3882 if (dpo1->sort_priority != dpo2->sort_priority)
3883 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3885 compare_result = dpo1->nr - dpo2->nr;
3887 return compare_result;
3890 void InitGraphicCompatibilityInfo_Doors()
3896 struct DoorInfo *door;
3900 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3901 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3903 { -1, -1, -1, NULL }
3905 struct Rect door_rect_list[] =
3907 { DX, DY, DXSIZE, DYSIZE },
3908 { VX, VY, VXSIZE, VYSIZE }
3912 for (i = 0; doors[i].door_token != -1; i++)
3914 int door_token = doors[i].door_token;
3915 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3916 int part_1 = doors[i].part_1;
3917 int part_8 = doors[i].part_8;
3918 int part_2 = part_1 + 1;
3919 int part_3 = part_1 + 2;
3920 struct DoorInfo *door = doors[i].door;
3921 struct Rect *door_rect = &door_rect_list[door_index];
3922 boolean door_gfx_redefined = FALSE;
3924 /* check if any door part graphic definitions have been redefined */
3926 for (j = 0; door_part_controls[j].door_token != -1; j++)
3928 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3929 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3931 if (dpc->door_token == door_token && fi->redefined)
3932 door_gfx_redefined = TRUE;
3935 /* check for old-style door graphic/animation modifications */
3937 if (!door_gfx_redefined)
3939 if (door->anim_mode & ANIM_STATIC_PANEL)
3941 door->panel.step_xoffset = 0;
3942 door->panel.step_yoffset = 0;
3945 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3947 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3948 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3949 int num_door_steps, num_panel_steps;
3951 /* remove door part graphics other than the two default wings */
3953 for (j = 0; door_part_controls[j].door_token != -1; j++)
3955 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3956 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3958 if (dpc->graphic >= part_3 &&
3959 dpc->graphic <= part_8)
3963 /* set graphics and screen positions of the default wings */
3965 g_part_1->width = door_rect->width;
3966 g_part_1->height = door_rect->height;
3967 g_part_2->width = door_rect->width;
3968 g_part_2->height = door_rect->height;
3969 g_part_2->src_x = door_rect->width;
3970 g_part_2->src_y = g_part_1->src_y;
3972 door->part_2.x = door->part_1.x;
3973 door->part_2.y = door->part_1.y;
3975 if (door->width != -1)
3977 g_part_1->width = door->width;
3978 g_part_2->width = door->width;
3980 // special treatment for graphics and screen position of right wing
3981 g_part_2->src_x += door_rect->width - door->width;
3982 door->part_2.x += door_rect->width - door->width;
3985 if (door->height != -1)
3987 g_part_1->height = door->height;
3988 g_part_2->height = door->height;
3990 // special treatment for graphics and screen position of bottom wing
3991 g_part_2->src_y += door_rect->height - door->height;
3992 door->part_2.y += door_rect->height - door->height;
3995 /* set animation delays for the default wings and panels */
3997 door->part_1.step_delay = door->step_delay;
3998 door->part_2.step_delay = door->step_delay;
3999 door->panel.step_delay = door->step_delay;
4001 /* set animation draw order for the default wings */
4003 door->part_1.sort_priority = 2; /* draw left wing over ... */
4004 door->part_2.sort_priority = 1; /* ... right wing */
4006 /* set animation draw offset for the default wings */
4008 if (door->anim_mode & ANIM_HORIZONTAL)
4010 door->part_1.step_xoffset = door->step_offset;
4011 door->part_1.step_yoffset = 0;
4012 door->part_2.step_xoffset = door->step_offset * -1;
4013 door->part_2.step_yoffset = 0;
4015 num_door_steps = g_part_1->width / door->step_offset;
4017 else // ANIM_VERTICAL
4019 door->part_1.step_xoffset = 0;
4020 door->part_1.step_yoffset = door->step_offset;
4021 door->part_2.step_xoffset = 0;
4022 door->part_2.step_yoffset = door->step_offset * -1;
4024 num_door_steps = g_part_1->height / door->step_offset;
4027 /* set animation draw offset for the default panels */
4029 if (door->step_offset > 1)
4031 num_panel_steps = 2 * door_rect->height / door->step_offset;
4032 door->panel.start_step = num_panel_steps - num_door_steps;
4033 door->panel.start_step_closing = door->panel.start_step;
4037 num_panel_steps = door_rect->height / door->step_offset;
4038 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4039 door->panel.start_step_closing = door->panel.start_step;
4040 door->panel.step_delay *= 2;
4051 for (i = 0; door_part_controls[i].door_token != -1; i++)
4053 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4054 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4056 /* initialize "start_step_opening" and "start_step_closing", if needed */
4057 if (dpc->pos->start_step_opening == 0 &&
4058 dpc->pos->start_step_closing == 0)
4060 // dpc->pos->start_step_opening = dpc->pos->start_step;
4061 dpc->pos->start_step_closing = dpc->pos->start_step;
4064 /* fill structure for door part draw order (sorted below) */
4066 dpo->sort_priority = dpc->pos->sort_priority;
4069 /* sort door part controls according to sort_priority and graphic number */
4070 qsort(door_part_order, MAX_DOOR_PARTS,
4071 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4074 unsigned int OpenDoor(unsigned int door_state)
4076 if (door_state & DOOR_COPY_BACK)
4078 if (door_state & DOOR_OPEN_1)
4079 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4080 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4082 if (door_state & DOOR_OPEN_2)
4083 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4084 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4086 door_state &= ~DOOR_COPY_BACK;
4089 return MoveDoor(door_state);
4092 unsigned int CloseDoor(unsigned int door_state)
4094 unsigned int old_door_state = GetDoorState();
4096 if (!(door_state & DOOR_NO_COPY_BACK))
4098 if (old_door_state & DOOR_OPEN_1)
4099 BlitBitmap(backbuffer, bitmap_db_door_1,
4100 DX, DY, DXSIZE, DYSIZE, 0, 0);
4102 if (old_door_state & DOOR_OPEN_2)
4103 BlitBitmap(backbuffer, bitmap_db_door_2,
4104 VX, VY, VXSIZE, VYSIZE, 0, 0);
4106 door_state &= ~DOOR_NO_COPY_BACK;
4109 return MoveDoor(door_state);
4112 unsigned int GetDoorState()
4114 return MoveDoor(DOOR_GET_STATE);
4117 unsigned int SetDoorState(unsigned int door_state)
4119 return MoveDoor(door_state | DOOR_SET_STATE);
4122 int euclid(int a, int b)
4124 return (b ? euclid(b, a % b) : a);
4127 unsigned int MoveDoor(unsigned int door_state)
4129 struct Rect door_rect_list[] =
4131 { DX, DY, DXSIZE, DYSIZE },
4132 { VX, VY, VXSIZE, VYSIZE }
4134 static int door1 = DOOR_OPEN_1;
4135 static int door2 = DOOR_CLOSE_2;
4136 unsigned int door_delay = 0;
4137 unsigned int door_delay_value;
4140 if (door_state == DOOR_GET_STATE)
4141 return (door1 | door2);
4143 if (door_state & DOOR_SET_STATE)
4145 if (door_state & DOOR_ACTION_1)
4146 door1 = door_state & DOOR_ACTION_1;
4147 if (door_state & DOOR_ACTION_2)
4148 door2 = door_state & DOOR_ACTION_2;
4150 return (door1 | door2);
4153 if (!(door_state & DOOR_FORCE_REDRAW))
4155 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4156 door_state &= ~DOOR_OPEN_1;
4157 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4158 door_state &= ~DOOR_CLOSE_1;
4159 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4160 door_state &= ~DOOR_OPEN_2;
4161 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4162 door_state &= ~DOOR_CLOSE_2;
4165 if (global.autoplay_leveldir)
4167 door_state |= DOOR_NO_DELAY;
4168 door_state &= ~DOOR_CLOSE_ALL;
4171 if (game_status == GAME_MODE_EDITOR)
4172 door_state |= DOOR_NO_DELAY;
4174 if (door_state & DOOR_ACTION)
4176 boolean door_panel_drawn[NUM_DOORS];
4177 boolean panel_has_doors[NUM_DOORS];
4178 boolean door_part_skip[MAX_DOOR_PARTS];
4179 boolean door_part_done[MAX_DOOR_PARTS];
4180 boolean door_part_done_all;
4181 int num_steps[MAX_DOOR_PARTS];
4182 int max_move_delay = 0; // delay for complete animations of all doors
4183 int max_step_delay = 0; // delay (ms) between two animation frames
4184 int num_move_steps = 0; // number of animation steps for all doors
4185 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4186 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4187 int current_move_delay = 0;
4191 for (i = 0; i < NUM_DOORS; i++)
4192 panel_has_doors[i] = FALSE;
4194 for (i = 0; i < MAX_DOOR_PARTS; i++)
4196 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4197 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4198 int door_token = dpc->door_token;
4200 door_part_done[i] = FALSE;
4201 door_part_skip[i] = (!(door_state & door_token) ||
4205 for (i = 0; i < MAX_DOOR_PARTS; i++)
4207 int nr = door_part_order[i].nr;
4208 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4209 struct DoorPartPosInfo *pos = dpc->pos;
4210 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4211 int door_token = dpc->door_token;
4212 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4213 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4214 int step_xoffset = ABS(pos->step_xoffset);
4215 int step_yoffset = ABS(pos->step_yoffset);
4216 int step_delay = pos->step_delay;
4217 int current_door_state = door_state & door_token;
4218 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4219 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4220 boolean part_opening = (is_panel ? door_closing : door_opening);
4221 int start_step = (part_opening ? pos->start_step_opening :
4222 pos->start_step_closing);
4223 float move_xsize = (step_xoffset ? g->width : 0);
4224 float move_ysize = (step_yoffset ? g->height : 0);
4225 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4226 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4227 int move_steps = (move_xsteps && move_ysteps ?
4228 MIN(move_xsteps, move_ysteps) :
4229 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4230 int move_delay = move_steps * step_delay;
4232 if (door_part_skip[nr])
4235 max_move_delay = MAX(max_move_delay, move_delay);
4236 max_step_delay = (max_step_delay == 0 ? step_delay :
4237 euclid(max_step_delay, step_delay));
4238 num_steps[nr] = move_steps;
4242 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4244 panel_has_doors[door_index] = TRUE;
4248 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4250 num_move_steps = max_move_delay / max_step_delay;
4251 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4253 door_delay_value = max_step_delay;
4255 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4257 start = num_move_steps - 1;
4261 /* opening door sound has priority over simultaneously closing door */
4262 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4263 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4264 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4265 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4268 for (k = start; k < num_move_steps; k++)
4270 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4272 door_part_done_all = TRUE;
4274 for (i = 0; i < NUM_DOORS; i++)
4275 door_panel_drawn[i] = FALSE;
4277 for (i = 0; i < MAX_DOOR_PARTS; i++)
4279 int nr = door_part_order[i].nr;
4280 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4281 struct DoorPartPosInfo *pos = dpc->pos;
4282 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4283 int door_token = dpc->door_token;
4284 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4285 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4286 boolean is_panel_and_door_has_closed = FALSE;
4287 struct Rect *door_rect = &door_rect_list[door_index];
4288 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4290 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4291 int current_door_state = door_state & door_token;
4292 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4293 boolean door_closing = !door_opening;
4294 boolean part_opening = (is_panel ? door_closing : door_opening);
4295 boolean part_closing = !part_opening;
4296 int start_step = (part_opening ? pos->start_step_opening :
4297 pos->start_step_closing);
4298 int step_delay = pos->step_delay;
4299 int step_factor = step_delay / max_step_delay;
4300 int k1 = (step_factor ? k / step_factor + 1 : k);
4301 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4302 int kk = MAX(0, k2);
4305 int src_x, src_y, src_xx, src_yy;
4306 int dst_x, dst_y, dst_xx, dst_yy;
4309 if (door_part_skip[nr])
4312 if (!(door_state & door_token))
4320 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4321 int kk_door = MAX(0, k2_door);
4322 int sync_frame = kk_door * door_delay_value;
4323 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4325 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4330 if (!door_panel_drawn[door_index])
4332 ClearRectangle(drawto, door_rect->x, door_rect->y,
4333 door_rect->width, door_rect->height);
4335 door_panel_drawn[door_index] = TRUE;
4338 // draw opening or closing door parts
4340 if (pos->step_xoffset < 0) // door part on right side
4343 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4346 if (dst_xx + width > door_rect->width)
4347 width = door_rect->width - dst_xx;
4349 else // door part on left side
4352 dst_xx = pos->x - kk * pos->step_xoffset;
4356 src_xx = ABS(dst_xx);
4360 width = g->width - src_xx;
4362 if (width > door_rect->width)
4363 width = door_rect->width;
4365 // printf("::: k == %d [%d] \n", k, start_step);
4368 if (pos->step_yoffset < 0) // door part on bottom side
4371 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4374 if (dst_yy + height > door_rect->height)
4375 height = door_rect->height - dst_yy;
4377 else // door part on top side
4380 dst_yy = pos->y - kk * pos->step_yoffset;
4384 src_yy = ABS(dst_yy);
4388 height = g->height - src_yy;
4391 src_x = g_src_x + src_xx;
4392 src_y = g_src_y + src_yy;
4394 dst_x = door_rect->x + dst_xx;
4395 dst_y = door_rect->y + dst_yy;
4397 is_panel_and_door_has_closed =
4400 panel_has_doors[door_index] &&
4401 k >= num_move_steps_doors_only - 1);
4403 if (width >= 0 && width <= g->width &&
4404 height >= 0 && height <= g->height &&
4405 !is_panel_and_door_has_closed)
4407 if (is_panel || !pos->draw_masked)
4408 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4411 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4415 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4417 if ((part_opening && (width < 0 || height < 0)) ||
4418 (part_closing && (width >= g->width && height >= g->height)))
4419 door_part_done[nr] = TRUE;
4421 // continue door part animations, but not panel after door has closed
4422 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4423 door_part_done_all = FALSE;
4426 if (!(door_state & DOOR_NO_DELAY))
4430 if (game_status == GAME_MODE_MAIN)
4433 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4435 current_move_delay += max_step_delay;
4438 if (door_part_done_all)
4443 if (door_state & DOOR_ACTION_1)
4444 door1 = door_state & DOOR_ACTION_1;
4445 if (door_state & DOOR_ACTION_2)
4446 door2 = door_state & DOOR_ACTION_2;
4448 return (door1 | door2);
4451 static boolean useSpecialEditorDoor()
4453 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4454 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4456 // do not draw special editor door if editor border defined or redefined
4457 if (graphic_info[graphic].bitmap != NULL || redefined)
4460 // do not draw special editor door if global border defined to be empty
4461 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4464 // do not draw special editor door if viewport definitions do not match
4468 EY + EYSIZE != VY + VYSIZE)
4474 void DrawSpecialEditorDoor()
4476 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4477 int top_border_width = gfx1->width;
4478 int top_border_height = gfx1->height;
4479 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4480 int ex = EX - outer_border;
4481 int ey = EY - outer_border;
4482 int vy = VY - outer_border;
4483 int exsize = EXSIZE + 2 * outer_border;
4485 if (!useSpecialEditorDoor())
4488 /* draw bigger level editor toolbox window */
4489 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4490 top_border_width, top_border_height, ex, ey - top_border_height);
4491 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4492 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4494 redraw_mask |= REDRAW_ALL;
4497 void UndrawSpecialEditorDoor()
4499 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4500 int top_border_width = gfx1->width;
4501 int top_border_height = gfx1->height;
4502 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4503 int ex = EX - outer_border;
4504 int ey = EY - outer_border;
4505 int ey_top = ey - top_border_height;
4506 int exsize = EXSIZE + 2 * outer_border;
4507 int eysize = EYSIZE + 2 * outer_border;
4509 if (!useSpecialEditorDoor())
4512 /* draw normal tape recorder window */
4513 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4515 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4516 ex, ey_top, top_border_width, top_border_height,
4518 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4519 ex, ey, exsize, eysize, ex, ey);
4523 // if screen background is set to "[NONE]", clear editor toolbox window
4524 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4525 ClearRectangle(drawto, ex, ey, exsize, eysize);
4528 redraw_mask |= REDRAW_ALL;
4532 /* ---------- new tool button stuff ---------------------------------------- */
4537 struct TextPosInfo *pos;
4540 } toolbutton_info[NUM_TOOL_BUTTONS] =
4543 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4544 TOOL_CTRL_ID_YES, "yes"
4547 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4548 TOOL_CTRL_ID_NO, "no"
4551 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4552 TOOL_CTRL_ID_CONFIRM, "confirm"
4555 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4556 TOOL_CTRL_ID_PLAYER_1, "player 1"
4559 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4560 TOOL_CTRL_ID_PLAYER_2, "player 2"
4563 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4564 TOOL_CTRL_ID_PLAYER_3, "player 3"
4567 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4568 TOOL_CTRL_ID_PLAYER_4, "player 4"
4572 void CreateToolButtons()
4576 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4578 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4579 struct TextPosInfo *pos = toolbutton_info[i].pos;
4580 struct GadgetInfo *gi;
4581 Bitmap *deco_bitmap = None;
4582 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4583 unsigned int event_mask = GD_EVENT_RELEASED;
4586 int gd_x = gfx->src_x;
4587 int gd_y = gfx->src_y;
4588 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4589 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4592 if (global.use_envelope_request)
4593 setRequestPosition(&dx, &dy, TRUE);
4595 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4597 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4599 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4600 pos->size, &deco_bitmap, &deco_x, &deco_y);
4601 deco_xpos = (gfx->width - pos->size) / 2;
4602 deco_ypos = (gfx->height - pos->size) / 2;
4605 gi = CreateGadget(GDI_CUSTOM_ID, id,
4606 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4607 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4608 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4609 GDI_WIDTH, gfx->width,
4610 GDI_HEIGHT, gfx->height,
4611 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4612 GDI_STATE, GD_BUTTON_UNPRESSED,
4613 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4614 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4615 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4616 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4617 GDI_DECORATION_SIZE, pos->size, pos->size,
4618 GDI_DECORATION_SHIFTING, 1, 1,
4619 GDI_DIRECT_DRAW, FALSE,
4620 GDI_EVENT_MASK, event_mask,
4621 GDI_CALLBACK_ACTION, HandleToolButtons,
4625 Error(ERR_EXIT, "cannot create gadget");
4627 tool_gadget[id] = gi;
4631 void FreeToolButtons()
4635 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4636 FreeGadget(tool_gadget[i]);
4639 static void UnmapToolButtons()
4643 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4644 UnmapGadget(tool_gadget[i]);
4647 static void HandleToolButtons(struct GadgetInfo *gi)
4649 request_gadget_id = gi->custom_id;
4652 static struct Mapping_EM_to_RND_object
4655 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4656 boolean is_backside; /* backside of moving element */
4662 em_object_mapping_list[] =
4665 Xblank, TRUE, FALSE,
4669 Yacid_splash_eB, FALSE, FALSE,
4670 EL_ACID_SPLASH_RIGHT, -1, -1
4673 Yacid_splash_wB, FALSE, FALSE,
4674 EL_ACID_SPLASH_LEFT, -1, -1
4677 #ifdef EM_ENGINE_BAD_ROLL
4679 Xstone_force_e, FALSE, FALSE,
4680 EL_ROCK, -1, MV_BIT_RIGHT
4683 Xstone_force_w, FALSE, FALSE,
4684 EL_ROCK, -1, MV_BIT_LEFT
4687 Xnut_force_e, FALSE, FALSE,
4688 EL_NUT, -1, MV_BIT_RIGHT
4691 Xnut_force_w, FALSE, FALSE,
4692 EL_NUT, -1, MV_BIT_LEFT
4695 Xspring_force_e, FALSE, FALSE,
4696 EL_SPRING, -1, MV_BIT_RIGHT
4699 Xspring_force_w, FALSE, FALSE,
4700 EL_SPRING, -1, MV_BIT_LEFT
4703 Xemerald_force_e, FALSE, FALSE,
4704 EL_EMERALD, -1, MV_BIT_RIGHT
4707 Xemerald_force_w, FALSE, FALSE,
4708 EL_EMERALD, -1, MV_BIT_LEFT
4711 Xdiamond_force_e, FALSE, FALSE,
4712 EL_DIAMOND, -1, MV_BIT_RIGHT
4715 Xdiamond_force_w, FALSE, FALSE,
4716 EL_DIAMOND, -1, MV_BIT_LEFT
4719 Xbomb_force_e, FALSE, FALSE,
4720 EL_BOMB, -1, MV_BIT_RIGHT
4723 Xbomb_force_w, FALSE, FALSE,
4724 EL_BOMB, -1, MV_BIT_LEFT
4726 #endif /* EM_ENGINE_BAD_ROLL */
4729 Xstone, TRUE, FALSE,
4733 Xstone_pause, FALSE, FALSE,
4737 Xstone_fall, FALSE, FALSE,
4741 Ystone_s, FALSE, FALSE,
4742 EL_ROCK, ACTION_FALLING, -1
4745 Ystone_sB, FALSE, TRUE,
4746 EL_ROCK, ACTION_FALLING, -1
4749 Ystone_e, FALSE, FALSE,
4750 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4753 Ystone_eB, FALSE, TRUE,
4754 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4757 Ystone_w, FALSE, FALSE,
4758 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4761 Ystone_wB, FALSE, TRUE,
4762 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4769 Xnut_pause, FALSE, FALSE,
4773 Xnut_fall, FALSE, FALSE,
4777 Ynut_s, FALSE, FALSE,
4778 EL_NUT, ACTION_FALLING, -1
4781 Ynut_sB, FALSE, TRUE,
4782 EL_NUT, ACTION_FALLING, -1
4785 Ynut_e, FALSE, FALSE,
4786 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4789 Ynut_eB, FALSE, TRUE,
4790 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4793 Ynut_w, FALSE, FALSE,
4794 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4797 Ynut_wB, FALSE, TRUE,
4798 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4801 Xbug_n, TRUE, FALSE,
4805 Xbug_e, TRUE, FALSE,
4806 EL_BUG_RIGHT, -1, -1
4809 Xbug_s, TRUE, FALSE,
4813 Xbug_w, TRUE, FALSE,
4817 Xbug_gon, FALSE, FALSE,
4821 Xbug_goe, FALSE, FALSE,
4822 EL_BUG_RIGHT, -1, -1
4825 Xbug_gos, FALSE, FALSE,
4829 Xbug_gow, FALSE, FALSE,
4833 Ybug_n, FALSE, FALSE,
4834 EL_BUG, ACTION_MOVING, MV_BIT_UP
4837 Ybug_nB, FALSE, TRUE,
4838 EL_BUG, ACTION_MOVING, MV_BIT_UP
4841 Ybug_e, FALSE, FALSE,
4842 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4845 Ybug_eB, FALSE, TRUE,
4846 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4849 Ybug_s, FALSE, FALSE,
4850 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4853 Ybug_sB, FALSE, TRUE,
4854 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4857 Ybug_w, FALSE, FALSE,
4858 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4861 Ybug_wB, FALSE, TRUE,
4862 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4865 Ybug_w_n, FALSE, FALSE,
4866 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4869 Ybug_n_e, FALSE, FALSE,
4870 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4873 Ybug_e_s, FALSE, FALSE,
4874 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4877 Ybug_s_w, FALSE, FALSE,
4878 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4881 Ybug_e_n, FALSE, FALSE,
4882 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4885 Ybug_s_e, FALSE, FALSE,
4886 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4889 Ybug_w_s, FALSE, FALSE,
4890 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4893 Ybug_n_w, FALSE, FALSE,
4894 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4897 Ybug_stone, FALSE, FALSE,
4898 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4901 Ybug_spring, FALSE, FALSE,
4902 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4905 Xtank_n, TRUE, FALSE,
4906 EL_SPACESHIP_UP, -1, -1
4909 Xtank_e, TRUE, FALSE,
4910 EL_SPACESHIP_RIGHT, -1, -1
4913 Xtank_s, TRUE, FALSE,
4914 EL_SPACESHIP_DOWN, -1, -1
4917 Xtank_w, TRUE, FALSE,
4918 EL_SPACESHIP_LEFT, -1, -1
4921 Xtank_gon, FALSE, FALSE,
4922 EL_SPACESHIP_UP, -1, -1
4925 Xtank_goe, FALSE, FALSE,
4926 EL_SPACESHIP_RIGHT, -1, -1
4929 Xtank_gos, FALSE, FALSE,
4930 EL_SPACESHIP_DOWN, -1, -1
4933 Xtank_gow, FALSE, FALSE,
4934 EL_SPACESHIP_LEFT, -1, -1
4937 Ytank_n, FALSE, FALSE,
4938 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4941 Ytank_nB, FALSE, TRUE,
4942 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4945 Ytank_e, FALSE, FALSE,
4946 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4949 Ytank_eB, FALSE, TRUE,
4950 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4953 Ytank_s, FALSE, FALSE,
4954 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4957 Ytank_sB, FALSE, TRUE,
4958 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4961 Ytank_w, FALSE, FALSE,
4962 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4965 Ytank_wB, FALSE, TRUE,
4966 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4969 Ytank_w_n, FALSE, FALSE,
4970 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4973 Ytank_n_e, FALSE, FALSE,
4974 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4977 Ytank_e_s, FALSE, FALSE,
4978 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4981 Ytank_s_w, FALSE, FALSE,
4982 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4985 Ytank_e_n, FALSE, FALSE,
4986 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4989 Ytank_s_e, FALSE, FALSE,
4990 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4993 Ytank_w_s, FALSE, FALSE,
4994 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4997 Ytank_n_w, FALSE, FALSE,
4998 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5001 Ytank_stone, FALSE, FALSE,
5002 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5005 Ytank_spring, FALSE, FALSE,
5006 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5009 Xandroid, TRUE, FALSE,
5010 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5013 Xandroid_1_n, FALSE, FALSE,
5014 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5017 Xandroid_2_n, FALSE, FALSE,
5018 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5021 Xandroid_1_e, FALSE, FALSE,
5022 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5025 Xandroid_2_e, FALSE, FALSE,
5026 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5029 Xandroid_1_w, FALSE, FALSE,
5030 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5033 Xandroid_2_w, FALSE, FALSE,
5034 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5037 Xandroid_1_s, FALSE, FALSE,
5038 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5041 Xandroid_2_s, FALSE, FALSE,
5042 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5045 Yandroid_n, FALSE, FALSE,
5046 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5049 Yandroid_nB, FALSE, TRUE,
5050 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5053 Yandroid_ne, FALSE, FALSE,
5054 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5057 Yandroid_neB, FALSE, TRUE,
5058 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5061 Yandroid_e, FALSE, FALSE,
5062 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5065 Yandroid_eB, FALSE, TRUE,
5066 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5069 Yandroid_se, FALSE, FALSE,
5070 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5073 Yandroid_seB, FALSE, TRUE,
5074 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5077 Yandroid_s, FALSE, FALSE,
5078 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5081 Yandroid_sB, FALSE, TRUE,
5082 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5085 Yandroid_sw, FALSE, FALSE,
5086 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5089 Yandroid_swB, FALSE, TRUE,
5090 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5093 Yandroid_w, FALSE, FALSE,
5094 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5097 Yandroid_wB, FALSE, TRUE,
5098 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5101 Yandroid_nw, FALSE, FALSE,
5102 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5105 Yandroid_nwB, FALSE, TRUE,
5106 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5109 Xspring, TRUE, FALSE,
5113 Xspring_pause, FALSE, FALSE,
5117 Xspring_e, FALSE, FALSE,
5121 Xspring_w, FALSE, FALSE,
5125 Xspring_fall, FALSE, FALSE,
5129 Yspring_s, FALSE, FALSE,
5130 EL_SPRING, ACTION_FALLING, -1
5133 Yspring_sB, FALSE, TRUE,
5134 EL_SPRING, ACTION_FALLING, -1
5137 Yspring_e, FALSE, FALSE,
5138 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5141 Yspring_eB, FALSE, TRUE,
5142 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5145 Yspring_w, FALSE, FALSE,
5146 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5149 Yspring_wB, FALSE, TRUE,
5150 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5153 Yspring_kill_e, FALSE, FALSE,
5154 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5157 Yspring_kill_eB, FALSE, TRUE,
5158 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5161 Yspring_kill_w, FALSE, FALSE,
5162 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5165 Yspring_kill_wB, FALSE, TRUE,
5166 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5169 Xeater_n, TRUE, FALSE,
5170 EL_YAMYAM_UP, -1, -1
5173 Xeater_e, TRUE, FALSE,
5174 EL_YAMYAM_RIGHT, -1, -1
5177 Xeater_w, TRUE, FALSE,
5178 EL_YAMYAM_LEFT, -1, -1
5181 Xeater_s, TRUE, FALSE,
5182 EL_YAMYAM_DOWN, -1, -1
5185 Yeater_n, FALSE, FALSE,
5186 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5189 Yeater_nB, FALSE, TRUE,
5190 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5193 Yeater_e, FALSE, FALSE,
5194 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5197 Yeater_eB, FALSE, TRUE,
5198 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5201 Yeater_s, FALSE, FALSE,
5202 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5205 Yeater_sB, FALSE, TRUE,
5206 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5209 Yeater_w, FALSE, FALSE,
5210 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5213 Yeater_wB, FALSE, TRUE,
5214 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5217 Yeater_stone, FALSE, FALSE,
5218 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5221 Yeater_spring, FALSE, FALSE,
5222 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5225 Xalien, TRUE, FALSE,
5229 Xalien_pause, FALSE, FALSE,
5233 Yalien_n, FALSE, FALSE,
5234 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5237 Yalien_nB, FALSE, TRUE,
5238 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5241 Yalien_e, FALSE, FALSE,
5242 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5245 Yalien_eB, FALSE, TRUE,
5246 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5249 Yalien_s, FALSE, FALSE,
5250 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5253 Yalien_sB, FALSE, TRUE,
5254 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5257 Yalien_w, FALSE, FALSE,
5258 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5261 Yalien_wB, FALSE, TRUE,
5262 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5265 Yalien_stone, FALSE, FALSE,
5266 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5269 Yalien_spring, FALSE, FALSE,
5270 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5273 Xemerald, TRUE, FALSE,
5277 Xemerald_pause, FALSE, FALSE,
5281 Xemerald_fall, FALSE, FALSE,
5285 Xemerald_shine, FALSE, FALSE,
5286 EL_EMERALD, ACTION_TWINKLING, -1
5289 Yemerald_s, FALSE, FALSE,
5290 EL_EMERALD, ACTION_FALLING, -1
5293 Yemerald_sB, FALSE, TRUE,
5294 EL_EMERALD, ACTION_FALLING, -1
5297 Yemerald_e, FALSE, FALSE,
5298 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5301 Yemerald_eB, FALSE, TRUE,
5302 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5305 Yemerald_w, FALSE, FALSE,
5306 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5309 Yemerald_wB, FALSE, TRUE,
5310 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5313 Yemerald_eat, FALSE, FALSE,
5314 EL_EMERALD, ACTION_COLLECTING, -1
5317 Yemerald_stone, FALSE, FALSE,
5318 EL_NUT, ACTION_BREAKING, -1
5321 Xdiamond, TRUE, FALSE,
5325 Xdiamond_pause, FALSE, FALSE,
5329 Xdiamond_fall, FALSE, FALSE,
5333 Xdiamond_shine, FALSE, FALSE,
5334 EL_DIAMOND, ACTION_TWINKLING, -1
5337 Ydiamond_s, FALSE, FALSE,
5338 EL_DIAMOND, ACTION_FALLING, -1
5341 Ydiamond_sB, FALSE, TRUE,
5342 EL_DIAMOND, ACTION_FALLING, -1
5345 Ydiamond_e, FALSE, FALSE,
5346 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5349 Ydiamond_eB, FALSE, TRUE,
5350 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5353 Ydiamond_w, FALSE, FALSE,
5354 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5357 Ydiamond_wB, FALSE, TRUE,
5358 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5361 Ydiamond_eat, FALSE, FALSE,
5362 EL_DIAMOND, ACTION_COLLECTING, -1
5365 Ydiamond_stone, FALSE, FALSE,
5366 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5369 Xdrip_fall, TRUE, FALSE,
5370 EL_AMOEBA_DROP, -1, -1
5373 Xdrip_stretch, FALSE, FALSE,
5374 EL_AMOEBA_DROP, ACTION_FALLING, -1
5377 Xdrip_stretchB, FALSE, TRUE,
5378 EL_AMOEBA_DROP, ACTION_FALLING, -1
5381 Xdrip_eat, FALSE, FALSE,
5382 EL_AMOEBA_DROP, ACTION_GROWING, -1
5385 Ydrip_s1, FALSE, FALSE,
5386 EL_AMOEBA_DROP, ACTION_FALLING, -1
5389 Ydrip_s1B, FALSE, TRUE,
5390 EL_AMOEBA_DROP, ACTION_FALLING, -1
5393 Ydrip_s2, FALSE, FALSE,
5394 EL_AMOEBA_DROP, ACTION_FALLING, -1
5397 Ydrip_s2B, FALSE, TRUE,
5398 EL_AMOEBA_DROP, ACTION_FALLING, -1
5405 Xbomb_pause, FALSE, FALSE,
5409 Xbomb_fall, FALSE, FALSE,
5413 Ybomb_s, FALSE, FALSE,
5414 EL_BOMB, ACTION_FALLING, -1
5417 Ybomb_sB, FALSE, TRUE,
5418 EL_BOMB, ACTION_FALLING, -1
5421 Ybomb_e, FALSE, FALSE,
5422 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5425 Ybomb_eB, FALSE, TRUE,
5426 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5429 Ybomb_w, FALSE, FALSE,
5430 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5433 Ybomb_wB, FALSE, TRUE,
5434 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5437 Ybomb_eat, FALSE, FALSE,
5438 EL_BOMB, ACTION_ACTIVATING, -1
5441 Xballoon, TRUE, FALSE,
5445 Yballoon_n, FALSE, FALSE,
5446 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5449 Yballoon_nB, FALSE, TRUE,
5450 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5453 Yballoon_e, FALSE, FALSE,
5454 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5457 Yballoon_eB, FALSE, TRUE,
5458 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5461 Yballoon_s, FALSE, FALSE,
5462 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5465 Yballoon_sB, FALSE, TRUE,
5466 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5469 Yballoon_w, FALSE, FALSE,
5470 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5473 Yballoon_wB, FALSE, TRUE,
5474 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5477 Xgrass, TRUE, FALSE,
5478 EL_EMC_GRASS, -1, -1
5481 Ygrass_nB, FALSE, FALSE,
5482 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5485 Ygrass_eB, FALSE, FALSE,
5486 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5489 Ygrass_sB, FALSE, FALSE,
5490 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5493 Ygrass_wB, FALSE, FALSE,
5494 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5501 Ydirt_nB, FALSE, FALSE,
5502 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5505 Ydirt_eB, FALSE, FALSE,
5506 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5509 Ydirt_sB, FALSE, FALSE,
5510 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5513 Ydirt_wB, FALSE, FALSE,
5514 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5517 Xacid_ne, TRUE, FALSE,
5518 EL_ACID_POOL_TOPRIGHT, -1, -1
5521 Xacid_se, TRUE, FALSE,
5522 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5525 Xacid_s, TRUE, FALSE,
5526 EL_ACID_POOL_BOTTOM, -1, -1
5529 Xacid_sw, TRUE, FALSE,
5530 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5533 Xacid_nw, TRUE, FALSE,
5534 EL_ACID_POOL_TOPLEFT, -1, -1
5537 Xacid_1, TRUE, FALSE,
5541 Xacid_2, FALSE, FALSE,
5545 Xacid_3, FALSE, FALSE,
5549 Xacid_4, FALSE, FALSE,
5553 Xacid_5, FALSE, FALSE,
5557 Xacid_6, FALSE, FALSE,
5561 Xacid_7, FALSE, FALSE,
5565 Xacid_8, FALSE, FALSE,
5569 Xball_1, TRUE, FALSE,
5570 EL_EMC_MAGIC_BALL, -1, -1
5573 Xball_1B, FALSE, FALSE,
5574 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5577 Xball_2, FALSE, FALSE,
5578 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5581 Xball_2B, FALSE, FALSE,
5582 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5585 Yball_eat, FALSE, FALSE,
5586 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5589 Ykey_1_eat, FALSE, FALSE,
5590 EL_EM_KEY_1, ACTION_COLLECTING, -1
5593 Ykey_2_eat, FALSE, FALSE,
5594 EL_EM_KEY_2, ACTION_COLLECTING, -1
5597 Ykey_3_eat, FALSE, FALSE,
5598 EL_EM_KEY_3, ACTION_COLLECTING, -1
5601 Ykey_4_eat, FALSE, FALSE,
5602 EL_EM_KEY_4, ACTION_COLLECTING, -1
5605 Ykey_5_eat, FALSE, FALSE,
5606 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5609 Ykey_6_eat, FALSE, FALSE,
5610 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5613 Ykey_7_eat, FALSE, FALSE,
5614 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5617 Ykey_8_eat, FALSE, FALSE,
5618 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5621 Ylenses_eat, FALSE, FALSE,
5622 EL_EMC_LENSES, ACTION_COLLECTING, -1
5625 Ymagnify_eat, FALSE, FALSE,
5626 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5629 Ygrass_eat, FALSE, FALSE,
5630 EL_EMC_GRASS, ACTION_SNAPPING, -1
5633 Ydirt_eat, FALSE, FALSE,
5634 EL_SAND, ACTION_SNAPPING, -1
5637 Xgrow_ns, TRUE, FALSE,
5638 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5641 Ygrow_ns_eat, FALSE, FALSE,
5642 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5645 Xgrow_ew, TRUE, FALSE,
5646 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5649 Ygrow_ew_eat, FALSE, FALSE,
5650 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5653 Xwonderwall, TRUE, FALSE,
5654 EL_MAGIC_WALL, -1, -1
5657 XwonderwallB, FALSE, FALSE,
5658 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5661 Xamoeba_1, TRUE, FALSE,
5662 EL_AMOEBA_DRY, ACTION_OTHER, -1
5665 Xamoeba_2, FALSE, FALSE,
5666 EL_AMOEBA_DRY, ACTION_OTHER, -1
5669 Xamoeba_3, FALSE, FALSE,
5670 EL_AMOEBA_DRY, ACTION_OTHER, -1
5673 Xamoeba_4, FALSE, FALSE,
5674 EL_AMOEBA_DRY, ACTION_OTHER, -1
5677 Xamoeba_5, TRUE, FALSE,
5678 EL_AMOEBA_WET, ACTION_OTHER, -1
5681 Xamoeba_6, FALSE, FALSE,
5682 EL_AMOEBA_WET, ACTION_OTHER, -1
5685 Xamoeba_7, FALSE, FALSE,
5686 EL_AMOEBA_WET, ACTION_OTHER, -1
5689 Xamoeba_8, FALSE, FALSE,
5690 EL_AMOEBA_WET, ACTION_OTHER, -1
5693 Xdoor_1, TRUE, FALSE,
5694 EL_EM_GATE_1, -1, -1
5697 Xdoor_2, TRUE, FALSE,
5698 EL_EM_GATE_2, -1, -1
5701 Xdoor_3, TRUE, FALSE,
5702 EL_EM_GATE_3, -1, -1
5705 Xdoor_4, TRUE, FALSE,
5706 EL_EM_GATE_4, -1, -1
5709 Xdoor_5, TRUE, FALSE,
5710 EL_EMC_GATE_5, -1, -1
5713 Xdoor_6, TRUE, FALSE,
5714 EL_EMC_GATE_6, -1, -1
5717 Xdoor_7, TRUE, FALSE,
5718 EL_EMC_GATE_7, -1, -1
5721 Xdoor_8, TRUE, FALSE,
5722 EL_EMC_GATE_8, -1, -1
5725 Xkey_1, TRUE, FALSE,
5729 Xkey_2, TRUE, FALSE,
5733 Xkey_3, TRUE, FALSE,
5737 Xkey_4, TRUE, FALSE,
5741 Xkey_5, TRUE, FALSE,
5742 EL_EMC_KEY_5, -1, -1
5745 Xkey_6, TRUE, FALSE,
5746 EL_EMC_KEY_6, -1, -1
5749 Xkey_7, TRUE, FALSE,
5750 EL_EMC_KEY_7, -1, -1
5753 Xkey_8, TRUE, FALSE,
5754 EL_EMC_KEY_8, -1, -1
5757 Xwind_n, TRUE, FALSE,
5758 EL_BALLOON_SWITCH_UP, -1, -1
5761 Xwind_e, TRUE, FALSE,
5762 EL_BALLOON_SWITCH_RIGHT, -1, -1
5765 Xwind_s, TRUE, FALSE,
5766 EL_BALLOON_SWITCH_DOWN, -1, -1
5769 Xwind_w, TRUE, FALSE,
5770 EL_BALLOON_SWITCH_LEFT, -1, -1
5773 Xwind_nesw, TRUE, FALSE,
5774 EL_BALLOON_SWITCH_ANY, -1, -1
5777 Xwind_stop, TRUE, FALSE,
5778 EL_BALLOON_SWITCH_NONE, -1, -1
5782 EL_EM_EXIT_CLOSED, -1, -1
5785 Xexit_1, TRUE, FALSE,
5786 EL_EM_EXIT_OPEN, -1, -1
5789 Xexit_2, FALSE, FALSE,
5790 EL_EM_EXIT_OPEN, -1, -1
5793 Xexit_3, FALSE, FALSE,
5794 EL_EM_EXIT_OPEN, -1, -1
5797 Xdynamite, TRUE, FALSE,
5798 EL_EM_DYNAMITE, -1, -1
5801 Ydynamite_eat, FALSE, FALSE,
5802 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5805 Xdynamite_1, TRUE, FALSE,
5806 EL_EM_DYNAMITE_ACTIVE, -1, -1
5809 Xdynamite_2, FALSE, FALSE,
5810 EL_EM_DYNAMITE_ACTIVE, -1, -1
5813 Xdynamite_3, FALSE, FALSE,
5814 EL_EM_DYNAMITE_ACTIVE, -1, -1
5817 Xdynamite_4, FALSE, FALSE,
5818 EL_EM_DYNAMITE_ACTIVE, -1, -1
5821 Xbumper, TRUE, FALSE,
5822 EL_EMC_SPRING_BUMPER, -1, -1
5825 XbumperB, FALSE, FALSE,
5826 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5829 Xwheel, TRUE, FALSE,
5830 EL_ROBOT_WHEEL, -1, -1
5833 XwheelB, FALSE, FALSE,
5834 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5837 Xswitch, TRUE, FALSE,
5838 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5841 XswitchB, FALSE, FALSE,
5842 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5846 EL_QUICKSAND_EMPTY, -1, -1
5849 Xsand_stone, TRUE, FALSE,
5850 EL_QUICKSAND_FULL, -1, -1
5853 Xsand_stonein_1, FALSE, TRUE,
5854 EL_ROCK, ACTION_FILLING, -1
5857 Xsand_stonein_2, FALSE, TRUE,
5858 EL_ROCK, ACTION_FILLING, -1
5861 Xsand_stonein_3, FALSE, TRUE,
5862 EL_ROCK, ACTION_FILLING, -1
5865 Xsand_stonein_4, FALSE, TRUE,
5866 EL_ROCK, ACTION_FILLING, -1
5869 Xsand_stonesand_1, FALSE, FALSE,
5870 EL_QUICKSAND_EMPTYING, -1, -1
5873 Xsand_stonesand_2, FALSE, FALSE,
5874 EL_QUICKSAND_EMPTYING, -1, -1
5877 Xsand_stonesand_3, FALSE, FALSE,
5878 EL_QUICKSAND_EMPTYING, -1, -1
5881 Xsand_stonesand_4, FALSE, FALSE,
5882 EL_QUICKSAND_EMPTYING, -1, -1
5885 Xsand_stonesand_quickout_1, FALSE, FALSE,
5886 EL_QUICKSAND_EMPTYING, -1, -1
5889 Xsand_stonesand_quickout_2, FALSE, FALSE,
5890 EL_QUICKSAND_EMPTYING, -1, -1
5893 Xsand_stoneout_1, FALSE, FALSE,
5894 EL_ROCK, ACTION_EMPTYING, -1
5897 Xsand_stoneout_2, FALSE, FALSE,
5898 EL_ROCK, ACTION_EMPTYING, -1
5901 Xsand_sandstone_1, FALSE, FALSE,
5902 EL_QUICKSAND_FILLING, -1, -1
5905 Xsand_sandstone_2, FALSE, FALSE,
5906 EL_QUICKSAND_FILLING, -1, -1
5909 Xsand_sandstone_3, FALSE, FALSE,
5910 EL_QUICKSAND_FILLING, -1, -1
5913 Xsand_sandstone_4, FALSE, FALSE,
5914 EL_QUICKSAND_FILLING, -1, -1
5917 Xplant, TRUE, FALSE,
5918 EL_EMC_PLANT, -1, -1
5921 Yplant, FALSE, FALSE,
5922 EL_EMC_PLANT, -1, -1
5925 Xlenses, TRUE, FALSE,
5926 EL_EMC_LENSES, -1, -1
5929 Xmagnify, TRUE, FALSE,
5930 EL_EMC_MAGNIFIER, -1, -1
5933 Xdripper, TRUE, FALSE,
5934 EL_EMC_DRIPPER, -1, -1
5937 XdripperB, FALSE, FALSE,
5938 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5941 Xfake_blank, TRUE, FALSE,
5942 EL_INVISIBLE_WALL, -1, -1
5945 Xfake_blankB, FALSE, FALSE,
5946 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5949 Xfake_grass, TRUE, FALSE,
5950 EL_EMC_FAKE_GRASS, -1, -1
5953 Xfake_grassB, FALSE, FALSE,
5954 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5957 Xfake_door_1, TRUE, FALSE,
5958 EL_EM_GATE_1_GRAY, -1, -1
5961 Xfake_door_2, TRUE, FALSE,
5962 EL_EM_GATE_2_GRAY, -1, -1
5965 Xfake_door_3, TRUE, FALSE,
5966 EL_EM_GATE_3_GRAY, -1, -1
5969 Xfake_door_4, TRUE, FALSE,
5970 EL_EM_GATE_4_GRAY, -1, -1
5973 Xfake_door_5, TRUE, FALSE,
5974 EL_EMC_GATE_5_GRAY, -1, -1
5977 Xfake_door_6, TRUE, FALSE,
5978 EL_EMC_GATE_6_GRAY, -1, -1
5981 Xfake_door_7, TRUE, FALSE,
5982 EL_EMC_GATE_7_GRAY, -1, -1
5985 Xfake_door_8, TRUE, FALSE,
5986 EL_EMC_GATE_8_GRAY, -1, -1
5989 Xfake_acid_1, TRUE, FALSE,
5990 EL_EMC_FAKE_ACID, -1, -1
5993 Xfake_acid_2, FALSE, FALSE,
5994 EL_EMC_FAKE_ACID, -1, -1
5997 Xfake_acid_3, FALSE, FALSE,
5998 EL_EMC_FAKE_ACID, -1, -1
6001 Xfake_acid_4, FALSE, FALSE,
6002 EL_EMC_FAKE_ACID, -1, -1
6005 Xfake_acid_5, FALSE, FALSE,
6006 EL_EMC_FAKE_ACID, -1, -1
6009 Xfake_acid_6, FALSE, FALSE,
6010 EL_EMC_FAKE_ACID, -1, -1
6013 Xfake_acid_7, FALSE, FALSE,
6014 EL_EMC_FAKE_ACID, -1, -1
6017 Xfake_acid_8, FALSE, FALSE,
6018 EL_EMC_FAKE_ACID, -1, -1
6021 Xsteel_1, TRUE, FALSE,
6022 EL_STEELWALL, -1, -1
6025 Xsteel_2, TRUE, FALSE,
6026 EL_EMC_STEELWALL_2, -1, -1
6029 Xsteel_3, TRUE, FALSE,
6030 EL_EMC_STEELWALL_3, -1, -1
6033 Xsteel_4, TRUE, FALSE,
6034 EL_EMC_STEELWALL_4, -1, -1
6037 Xwall_1, TRUE, FALSE,
6041 Xwall_2, TRUE, FALSE,
6042 EL_EMC_WALL_14, -1, -1
6045 Xwall_3, TRUE, FALSE,
6046 EL_EMC_WALL_15, -1, -1
6049 Xwall_4, TRUE, FALSE,
6050 EL_EMC_WALL_16, -1, -1
6053 Xround_wall_1, TRUE, FALSE,
6054 EL_WALL_SLIPPERY, -1, -1
6057 Xround_wall_2, TRUE, FALSE,
6058 EL_EMC_WALL_SLIPPERY_2, -1, -1
6061 Xround_wall_3, TRUE, FALSE,
6062 EL_EMC_WALL_SLIPPERY_3, -1, -1
6065 Xround_wall_4, TRUE, FALSE,
6066 EL_EMC_WALL_SLIPPERY_4, -1, -1
6069 Xdecor_1, TRUE, FALSE,
6070 EL_EMC_WALL_8, -1, -1
6073 Xdecor_2, TRUE, FALSE,
6074 EL_EMC_WALL_6, -1, -1
6077 Xdecor_3, TRUE, FALSE,
6078 EL_EMC_WALL_4, -1, -1
6081 Xdecor_4, TRUE, FALSE,
6082 EL_EMC_WALL_7, -1, -1
6085 Xdecor_5, TRUE, FALSE,
6086 EL_EMC_WALL_5, -1, -1
6089 Xdecor_6, TRUE, FALSE,
6090 EL_EMC_WALL_9, -1, -1
6093 Xdecor_7, TRUE, FALSE,
6094 EL_EMC_WALL_10, -1, -1
6097 Xdecor_8, TRUE, FALSE,
6098 EL_EMC_WALL_1, -1, -1
6101 Xdecor_9, TRUE, FALSE,
6102 EL_EMC_WALL_2, -1, -1
6105 Xdecor_10, TRUE, FALSE,
6106 EL_EMC_WALL_3, -1, -1
6109 Xdecor_11, TRUE, FALSE,
6110 EL_EMC_WALL_11, -1, -1
6113 Xdecor_12, TRUE, FALSE,
6114 EL_EMC_WALL_12, -1, -1
6117 Xalpha_0, TRUE, FALSE,
6118 EL_CHAR('0'), -1, -1
6121 Xalpha_1, TRUE, FALSE,
6122 EL_CHAR('1'), -1, -1
6125 Xalpha_2, TRUE, FALSE,
6126 EL_CHAR('2'), -1, -1
6129 Xalpha_3, TRUE, FALSE,
6130 EL_CHAR('3'), -1, -1
6133 Xalpha_4, TRUE, FALSE,
6134 EL_CHAR('4'), -1, -1
6137 Xalpha_5, TRUE, FALSE,
6138 EL_CHAR('5'), -1, -1
6141 Xalpha_6, TRUE, FALSE,
6142 EL_CHAR('6'), -1, -1
6145 Xalpha_7, TRUE, FALSE,
6146 EL_CHAR('7'), -1, -1
6149 Xalpha_8, TRUE, FALSE,
6150 EL_CHAR('8'), -1, -1
6153 Xalpha_9, TRUE, FALSE,
6154 EL_CHAR('9'), -1, -1
6157 Xalpha_excla, TRUE, FALSE,
6158 EL_CHAR('!'), -1, -1
6161 Xalpha_quote, TRUE, FALSE,
6162 EL_CHAR('"'), -1, -1
6165 Xalpha_comma, TRUE, FALSE,
6166 EL_CHAR(','), -1, -1
6169 Xalpha_minus, TRUE, FALSE,
6170 EL_CHAR('-'), -1, -1
6173 Xalpha_perio, TRUE, FALSE,
6174 EL_CHAR('.'), -1, -1
6177 Xalpha_colon, TRUE, FALSE,
6178 EL_CHAR(':'), -1, -1
6181 Xalpha_quest, TRUE, FALSE,
6182 EL_CHAR('?'), -1, -1
6185 Xalpha_a, TRUE, FALSE,
6186 EL_CHAR('A'), -1, -1
6189 Xalpha_b, TRUE, FALSE,
6190 EL_CHAR('B'), -1, -1
6193 Xalpha_c, TRUE, FALSE,
6194 EL_CHAR('C'), -1, -1
6197 Xalpha_d, TRUE, FALSE,
6198 EL_CHAR('D'), -1, -1
6201 Xalpha_e, TRUE, FALSE,
6202 EL_CHAR('E'), -1, -1
6205 Xalpha_f, TRUE, FALSE,
6206 EL_CHAR('F'), -1, -1
6209 Xalpha_g, TRUE, FALSE,
6210 EL_CHAR('G'), -1, -1
6213 Xalpha_h, TRUE, FALSE,
6214 EL_CHAR('H'), -1, -1
6217 Xalpha_i, TRUE, FALSE,
6218 EL_CHAR('I'), -1, -1
6221 Xalpha_j, TRUE, FALSE,
6222 EL_CHAR('J'), -1, -1
6225 Xalpha_k, TRUE, FALSE,
6226 EL_CHAR('K'), -1, -1
6229 Xalpha_l, TRUE, FALSE,
6230 EL_CHAR('L'), -1, -1
6233 Xalpha_m, TRUE, FALSE,
6234 EL_CHAR('M'), -1, -1
6237 Xalpha_n, TRUE, FALSE,
6238 EL_CHAR('N'), -1, -1
6241 Xalpha_o, TRUE, FALSE,
6242 EL_CHAR('O'), -1, -1
6245 Xalpha_p, TRUE, FALSE,
6246 EL_CHAR('P'), -1, -1
6249 Xalpha_q, TRUE, FALSE,
6250 EL_CHAR('Q'), -1, -1
6253 Xalpha_r, TRUE, FALSE,
6254 EL_CHAR('R'), -1, -1
6257 Xalpha_s, TRUE, FALSE,
6258 EL_CHAR('S'), -1, -1
6261 Xalpha_t, TRUE, FALSE,
6262 EL_CHAR('T'), -1, -1
6265 Xalpha_u, TRUE, FALSE,
6266 EL_CHAR('U'), -1, -1
6269 Xalpha_v, TRUE, FALSE,
6270 EL_CHAR('V'), -1, -1
6273 Xalpha_w, TRUE, FALSE,
6274 EL_CHAR('W'), -1, -1
6277 Xalpha_x, TRUE, FALSE,
6278 EL_CHAR('X'), -1, -1
6281 Xalpha_y, TRUE, FALSE,
6282 EL_CHAR('Y'), -1, -1
6285 Xalpha_z, TRUE, FALSE,
6286 EL_CHAR('Z'), -1, -1
6289 Xalpha_arrow_e, TRUE, FALSE,
6290 EL_CHAR('>'), -1, -1
6293 Xalpha_arrow_w, TRUE, FALSE,
6294 EL_CHAR('<'), -1, -1
6297 Xalpha_copyr, TRUE, FALSE,
6298 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6302 Xboom_bug, FALSE, FALSE,
6303 EL_BUG, ACTION_EXPLODING, -1
6306 Xboom_bomb, FALSE, FALSE,
6307 EL_BOMB, ACTION_EXPLODING, -1
6310 Xboom_android, FALSE, FALSE,
6311 EL_EMC_ANDROID, ACTION_OTHER, -1
6314 Xboom_1, FALSE, FALSE,
6315 EL_DEFAULT, ACTION_EXPLODING, -1
6318 Xboom_2, FALSE, FALSE,
6319 EL_DEFAULT, ACTION_EXPLODING, -1
6322 Znormal, FALSE, FALSE,
6326 Zdynamite, FALSE, FALSE,
6330 Zplayer, FALSE, FALSE,
6334 ZBORDER, FALSE, FALSE,
6344 static struct Mapping_EM_to_RND_player
6353 em_player_mapping_list[] =
6357 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6361 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6365 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6369 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6373 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6377 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6381 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6385 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6389 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6393 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6397 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6401 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6405 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6409 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6413 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6417 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6421 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6425 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6429 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6433 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6437 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6441 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6445 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6449 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6453 EL_PLAYER_1, ACTION_DEFAULT, -1,
6457 EL_PLAYER_2, ACTION_DEFAULT, -1,
6461 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6465 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6469 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6473 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6477 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6481 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6485 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6489 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6493 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6497 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6501 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6505 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6509 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6513 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6517 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6521 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6525 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6529 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6533 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6537 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6541 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6545 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6549 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6553 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6557 EL_PLAYER_3, ACTION_DEFAULT, -1,
6561 EL_PLAYER_4, ACTION_DEFAULT, -1,
6570 int map_element_RND_to_EM(int element_rnd)
6572 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6573 static boolean mapping_initialized = FALSE;
6575 if (!mapping_initialized)
6579 /* return "Xalpha_quest" for all undefined elements in mapping array */
6580 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6581 mapping_RND_to_EM[i] = Xalpha_quest;
6583 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6584 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6585 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6586 em_object_mapping_list[i].element_em;
6588 mapping_initialized = TRUE;
6591 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6592 return mapping_RND_to_EM[element_rnd];
6594 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6599 int map_element_EM_to_RND(int element_em)
6601 static unsigned short mapping_EM_to_RND[TILE_MAX];
6602 static boolean mapping_initialized = FALSE;
6604 if (!mapping_initialized)
6608 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6609 for (i = 0; i < TILE_MAX; i++)
6610 mapping_EM_to_RND[i] = EL_UNKNOWN;
6612 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6613 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6614 em_object_mapping_list[i].element_rnd;
6616 mapping_initialized = TRUE;
6619 if (element_em >= 0 && element_em < TILE_MAX)
6620 return mapping_EM_to_RND[element_em];
6622 Error(ERR_WARN, "invalid EM level element %d", element_em);
6627 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6629 struct LevelInfo_EM *level_em = level->native_em_level;
6630 struct LEVEL *lev = level_em->lev;
6633 for (i = 0; i < TILE_MAX; i++)
6634 lev->android_array[i] = Xblank;
6636 for (i = 0; i < level->num_android_clone_elements; i++)
6638 int element_rnd = level->android_clone_element[i];
6639 int element_em = map_element_RND_to_EM(element_rnd);
6641 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6642 if (em_object_mapping_list[j].element_rnd == element_rnd)
6643 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6647 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6649 struct LevelInfo_EM *level_em = level->native_em_level;
6650 struct LEVEL *lev = level_em->lev;
6653 level->num_android_clone_elements = 0;
6655 for (i = 0; i < TILE_MAX; i++)
6657 int element_em = lev->android_array[i];
6659 boolean element_found = FALSE;
6661 if (element_em == Xblank)
6664 element_rnd = map_element_EM_to_RND(element_em);
6666 for (j = 0; j < level->num_android_clone_elements; j++)
6667 if (level->android_clone_element[j] == element_rnd)
6668 element_found = TRUE;
6672 level->android_clone_element[level->num_android_clone_elements++] =
6675 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6680 if (level->num_android_clone_elements == 0)
6682 level->num_android_clone_elements = 1;
6683 level->android_clone_element[0] = EL_EMPTY;
6687 int map_direction_RND_to_EM(int direction)
6689 return (direction == MV_UP ? 0 :
6690 direction == MV_RIGHT ? 1 :
6691 direction == MV_DOWN ? 2 :
6692 direction == MV_LEFT ? 3 :
6696 int map_direction_EM_to_RND(int direction)
6698 return (direction == 0 ? MV_UP :
6699 direction == 1 ? MV_RIGHT :
6700 direction == 2 ? MV_DOWN :
6701 direction == 3 ? MV_LEFT :
6705 int map_element_RND_to_SP(int element_rnd)
6707 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6709 if (element_rnd >= EL_SP_START &&
6710 element_rnd <= EL_SP_END)
6711 element_sp = element_rnd - EL_SP_START;
6712 else if (element_rnd == EL_EMPTY_SPACE)
6714 else if (element_rnd == EL_INVISIBLE_WALL)
6720 int map_element_SP_to_RND(int element_sp)
6722 int element_rnd = EL_UNKNOWN;
6724 if (element_sp >= 0x00 &&
6726 element_rnd = EL_SP_START + element_sp;
6727 else if (element_sp == 0x28)
6728 element_rnd = EL_INVISIBLE_WALL;
6733 int map_action_SP_to_RND(int action_sp)
6737 case actActive: return ACTION_ACTIVE;
6738 case actImpact: return ACTION_IMPACT;
6739 case actExploding: return ACTION_EXPLODING;
6740 case actDigging: return ACTION_DIGGING;
6741 case actSnapping: return ACTION_SNAPPING;
6742 case actCollecting: return ACTION_COLLECTING;
6743 case actPassing: return ACTION_PASSING;
6744 case actPushing: return ACTION_PUSHING;
6745 case actDropping: return ACTION_DROPPING;
6747 default: return ACTION_DEFAULT;
6751 int get_next_element(int element)
6755 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6756 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6757 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6758 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6759 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6760 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6761 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6762 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6763 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6764 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6765 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6767 default: return element;
6771 int el_act_dir2img(int element, int action, int direction)
6773 element = GFX_ELEMENT(element);
6774 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6776 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6777 return element_info[element].direction_graphic[action][direction];
6780 static int el_act_dir2crm(int element, int action, int direction)
6782 element = GFX_ELEMENT(element);
6783 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6785 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6786 return element_info[element].direction_crumbled[action][direction];
6789 int el_act2img(int element, int action)
6791 element = GFX_ELEMENT(element);
6793 return element_info[element].graphic[action];
6796 int el_act2crm(int element, int action)
6798 element = GFX_ELEMENT(element);
6800 return element_info[element].crumbled[action];
6803 int el_dir2img(int element, int direction)
6805 element = GFX_ELEMENT(element);
6807 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6810 int el2baseimg(int element)
6812 return element_info[element].graphic[ACTION_DEFAULT];
6815 int el2img(int element)
6817 element = GFX_ELEMENT(element);
6819 return element_info[element].graphic[ACTION_DEFAULT];
6822 int el2edimg(int element)
6824 element = GFX_ELEMENT(element);
6826 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6829 int el2preimg(int element)
6831 element = GFX_ELEMENT(element);
6833 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6836 int el2panelimg(int element)
6838 element = GFX_ELEMENT(element);
6840 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6843 int font2baseimg(int font_nr)
6845 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6848 int getBeltNrFromBeltElement(int element)
6850 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6851 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6852 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6855 int getBeltNrFromBeltActiveElement(int element)
6857 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6858 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6859 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6862 int getBeltNrFromBeltSwitchElement(int element)
6864 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6865 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6866 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6869 int getBeltDirNrFromBeltElement(int element)
6871 static int belt_base_element[4] =
6873 EL_CONVEYOR_BELT_1_LEFT,
6874 EL_CONVEYOR_BELT_2_LEFT,
6875 EL_CONVEYOR_BELT_3_LEFT,
6876 EL_CONVEYOR_BELT_4_LEFT
6879 int belt_nr = getBeltNrFromBeltElement(element);
6880 int belt_dir_nr = element - belt_base_element[belt_nr];
6882 return (belt_dir_nr % 3);
6885 int getBeltDirNrFromBeltSwitchElement(int element)
6887 static int belt_base_element[4] =
6889 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6890 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6891 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6892 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6895 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6896 int belt_dir_nr = element - belt_base_element[belt_nr];
6898 return (belt_dir_nr % 3);
6901 int getBeltDirFromBeltElement(int element)
6903 static int belt_move_dir[3] =
6910 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6912 return belt_move_dir[belt_dir_nr];
6915 int getBeltDirFromBeltSwitchElement(int element)
6917 static int belt_move_dir[3] =
6924 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6926 return belt_move_dir[belt_dir_nr];
6929 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6931 static int belt_base_element[4] =
6933 EL_CONVEYOR_BELT_1_LEFT,
6934 EL_CONVEYOR_BELT_2_LEFT,
6935 EL_CONVEYOR_BELT_3_LEFT,
6936 EL_CONVEYOR_BELT_4_LEFT
6939 return belt_base_element[belt_nr] + belt_dir_nr;
6942 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6944 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6946 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6949 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6951 static int belt_base_element[4] =
6953 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6954 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6955 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6956 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6959 return belt_base_element[belt_nr] + belt_dir_nr;
6962 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6964 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6966 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6969 boolean getTeamMode_EM()
6971 return game.team_mode;
6974 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6976 int game_frame_delay_value;
6978 game_frame_delay_value =
6979 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6980 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6983 if (tape.playing && tape.warp_forward && !tape.pausing)
6984 game_frame_delay_value = 0;
6986 return game_frame_delay_value;
6989 unsigned int InitRND(int seed)
6991 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6992 return InitEngineRandom_EM(seed);
6993 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6994 return InitEngineRandom_SP(seed);
6996 return InitEngineRandom_RND(seed);
6999 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7000 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7002 inline static int get_effective_element_EM(int tile, int frame_em)
7004 int element = object_mapping[tile].element_rnd;
7005 int action = object_mapping[tile].action;
7006 boolean is_backside = object_mapping[tile].is_backside;
7007 boolean action_removing = (action == ACTION_DIGGING ||
7008 action == ACTION_SNAPPING ||
7009 action == ACTION_COLLECTING);
7015 case Yacid_splash_eB:
7016 case Yacid_splash_wB:
7017 return (frame_em > 5 ? EL_EMPTY : element);
7023 else /* frame_em == 7 */
7027 case Yacid_splash_eB:
7028 case Yacid_splash_wB:
7031 case Yemerald_stone:
7034 case Ydiamond_stone:
7038 case Xdrip_stretchB:
7057 case Xsand_stonein_1:
7058 case Xsand_stonein_2:
7059 case Xsand_stonein_3:
7060 case Xsand_stonein_4:
7064 return (is_backside || action_removing ? EL_EMPTY : element);
7069 inline static boolean check_linear_animation_EM(int tile)
7073 case Xsand_stonesand_1:
7074 case Xsand_stonesand_quickout_1:
7075 case Xsand_sandstone_1:
7076 case Xsand_stonein_1:
7077 case Xsand_stoneout_1:
7096 case Yacid_splash_eB:
7097 case Yacid_splash_wB:
7098 case Yemerald_stone:
7105 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7106 boolean has_crumbled_graphics,
7107 int crumbled, int sync_frame)
7109 /* if element can be crumbled, but certain action graphics are just empty
7110 space (like instantly snapping sand to empty space in 1 frame), do not
7111 treat these empty space graphics as crumbled graphics in EMC engine */
7112 if (crumbled == IMG_EMPTY_SPACE)
7113 has_crumbled_graphics = FALSE;
7115 if (has_crumbled_graphics)
7117 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7118 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7119 g_crumbled->anim_delay,
7120 g_crumbled->anim_mode,
7121 g_crumbled->anim_start_frame,
7124 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7125 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7127 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7129 g_em->has_crumbled_graphics = TRUE;
7133 g_em->crumbled_bitmap = NULL;
7134 g_em->crumbled_src_x = 0;
7135 g_em->crumbled_src_y = 0;
7136 g_em->crumbled_border_size = 0;
7138 g_em->has_crumbled_graphics = FALSE;
7142 void ResetGfxAnimation_EM(int x, int y, int tile)
7147 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7148 int tile, int frame_em, int x, int y)
7150 int action = object_mapping[tile].action;
7151 int direction = object_mapping[tile].direction;
7152 int effective_element = get_effective_element_EM(tile, frame_em);
7153 int graphic = (direction == MV_NONE ?
7154 el_act2img(effective_element, action) :
7155 el_act_dir2img(effective_element, action, direction));
7156 struct GraphicInfo *g = &graphic_info[graphic];
7158 boolean action_removing = (action == ACTION_DIGGING ||
7159 action == ACTION_SNAPPING ||
7160 action == ACTION_COLLECTING);
7161 boolean action_moving = (action == ACTION_FALLING ||
7162 action == ACTION_MOVING ||
7163 action == ACTION_PUSHING ||
7164 action == ACTION_EATING ||
7165 action == ACTION_FILLING ||
7166 action == ACTION_EMPTYING);
7167 boolean action_falling = (action == ACTION_FALLING ||
7168 action == ACTION_FILLING ||
7169 action == ACTION_EMPTYING);
7171 /* special case: graphic uses "2nd movement tile" and has defined
7172 7 frames for movement animation (or less) => use default graphic
7173 for last (8th) frame which ends the movement animation */
7174 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7176 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7177 graphic = (direction == MV_NONE ?
7178 el_act2img(effective_element, action) :
7179 el_act_dir2img(effective_element, action, direction));
7181 g = &graphic_info[graphic];
7184 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7188 else if (action_moving)
7190 boolean is_backside = object_mapping[tile].is_backside;
7194 int direction = object_mapping[tile].direction;
7195 int move_dir = (action_falling ? MV_DOWN : direction);
7200 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7201 if (g->double_movement && frame_em == 0)
7205 if (move_dir == MV_LEFT)
7206 GfxFrame[x - 1][y] = GfxFrame[x][y];
7207 else if (move_dir == MV_RIGHT)
7208 GfxFrame[x + 1][y] = GfxFrame[x][y];
7209 else if (move_dir == MV_UP)
7210 GfxFrame[x][y - 1] = GfxFrame[x][y];
7211 else if (move_dir == MV_DOWN)
7212 GfxFrame[x][y + 1] = GfxFrame[x][y];
7219 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7220 if (tile == Xsand_stonesand_quickout_1 ||
7221 tile == Xsand_stonesand_quickout_2)
7225 if (graphic_info[graphic].anim_global_sync)
7226 sync_frame = FrameCounter;
7227 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7228 sync_frame = GfxFrame[x][y];
7230 sync_frame = 0; /* playfield border (pseudo steel) */
7232 SetRandomAnimationValue(x, y);
7234 int frame = getAnimationFrame(g->anim_frames,
7237 g->anim_start_frame,
7240 g_em->unique_identifier =
7241 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7244 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7245 int tile, int frame_em, int x, int y)
7247 int action = object_mapping[tile].action;
7248 int direction = object_mapping[tile].direction;
7249 boolean is_backside = object_mapping[tile].is_backside;
7250 int effective_element = get_effective_element_EM(tile, frame_em);
7251 int effective_action = action;
7252 int graphic = (direction == MV_NONE ?
7253 el_act2img(effective_element, effective_action) :
7254 el_act_dir2img(effective_element, effective_action,
7256 int crumbled = (direction == MV_NONE ?
7257 el_act2crm(effective_element, effective_action) :
7258 el_act_dir2crm(effective_element, effective_action,
7260 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7261 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7262 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7263 struct GraphicInfo *g = &graphic_info[graphic];
7266 /* special case: graphic uses "2nd movement tile" and has defined
7267 7 frames for movement animation (or less) => use default graphic
7268 for last (8th) frame which ends the movement animation */
7269 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7271 effective_action = ACTION_DEFAULT;
7272 graphic = (direction == MV_NONE ?
7273 el_act2img(effective_element, effective_action) :
7274 el_act_dir2img(effective_element, effective_action,
7276 crumbled = (direction == MV_NONE ?
7277 el_act2crm(effective_element, effective_action) :
7278 el_act_dir2crm(effective_element, effective_action,
7281 g = &graphic_info[graphic];
7284 if (graphic_info[graphic].anim_global_sync)
7285 sync_frame = FrameCounter;
7286 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7287 sync_frame = GfxFrame[x][y];
7289 sync_frame = 0; /* playfield border (pseudo steel) */
7291 SetRandomAnimationValue(x, y);
7293 int frame = getAnimationFrame(g->anim_frames,
7296 g->anim_start_frame,
7299 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7300 g->double_movement && is_backside);
7302 /* (updating the "crumbled" graphic definitions is probably not really needed,
7303 as animations for crumbled graphics can't be longer than one EMC cycle) */
7304 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7308 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7309 int player_nr, int anim, int frame_em)
7311 int element = player_mapping[player_nr][anim].element_rnd;
7312 int action = player_mapping[player_nr][anim].action;
7313 int direction = player_mapping[player_nr][anim].direction;
7314 int graphic = (direction == MV_NONE ?
7315 el_act2img(element, action) :
7316 el_act_dir2img(element, action, direction));
7317 struct GraphicInfo *g = &graphic_info[graphic];
7320 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7322 stored_player[player_nr].StepFrame = frame_em;
7324 sync_frame = stored_player[player_nr].Frame;
7326 int frame = getAnimationFrame(g->anim_frames,
7329 g->anim_start_frame,
7332 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7333 &g_em->src_x, &g_em->src_y, FALSE);
7336 void InitGraphicInfo_EM(void)
7341 int num_em_gfx_errors = 0;
7343 if (graphic_info_em_object[0][0].bitmap == NULL)
7345 /* EM graphics not yet initialized in em_open_all() */
7350 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7353 /* always start with reliable default values */
7354 for (i = 0; i < TILE_MAX; i++)
7356 object_mapping[i].element_rnd = EL_UNKNOWN;
7357 object_mapping[i].is_backside = FALSE;
7358 object_mapping[i].action = ACTION_DEFAULT;
7359 object_mapping[i].direction = MV_NONE;
7362 /* always start with reliable default values */
7363 for (p = 0; p < MAX_PLAYERS; p++)
7365 for (i = 0; i < SPR_MAX; i++)
7367 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7368 player_mapping[p][i].action = ACTION_DEFAULT;
7369 player_mapping[p][i].direction = MV_NONE;
7373 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7375 int e = em_object_mapping_list[i].element_em;
7377 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7378 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7380 if (em_object_mapping_list[i].action != -1)
7381 object_mapping[e].action = em_object_mapping_list[i].action;
7383 if (em_object_mapping_list[i].direction != -1)
7384 object_mapping[e].direction =
7385 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7388 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7390 int a = em_player_mapping_list[i].action_em;
7391 int p = em_player_mapping_list[i].player_nr;
7393 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7395 if (em_player_mapping_list[i].action != -1)
7396 player_mapping[p][a].action = em_player_mapping_list[i].action;
7398 if (em_player_mapping_list[i].direction != -1)
7399 player_mapping[p][a].direction =
7400 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7403 for (i = 0; i < TILE_MAX; i++)
7405 int element = object_mapping[i].element_rnd;
7406 int action = object_mapping[i].action;
7407 int direction = object_mapping[i].direction;
7408 boolean is_backside = object_mapping[i].is_backside;
7409 boolean action_exploding = ((action == ACTION_EXPLODING ||
7410 action == ACTION_SMASHED_BY_ROCK ||
7411 action == ACTION_SMASHED_BY_SPRING) &&
7412 element != EL_DIAMOND);
7413 boolean action_active = (action == ACTION_ACTIVE);
7414 boolean action_other = (action == ACTION_OTHER);
7416 for (j = 0; j < 8; j++)
7418 int effective_element = get_effective_element_EM(i, j);
7419 int effective_action = (j < 7 ? action :
7420 i == Xdrip_stretch ? action :
7421 i == Xdrip_stretchB ? action :
7422 i == Ydrip_s1 ? action :
7423 i == Ydrip_s1B ? action :
7424 i == Xball_1B ? action :
7425 i == Xball_2 ? action :
7426 i == Xball_2B ? action :
7427 i == Yball_eat ? action :
7428 i == Ykey_1_eat ? action :
7429 i == Ykey_2_eat ? action :
7430 i == Ykey_3_eat ? action :
7431 i == Ykey_4_eat ? action :
7432 i == Ykey_5_eat ? action :
7433 i == Ykey_6_eat ? action :
7434 i == Ykey_7_eat ? action :
7435 i == Ykey_8_eat ? action :
7436 i == Ylenses_eat ? action :
7437 i == Ymagnify_eat ? action :
7438 i == Ygrass_eat ? action :
7439 i == Ydirt_eat ? action :
7440 i == Xsand_stonein_1 ? action :
7441 i == Xsand_stonein_2 ? action :
7442 i == Xsand_stonein_3 ? action :
7443 i == Xsand_stonein_4 ? action :
7444 i == Xsand_stoneout_1 ? action :
7445 i == Xsand_stoneout_2 ? action :
7446 i == Xboom_android ? ACTION_EXPLODING :
7447 action_exploding ? ACTION_EXPLODING :
7448 action_active ? action :
7449 action_other ? action :
7451 int graphic = (el_act_dir2img(effective_element, effective_action,
7453 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7455 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7456 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7457 boolean has_action_graphics = (graphic != base_graphic);
7458 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7459 struct GraphicInfo *g = &graphic_info[graphic];
7460 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7463 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7464 boolean special_animation = (action != ACTION_DEFAULT &&
7465 g->anim_frames == 3 &&
7466 g->anim_delay == 2 &&
7467 g->anim_mode & ANIM_LINEAR);
7468 int sync_frame = (i == Xdrip_stretch ? 7 :
7469 i == Xdrip_stretchB ? 7 :
7470 i == Ydrip_s2 ? j + 8 :
7471 i == Ydrip_s2B ? j + 8 :
7480 i == Xfake_acid_1 ? 0 :
7481 i == Xfake_acid_2 ? 10 :
7482 i == Xfake_acid_3 ? 20 :
7483 i == Xfake_acid_4 ? 30 :
7484 i == Xfake_acid_5 ? 40 :
7485 i == Xfake_acid_6 ? 50 :
7486 i == Xfake_acid_7 ? 60 :
7487 i == Xfake_acid_8 ? 70 :
7489 i == Xball_2B ? j + 8 :
7490 i == Yball_eat ? j + 1 :
7491 i == Ykey_1_eat ? j + 1 :
7492 i == Ykey_2_eat ? j + 1 :
7493 i == Ykey_3_eat ? j + 1 :
7494 i == Ykey_4_eat ? j + 1 :
7495 i == Ykey_5_eat ? j + 1 :
7496 i == Ykey_6_eat ? j + 1 :
7497 i == Ykey_7_eat ? j + 1 :
7498 i == Ykey_8_eat ? j + 1 :
7499 i == Ylenses_eat ? j + 1 :
7500 i == Ymagnify_eat ? j + 1 :
7501 i == Ygrass_eat ? j + 1 :
7502 i == Ydirt_eat ? j + 1 :
7503 i == Xamoeba_1 ? 0 :
7504 i == Xamoeba_2 ? 1 :
7505 i == Xamoeba_3 ? 2 :
7506 i == Xamoeba_4 ? 3 :
7507 i == Xamoeba_5 ? 0 :
7508 i == Xamoeba_6 ? 1 :
7509 i == Xamoeba_7 ? 2 :
7510 i == Xamoeba_8 ? 3 :
7511 i == Xexit_2 ? j + 8 :
7512 i == Xexit_3 ? j + 16 :
7513 i == Xdynamite_1 ? 0 :
7514 i == Xdynamite_2 ? 8 :
7515 i == Xdynamite_3 ? 16 :
7516 i == Xdynamite_4 ? 24 :
7517 i == Xsand_stonein_1 ? j + 1 :
7518 i == Xsand_stonein_2 ? j + 9 :
7519 i == Xsand_stonein_3 ? j + 17 :
7520 i == Xsand_stonein_4 ? j + 25 :
7521 i == Xsand_stoneout_1 && j == 0 ? 0 :
7522 i == Xsand_stoneout_1 && j == 1 ? 0 :
7523 i == Xsand_stoneout_1 && j == 2 ? 1 :
7524 i == Xsand_stoneout_1 && j == 3 ? 2 :
7525 i == Xsand_stoneout_1 && j == 4 ? 2 :
7526 i == Xsand_stoneout_1 && j == 5 ? 3 :
7527 i == Xsand_stoneout_1 && j == 6 ? 4 :
7528 i == Xsand_stoneout_1 && j == 7 ? 4 :
7529 i == Xsand_stoneout_2 && j == 0 ? 5 :
7530 i == Xsand_stoneout_2 && j == 1 ? 6 :
7531 i == Xsand_stoneout_2 && j == 2 ? 7 :
7532 i == Xsand_stoneout_2 && j == 3 ? 8 :
7533 i == Xsand_stoneout_2 && j == 4 ? 9 :
7534 i == Xsand_stoneout_2 && j == 5 ? 11 :
7535 i == Xsand_stoneout_2 && j == 6 ? 13 :
7536 i == Xsand_stoneout_2 && j == 7 ? 15 :
7537 i == Xboom_bug && j == 1 ? 2 :
7538 i == Xboom_bug && j == 2 ? 2 :
7539 i == Xboom_bug && j == 3 ? 4 :
7540 i == Xboom_bug && j == 4 ? 4 :
7541 i == Xboom_bug && j == 5 ? 2 :
7542 i == Xboom_bug && j == 6 ? 2 :
7543 i == Xboom_bug && j == 7 ? 0 :
7544 i == Xboom_bomb && j == 1 ? 2 :
7545 i == Xboom_bomb && j == 2 ? 2 :
7546 i == Xboom_bomb && j == 3 ? 4 :
7547 i == Xboom_bomb && j == 4 ? 4 :
7548 i == Xboom_bomb && j == 5 ? 2 :
7549 i == Xboom_bomb && j == 6 ? 2 :
7550 i == Xboom_bomb && j == 7 ? 0 :
7551 i == Xboom_android && j == 7 ? 6 :
7552 i == Xboom_1 && j == 1 ? 2 :
7553 i == Xboom_1 && j == 2 ? 2 :
7554 i == Xboom_1 && j == 3 ? 4 :
7555 i == Xboom_1 && j == 4 ? 4 :
7556 i == Xboom_1 && j == 5 ? 6 :
7557 i == Xboom_1 && j == 6 ? 6 :
7558 i == Xboom_1 && j == 7 ? 8 :
7559 i == Xboom_2 && j == 0 ? 8 :
7560 i == Xboom_2 && j == 1 ? 8 :
7561 i == Xboom_2 && j == 2 ? 10 :
7562 i == Xboom_2 && j == 3 ? 10 :
7563 i == Xboom_2 && j == 4 ? 10 :
7564 i == Xboom_2 && j == 5 ? 12 :
7565 i == Xboom_2 && j == 6 ? 12 :
7566 i == Xboom_2 && j == 7 ? 12 :
7567 special_animation && j == 4 ? 3 :
7568 effective_action != action ? 0 :
7572 Bitmap *debug_bitmap = g_em->bitmap;
7573 int debug_src_x = g_em->src_x;
7574 int debug_src_y = g_em->src_y;
7577 int frame = getAnimationFrame(g->anim_frames,
7580 g->anim_start_frame,
7583 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7584 g->double_movement && is_backside);
7586 g_em->bitmap = src_bitmap;
7587 g_em->src_x = src_x;
7588 g_em->src_y = src_y;
7589 g_em->src_offset_x = 0;
7590 g_em->src_offset_y = 0;
7591 g_em->dst_offset_x = 0;
7592 g_em->dst_offset_y = 0;
7593 g_em->width = TILEX;
7594 g_em->height = TILEY;
7596 g_em->preserve_background = FALSE;
7598 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7601 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7602 effective_action == ACTION_MOVING ||
7603 effective_action == ACTION_PUSHING ||
7604 effective_action == ACTION_EATING)) ||
7605 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7606 effective_action == ACTION_EMPTYING)))
7609 (effective_action == ACTION_FALLING ||
7610 effective_action == ACTION_FILLING ||
7611 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7612 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7613 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7614 int num_steps = (i == Ydrip_s1 ? 16 :
7615 i == Ydrip_s1B ? 16 :
7616 i == Ydrip_s2 ? 16 :
7617 i == Ydrip_s2B ? 16 :
7618 i == Xsand_stonein_1 ? 32 :
7619 i == Xsand_stonein_2 ? 32 :
7620 i == Xsand_stonein_3 ? 32 :
7621 i == Xsand_stonein_4 ? 32 :
7622 i == Xsand_stoneout_1 ? 16 :
7623 i == Xsand_stoneout_2 ? 16 : 8);
7624 int cx = ABS(dx) * (TILEX / num_steps);
7625 int cy = ABS(dy) * (TILEY / num_steps);
7626 int step_frame = (i == Ydrip_s2 ? j + 8 :
7627 i == Ydrip_s2B ? j + 8 :
7628 i == Xsand_stonein_2 ? j + 8 :
7629 i == Xsand_stonein_3 ? j + 16 :
7630 i == Xsand_stonein_4 ? j + 24 :
7631 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7632 int step = (is_backside ? step_frame : num_steps - step_frame);
7634 if (is_backside) /* tile where movement starts */
7636 if (dx < 0 || dy < 0)
7638 g_em->src_offset_x = cx * step;
7639 g_em->src_offset_y = cy * step;
7643 g_em->dst_offset_x = cx * step;
7644 g_em->dst_offset_y = cy * step;
7647 else /* tile where movement ends */
7649 if (dx < 0 || dy < 0)
7651 g_em->dst_offset_x = cx * step;
7652 g_em->dst_offset_y = cy * step;
7656 g_em->src_offset_x = cx * step;
7657 g_em->src_offset_y = cy * step;
7661 g_em->width = TILEX - cx * step;
7662 g_em->height = TILEY - cy * step;
7665 /* create unique graphic identifier to decide if tile must be redrawn */
7666 /* bit 31 - 16 (16 bit): EM style graphic
7667 bit 15 - 12 ( 4 bit): EM style frame
7668 bit 11 - 6 ( 6 bit): graphic width
7669 bit 5 - 0 ( 6 bit): graphic height */
7670 g_em->unique_identifier =
7671 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7675 /* skip check for EMC elements not contained in original EMC artwork */
7676 if (element == EL_EMC_FAKE_ACID)
7679 if (g_em->bitmap != debug_bitmap ||
7680 g_em->src_x != debug_src_x ||
7681 g_em->src_y != debug_src_y ||
7682 g_em->src_offset_x != 0 ||
7683 g_em->src_offset_y != 0 ||
7684 g_em->dst_offset_x != 0 ||
7685 g_em->dst_offset_y != 0 ||
7686 g_em->width != TILEX ||
7687 g_em->height != TILEY)
7689 static int last_i = -1;
7697 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7698 i, element, element_info[element].token_name,
7699 element_action_info[effective_action].suffix, direction);
7701 if (element != effective_element)
7702 printf(" [%d ('%s')]",
7704 element_info[effective_element].token_name);
7708 if (g_em->bitmap != debug_bitmap)
7709 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7710 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7712 if (g_em->src_x != debug_src_x ||
7713 g_em->src_y != debug_src_y)
7714 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7715 j, (is_backside ? 'B' : 'F'),
7716 g_em->src_x, g_em->src_y,
7717 g_em->src_x / 32, g_em->src_y / 32,
7718 debug_src_x, debug_src_y,
7719 debug_src_x / 32, debug_src_y / 32);
7721 if (g_em->src_offset_x != 0 ||
7722 g_em->src_offset_y != 0 ||
7723 g_em->dst_offset_x != 0 ||
7724 g_em->dst_offset_y != 0)
7725 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7727 g_em->src_offset_x, g_em->src_offset_y,
7728 g_em->dst_offset_x, g_em->dst_offset_y);
7730 if (g_em->width != TILEX ||
7731 g_em->height != TILEY)
7732 printf(" %d (%d): size %d,%d should be %d,%d\n",
7734 g_em->width, g_em->height, TILEX, TILEY);
7736 num_em_gfx_errors++;
7743 for (i = 0; i < TILE_MAX; i++)
7745 for (j = 0; j < 8; j++)
7747 int element = object_mapping[i].element_rnd;
7748 int action = object_mapping[i].action;
7749 int direction = object_mapping[i].direction;
7750 boolean is_backside = object_mapping[i].is_backside;
7751 int graphic_action = el_act_dir2img(element, action, direction);
7752 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7754 if ((action == ACTION_SMASHED_BY_ROCK ||
7755 action == ACTION_SMASHED_BY_SPRING ||
7756 action == ACTION_EATING) &&
7757 graphic_action == graphic_default)
7759 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7760 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7761 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7762 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7765 /* no separate animation for "smashed by rock" -- use rock instead */
7766 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7767 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7769 g_em->bitmap = g_xx->bitmap;
7770 g_em->src_x = g_xx->src_x;
7771 g_em->src_y = g_xx->src_y;
7772 g_em->src_offset_x = g_xx->src_offset_x;
7773 g_em->src_offset_y = g_xx->src_offset_y;
7774 g_em->dst_offset_x = g_xx->dst_offset_x;
7775 g_em->dst_offset_y = g_xx->dst_offset_y;
7776 g_em->width = g_xx->width;
7777 g_em->height = g_xx->height;
7778 g_em->unique_identifier = g_xx->unique_identifier;
7781 g_em->preserve_background = TRUE;
7786 for (p = 0; p < MAX_PLAYERS; p++)
7788 for (i = 0; i < SPR_MAX; i++)
7790 int element = player_mapping[p][i].element_rnd;
7791 int action = player_mapping[p][i].action;
7792 int direction = player_mapping[p][i].direction;
7794 for (j = 0; j < 8; j++)
7796 int effective_element = element;
7797 int effective_action = action;
7798 int graphic = (direction == MV_NONE ?
7799 el_act2img(effective_element, effective_action) :
7800 el_act_dir2img(effective_element, effective_action,
7802 struct GraphicInfo *g = &graphic_info[graphic];
7803 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7809 Bitmap *debug_bitmap = g_em->bitmap;
7810 int debug_src_x = g_em->src_x;
7811 int debug_src_y = g_em->src_y;
7814 int frame = getAnimationFrame(g->anim_frames,
7817 g->anim_start_frame,
7820 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7822 g_em->bitmap = src_bitmap;
7823 g_em->src_x = src_x;
7824 g_em->src_y = src_y;
7825 g_em->src_offset_x = 0;
7826 g_em->src_offset_y = 0;
7827 g_em->dst_offset_x = 0;
7828 g_em->dst_offset_y = 0;
7829 g_em->width = TILEX;
7830 g_em->height = TILEY;
7834 /* skip check for EMC elements not contained in original EMC artwork */
7835 if (element == EL_PLAYER_3 ||
7836 element == EL_PLAYER_4)
7839 if (g_em->bitmap != debug_bitmap ||
7840 g_em->src_x != debug_src_x ||
7841 g_em->src_y != debug_src_y)
7843 static int last_i = -1;
7851 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7852 p, i, element, element_info[element].token_name,
7853 element_action_info[effective_action].suffix, direction);
7855 if (element != effective_element)
7856 printf(" [%d ('%s')]",
7858 element_info[effective_element].token_name);
7862 if (g_em->bitmap != debug_bitmap)
7863 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7864 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7866 if (g_em->src_x != debug_src_x ||
7867 g_em->src_y != debug_src_y)
7868 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7870 g_em->src_x, g_em->src_y,
7871 g_em->src_x / 32, g_em->src_y / 32,
7872 debug_src_x, debug_src_y,
7873 debug_src_x / 32, debug_src_y / 32);
7875 num_em_gfx_errors++;
7885 printf("::: [%d errors found]\n", num_em_gfx_errors);
7891 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7892 boolean any_player_moving,
7893 boolean any_player_snapping,
7894 boolean any_player_dropping)
7896 static boolean player_was_waiting = TRUE;
7898 if (frame == 0 && !any_player_dropping)
7900 if (!player_was_waiting)
7902 if (!SaveEngineSnapshotToList())
7905 player_was_waiting = TRUE;
7908 else if (any_player_moving || any_player_snapping || any_player_dropping)
7910 player_was_waiting = FALSE;
7914 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7915 boolean murphy_is_dropping)
7917 static boolean player_was_waiting = TRUE;
7919 if (murphy_is_waiting)
7921 if (!player_was_waiting)
7923 if (!SaveEngineSnapshotToList())
7926 player_was_waiting = TRUE;
7931 player_was_waiting = FALSE;
7935 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7936 boolean any_player_moving,
7937 boolean any_player_snapping,
7938 boolean any_player_dropping)
7940 if (tape.single_step && tape.recording && !tape.pausing)
7941 if (frame == 0 && !any_player_dropping)
7942 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7944 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7945 any_player_snapping, any_player_dropping);
7948 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7949 boolean murphy_is_dropping)
7951 if (tape.single_step && tape.recording && !tape.pausing)
7952 if (murphy_is_waiting)
7953 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7955 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7958 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7959 int graphic, int sync_frame, int x, int y)
7961 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7963 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7966 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7968 return (IS_NEXT_FRAME(sync_frame, graphic));
7971 int getGraphicInfo_Delay(int graphic)
7973 return graphic_info[graphic].anim_delay;
7976 void PlayMenuSoundExt(int sound)
7978 if (sound == SND_UNDEFINED)
7981 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7982 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7985 if (IS_LOOP_SOUND(sound))
7986 PlaySoundLoop(sound);
7991 void PlayMenuSound()
7993 PlayMenuSoundExt(menu.sound[game_status]);
7996 void PlayMenuSoundStereo(int sound, int stereo_position)
7998 if (sound == SND_UNDEFINED)
8001 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8002 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8005 if (IS_LOOP_SOUND(sound))
8006 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8008 PlaySoundStereo(sound, stereo_position);
8011 void PlayMenuSoundIfLoopExt(int sound)
8013 if (sound == SND_UNDEFINED)
8016 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8017 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8020 if (IS_LOOP_SOUND(sound))
8021 PlaySoundLoop(sound);
8024 void PlayMenuSoundIfLoop()
8026 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8029 void PlayMenuMusicExt(int music)
8031 if (music == MUS_UNDEFINED)
8034 if (!setup.sound_music)
8040 void PlayMenuMusic()
8042 PlayMenuMusicExt(menu.music[game_status]);
8045 void PlaySoundActivating()
8048 PlaySound(SND_MENU_ITEM_ACTIVATING);
8052 void PlaySoundSelecting()
8055 PlaySound(SND_MENU_ITEM_SELECTING);
8059 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8061 boolean change_fullscreen = (setup.fullscreen !=
8062 video.fullscreen_enabled);
8063 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8064 !strEqual(setup.fullscreen_mode,
8065 video.fullscreen_mode_current));
8066 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8067 setup.window_scaling_percent !=
8068 video.window_scaling_percent);
8070 if (change_window_scaling_percent && video.fullscreen_enabled)
8073 if (!change_window_scaling_percent && !video.fullscreen_available)
8076 #if defined(TARGET_SDL2)
8077 if (change_window_scaling_percent)
8079 SDLSetWindowScaling(setup.window_scaling_percent);
8083 else if (change_fullscreen)
8085 SDLSetWindowFullscreen(setup.fullscreen);
8087 /* set setup value according to successfully changed fullscreen mode */
8088 setup.fullscreen = video.fullscreen_enabled;
8094 if (change_fullscreen ||
8095 change_fullscreen_mode ||
8096 change_window_scaling_percent)
8098 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8100 /* save backbuffer content which gets lost when toggling fullscreen mode */
8101 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8103 if (change_fullscreen_mode)
8105 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8106 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8109 if (change_window_scaling_percent)
8111 /* keep window mode, but change window scaling */
8112 video.fullscreen_enabled = TRUE; /* force new window scaling */
8115 /* toggle fullscreen */
8116 ChangeVideoModeIfNeeded(setup.fullscreen);
8118 /* set setup value according to successfully changed fullscreen mode */
8119 setup.fullscreen = video.fullscreen_enabled;
8121 /* restore backbuffer content from temporary backbuffer backup bitmap */
8122 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8124 FreeBitmap(tmp_backbuffer);
8126 /* update visible window/screen */
8127 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8131 void JoinRectangles(int *x, int *y, int *width, int *height,
8132 int x2, int y2, int width2, int height2)
8134 // do not join with "off-screen" rectangle
8135 if (x2 == -1 || y2 == -1)
8140 *width = MAX(*width, width2);
8141 *height = MAX(*height, height2);
8144 void ChangeViewportPropertiesIfNeeded()
8146 int gfx_game_mode = game_status;
8147 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8149 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8150 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8151 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8152 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8153 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8154 int new_win_xsize = vp_window->width;
8155 int new_win_ysize = vp_window->height;
8156 int border_size = vp_playfield->border_size;
8157 int new_sx = vp_playfield->x + border_size;
8158 int new_sy = vp_playfield->y + border_size;
8159 int new_sxsize = vp_playfield->width - 2 * border_size;
8160 int new_sysize = vp_playfield->height - 2 * border_size;
8161 int new_real_sx = vp_playfield->x;
8162 int new_real_sy = vp_playfield->y;
8163 int new_full_sxsize = vp_playfield->width;
8164 int new_full_sysize = vp_playfield->height;
8165 int new_dx = vp_door_1->x;
8166 int new_dy = vp_door_1->y;
8167 int new_dxsize = vp_door_1->width;
8168 int new_dysize = vp_door_1->height;
8169 int new_vx = vp_door_2->x;
8170 int new_vy = vp_door_2->y;
8171 int new_vxsize = vp_door_2->width;
8172 int new_vysize = vp_door_2->height;
8173 int new_ex = vp_door_3->x;
8174 int new_ey = vp_door_3->y;
8175 int new_exsize = vp_door_3->width;
8176 int new_eysize = vp_door_3->height;
8177 int new_tilesize_var =
8178 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8180 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8181 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8182 int new_scr_fieldx = new_sxsize / tilesize;
8183 int new_scr_fieldy = new_sysize / tilesize;
8184 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8185 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8186 boolean init_gfx_buffers = FALSE;
8187 boolean init_video_buffer = FALSE;
8188 boolean init_gadgets_and_toons = FALSE;
8189 boolean init_em_graphics = FALSE;
8191 if (new_win_xsize != WIN_XSIZE ||
8192 new_win_ysize != WIN_YSIZE)
8194 WIN_XSIZE = new_win_xsize;
8195 WIN_YSIZE = new_win_ysize;
8197 init_video_buffer = TRUE;
8198 init_gfx_buffers = TRUE;
8200 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8203 if (new_scr_fieldx != SCR_FIELDX ||
8204 new_scr_fieldy != SCR_FIELDY)
8206 /* this always toggles between MAIN and GAME when using small tile size */
8208 SCR_FIELDX = new_scr_fieldx;
8209 SCR_FIELDY = new_scr_fieldy;
8211 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8222 new_sxsize != SXSIZE ||
8223 new_sysize != SYSIZE ||
8224 new_dxsize != DXSIZE ||
8225 new_dysize != DYSIZE ||
8226 new_vxsize != VXSIZE ||
8227 new_vysize != VYSIZE ||
8228 new_exsize != EXSIZE ||
8229 new_eysize != EYSIZE ||
8230 new_real_sx != REAL_SX ||
8231 new_real_sy != REAL_SY ||
8232 new_full_sxsize != FULL_SXSIZE ||
8233 new_full_sysize != FULL_SYSIZE ||
8234 new_tilesize_var != TILESIZE_VAR
8237 // ------------------------------------------------------------------------
8238 // determine next fading area for changed viewport definitions
8239 // ------------------------------------------------------------------------
8241 // start with current playfield area (default fading area)
8244 FADE_SXSIZE = FULL_SXSIZE;
8245 FADE_SYSIZE = FULL_SYSIZE;
8247 // add new playfield area if position or size has changed
8248 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8249 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8251 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8252 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8255 // add current and new door 1 area if position or size has changed
8256 if (new_dx != DX || new_dy != DY ||
8257 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8259 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8260 DX, DY, DXSIZE, DYSIZE);
8261 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8262 new_dx, new_dy, new_dxsize, new_dysize);
8265 // add current and new door 2 area if position or size has changed
8266 if (new_dx != VX || new_dy != VY ||
8267 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8269 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8270 VX, VY, VXSIZE, VYSIZE);
8271 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8272 new_vx, new_vy, new_vxsize, new_vysize);
8275 // ------------------------------------------------------------------------
8276 // handle changed tile size
8277 // ------------------------------------------------------------------------
8279 if (new_tilesize_var != TILESIZE_VAR)
8281 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8283 // changing tile size invalidates scroll values of engine snapshots
8284 FreeEngineSnapshotSingle();
8286 // changing tile size requires update of graphic mapping for EM engine
8287 init_em_graphics = TRUE;
8298 SXSIZE = new_sxsize;
8299 SYSIZE = new_sysize;
8300 DXSIZE = new_dxsize;
8301 DYSIZE = new_dysize;
8302 VXSIZE = new_vxsize;
8303 VYSIZE = new_vysize;
8304 EXSIZE = new_exsize;
8305 EYSIZE = new_eysize;
8306 REAL_SX = new_real_sx;
8307 REAL_SY = new_real_sy;
8308 FULL_SXSIZE = new_full_sxsize;
8309 FULL_SYSIZE = new_full_sysize;
8310 TILESIZE_VAR = new_tilesize_var;
8312 init_gfx_buffers = TRUE;
8313 init_gadgets_and_toons = TRUE;
8315 // printf("::: viewports: init_gfx_buffers\n");
8316 // printf("::: viewports: init_gadgets_and_toons\n");
8319 if (init_gfx_buffers)
8321 // printf("::: init_gfx_buffers\n");
8323 SCR_FIELDX = new_scr_fieldx_buffers;
8324 SCR_FIELDY = new_scr_fieldy_buffers;
8328 SCR_FIELDX = new_scr_fieldx;
8329 SCR_FIELDY = new_scr_fieldy;
8331 SetDrawDeactivationMask(REDRAW_NONE);
8332 SetDrawBackgroundMask(REDRAW_FIELD);
8335 if (init_video_buffer)
8337 // printf("::: init_video_buffer\n");
8339 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8342 if (init_gadgets_and_toons)
8344 // printf("::: init_gadgets_and_toons\n");
8350 if (init_em_graphics)
8352 InitGraphicInfo_EM();