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 / TILESIZE;
1069 *y = src_y * tilesize / TILESIZE;
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;
1132 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1134 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1137 void DrawGraphic(int x, int y, int graphic, int frame)
1140 if (!IN_SCR_FIELD(x, y))
1142 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1143 printf("DrawGraphic(): This should never happen!\n");
1148 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1151 MarkTileDirty(x, y);
1154 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1157 if (!IN_SCR_FIELD(x, y))
1159 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1160 printf("DrawGraphic(): This should never happen!\n");
1165 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1167 MarkTileDirty(x, y);
1170 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1176 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1178 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1181 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1187 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1188 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1191 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1194 if (!IN_SCR_FIELD(x, y))
1196 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1197 printf("DrawGraphicThruMask(): This should never happen!\n");
1202 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1205 MarkTileDirty(x, y);
1208 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1211 if (!IN_SCR_FIELD(x, y))
1213 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1214 printf("DrawGraphicThruMask(): This should never happen!\n");
1219 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1221 MarkTileDirty(x, y);
1224 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1230 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1232 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1236 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1237 int graphic, int frame)
1239 struct GraphicInfo *g = &graphic_info[graphic];
1243 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1245 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1249 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1251 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1253 MarkTileDirty(x / tilesize, y / tilesize);
1256 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1262 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1263 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1266 void DrawMiniGraphic(int x, int y, int graphic)
1268 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1269 MarkTileDirty(x / 2, y / 2);
1272 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1277 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1278 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1281 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1282 int graphic, int frame,
1283 int cut_mode, int mask_mode)
1288 int width = TILEX, height = TILEY;
1291 if (dx || dy) /* shifted graphic */
1293 if (x < BX1) /* object enters playfield from the left */
1300 else if (x > BX2) /* object enters playfield from the right */
1306 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1312 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1314 else if (dx) /* general horizontal movement */
1315 MarkTileDirty(x + SIGN(dx), y);
1317 if (y < BY1) /* object enters playfield from the top */
1319 if (cut_mode == CUT_BELOW) /* object completely above top border */
1327 else if (y > BY2) /* object enters playfield from the bottom */
1333 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1339 else if (dy > 0 && cut_mode == CUT_ABOVE)
1341 if (y == BY2) /* object completely above bottom border */
1347 MarkTileDirty(x, y + 1);
1348 } /* object leaves playfield to the bottom */
1349 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1351 else if (dy) /* general vertical movement */
1352 MarkTileDirty(x, y + SIGN(dy));
1356 if (!IN_SCR_FIELD(x, y))
1358 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1359 printf("DrawGraphicShifted(): This should never happen!\n");
1364 width = width * TILESIZE_VAR / TILESIZE;
1365 height = height * TILESIZE_VAR / TILESIZE;
1366 cx = cx * TILESIZE_VAR / TILESIZE;
1367 cy = cy * TILESIZE_VAR / TILESIZE;
1368 dx = dx * TILESIZE_VAR / TILESIZE;
1369 dy = dy * TILESIZE_VAR / TILESIZE;
1371 if (width > 0 && height > 0)
1373 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1378 dst_x = FX + x * TILEX_VAR + dx;
1379 dst_y = FY + y * TILEY_VAR + dy;
1381 if (mask_mode == USE_MASKING)
1382 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1385 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1388 MarkTileDirty(x, y);
1392 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1393 int graphic, int frame,
1394 int cut_mode, int mask_mode)
1399 int width = TILEX_VAR, height = TILEY_VAR;
1402 int x2 = x + SIGN(dx);
1403 int y2 = y + SIGN(dy);
1405 /* movement with two-tile animations must be sync'ed with movement position,
1406 not with current GfxFrame (which can be higher when using slow movement) */
1407 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1408 int anim_frames = graphic_info[graphic].anim_frames;
1410 /* (we also need anim_delay here for movement animations with less frames) */
1411 int anim_delay = graphic_info[graphic].anim_delay;
1412 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1414 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1415 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1417 /* re-calculate animation frame for two-tile movement animation */
1418 frame = getGraphicAnimationFrame(graphic, sync_frame);
1420 /* check if movement start graphic inside screen area and should be drawn */
1421 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1423 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1425 dst_x = FX + x1 * TILEX_VAR;
1426 dst_y = FY + y1 * TILEY_VAR;
1428 if (mask_mode == USE_MASKING)
1429 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1432 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1435 MarkTileDirty(x1, y1);
1438 /* check if movement end graphic inside screen area and should be drawn */
1439 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1441 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1443 dst_x = FX + x2 * TILEX_VAR;
1444 dst_y = FY + y2 * TILEY_VAR;
1446 if (mask_mode == USE_MASKING)
1447 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1450 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1453 MarkTileDirty(x2, y2);
1457 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1458 int graphic, int frame,
1459 int cut_mode, int mask_mode)
1463 DrawGraphic(x, y, graphic, frame);
1468 if (graphic_info[graphic].double_movement) /* EM style movement images */
1469 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1471 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1474 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1475 int frame, int cut_mode)
1477 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1480 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1481 int cut_mode, int mask_mode)
1483 int lx = LEVELX(x), ly = LEVELY(y);
1487 if (IN_LEV_FIELD(lx, ly))
1489 SetRandomAnimationValue(lx, ly);
1491 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1492 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1494 /* do not use double (EM style) movement graphic when not moving */
1495 if (graphic_info[graphic].double_movement && !dx && !dy)
1497 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1498 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1501 else /* border element */
1503 graphic = el2img(element);
1504 frame = getGraphicAnimationFrame(graphic, -1);
1507 if (element == EL_EXPANDABLE_WALL)
1509 boolean left_stopped = FALSE, right_stopped = FALSE;
1511 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1512 left_stopped = TRUE;
1513 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1514 right_stopped = TRUE;
1516 if (left_stopped && right_stopped)
1518 else if (left_stopped)
1520 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1521 frame = graphic_info[graphic].anim_frames - 1;
1523 else if (right_stopped)
1525 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1526 frame = graphic_info[graphic].anim_frames - 1;
1531 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1532 else if (mask_mode == USE_MASKING)
1533 DrawGraphicThruMask(x, y, graphic, frame);
1535 DrawGraphic(x, y, graphic, frame);
1538 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1539 int cut_mode, int mask_mode)
1541 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1542 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1543 cut_mode, mask_mode);
1546 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1549 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1552 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1555 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1558 void DrawLevelElementThruMask(int x, int y, int element)
1560 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1563 void DrawLevelFieldThruMask(int x, int y)
1565 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1568 /* !!! implementation of quicksand is totally broken !!! */
1569 #define IS_CRUMBLED_TILE(x, y, e) \
1570 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1571 !IS_MOVING(x, y) || \
1572 (e) == EL_QUICKSAND_EMPTYING || \
1573 (e) == EL_QUICKSAND_FAST_EMPTYING))
1575 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1580 int width, height, cx, cy;
1581 int sx = SCREENX(x), sy = SCREENY(y);
1582 int crumbled_border_size = graphic_info[graphic].border_size;
1585 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1587 for (i = 1; i < 4; i++)
1589 int dxx = (i & 1 ? dx : 0);
1590 int dyy = (i & 2 ? dy : 0);
1593 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1596 /* check if neighbour field is of same crumble type */
1597 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1598 graphic_info[graphic].class ==
1599 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1601 /* return if check prevents inner corner */
1602 if (same == (dxx == dx && dyy == dy))
1606 /* if we reach this point, we have an inner corner */
1608 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1610 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1611 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1612 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1613 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1615 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1616 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1619 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1624 int width, height, bx, by, cx, cy;
1625 int sx = SCREENX(x), sy = SCREENY(y);
1626 int crumbled_border_size = graphic_info[graphic].border_size;
1627 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1628 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1631 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1633 /* draw simple, sloppy, non-corner-accurate crumbled border */
1635 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1636 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1637 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1638 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1640 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1641 FX + sx * TILEX_VAR + cx,
1642 FY + sy * TILEY_VAR + cy);
1644 /* (remaining middle border part must be at least as big as corner part) */
1645 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1646 crumbled_border_size >= TILESIZE / 3)
1649 /* correct corners of crumbled border, if needed */
1651 for (i = -1; i <= 1; i += 2)
1653 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1654 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1655 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1658 /* check if neighbour field is of same crumble type */
1659 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1660 graphic_info[graphic].class ==
1661 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1663 /* no crumbled corner, but continued crumbled border */
1665 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1666 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1667 int b1 = (i == 1 ? crumbled_border_size_var :
1668 TILESIZE_VAR - 2 * crumbled_border_size_var);
1670 width = crumbled_border_size_var;
1671 height = crumbled_border_size_var;
1673 if (dir == 1 || dir == 2)
1688 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1690 FX + sx * TILEX_VAR + cx,
1691 FY + sy * TILEY_VAR + cy);
1696 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1698 int sx = SCREENX(x), sy = SCREENY(y);
1701 static int xy[4][2] =
1709 if (!IN_LEV_FIELD(x, y))
1712 element = TILE_GFX_ELEMENT(x, y);
1714 /* crumble field itself */
1715 if (IS_CRUMBLED_TILE(x, y, element))
1717 if (!IN_SCR_FIELD(sx, sy))
1720 for (i = 0; i < 4; i++)
1722 int xx = x + xy[i][0];
1723 int yy = y + xy[i][1];
1725 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1728 /* check if neighbour field is of same crumble type */
1729 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1730 graphic_info[graphic].class ==
1731 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1734 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1737 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1738 graphic_info[graphic].anim_frames == 2)
1740 for (i = 0; i < 4; i++)
1742 int dx = (i & 1 ? +1 : -1);
1743 int dy = (i & 2 ? +1 : -1);
1745 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1749 MarkTileDirty(sx, sy);
1751 else /* center field not crumbled -- crumble neighbour fields */
1753 for (i = 0; i < 4; i++)
1755 int xx = x + xy[i][0];
1756 int yy = y + xy[i][1];
1757 int sxx = sx + xy[i][0];
1758 int syy = sy + xy[i][1];
1760 if (!IN_LEV_FIELD(xx, yy) ||
1761 !IN_SCR_FIELD(sxx, syy))
1764 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1767 element = TILE_GFX_ELEMENT(xx, yy);
1769 if (!IS_CRUMBLED_TILE(xx, yy, element))
1772 graphic = el_act2crm(element, ACTION_DEFAULT);
1774 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1776 MarkTileDirty(sxx, syy);
1781 void DrawLevelFieldCrumbled(int x, int y)
1785 if (!IN_LEV_FIELD(x, y))
1788 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1789 GfxElement[x][y] != EL_UNDEFINED &&
1790 GFX_CRUMBLED(GfxElement[x][y]))
1792 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1797 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1799 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1802 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1805 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1806 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1807 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1808 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1809 int sx = SCREENX(x), sy = SCREENY(y);
1811 DrawGraphic(sx, sy, graphic1, frame1);
1812 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1815 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1817 int sx = SCREENX(x), sy = SCREENY(y);
1818 static int xy[4][2] =
1827 for (i = 0; i < 4; i++)
1829 int xx = x + xy[i][0];
1830 int yy = y + xy[i][1];
1831 int sxx = sx + xy[i][0];
1832 int syy = sy + xy[i][1];
1834 if (!IN_LEV_FIELD(xx, yy) ||
1835 !IN_SCR_FIELD(sxx, syy) ||
1836 !GFX_CRUMBLED(Feld[xx][yy]) ||
1840 DrawLevelField(xx, yy);
1844 static int getBorderElement(int x, int y)
1848 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1849 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1850 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1851 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1852 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1853 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1854 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1856 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1857 int steel_position = (x == -1 && y == -1 ? 0 :
1858 x == lev_fieldx && y == -1 ? 1 :
1859 x == -1 && y == lev_fieldy ? 2 :
1860 x == lev_fieldx && y == lev_fieldy ? 3 :
1861 x == -1 || x == lev_fieldx ? 4 :
1862 y == -1 || y == lev_fieldy ? 5 : 6);
1864 return border[steel_position][steel_type];
1867 void DrawScreenElement(int x, int y, int element)
1869 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1870 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1873 void DrawLevelElement(int x, int y, int element)
1875 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1876 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1879 void DrawScreenField(int x, int y)
1881 int lx = LEVELX(x), ly = LEVELY(y);
1882 int element, content;
1884 if (!IN_LEV_FIELD(lx, ly))
1886 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1889 element = getBorderElement(lx, ly);
1891 DrawScreenElement(x, y, element);
1896 element = Feld[lx][ly];
1897 content = Store[lx][ly];
1899 if (IS_MOVING(lx, ly))
1901 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1902 boolean cut_mode = NO_CUTTING;
1904 if (element == EL_QUICKSAND_EMPTYING ||
1905 element == EL_QUICKSAND_FAST_EMPTYING ||
1906 element == EL_MAGIC_WALL_EMPTYING ||
1907 element == EL_BD_MAGIC_WALL_EMPTYING ||
1908 element == EL_DC_MAGIC_WALL_EMPTYING ||
1909 element == EL_AMOEBA_DROPPING)
1910 cut_mode = CUT_ABOVE;
1911 else if (element == EL_QUICKSAND_FILLING ||
1912 element == EL_QUICKSAND_FAST_FILLING ||
1913 element == EL_MAGIC_WALL_FILLING ||
1914 element == EL_BD_MAGIC_WALL_FILLING ||
1915 element == EL_DC_MAGIC_WALL_FILLING)
1916 cut_mode = CUT_BELOW;
1918 if (cut_mode == CUT_ABOVE)
1919 DrawScreenElement(x, y, element);
1921 DrawScreenElement(x, y, EL_EMPTY);
1924 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1925 else if (cut_mode == NO_CUTTING)
1926 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1929 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1931 if (cut_mode == CUT_BELOW &&
1932 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1933 DrawLevelElement(lx, ly + 1, element);
1936 if (content == EL_ACID)
1938 int dir = MovDir[lx][ly];
1939 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1940 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1942 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1945 else if (IS_BLOCKED(lx, ly))
1950 boolean cut_mode = NO_CUTTING;
1951 int element_old, content_old;
1953 Blocked2Moving(lx, ly, &oldx, &oldy);
1956 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1957 MovDir[oldx][oldy] == MV_RIGHT);
1959 element_old = Feld[oldx][oldy];
1960 content_old = Store[oldx][oldy];
1962 if (element_old == EL_QUICKSAND_EMPTYING ||
1963 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1964 element_old == EL_MAGIC_WALL_EMPTYING ||
1965 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1966 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1967 element_old == EL_AMOEBA_DROPPING)
1968 cut_mode = CUT_ABOVE;
1970 DrawScreenElement(x, y, EL_EMPTY);
1973 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1975 else if (cut_mode == NO_CUTTING)
1976 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1979 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1982 else if (IS_DRAWABLE(element))
1983 DrawScreenElement(x, y, element);
1985 DrawScreenElement(x, y, EL_EMPTY);
1988 void DrawLevelField(int x, int y)
1990 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1991 DrawScreenField(SCREENX(x), SCREENY(y));
1992 else if (IS_MOVING(x, y))
1996 Moving2Blocked(x, y, &newx, &newy);
1997 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1998 DrawScreenField(SCREENX(newx), SCREENY(newy));
2000 else if (IS_BLOCKED(x, y))
2004 Blocked2Moving(x, y, &oldx, &oldy);
2005 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2006 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2010 void DrawSizedElement(int x, int y, int element, int tilesize)
2014 graphic = el2edimg(element);
2015 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2018 void DrawMiniElement(int x, int y, int element)
2022 graphic = el2edimg(element);
2023 DrawMiniGraphic(x, y, graphic);
2026 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2029 int x = sx + scroll_x, y = sy + scroll_y;
2031 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2032 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2033 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2034 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2036 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2039 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2041 int x = sx + scroll_x, y = sy + scroll_y;
2043 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2044 DrawMiniElement(sx, sy, EL_EMPTY);
2045 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2046 DrawMiniElement(sx, sy, Feld[x][y]);
2048 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2051 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2052 int x, int y, int xsize, int ysize,
2053 int tile_width, int tile_height)
2057 int dst_x = startx + x * tile_width;
2058 int dst_y = starty + y * tile_height;
2059 int width = graphic_info[graphic].width;
2060 int height = graphic_info[graphic].height;
2061 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2062 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2063 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2064 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2065 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2066 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2067 boolean draw_masked = graphic_info[graphic].draw_masked;
2069 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2071 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2073 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2077 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2078 inner_sx + (x - 1) * tile_width % inner_width);
2079 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2080 inner_sy + (y - 1) * tile_height % inner_height);
2083 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2086 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2090 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2091 int x, int y, int xsize, int ysize, int font_nr)
2093 int font_width = getFontWidth(font_nr);
2094 int font_height = getFontHeight(font_nr);
2096 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2097 font_width, font_height);
2100 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2102 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2103 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2104 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2105 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2106 boolean no_delay = (tape.warp_forward);
2107 unsigned int anim_delay = 0;
2108 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2109 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2110 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2111 int font_width = getFontWidth(font_nr);
2112 int font_height = getFontHeight(font_nr);
2113 int max_xsize = level.envelope[envelope_nr].xsize;
2114 int max_ysize = level.envelope[envelope_nr].ysize;
2115 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2116 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2117 int xend = max_xsize;
2118 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2119 int xstep = (xstart < xend ? 1 : 0);
2120 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2122 int end = MAX(xend - xstart, yend - ystart);
2125 for (i = start; i <= end; i++)
2127 int last_frame = end; // last frame of this "for" loop
2128 int x = xstart + i * xstep;
2129 int y = ystart + i * ystep;
2130 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2131 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2132 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2133 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2136 SetDrawtoField(DRAW_FIELDBUFFER);
2138 BlitScreenToBitmap(backbuffer);
2140 SetDrawtoField(DRAW_BACKBUFFER);
2142 for (yy = 0; yy < ysize; yy++)
2143 for (xx = 0; xx < xsize; xx++)
2144 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2146 DrawTextBuffer(sx + font_width, sy + font_height,
2147 level.envelope[envelope_nr].text, font_nr, max_xsize,
2148 xsize - 2, ysize - 2, 0, mask_mode,
2149 level.envelope[envelope_nr].autowrap,
2150 level.envelope[envelope_nr].centered, FALSE);
2152 redraw_mask |= REDRAW_FIELD;
2155 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2159 void ShowEnvelope(int envelope_nr)
2161 int element = EL_ENVELOPE_1 + envelope_nr;
2162 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2163 int sound_opening = element_info[element].sound[ACTION_OPENING];
2164 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2165 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2166 boolean no_delay = (tape.warp_forward);
2167 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2168 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2169 int anim_mode = graphic_info[graphic].anim_mode;
2170 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2171 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2173 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2175 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2177 if (anim_mode == ANIM_DEFAULT)
2178 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2180 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2183 Delay(wait_delay_value);
2185 WaitForEventToContinue();
2187 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2189 if (anim_mode != ANIM_NONE)
2190 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2192 if (anim_mode == ANIM_DEFAULT)
2193 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2195 game.envelope_active = FALSE;
2197 SetDrawtoField(DRAW_FIELDBUFFER);
2199 redraw_mask |= REDRAW_FIELD;
2203 static void setRequestBasePosition(int *x, int *y)
2205 int sx_base, sy_base;
2207 if (request.x != -1)
2208 sx_base = request.x;
2209 else if (request.align == ALIGN_LEFT)
2211 else if (request.align == ALIGN_RIGHT)
2212 sx_base = SX + SXSIZE;
2214 sx_base = SX + SXSIZE / 2;
2216 if (request.y != -1)
2217 sy_base = request.y;
2218 else if (request.valign == VALIGN_TOP)
2220 else if (request.valign == VALIGN_BOTTOM)
2221 sy_base = SY + SYSIZE;
2223 sy_base = SY + SYSIZE / 2;
2229 static void setRequestPositionExt(int *x, int *y, int width, int height,
2230 boolean add_border_size)
2232 int border_size = request.border_size;
2233 int sx_base, sy_base;
2236 setRequestBasePosition(&sx_base, &sy_base);
2238 if (request.align == ALIGN_LEFT)
2240 else if (request.align == ALIGN_RIGHT)
2241 sx = sx_base - width;
2243 sx = sx_base - width / 2;
2245 if (request.valign == VALIGN_TOP)
2247 else if (request.valign == VALIGN_BOTTOM)
2248 sy = sy_base - height;
2250 sy = sy_base - height / 2;
2252 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2253 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2255 if (add_border_size)
2265 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2267 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2270 void DrawEnvelopeRequest(char *text)
2272 int last_game_status = game_status; /* save current game status */
2273 char *text_final = text;
2274 char *text_door_style = NULL;
2275 int graphic = IMG_BACKGROUND_REQUEST;
2276 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2277 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2278 int font_nr = FONT_REQUEST;
2279 int font_width = getFontWidth(font_nr);
2280 int font_height = getFontHeight(font_nr);
2281 int border_size = request.border_size;
2282 int line_spacing = request.line_spacing;
2283 int line_height = font_height + line_spacing;
2284 int max_text_width = request.width - 2 * border_size;
2285 int max_text_height = request.height - 2 * border_size;
2286 int line_length = max_text_width / font_width;
2287 int max_lines = max_text_height / line_height;
2288 int text_width = line_length * font_width;
2289 int width = request.width;
2290 int height = request.height;
2291 int tile_size = MAX(request.step_offset, 1);
2292 int x_steps = width / tile_size;
2293 int y_steps = height / tile_size;
2294 int sx_offset = border_size;
2295 int sy_offset = border_size;
2299 if (request.centered)
2300 sx_offset = (request.width - text_width) / 2;
2302 if (request.wrap_single_words && !request.autowrap)
2304 char *src_text_ptr, *dst_text_ptr;
2306 text_door_style = checked_malloc(2 * strlen(text) + 1);
2308 src_text_ptr = text;
2309 dst_text_ptr = text_door_style;
2311 while (*src_text_ptr)
2313 if (*src_text_ptr == ' ' ||
2314 *src_text_ptr == '?' ||
2315 *src_text_ptr == '!')
2316 *dst_text_ptr++ = '\n';
2318 if (*src_text_ptr != ' ')
2319 *dst_text_ptr++ = *src_text_ptr;
2324 *dst_text_ptr = '\0';
2326 text_final = text_door_style;
2329 setRequestPosition(&sx, &sy, FALSE);
2331 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2333 for (y = 0; y < y_steps; y++)
2334 for (x = 0; x < x_steps; x++)
2335 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2336 x, y, x_steps, y_steps,
2337 tile_size, tile_size);
2339 /* force DOOR font inside door area */
2340 game_status = GAME_MODE_PSEUDO_DOOR;
2342 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2343 line_length, -1, max_lines, line_spacing, mask_mode,
2344 request.autowrap, request.centered, FALSE);
2346 game_status = last_game_status; /* restore current game status */
2348 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2349 RedrawGadget(tool_gadget[i]);
2351 // store readily prepared envelope request for later use when animating
2352 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2354 if (text_door_style)
2355 free(text_door_style);
2358 void AnimateEnvelopeRequest(int anim_mode, int action)
2360 int graphic = IMG_BACKGROUND_REQUEST;
2361 boolean draw_masked = graphic_info[graphic].draw_masked;
2362 int delay_value_normal = request.step_delay;
2363 int delay_value_fast = delay_value_normal / 2;
2364 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2365 boolean no_delay = (tape.warp_forward);
2366 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2367 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2368 unsigned int anim_delay = 0;
2370 int tile_size = MAX(request.step_offset, 1);
2371 int max_xsize = request.width / tile_size;
2372 int max_ysize = request.height / tile_size;
2373 int max_xsize_inner = max_xsize - 2;
2374 int max_ysize_inner = max_ysize - 2;
2376 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2377 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2378 int xend = max_xsize_inner;
2379 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2380 int xstep = (xstart < xend ? 1 : 0);
2381 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2383 int end = MAX(xend - xstart, yend - ystart);
2386 if (setup.quick_doors)
2394 if (action == ACTION_OPENING)
2395 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2396 else if (action == ACTION_CLOSING)
2397 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2400 for (i = start; i <= end; i++)
2402 int last_frame = end; // last frame of this "for" loop
2403 int x = xstart + i * xstep;
2404 int y = ystart + i * ystep;
2405 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2406 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2407 int xsize_size_left = (xsize - 1) * tile_size;
2408 int ysize_size_top = (ysize - 1) * tile_size;
2409 int max_xsize_pos = (max_xsize - 1) * tile_size;
2410 int max_ysize_pos = (max_ysize - 1) * tile_size;
2411 int width = xsize * tile_size;
2412 int height = ysize * tile_size;
2417 setRequestPosition(&src_x, &src_y, FALSE);
2418 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2420 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2422 for (yy = 0; yy < 2; yy++)
2424 for (xx = 0; xx < 2; xx++)
2426 int src_xx = src_x + xx * max_xsize_pos;
2427 int src_yy = src_y + yy * max_ysize_pos;
2428 int dst_xx = dst_x + xx * xsize_size_left;
2429 int dst_yy = dst_y + yy * ysize_size_top;
2430 int xx_size = (xx ? tile_size : xsize_size_left);
2431 int yy_size = (yy ? tile_size : ysize_size_top);
2434 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2435 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2437 BlitBitmap(bitmap_db_cross, backbuffer,
2438 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2442 redraw_mask |= REDRAW_FIELD;
2447 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2451 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2453 int graphic = IMG_BACKGROUND_REQUEST;
2454 int sound_opening = SND_REQUEST_OPENING;
2455 int sound_closing = SND_REQUEST_CLOSING;
2456 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2457 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2458 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2459 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2460 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2462 if (game_status == GAME_MODE_PLAYING)
2463 BlitScreenToBitmap(backbuffer);
2465 SetDrawtoField(DRAW_BACKBUFFER);
2467 // SetDrawBackgroundMask(REDRAW_NONE);
2469 if (action == ACTION_OPENING)
2471 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2473 if (req_state & REQ_ASK)
2475 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2476 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2478 else if (req_state & REQ_CONFIRM)
2480 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2482 else if (req_state & REQ_PLAYER)
2484 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2485 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2486 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2487 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2490 DrawEnvelopeRequest(text);
2492 if (game_status != GAME_MODE_MAIN)
2496 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2498 if (action == ACTION_OPENING)
2500 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2502 if (anim_mode == ANIM_DEFAULT)
2503 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2505 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2509 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2511 if (anim_mode != ANIM_NONE)
2512 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2514 if (anim_mode == ANIM_DEFAULT)
2515 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2518 game.envelope_active = FALSE;
2520 if (action == ACTION_CLOSING)
2522 if (game_status != GAME_MODE_MAIN)
2525 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2528 // SetDrawBackgroundMask(last_draw_background_mask);
2530 redraw_mask |= REDRAW_FIELD;
2532 if (game_status == GAME_MODE_MAIN)
2537 if (action == ACTION_CLOSING &&
2538 game_status == GAME_MODE_PLAYING &&
2539 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2540 SetDrawtoField(DRAW_FIELDBUFFER);
2543 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2547 int graphic = el2preimg(element);
2549 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2550 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2553 void DrawLevel(int draw_background_mask)
2557 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2558 SetDrawBackgroundMask(draw_background_mask);
2562 for (x = BX1; x <= BX2; x++)
2563 for (y = BY1; y <= BY2; y++)
2564 DrawScreenField(x, y);
2566 redraw_mask |= REDRAW_FIELD;
2569 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2574 for (x = 0; x < size_x; x++)
2575 for (y = 0; y < size_y; y++)
2576 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2578 redraw_mask |= REDRAW_FIELD;
2581 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2585 for (x = 0; x < size_x; x++)
2586 for (y = 0; y < size_y; y++)
2587 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2589 redraw_mask |= REDRAW_FIELD;
2592 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2594 boolean show_level_border = (BorderElement != EL_EMPTY);
2595 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2596 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2597 int tile_size = preview.tile_size;
2598 int preview_width = preview.xsize * tile_size;
2599 int preview_height = preview.ysize * tile_size;
2600 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2601 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2602 int real_preview_width = real_preview_xsize * tile_size;
2603 int real_preview_height = real_preview_ysize * tile_size;
2604 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2605 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2608 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2611 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2613 dst_x += (preview_width - real_preview_width) / 2;
2614 dst_y += (preview_height - real_preview_height) / 2;
2616 for (x = 0; x < real_preview_xsize; x++)
2618 for (y = 0; y < real_preview_ysize; y++)
2620 int lx = from_x + x + (show_level_border ? -1 : 0);
2621 int ly = from_y + y + (show_level_border ? -1 : 0);
2622 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2623 getBorderElement(lx, ly));
2625 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2626 element, tile_size);
2630 redraw_mask |= REDRAW_FIELD;
2633 #define MICROLABEL_EMPTY 0
2634 #define MICROLABEL_LEVEL_NAME 1
2635 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2636 #define MICROLABEL_LEVEL_AUTHOR 3
2637 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2638 #define MICROLABEL_IMPORTED_FROM 5
2639 #define MICROLABEL_IMPORTED_BY_HEAD 6
2640 #define MICROLABEL_IMPORTED_BY 7
2642 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2644 int max_text_width = SXSIZE;
2645 int font_width = getFontWidth(font_nr);
2647 if (pos->align == ALIGN_CENTER)
2648 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2649 else if (pos->align == ALIGN_RIGHT)
2650 max_text_width = pos->x;
2652 max_text_width = SXSIZE - pos->x;
2654 return max_text_width / font_width;
2657 static void DrawPreviewLevelLabelExt(int mode)
2659 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2660 char label_text[MAX_OUTPUT_LINESIZE + 1];
2661 int max_len_label_text;
2662 int font_nr = pos->font;
2665 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2668 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2669 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2670 mode == MICROLABEL_IMPORTED_BY_HEAD)
2671 font_nr = pos->font_alt;
2673 max_len_label_text = getMaxTextLength(pos, font_nr);
2675 if (pos->size != -1)
2676 max_len_label_text = pos->size;
2678 for (i = 0; i < max_len_label_text; i++)
2679 label_text[i] = ' ';
2680 label_text[max_len_label_text] = '\0';
2682 if (strlen(label_text) > 0)
2683 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2686 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2687 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2688 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2689 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2690 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2691 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2692 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2693 max_len_label_text);
2694 label_text[max_len_label_text] = '\0';
2696 if (strlen(label_text) > 0)
2697 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2699 redraw_mask |= REDRAW_FIELD;
2702 static void DrawPreviewLevelExt(boolean restart)
2704 static unsigned int scroll_delay = 0;
2705 static unsigned int label_delay = 0;
2706 static int from_x, from_y, scroll_direction;
2707 static int label_state, label_counter;
2708 unsigned int scroll_delay_value = preview.step_delay;
2709 boolean show_level_border = (BorderElement != EL_EMPTY);
2710 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2711 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2712 int last_game_status = game_status; /* save current game status */
2719 if (preview.anim_mode == ANIM_CENTERED)
2721 if (level_xsize > preview.xsize)
2722 from_x = (level_xsize - preview.xsize) / 2;
2723 if (level_ysize > preview.ysize)
2724 from_y = (level_ysize - preview.ysize) / 2;
2727 from_x += preview.xoffset;
2728 from_y += preview.yoffset;
2730 scroll_direction = MV_RIGHT;
2734 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2735 DrawPreviewLevelLabelExt(label_state);
2737 /* initialize delay counters */
2738 DelayReached(&scroll_delay, 0);
2739 DelayReached(&label_delay, 0);
2741 if (leveldir_current->name)
2743 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2744 char label_text[MAX_OUTPUT_LINESIZE + 1];
2745 int font_nr = pos->font;
2746 int max_len_label_text = getMaxTextLength(pos, font_nr);
2748 if (pos->size != -1)
2749 max_len_label_text = pos->size;
2751 strncpy(label_text, leveldir_current->name, max_len_label_text);
2752 label_text[max_len_label_text] = '\0';
2754 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2755 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2758 game_status = last_game_status; /* restore current game status */
2763 /* scroll preview level, if needed */
2764 if (preview.anim_mode != ANIM_NONE &&
2765 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2766 DelayReached(&scroll_delay, scroll_delay_value))
2768 switch (scroll_direction)
2773 from_x -= preview.step_offset;
2774 from_x = (from_x < 0 ? 0 : from_x);
2777 scroll_direction = MV_UP;
2781 if (from_x < level_xsize - preview.xsize)
2783 from_x += preview.step_offset;
2784 from_x = (from_x > level_xsize - preview.xsize ?
2785 level_xsize - preview.xsize : from_x);
2788 scroll_direction = MV_DOWN;
2794 from_y -= preview.step_offset;
2795 from_y = (from_y < 0 ? 0 : from_y);
2798 scroll_direction = MV_RIGHT;
2802 if (from_y < level_ysize - preview.ysize)
2804 from_y += preview.step_offset;
2805 from_y = (from_y > level_ysize - preview.ysize ?
2806 level_ysize - preview.ysize : from_y);
2809 scroll_direction = MV_LEFT;
2816 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2819 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2820 /* redraw micro level label, if needed */
2821 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2822 !strEqual(level.author, ANONYMOUS_NAME) &&
2823 !strEqual(level.author, leveldir_current->name) &&
2824 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2826 int max_label_counter = 23;
2828 if (leveldir_current->imported_from != NULL &&
2829 strlen(leveldir_current->imported_from) > 0)
2830 max_label_counter += 14;
2831 if (leveldir_current->imported_by != NULL &&
2832 strlen(leveldir_current->imported_by) > 0)
2833 max_label_counter += 14;
2835 label_counter = (label_counter + 1) % max_label_counter;
2836 label_state = (label_counter >= 0 && label_counter <= 7 ?
2837 MICROLABEL_LEVEL_NAME :
2838 label_counter >= 9 && label_counter <= 12 ?
2839 MICROLABEL_LEVEL_AUTHOR_HEAD :
2840 label_counter >= 14 && label_counter <= 21 ?
2841 MICROLABEL_LEVEL_AUTHOR :
2842 label_counter >= 23 && label_counter <= 26 ?
2843 MICROLABEL_IMPORTED_FROM_HEAD :
2844 label_counter >= 28 && label_counter <= 35 ?
2845 MICROLABEL_IMPORTED_FROM :
2846 label_counter >= 37 && label_counter <= 40 ?
2847 MICROLABEL_IMPORTED_BY_HEAD :
2848 label_counter >= 42 && label_counter <= 49 ?
2849 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2851 if (leveldir_current->imported_from == NULL &&
2852 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2853 label_state == MICROLABEL_IMPORTED_FROM))
2854 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2855 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2857 DrawPreviewLevelLabelExt(label_state);
2860 game_status = last_game_status; /* restore current game status */
2863 void DrawPreviewLevelInitial()
2865 DrawPreviewLevelExt(TRUE);
2868 void DrawPreviewLevelAnimation()
2870 DrawPreviewLevelExt(FALSE);
2873 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2874 int graphic, int sync_frame,
2877 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2879 if (mask_mode == USE_MASKING)
2880 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2882 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2885 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2886 int graphic, int sync_frame, int mask_mode)
2888 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2890 if (mask_mode == USE_MASKING)
2891 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2893 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2896 inline static void DrawGraphicAnimation(int x, int y, int graphic)
2898 int lx = LEVELX(x), ly = LEVELY(y);
2900 if (!IN_SCR_FIELD(x, y))
2903 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2904 graphic, GfxFrame[lx][ly], NO_MASKING);
2906 MarkTileDirty(x, y);
2909 void DrawFixedGraphicAnimation(int x, int y, int graphic)
2911 int lx = LEVELX(x), ly = LEVELY(y);
2913 if (!IN_SCR_FIELD(x, y))
2916 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2917 graphic, GfxFrame[lx][ly], NO_MASKING);
2918 MarkTileDirty(x, y);
2921 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2923 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2926 void DrawLevelElementAnimation(int x, int y, int element)
2928 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2930 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2933 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2935 int sx = SCREENX(x), sy = SCREENY(y);
2937 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2940 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2943 DrawGraphicAnimation(sx, sy, graphic);
2946 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2947 DrawLevelFieldCrumbled(x, y);
2949 if (GFX_CRUMBLED(Feld[x][y]))
2950 DrawLevelFieldCrumbled(x, y);
2954 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2956 int sx = SCREENX(x), sy = SCREENY(y);
2959 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2962 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2964 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2967 DrawGraphicAnimation(sx, sy, graphic);
2969 if (GFX_CRUMBLED(element))
2970 DrawLevelFieldCrumbled(x, y);
2973 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2975 if (player->use_murphy)
2977 /* this works only because currently only one player can be "murphy" ... */
2978 static int last_horizontal_dir = MV_LEFT;
2979 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2981 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2982 last_horizontal_dir = move_dir;
2984 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2986 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2988 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2994 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2997 static boolean equalGraphics(int graphic1, int graphic2)
2999 struct GraphicInfo *g1 = &graphic_info[graphic1];
3000 struct GraphicInfo *g2 = &graphic_info[graphic2];
3002 return (g1->bitmap == g2->bitmap &&
3003 g1->src_x == g2->src_x &&
3004 g1->src_y == g2->src_y &&
3005 g1->anim_frames == g2->anim_frames &&
3006 g1->anim_delay == g2->anim_delay &&
3007 g1->anim_mode == g2->anim_mode);
3010 void DrawAllPlayers()
3014 for (i = 0; i < MAX_PLAYERS; i++)
3015 if (stored_player[i].active)
3016 DrawPlayer(&stored_player[i]);
3019 void DrawPlayerField(int x, int y)
3021 if (!IS_PLAYER(x, y))
3024 DrawPlayer(PLAYERINFO(x, y));
3027 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3029 void DrawPlayer(struct PlayerInfo *player)
3031 int jx = player->jx;
3032 int jy = player->jy;
3033 int move_dir = player->MovDir;
3034 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3035 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3036 int last_jx = (player->is_moving ? jx - dx : jx);
3037 int last_jy = (player->is_moving ? jy - dy : jy);
3038 int next_jx = jx + dx;
3039 int next_jy = jy + dy;
3040 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3041 boolean player_is_opaque = FALSE;
3042 int sx = SCREENX(jx), sy = SCREENY(jy);
3043 int sxx = 0, syy = 0;
3044 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3046 int action = ACTION_DEFAULT;
3047 int last_player_graphic = getPlayerGraphic(player, move_dir);
3048 int last_player_frame = player->Frame;
3051 /* GfxElement[][] is set to the element the player is digging or collecting;
3052 remove also for off-screen player if the player is not moving anymore */
3053 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3054 GfxElement[jx][jy] = EL_UNDEFINED;
3056 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3060 if (!IN_LEV_FIELD(jx, jy))
3062 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3063 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3064 printf("DrawPlayerField(): This should never happen!\n");
3069 if (element == EL_EXPLOSION)
3072 action = (player->is_pushing ? ACTION_PUSHING :
3073 player->is_digging ? ACTION_DIGGING :
3074 player->is_collecting ? ACTION_COLLECTING :
3075 player->is_moving ? ACTION_MOVING :
3076 player->is_snapping ? ACTION_SNAPPING :
3077 player->is_dropping ? ACTION_DROPPING :
3078 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3080 if (player->is_waiting)
3081 move_dir = player->dir_waiting;
3083 InitPlayerGfxAnimation(player, action, move_dir);
3085 /* ----------------------------------------------------------------------- */
3086 /* draw things in the field the player is leaving, if needed */
3087 /* ----------------------------------------------------------------------- */
3089 if (player->is_moving)
3091 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3093 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3095 if (last_element == EL_DYNAMITE_ACTIVE ||
3096 last_element == EL_EM_DYNAMITE_ACTIVE ||
3097 last_element == EL_SP_DISK_RED_ACTIVE)
3098 DrawDynamite(last_jx, last_jy);
3100 DrawLevelFieldThruMask(last_jx, last_jy);
3102 else if (last_element == EL_DYNAMITE_ACTIVE ||
3103 last_element == EL_EM_DYNAMITE_ACTIVE ||
3104 last_element == EL_SP_DISK_RED_ACTIVE)
3105 DrawDynamite(last_jx, last_jy);
3107 /* !!! this is not enough to prevent flickering of players which are
3108 moving next to each others without a free tile between them -- this
3109 can only be solved by drawing all players layer by layer (first the
3110 background, then the foreground etc.) !!! => TODO */
3111 else if (!IS_PLAYER(last_jx, last_jy))
3112 DrawLevelField(last_jx, last_jy);
3115 DrawLevelField(last_jx, last_jy);
3118 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3119 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3122 if (!IN_SCR_FIELD(sx, sy))
3125 /* ----------------------------------------------------------------------- */
3126 /* draw things behind the player, if needed */
3127 /* ----------------------------------------------------------------------- */
3130 DrawLevelElement(jx, jy, Back[jx][jy]);
3131 else if (IS_ACTIVE_BOMB(element))
3132 DrawLevelElement(jx, jy, EL_EMPTY);
3135 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3137 int old_element = GfxElement[jx][jy];
3138 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3139 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3141 if (GFX_CRUMBLED(old_element))
3142 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3144 DrawGraphic(sx, sy, old_graphic, frame);
3146 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3147 player_is_opaque = TRUE;
3151 GfxElement[jx][jy] = EL_UNDEFINED;
3153 /* make sure that pushed elements are drawn with correct frame rate */
3154 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3156 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3157 GfxFrame[jx][jy] = player->StepFrame;
3159 DrawLevelField(jx, jy);
3163 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3164 /* ----------------------------------------------------------------------- */
3165 /* draw player himself */
3166 /* ----------------------------------------------------------------------- */
3168 graphic = getPlayerGraphic(player, move_dir);
3170 /* in the case of changed player action or direction, prevent the current
3171 animation frame from being restarted for identical animations */
3172 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3173 player->Frame = last_player_frame;
3175 frame = getGraphicAnimationFrame(graphic, player->Frame);
3179 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3180 sxx = player->GfxPos;
3182 syy = player->GfxPos;
3185 if (player_is_opaque)
3186 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3188 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3190 if (SHIELD_ON(player))
3192 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3193 IMG_SHIELD_NORMAL_ACTIVE);
3194 int frame = getGraphicAnimationFrame(graphic, -1);
3196 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3200 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3203 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3204 sxx = player->GfxPos;
3206 syy = player->GfxPos;
3210 /* ----------------------------------------------------------------------- */
3211 /* draw things the player is pushing, if needed */
3212 /* ----------------------------------------------------------------------- */
3214 if (player->is_pushing && player->is_moving)
3216 int px = SCREENX(jx), py = SCREENY(jy);
3217 int pxx = (TILEX - ABS(sxx)) * dx;
3218 int pyy = (TILEY - ABS(syy)) * dy;
3219 int gfx_frame = GfxFrame[jx][jy];
3225 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3227 element = Feld[next_jx][next_jy];
3228 gfx_frame = GfxFrame[next_jx][next_jy];
3231 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3233 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3234 frame = getGraphicAnimationFrame(graphic, sync_frame);
3236 /* draw background element under pushed element (like the Sokoban field) */
3237 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3239 /* this allows transparent pushing animation over non-black background */
3242 DrawLevelElement(jx, jy, Back[jx][jy]);
3244 DrawLevelElement(jx, jy, EL_EMPTY);
3246 if (Back[next_jx][next_jy])
3247 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3249 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3251 else if (Back[next_jx][next_jy])
3252 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3255 /* do not draw (EM style) pushing animation when pushing is finished */
3256 /* (two-tile animations usually do not contain start and end frame) */
3257 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3258 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3260 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3262 /* masked drawing is needed for EMC style (double) movement graphics */
3263 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3264 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3268 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3269 /* ----------------------------------------------------------------------- */
3270 /* draw player himself */
3271 /* ----------------------------------------------------------------------- */
3273 graphic = getPlayerGraphic(player, move_dir);
3275 /* in the case of changed player action or direction, prevent the current
3276 animation frame from being restarted for identical animations */
3277 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3278 player->Frame = last_player_frame;
3280 frame = getGraphicAnimationFrame(graphic, player->Frame);
3284 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3285 sxx = player->GfxPos;
3287 syy = player->GfxPos;
3290 if (player_is_opaque)
3291 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3293 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3295 if (SHIELD_ON(player))
3297 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3298 IMG_SHIELD_NORMAL_ACTIVE);
3299 int frame = getGraphicAnimationFrame(graphic, -1);
3301 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3305 /* ----------------------------------------------------------------------- */
3306 /* draw things in front of player (active dynamite or dynabombs) */
3307 /* ----------------------------------------------------------------------- */
3309 if (IS_ACTIVE_BOMB(element))
3311 graphic = el2img(element);
3312 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3314 if (game.emulation == EMU_SUPAPLEX)
3315 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3317 DrawGraphicThruMask(sx, sy, graphic, frame);
3320 if (player_is_moving && last_element == EL_EXPLOSION)
3322 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3323 GfxElement[last_jx][last_jy] : EL_EMPTY);
3324 int graphic = el_act2img(element, ACTION_EXPLODING);
3325 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3326 int phase = ExplodePhase[last_jx][last_jy] - 1;
3327 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3330 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3333 /* ----------------------------------------------------------------------- */
3334 /* draw elements the player is just walking/passing through/under */
3335 /* ----------------------------------------------------------------------- */
3337 if (player_is_moving)
3339 /* handle the field the player is leaving ... */
3340 if (IS_ACCESSIBLE_INSIDE(last_element))
3341 DrawLevelField(last_jx, last_jy);
3342 else if (IS_ACCESSIBLE_UNDER(last_element))
3343 DrawLevelFieldThruMask(last_jx, last_jy);
3346 /* do not redraw accessible elements if the player is just pushing them */
3347 if (!player_is_moving || !player->is_pushing)
3349 /* ... and the field the player is entering */
3350 if (IS_ACCESSIBLE_INSIDE(element))
3351 DrawLevelField(jx, jy);
3352 else if (IS_ACCESSIBLE_UNDER(element))
3353 DrawLevelFieldThruMask(jx, jy);
3356 MarkTileDirty(sx, sy);
3359 /* ------------------------------------------------------------------------- */
3361 void WaitForEventToContinue()
3363 boolean still_wait = TRUE;
3365 /* simulate releasing mouse button over last gadget, if still pressed */
3367 HandleGadgets(-1, -1, 0);
3369 button_status = MB_RELEASED;
3383 case EVENT_BUTTONPRESS:
3384 case EVENT_KEYPRESS:
3388 case EVENT_KEYRELEASE:
3389 ClearPlayerAction();
3393 HandleOtherEvents(&event);
3397 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3404 /* don't eat all CPU time */
3409 #define MAX_REQUEST_LINES 13
3410 #define MAX_REQUEST_LINE_FONT1_LEN 7
3411 #define MAX_REQUEST_LINE_FONT2_LEN 10
3413 static int RequestHandleEvents(unsigned int req_state)
3415 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3416 local_player->LevelSolved_GameEnd);
3417 int width = request.width;
3418 int height = request.height;
3422 setRequestPosition(&sx, &sy, FALSE);
3424 button_status = MB_RELEASED;
3426 request_gadget_id = -1;
3433 SetDrawtoField(DRAW_FIELDBUFFER);
3435 HandleGameActions();
3437 SetDrawtoField(DRAW_BACKBUFFER);
3439 if (global.use_envelope_request)
3441 /* copy current state of request area to middle of playfield area */
3442 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3450 while (NextValidEvent(&event))
3454 case EVENT_BUTTONPRESS:
3455 case EVENT_BUTTONRELEASE:
3456 case EVENT_MOTIONNOTIFY:
3460 if (event.type == EVENT_MOTIONNOTIFY)
3465 motion_status = TRUE;
3466 mx = ((MotionEvent *) &event)->x;
3467 my = ((MotionEvent *) &event)->y;
3471 motion_status = FALSE;
3472 mx = ((ButtonEvent *) &event)->x;
3473 my = ((ButtonEvent *) &event)->y;
3474 if (event.type == EVENT_BUTTONPRESS)
3475 button_status = ((ButtonEvent *) &event)->button;
3477 button_status = MB_RELEASED;
3480 /* this sets 'request_gadget_id' */
3481 HandleGadgets(mx, my, button_status);
3483 switch (request_gadget_id)
3485 case TOOL_CTRL_ID_YES:
3488 case TOOL_CTRL_ID_NO:
3491 case TOOL_CTRL_ID_CONFIRM:
3492 result = TRUE | FALSE;
3495 case TOOL_CTRL_ID_PLAYER_1:
3498 case TOOL_CTRL_ID_PLAYER_2:
3501 case TOOL_CTRL_ID_PLAYER_3:
3504 case TOOL_CTRL_ID_PLAYER_4:
3515 case EVENT_KEYPRESS:
3516 switch (GetEventKey((KeyEvent *)&event, TRUE))
3519 if (req_state & REQ_CONFIRM)
3524 #if defined(TARGET_SDL2)
3531 #if defined(TARGET_SDL2)
3541 if (req_state & REQ_PLAYER)
3545 case EVENT_KEYRELEASE:
3546 ClearPlayerAction();
3550 HandleOtherEvents(&event);
3555 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3557 int joy = AnyJoystick();
3559 if (joy & JOY_BUTTON_1)
3561 else if (joy & JOY_BUTTON_2)
3567 if (global.use_envelope_request)
3569 /* copy back current state of pressed buttons inside request area */
3570 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3577 if (!PendingEvent()) /* delay only if no pending events */
3587 static boolean RequestDoor(char *text, unsigned int req_state)
3589 unsigned int old_door_state;
3590 int last_game_status = game_status; /* save current game status */
3591 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3592 int font_nr = FONT_TEXT_2;
3597 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3599 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3600 font_nr = FONT_TEXT_1;
3603 if (game_status == GAME_MODE_PLAYING)
3604 BlitScreenToBitmap(backbuffer);
3606 /* disable deactivated drawing when quick-loading level tape recording */
3607 if (tape.playing && tape.deactivate_display)
3608 TapeDeactivateDisplayOff(TRUE);
3610 SetMouseCursor(CURSOR_DEFAULT);
3612 #if defined(NETWORK_AVALIABLE)
3613 /* pause network game while waiting for request to answer */
3614 if (options.network &&
3615 game_status == GAME_MODE_PLAYING &&
3616 req_state & REQUEST_WAIT_FOR_INPUT)
3617 SendToServer_PausePlaying();
3620 old_door_state = GetDoorState();
3622 /* simulate releasing mouse button over last gadget, if still pressed */
3624 HandleGadgets(-1, -1, 0);
3628 /* draw released gadget before proceeding */
3631 if (old_door_state & DOOR_OPEN_1)
3633 CloseDoor(DOOR_CLOSE_1);
3635 /* save old door content */
3636 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3637 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3640 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3641 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3643 /* clear door drawing field */
3644 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3646 /* force DOOR font inside door area */
3647 game_status = GAME_MODE_PSEUDO_DOOR;
3649 /* write text for request */
3650 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3652 char text_line[max_request_line_len + 1];
3658 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3660 tc = *(text_ptr + tx);
3661 // if (!tc || tc == ' ')
3662 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3666 if ((tc == '?' || tc == '!') && tl == 0)
3676 strncpy(text_line, text_ptr, tl);
3679 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3680 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3681 text_line, font_nr);
3683 text_ptr += tl + (tc == ' ' ? 1 : 0);
3684 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3687 game_status = last_game_status; /* restore current game status */
3689 if (req_state & REQ_ASK)
3691 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3692 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3694 else if (req_state & REQ_CONFIRM)
3696 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3698 else if (req_state & REQ_PLAYER)
3700 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3701 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3702 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3703 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3706 /* copy request gadgets to door backbuffer */
3707 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3709 OpenDoor(DOOR_OPEN_1);
3711 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3713 if (game_status == GAME_MODE_PLAYING)
3715 SetPanelBackground();
3716 SetDrawBackgroundMask(REDRAW_DOOR_1);
3720 SetDrawBackgroundMask(REDRAW_FIELD);
3726 if (game_status != GAME_MODE_MAIN)
3729 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3731 // ---------- handle request buttons ----------
3732 result = RequestHandleEvents(req_state);
3734 if (game_status != GAME_MODE_MAIN)
3739 if (!(req_state & REQ_STAY_OPEN))
3741 CloseDoor(DOOR_CLOSE_1);
3743 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3744 (req_state & REQ_REOPEN))
3745 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3750 if (game_status == GAME_MODE_PLAYING)
3752 SetPanelBackground();
3753 SetDrawBackgroundMask(REDRAW_DOOR_1);
3757 SetDrawBackgroundMask(REDRAW_FIELD);
3760 #if defined(NETWORK_AVALIABLE)
3761 /* continue network game after request */
3762 if (options.network &&
3763 game_status == GAME_MODE_PLAYING &&
3764 req_state & REQUEST_WAIT_FOR_INPUT)
3765 SendToServer_ContinuePlaying();
3768 /* restore deactivated drawing when quick-loading level tape recording */
3769 if (tape.playing && tape.deactivate_display)
3770 TapeDeactivateDisplayOn();
3775 static boolean RequestEnvelope(char *text, unsigned int req_state)
3779 if (game_status == GAME_MODE_PLAYING)
3780 BlitScreenToBitmap(backbuffer);
3782 /* disable deactivated drawing when quick-loading level tape recording */
3783 if (tape.playing && tape.deactivate_display)
3784 TapeDeactivateDisplayOff(TRUE);
3786 SetMouseCursor(CURSOR_DEFAULT);
3788 #if defined(NETWORK_AVALIABLE)
3789 /* pause network game while waiting for request to answer */
3790 if (options.network &&
3791 game_status == GAME_MODE_PLAYING &&
3792 req_state & REQUEST_WAIT_FOR_INPUT)
3793 SendToServer_PausePlaying();
3796 /* simulate releasing mouse button over last gadget, if still pressed */
3798 HandleGadgets(-1, -1, 0);
3802 // (replace with setting corresponding request background)
3803 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3804 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3806 /* clear door drawing field */
3807 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3809 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3811 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3813 if (game_status == GAME_MODE_PLAYING)
3815 SetPanelBackground();
3816 SetDrawBackgroundMask(REDRAW_DOOR_1);
3820 SetDrawBackgroundMask(REDRAW_FIELD);
3826 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3828 // ---------- handle request buttons ----------
3829 result = RequestHandleEvents(req_state);
3831 if (game_status != GAME_MODE_MAIN)
3836 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3840 if (game_status == GAME_MODE_PLAYING)
3842 SetPanelBackground();
3843 SetDrawBackgroundMask(REDRAW_DOOR_1);
3847 SetDrawBackgroundMask(REDRAW_FIELD);
3850 #if defined(NETWORK_AVALIABLE)
3851 /* continue network game after request */
3852 if (options.network &&
3853 game_status == GAME_MODE_PLAYING &&
3854 req_state & REQUEST_WAIT_FOR_INPUT)
3855 SendToServer_ContinuePlaying();
3858 /* restore deactivated drawing when quick-loading level tape recording */
3859 if (tape.playing && tape.deactivate_display)
3860 TapeDeactivateDisplayOn();
3865 boolean Request(char *text, unsigned int req_state)
3867 if (global.use_envelope_request)
3868 return RequestEnvelope(text, req_state);
3870 return RequestDoor(text, req_state);
3873 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3875 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3876 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3879 if (dpo1->sort_priority != dpo2->sort_priority)
3880 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3882 compare_result = dpo1->nr - dpo2->nr;
3884 return compare_result;
3887 void InitGraphicCompatibilityInfo_Doors()
3893 struct DoorInfo *door;
3897 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3898 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3900 { -1, -1, -1, NULL }
3902 struct Rect door_rect_list[] =
3904 { DX, DY, DXSIZE, DYSIZE },
3905 { VX, VY, VXSIZE, VYSIZE }
3909 for (i = 0; doors[i].door_token != -1; i++)
3911 int door_token = doors[i].door_token;
3912 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3913 int part_1 = doors[i].part_1;
3914 int part_8 = doors[i].part_8;
3915 int part_2 = part_1 + 1;
3916 int part_3 = part_1 + 2;
3917 struct DoorInfo *door = doors[i].door;
3918 struct Rect *door_rect = &door_rect_list[door_index];
3919 boolean door_gfx_redefined = FALSE;
3921 /* check if any door part graphic definitions have been redefined */
3923 for (j = 0; door_part_controls[j].door_token != -1; j++)
3925 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3926 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3928 if (dpc->door_token == door_token && fi->redefined)
3929 door_gfx_redefined = TRUE;
3932 /* check for old-style door graphic/animation modifications */
3934 if (!door_gfx_redefined)
3936 if (door->anim_mode & ANIM_STATIC_PANEL)
3938 door->panel.step_xoffset = 0;
3939 door->panel.step_yoffset = 0;
3942 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3944 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3945 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3946 int num_door_steps, num_panel_steps;
3948 /* remove door part graphics other than the two default wings */
3950 for (j = 0; door_part_controls[j].door_token != -1; j++)
3952 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3953 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3955 if (dpc->graphic >= part_3 &&
3956 dpc->graphic <= part_8)
3960 /* set graphics and screen positions of the default wings */
3962 g_part_1->width = door_rect->width;
3963 g_part_1->height = door_rect->height;
3964 g_part_2->width = door_rect->width;
3965 g_part_2->height = door_rect->height;
3966 g_part_2->src_x = door_rect->width;
3967 g_part_2->src_y = g_part_1->src_y;
3969 door->part_2.x = door->part_1.x;
3970 door->part_2.y = door->part_1.y;
3972 if (door->width != -1)
3974 g_part_1->width = door->width;
3975 g_part_2->width = door->width;
3977 // special treatment for graphics and screen position of right wing
3978 g_part_2->src_x += door_rect->width - door->width;
3979 door->part_2.x += door_rect->width - door->width;
3982 if (door->height != -1)
3984 g_part_1->height = door->height;
3985 g_part_2->height = door->height;
3987 // special treatment for graphics and screen position of bottom wing
3988 g_part_2->src_y += door_rect->height - door->height;
3989 door->part_2.y += door_rect->height - door->height;
3992 /* set animation delays for the default wings and panels */
3994 door->part_1.step_delay = door->step_delay;
3995 door->part_2.step_delay = door->step_delay;
3996 door->panel.step_delay = door->step_delay;
3998 /* set animation draw order for the default wings */
4000 door->part_1.sort_priority = 2; /* draw left wing over ... */
4001 door->part_2.sort_priority = 1; /* ... right wing */
4003 /* set animation draw offset for the default wings */
4005 if (door->anim_mode & ANIM_HORIZONTAL)
4007 door->part_1.step_xoffset = door->step_offset;
4008 door->part_1.step_yoffset = 0;
4009 door->part_2.step_xoffset = door->step_offset * -1;
4010 door->part_2.step_yoffset = 0;
4012 num_door_steps = g_part_1->width / door->step_offset;
4014 else // ANIM_VERTICAL
4016 door->part_1.step_xoffset = 0;
4017 door->part_1.step_yoffset = door->step_offset;
4018 door->part_2.step_xoffset = 0;
4019 door->part_2.step_yoffset = door->step_offset * -1;
4021 num_door_steps = g_part_1->height / door->step_offset;
4024 /* set animation draw offset for the default panels */
4026 if (door->step_offset > 1)
4028 num_panel_steps = 2 * door_rect->height / door->step_offset;
4029 door->panel.start_step = num_panel_steps - num_door_steps;
4030 door->panel.start_step_closing = door->panel.start_step;
4034 num_panel_steps = door_rect->height / door->step_offset;
4035 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4036 door->panel.start_step_closing = door->panel.start_step;
4037 door->panel.step_delay *= 2;
4048 for (i = 0; door_part_controls[i].door_token != -1; i++)
4050 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4051 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4053 /* initialize "start_step_opening" and "start_step_closing", if needed */
4054 if (dpc->pos->start_step_opening == 0 &&
4055 dpc->pos->start_step_closing == 0)
4057 // dpc->pos->start_step_opening = dpc->pos->start_step;
4058 dpc->pos->start_step_closing = dpc->pos->start_step;
4061 /* fill structure for door part draw order (sorted below) */
4063 dpo->sort_priority = dpc->pos->sort_priority;
4066 /* sort door part controls according to sort_priority and graphic number */
4067 qsort(door_part_order, MAX_DOOR_PARTS,
4068 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4071 unsigned int OpenDoor(unsigned int door_state)
4073 if (door_state & DOOR_COPY_BACK)
4075 if (door_state & DOOR_OPEN_1)
4076 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4077 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4079 if (door_state & DOOR_OPEN_2)
4080 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4081 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4083 door_state &= ~DOOR_COPY_BACK;
4086 return MoveDoor(door_state);
4089 unsigned int CloseDoor(unsigned int door_state)
4091 unsigned int old_door_state = GetDoorState();
4093 if (!(door_state & DOOR_NO_COPY_BACK))
4095 if (old_door_state & DOOR_OPEN_1)
4096 BlitBitmap(backbuffer, bitmap_db_door_1,
4097 DX, DY, DXSIZE, DYSIZE, 0, 0);
4099 if (old_door_state & DOOR_OPEN_2)
4100 BlitBitmap(backbuffer, bitmap_db_door_2,
4101 VX, VY, VXSIZE, VYSIZE, 0, 0);
4103 door_state &= ~DOOR_NO_COPY_BACK;
4106 return MoveDoor(door_state);
4109 unsigned int GetDoorState()
4111 return MoveDoor(DOOR_GET_STATE);
4114 unsigned int SetDoorState(unsigned int door_state)
4116 return MoveDoor(door_state | DOOR_SET_STATE);
4119 int euclid(int a, int b)
4121 return (b ? euclid(b, a % b) : a);
4124 unsigned int MoveDoor(unsigned int door_state)
4126 struct Rect door_rect_list[] =
4128 { DX, DY, DXSIZE, DYSIZE },
4129 { VX, VY, VXSIZE, VYSIZE }
4131 static int door1 = DOOR_OPEN_1;
4132 static int door2 = DOOR_CLOSE_2;
4133 unsigned int door_delay = 0;
4134 unsigned int door_delay_value;
4137 if (door_state == DOOR_GET_STATE)
4138 return (door1 | door2);
4140 if (door_state & DOOR_SET_STATE)
4142 if (door_state & DOOR_ACTION_1)
4143 door1 = door_state & DOOR_ACTION_1;
4144 if (door_state & DOOR_ACTION_2)
4145 door2 = door_state & DOOR_ACTION_2;
4147 return (door1 | door2);
4150 if (!(door_state & DOOR_FORCE_REDRAW))
4152 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4153 door_state &= ~DOOR_OPEN_1;
4154 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4155 door_state &= ~DOOR_CLOSE_1;
4156 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4157 door_state &= ~DOOR_OPEN_2;
4158 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4159 door_state &= ~DOOR_CLOSE_2;
4162 if (global.autoplay_leveldir)
4164 door_state |= DOOR_NO_DELAY;
4165 door_state &= ~DOOR_CLOSE_ALL;
4168 if (game_status == GAME_MODE_EDITOR)
4169 door_state |= DOOR_NO_DELAY;
4171 if (door_state & DOOR_ACTION)
4173 boolean door_panel_drawn[NUM_DOORS];
4174 boolean panel_has_doors[NUM_DOORS];
4175 boolean door_part_skip[MAX_DOOR_PARTS];
4176 boolean door_part_done[MAX_DOOR_PARTS];
4177 boolean door_part_done_all;
4178 int num_steps[MAX_DOOR_PARTS];
4179 int max_move_delay = 0; // delay for complete animations of all doors
4180 int max_step_delay = 0; // delay (ms) between two animation frames
4181 int num_move_steps = 0; // number of animation steps for all doors
4182 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4183 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4184 int current_move_delay = 0;
4188 for (i = 0; i < NUM_DOORS; i++)
4189 panel_has_doors[i] = FALSE;
4191 for (i = 0; i < MAX_DOOR_PARTS; i++)
4193 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4194 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4195 int door_token = dpc->door_token;
4197 door_part_done[i] = FALSE;
4198 door_part_skip[i] = (!(door_state & door_token) ||
4202 for (i = 0; i < MAX_DOOR_PARTS; i++)
4204 int nr = door_part_order[i].nr;
4205 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4206 struct DoorPartPosInfo *pos = dpc->pos;
4207 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4208 int door_token = dpc->door_token;
4209 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4210 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4211 int step_xoffset = ABS(pos->step_xoffset);
4212 int step_yoffset = ABS(pos->step_yoffset);
4213 int step_delay = pos->step_delay;
4214 int current_door_state = door_state & door_token;
4215 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4216 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4217 boolean part_opening = (is_panel ? door_closing : door_opening);
4218 int start_step = (part_opening ? pos->start_step_opening :
4219 pos->start_step_closing);
4220 float move_xsize = (step_xoffset ? g->width : 0);
4221 float move_ysize = (step_yoffset ? g->height : 0);
4222 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4223 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4224 int move_steps = (move_xsteps && move_ysteps ?
4225 MIN(move_xsteps, move_ysteps) :
4226 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4227 int move_delay = move_steps * step_delay;
4229 if (door_part_skip[nr])
4232 max_move_delay = MAX(max_move_delay, move_delay);
4233 max_step_delay = (max_step_delay == 0 ? step_delay :
4234 euclid(max_step_delay, step_delay));
4235 num_steps[nr] = move_steps;
4239 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4241 panel_has_doors[door_index] = TRUE;
4245 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4247 num_move_steps = max_move_delay / max_step_delay;
4248 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4250 door_delay_value = max_step_delay;
4252 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4254 start = num_move_steps - 1;
4258 /* opening door sound has priority over simultaneously closing door */
4259 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4260 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4261 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4262 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4265 for (k = start; k < num_move_steps; k++)
4267 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4269 door_part_done_all = TRUE;
4271 for (i = 0; i < NUM_DOORS; i++)
4272 door_panel_drawn[i] = FALSE;
4274 for (i = 0; i < MAX_DOOR_PARTS; i++)
4276 int nr = door_part_order[i].nr;
4277 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4278 struct DoorPartPosInfo *pos = dpc->pos;
4279 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4280 int door_token = dpc->door_token;
4281 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4282 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4283 boolean is_panel_and_door_has_closed = FALSE;
4284 struct Rect *door_rect = &door_rect_list[door_index];
4285 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4287 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4288 int current_door_state = door_state & door_token;
4289 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4290 boolean door_closing = !door_opening;
4291 boolean part_opening = (is_panel ? door_closing : door_opening);
4292 boolean part_closing = !part_opening;
4293 int start_step = (part_opening ? pos->start_step_opening :
4294 pos->start_step_closing);
4295 int step_delay = pos->step_delay;
4296 int step_factor = step_delay / max_step_delay;
4297 int k1 = (step_factor ? k / step_factor + 1 : k);
4298 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4299 int kk = MAX(0, k2);
4302 int src_x, src_y, src_xx, src_yy;
4303 int dst_x, dst_y, dst_xx, dst_yy;
4306 if (door_part_skip[nr])
4309 if (!(door_state & door_token))
4317 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4318 int kk_door = MAX(0, k2_door);
4319 int sync_frame = kk_door * door_delay_value;
4320 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4322 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4327 if (!door_panel_drawn[door_index])
4329 ClearRectangle(drawto, door_rect->x, door_rect->y,
4330 door_rect->width, door_rect->height);
4332 door_panel_drawn[door_index] = TRUE;
4335 // draw opening or closing door parts
4337 if (pos->step_xoffset < 0) // door part on right side
4340 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4343 if (dst_xx + width > door_rect->width)
4344 width = door_rect->width - dst_xx;
4346 else // door part on left side
4349 dst_xx = pos->x - kk * pos->step_xoffset;
4353 src_xx = ABS(dst_xx);
4357 width = g->width - src_xx;
4359 if (width > door_rect->width)
4360 width = door_rect->width;
4362 // printf("::: k == %d [%d] \n", k, start_step);
4365 if (pos->step_yoffset < 0) // door part on bottom side
4368 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4371 if (dst_yy + height > door_rect->height)
4372 height = door_rect->height - dst_yy;
4374 else // door part on top side
4377 dst_yy = pos->y - kk * pos->step_yoffset;
4381 src_yy = ABS(dst_yy);
4385 height = g->height - src_yy;
4388 src_x = g_src_x + src_xx;
4389 src_y = g_src_y + src_yy;
4391 dst_x = door_rect->x + dst_xx;
4392 dst_y = door_rect->y + dst_yy;
4394 is_panel_and_door_has_closed =
4397 panel_has_doors[door_index] &&
4398 k >= num_move_steps_doors_only - 1);
4400 if (width >= 0 && width <= g->width &&
4401 height >= 0 && height <= g->height &&
4402 !is_panel_and_door_has_closed)
4404 if (is_panel || !pos->draw_masked)
4405 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4408 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4412 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4414 if ((part_opening && (width < 0 || height < 0)) ||
4415 (part_closing && (width >= g->width && height >= g->height)))
4416 door_part_done[nr] = TRUE;
4418 // continue door part animations, but not panel after door has closed
4419 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4420 door_part_done_all = FALSE;
4423 if (!(door_state & DOOR_NO_DELAY))
4427 if (game_status == GAME_MODE_MAIN)
4430 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4432 current_move_delay += max_step_delay;
4435 if (door_part_done_all)
4440 if (door_state & DOOR_ACTION_1)
4441 door1 = door_state & DOOR_ACTION_1;
4442 if (door_state & DOOR_ACTION_2)
4443 door2 = door_state & DOOR_ACTION_2;
4445 return (door1 | door2);
4448 static boolean useSpecialEditorDoor()
4450 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4451 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4453 // do not draw special editor door if editor border defined or redefined
4454 if (graphic_info[graphic].bitmap != NULL || redefined)
4457 // do not draw special editor door if global border defined to be empty
4458 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4461 // do not draw special editor door if viewport definitions do not match
4465 EY + EYSIZE != VY + VYSIZE)
4471 void DrawSpecialEditorDoor()
4473 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4474 int top_border_width = gfx1->width;
4475 int top_border_height = gfx1->height;
4476 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4477 int ex = EX - outer_border;
4478 int ey = EY - outer_border;
4479 int vy = VY - outer_border;
4480 int exsize = EXSIZE + 2 * outer_border;
4482 if (!useSpecialEditorDoor())
4485 /* draw bigger level editor toolbox window */
4486 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4487 top_border_width, top_border_height, ex, ey - top_border_height);
4488 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4489 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4491 redraw_mask |= REDRAW_ALL;
4494 void UndrawSpecialEditorDoor()
4496 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4497 int top_border_width = gfx1->width;
4498 int top_border_height = gfx1->height;
4499 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4500 int ex = EX - outer_border;
4501 int ey = EY - outer_border;
4502 int ey_top = ey - top_border_height;
4503 int exsize = EXSIZE + 2 * outer_border;
4504 int eysize = EYSIZE + 2 * outer_border;
4506 if (!useSpecialEditorDoor())
4509 /* draw normal tape recorder window */
4510 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4512 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4513 ex, ey_top, top_border_width, top_border_height,
4515 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4516 ex, ey, exsize, eysize, ex, ey);
4520 // if screen background is set to "[NONE]", clear editor toolbox window
4521 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4522 ClearRectangle(drawto, ex, ey, exsize, eysize);
4525 redraw_mask |= REDRAW_ALL;
4529 /* ---------- new tool button stuff ---------------------------------------- */
4534 struct TextPosInfo *pos;
4537 } toolbutton_info[NUM_TOOL_BUTTONS] =
4540 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4541 TOOL_CTRL_ID_YES, "yes"
4544 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4545 TOOL_CTRL_ID_NO, "no"
4548 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4549 TOOL_CTRL_ID_CONFIRM, "confirm"
4552 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4553 TOOL_CTRL_ID_PLAYER_1, "player 1"
4556 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4557 TOOL_CTRL_ID_PLAYER_2, "player 2"
4560 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4561 TOOL_CTRL_ID_PLAYER_3, "player 3"
4564 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4565 TOOL_CTRL_ID_PLAYER_4, "player 4"
4569 void CreateToolButtons()
4573 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4575 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4576 struct TextPosInfo *pos = toolbutton_info[i].pos;
4577 struct GadgetInfo *gi;
4578 Bitmap *deco_bitmap = None;
4579 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4580 unsigned int event_mask = GD_EVENT_RELEASED;
4583 int gd_x = gfx->src_x;
4584 int gd_y = gfx->src_y;
4585 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4586 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4589 if (global.use_envelope_request)
4590 setRequestPosition(&dx, &dy, TRUE);
4592 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4594 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4596 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4597 pos->size, &deco_bitmap, &deco_x, &deco_y);
4598 deco_xpos = (gfx->width - pos->size) / 2;
4599 deco_ypos = (gfx->height - pos->size) / 2;
4602 gi = CreateGadget(GDI_CUSTOM_ID, id,
4603 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4604 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4605 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4606 GDI_WIDTH, gfx->width,
4607 GDI_HEIGHT, gfx->height,
4608 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4609 GDI_STATE, GD_BUTTON_UNPRESSED,
4610 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4611 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4612 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4613 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4614 GDI_DECORATION_SIZE, pos->size, pos->size,
4615 GDI_DECORATION_SHIFTING, 1, 1,
4616 GDI_DIRECT_DRAW, FALSE,
4617 GDI_EVENT_MASK, event_mask,
4618 GDI_CALLBACK_ACTION, HandleToolButtons,
4622 Error(ERR_EXIT, "cannot create gadget");
4624 tool_gadget[id] = gi;
4628 void FreeToolButtons()
4632 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4633 FreeGadget(tool_gadget[i]);
4636 static void UnmapToolButtons()
4640 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4641 UnmapGadget(tool_gadget[i]);
4644 static void HandleToolButtons(struct GadgetInfo *gi)
4646 request_gadget_id = gi->custom_id;
4649 static struct Mapping_EM_to_RND_object
4652 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4653 boolean is_backside; /* backside of moving element */
4659 em_object_mapping_list[] =
4662 Xblank, TRUE, FALSE,
4666 Yacid_splash_eB, FALSE, FALSE,
4667 EL_ACID_SPLASH_RIGHT, -1, -1
4670 Yacid_splash_wB, FALSE, FALSE,
4671 EL_ACID_SPLASH_LEFT, -1, -1
4674 #ifdef EM_ENGINE_BAD_ROLL
4676 Xstone_force_e, FALSE, FALSE,
4677 EL_ROCK, -1, MV_BIT_RIGHT
4680 Xstone_force_w, FALSE, FALSE,
4681 EL_ROCK, -1, MV_BIT_LEFT
4684 Xnut_force_e, FALSE, FALSE,
4685 EL_NUT, -1, MV_BIT_RIGHT
4688 Xnut_force_w, FALSE, FALSE,
4689 EL_NUT, -1, MV_BIT_LEFT
4692 Xspring_force_e, FALSE, FALSE,
4693 EL_SPRING, -1, MV_BIT_RIGHT
4696 Xspring_force_w, FALSE, FALSE,
4697 EL_SPRING, -1, MV_BIT_LEFT
4700 Xemerald_force_e, FALSE, FALSE,
4701 EL_EMERALD, -1, MV_BIT_RIGHT
4704 Xemerald_force_w, FALSE, FALSE,
4705 EL_EMERALD, -1, MV_BIT_LEFT
4708 Xdiamond_force_e, FALSE, FALSE,
4709 EL_DIAMOND, -1, MV_BIT_RIGHT
4712 Xdiamond_force_w, FALSE, FALSE,
4713 EL_DIAMOND, -1, MV_BIT_LEFT
4716 Xbomb_force_e, FALSE, FALSE,
4717 EL_BOMB, -1, MV_BIT_RIGHT
4720 Xbomb_force_w, FALSE, FALSE,
4721 EL_BOMB, -1, MV_BIT_LEFT
4723 #endif /* EM_ENGINE_BAD_ROLL */
4726 Xstone, TRUE, FALSE,
4730 Xstone_pause, FALSE, FALSE,
4734 Xstone_fall, FALSE, FALSE,
4738 Ystone_s, FALSE, FALSE,
4739 EL_ROCK, ACTION_FALLING, -1
4742 Ystone_sB, FALSE, TRUE,
4743 EL_ROCK, ACTION_FALLING, -1
4746 Ystone_e, FALSE, FALSE,
4747 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4750 Ystone_eB, FALSE, TRUE,
4751 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4754 Ystone_w, FALSE, FALSE,
4755 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4758 Ystone_wB, FALSE, TRUE,
4759 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4766 Xnut_pause, FALSE, FALSE,
4770 Xnut_fall, FALSE, FALSE,
4774 Ynut_s, FALSE, FALSE,
4775 EL_NUT, ACTION_FALLING, -1
4778 Ynut_sB, FALSE, TRUE,
4779 EL_NUT, ACTION_FALLING, -1
4782 Ynut_e, FALSE, FALSE,
4783 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4786 Ynut_eB, FALSE, TRUE,
4787 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4790 Ynut_w, FALSE, FALSE,
4791 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4794 Ynut_wB, FALSE, TRUE,
4795 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4798 Xbug_n, TRUE, FALSE,
4802 Xbug_e, TRUE, FALSE,
4803 EL_BUG_RIGHT, -1, -1
4806 Xbug_s, TRUE, FALSE,
4810 Xbug_w, TRUE, FALSE,
4814 Xbug_gon, FALSE, FALSE,
4818 Xbug_goe, FALSE, FALSE,
4819 EL_BUG_RIGHT, -1, -1
4822 Xbug_gos, FALSE, FALSE,
4826 Xbug_gow, FALSE, FALSE,
4830 Ybug_n, FALSE, FALSE,
4831 EL_BUG, ACTION_MOVING, MV_BIT_UP
4834 Ybug_nB, FALSE, TRUE,
4835 EL_BUG, ACTION_MOVING, MV_BIT_UP
4838 Ybug_e, FALSE, FALSE,
4839 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4842 Ybug_eB, FALSE, TRUE,
4843 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4846 Ybug_s, FALSE, FALSE,
4847 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4850 Ybug_sB, FALSE, TRUE,
4851 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4854 Ybug_w, FALSE, FALSE,
4855 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4858 Ybug_wB, FALSE, TRUE,
4859 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4862 Ybug_w_n, FALSE, FALSE,
4863 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4866 Ybug_n_e, FALSE, FALSE,
4867 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4870 Ybug_e_s, FALSE, FALSE,
4871 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4874 Ybug_s_w, FALSE, FALSE,
4875 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4878 Ybug_e_n, FALSE, FALSE,
4879 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4882 Ybug_s_e, FALSE, FALSE,
4883 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4886 Ybug_w_s, FALSE, FALSE,
4887 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4890 Ybug_n_w, FALSE, FALSE,
4891 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4894 Ybug_stone, FALSE, FALSE,
4895 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4898 Ybug_spring, FALSE, FALSE,
4899 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4902 Xtank_n, TRUE, FALSE,
4903 EL_SPACESHIP_UP, -1, -1
4906 Xtank_e, TRUE, FALSE,
4907 EL_SPACESHIP_RIGHT, -1, -1
4910 Xtank_s, TRUE, FALSE,
4911 EL_SPACESHIP_DOWN, -1, -1
4914 Xtank_w, TRUE, FALSE,
4915 EL_SPACESHIP_LEFT, -1, -1
4918 Xtank_gon, FALSE, FALSE,
4919 EL_SPACESHIP_UP, -1, -1
4922 Xtank_goe, FALSE, FALSE,
4923 EL_SPACESHIP_RIGHT, -1, -1
4926 Xtank_gos, FALSE, FALSE,
4927 EL_SPACESHIP_DOWN, -1, -1
4930 Xtank_gow, FALSE, FALSE,
4931 EL_SPACESHIP_LEFT, -1, -1
4934 Ytank_n, FALSE, FALSE,
4935 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4938 Ytank_nB, FALSE, TRUE,
4939 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4942 Ytank_e, FALSE, FALSE,
4943 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4946 Ytank_eB, FALSE, TRUE,
4947 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4950 Ytank_s, FALSE, FALSE,
4951 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4954 Ytank_sB, FALSE, TRUE,
4955 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4958 Ytank_w, FALSE, FALSE,
4959 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4962 Ytank_wB, FALSE, TRUE,
4963 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4966 Ytank_w_n, FALSE, FALSE,
4967 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4970 Ytank_n_e, FALSE, FALSE,
4971 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4974 Ytank_e_s, FALSE, FALSE,
4975 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4978 Ytank_s_w, FALSE, FALSE,
4979 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4982 Ytank_e_n, FALSE, FALSE,
4983 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4986 Ytank_s_e, FALSE, FALSE,
4987 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4990 Ytank_w_s, FALSE, FALSE,
4991 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4994 Ytank_n_w, FALSE, FALSE,
4995 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4998 Ytank_stone, FALSE, FALSE,
4999 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5002 Ytank_spring, FALSE, FALSE,
5003 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5006 Xandroid, TRUE, FALSE,
5007 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5010 Xandroid_1_n, FALSE, FALSE,
5011 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5014 Xandroid_2_n, FALSE, FALSE,
5015 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5018 Xandroid_1_e, FALSE, FALSE,
5019 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5022 Xandroid_2_e, FALSE, FALSE,
5023 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5026 Xandroid_1_w, FALSE, FALSE,
5027 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5030 Xandroid_2_w, FALSE, FALSE,
5031 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5034 Xandroid_1_s, FALSE, FALSE,
5035 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5038 Xandroid_2_s, FALSE, FALSE,
5039 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5042 Yandroid_n, FALSE, FALSE,
5043 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5046 Yandroid_nB, FALSE, TRUE,
5047 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5050 Yandroid_ne, FALSE, FALSE,
5051 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5054 Yandroid_neB, FALSE, TRUE,
5055 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5058 Yandroid_e, FALSE, FALSE,
5059 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5062 Yandroid_eB, FALSE, TRUE,
5063 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5066 Yandroid_se, FALSE, FALSE,
5067 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5070 Yandroid_seB, FALSE, TRUE,
5071 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5074 Yandroid_s, FALSE, FALSE,
5075 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5078 Yandroid_sB, FALSE, TRUE,
5079 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5082 Yandroid_sw, FALSE, FALSE,
5083 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5086 Yandroid_swB, FALSE, TRUE,
5087 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5090 Yandroid_w, FALSE, FALSE,
5091 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5094 Yandroid_wB, FALSE, TRUE,
5095 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5098 Yandroid_nw, FALSE, FALSE,
5099 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5102 Yandroid_nwB, FALSE, TRUE,
5103 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5106 Xspring, TRUE, FALSE,
5110 Xspring_pause, FALSE, FALSE,
5114 Xspring_e, FALSE, FALSE,
5118 Xspring_w, FALSE, FALSE,
5122 Xspring_fall, FALSE, FALSE,
5126 Yspring_s, FALSE, FALSE,
5127 EL_SPRING, ACTION_FALLING, -1
5130 Yspring_sB, FALSE, TRUE,
5131 EL_SPRING, ACTION_FALLING, -1
5134 Yspring_e, FALSE, FALSE,
5135 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5138 Yspring_eB, FALSE, TRUE,
5139 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5142 Yspring_w, FALSE, FALSE,
5143 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5146 Yspring_wB, FALSE, TRUE,
5147 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5150 Yspring_kill_e, FALSE, FALSE,
5151 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5154 Yspring_kill_eB, FALSE, TRUE,
5155 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5158 Yspring_kill_w, FALSE, FALSE,
5159 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5162 Yspring_kill_wB, FALSE, TRUE,
5163 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5166 Xeater_n, TRUE, FALSE,
5167 EL_YAMYAM_UP, -1, -1
5170 Xeater_e, TRUE, FALSE,
5171 EL_YAMYAM_RIGHT, -1, -1
5174 Xeater_w, TRUE, FALSE,
5175 EL_YAMYAM_LEFT, -1, -1
5178 Xeater_s, TRUE, FALSE,
5179 EL_YAMYAM_DOWN, -1, -1
5182 Yeater_n, FALSE, FALSE,
5183 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5186 Yeater_nB, FALSE, TRUE,
5187 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5190 Yeater_e, FALSE, FALSE,
5191 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5194 Yeater_eB, FALSE, TRUE,
5195 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5198 Yeater_s, FALSE, FALSE,
5199 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5202 Yeater_sB, FALSE, TRUE,
5203 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5206 Yeater_w, FALSE, FALSE,
5207 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5210 Yeater_wB, FALSE, TRUE,
5211 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5214 Yeater_stone, FALSE, FALSE,
5215 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5218 Yeater_spring, FALSE, FALSE,
5219 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5222 Xalien, TRUE, FALSE,
5226 Xalien_pause, FALSE, FALSE,
5230 Yalien_n, FALSE, FALSE,
5231 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5234 Yalien_nB, FALSE, TRUE,
5235 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5238 Yalien_e, FALSE, FALSE,
5239 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5242 Yalien_eB, FALSE, TRUE,
5243 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5246 Yalien_s, FALSE, FALSE,
5247 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5250 Yalien_sB, FALSE, TRUE,
5251 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5254 Yalien_w, FALSE, FALSE,
5255 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5258 Yalien_wB, FALSE, TRUE,
5259 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5262 Yalien_stone, FALSE, FALSE,
5263 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5266 Yalien_spring, FALSE, FALSE,
5267 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5270 Xemerald, TRUE, FALSE,
5274 Xemerald_pause, FALSE, FALSE,
5278 Xemerald_fall, FALSE, FALSE,
5282 Xemerald_shine, FALSE, FALSE,
5283 EL_EMERALD, ACTION_TWINKLING, -1
5286 Yemerald_s, FALSE, FALSE,
5287 EL_EMERALD, ACTION_FALLING, -1
5290 Yemerald_sB, FALSE, TRUE,
5291 EL_EMERALD, ACTION_FALLING, -1
5294 Yemerald_e, FALSE, FALSE,
5295 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5298 Yemerald_eB, FALSE, TRUE,
5299 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5302 Yemerald_w, FALSE, FALSE,
5303 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5306 Yemerald_wB, FALSE, TRUE,
5307 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5310 Yemerald_eat, FALSE, FALSE,
5311 EL_EMERALD, ACTION_COLLECTING, -1
5314 Yemerald_stone, FALSE, FALSE,
5315 EL_NUT, ACTION_BREAKING, -1
5318 Xdiamond, TRUE, FALSE,
5322 Xdiamond_pause, FALSE, FALSE,
5326 Xdiamond_fall, FALSE, FALSE,
5330 Xdiamond_shine, FALSE, FALSE,
5331 EL_DIAMOND, ACTION_TWINKLING, -1
5334 Ydiamond_s, FALSE, FALSE,
5335 EL_DIAMOND, ACTION_FALLING, -1
5338 Ydiamond_sB, FALSE, TRUE,
5339 EL_DIAMOND, ACTION_FALLING, -1
5342 Ydiamond_e, FALSE, FALSE,
5343 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5346 Ydiamond_eB, FALSE, TRUE,
5347 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5350 Ydiamond_w, FALSE, FALSE,
5351 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5354 Ydiamond_wB, FALSE, TRUE,
5355 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5358 Ydiamond_eat, FALSE, FALSE,
5359 EL_DIAMOND, ACTION_COLLECTING, -1
5362 Ydiamond_stone, FALSE, FALSE,
5363 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5366 Xdrip_fall, TRUE, FALSE,
5367 EL_AMOEBA_DROP, -1, -1
5370 Xdrip_stretch, FALSE, FALSE,
5371 EL_AMOEBA_DROP, ACTION_FALLING, -1
5374 Xdrip_stretchB, FALSE, TRUE,
5375 EL_AMOEBA_DROP, ACTION_FALLING, -1
5378 Xdrip_eat, FALSE, FALSE,
5379 EL_AMOEBA_DROP, ACTION_GROWING, -1
5382 Ydrip_s1, FALSE, FALSE,
5383 EL_AMOEBA_DROP, ACTION_FALLING, -1
5386 Ydrip_s1B, FALSE, TRUE,
5387 EL_AMOEBA_DROP, ACTION_FALLING, -1
5390 Ydrip_s2, FALSE, FALSE,
5391 EL_AMOEBA_DROP, ACTION_FALLING, -1
5394 Ydrip_s2B, FALSE, TRUE,
5395 EL_AMOEBA_DROP, ACTION_FALLING, -1
5402 Xbomb_pause, FALSE, FALSE,
5406 Xbomb_fall, FALSE, FALSE,
5410 Ybomb_s, FALSE, FALSE,
5411 EL_BOMB, ACTION_FALLING, -1
5414 Ybomb_sB, FALSE, TRUE,
5415 EL_BOMB, ACTION_FALLING, -1
5418 Ybomb_e, FALSE, FALSE,
5419 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5422 Ybomb_eB, FALSE, TRUE,
5423 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5426 Ybomb_w, FALSE, FALSE,
5427 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5430 Ybomb_wB, FALSE, TRUE,
5431 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5434 Ybomb_eat, FALSE, FALSE,
5435 EL_BOMB, ACTION_ACTIVATING, -1
5438 Xballoon, TRUE, FALSE,
5442 Yballoon_n, FALSE, FALSE,
5443 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5446 Yballoon_nB, FALSE, TRUE,
5447 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5450 Yballoon_e, FALSE, FALSE,
5451 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5454 Yballoon_eB, FALSE, TRUE,
5455 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5458 Yballoon_s, FALSE, FALSE,
5459 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5462 Yballoon_sB, FALSE, TRUE,
5463 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5466 Yballoon_w, FALSE, FALSE,
5467 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5470 Yballoon_wB, FALSE, TRUE,
5471 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5474 Xgrass, TRUE, FALSE,
5475 EL_EMC_GRASS, -1, -1
5478 Ygrass_nB, FALSE, FALSE,
5479 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5482 Ygrass_eB, FALSE, FALSE,
5483 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5486 Ygrass_sB, FALSE, FALSE,
5487 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5490 Ygrass_wB, FALSE, FALSE,
5491 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5498 Ydirt_nB, FALSE, FALSE,
5499 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5502 Ydirt_eB, FALSE, FALSE,
5503 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5506 Ydirt_sB, FALSE, FALSE,
5507 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5510 Ydirt_wB, FALSE, FALSE,
5511 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5514 Xacid_ne, TRUE, FALSE,
5515 EL_ACID_POOL_TOPRIGHT, -1, -1
5518 Xacid_se, TRUE, FALSE,
5519 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5522 Xacid_s, TRUE, FALSE,
5523 EL_ACID_POOL_BOTTOM, -1, -1
5526 Xacid_sw, TRUE, FALSE,
5527 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5530 Xacid_nw, TRUE, FALSE,
5531 EL_ACID_POOL_TOPLEFT, -1, -1
5534 Xacid_1, TRUE, FALSE,
5538 Xacid_2, FALSE, FALSE,
5542 Xacid_3, FALSE, FALSE,
5546 Xacid_4, FALSE, FALSE,
5550 Xacid_5, FALSE, FALSE,
5554 Xacid_6, FALSE, FALSE,
5558 Xacid_7, FALSE, FALSE,
5562 Xacid_8, FALSE, FALSE,
5566 Xball_1, TRUE, FALSE,
5567 EL_EMC_MAGIC_BALL, -1, -1
5570 Xball_1B, FALSE, FALSE,
5571 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5574 Xball_2, FALSE, FALSE,
5575 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5578 Xball_2B, FALSE, FALSE,
5579 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5582 Yball_eat, FALSE, FALSE,
5583 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5586 Ykey_1_eat, FALSE, FALSE,
5587 EL_EM_KEY_1, ACTION_COLLECTING, -1
5590 Ykey_2_eat, FALSE, FALSE,
5591 EL_EM_KEY_2, ACTION_COLLECTING, -1
5594 Ykey_3_eat, FALSE, FALSE,
5595 EL_EM_KEY_3, ACTION_COLLECTING, -1
5598 Ykey_4_eat, FALSE, FALSE,
5599 EL_EM_KEY_4, ACTION_COLLECTING, -1
5602 Ykey_5_eat, FALSE, FALSE,
5603 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5606 Ykey_6_eat, FALSE, FALSE,
5607 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5610 Ykey_7_eat, FALSE, FALSE,
5611 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5614 Ykey_8_eat, FALSE, FALSE,
5615 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5618 Ylenses_eat, FALSE, FALSE,
5619 EL_EMC_LENSES, ACTION_COLLECTING, -1
5622 Ymagnify_eat, FALSE, FALSE,
5623 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5626 Ygrass_eat, FALSE, FALSE,
5627 EL_EMC_GRASS, ACTION_SNAPPING, -1
5630 Ydirt_eat, FALSE, FALSE,
5631 EL_SAND, ACTION_SNAPPING, -1
5634 Xgrow_ns, TRUE, FALSE,
5635 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5638 Ygrow_ns_eat, FALSE, FALSE,
5639 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5642 Xgrow_ew, TRUE, FALSE,
5643 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5646 Ygrow_ew_eat, FALSE, FALSE,
5647 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5650 Xwonderwall, TRUE, FALSE,
5651 EL_MAGIC_WALL, -1, -1
5654 XwonderwallB, FALSE, FALSE,
5655 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5658 Xamoeba_1, TRUE, FALSE,
5659 EL_AMOEBA_DRY, ACTION_OTHER, -1
5662 Xamoeba_2, FALSE, FALSE,
5663 EL_AMOEBA_DRY, ACTION_OTHER, -1
5666 Xamoeba_3, FALSE, FALSE,
5667 EL_AMOEBA_DRY, ACTION_OTHER, -1
5670 Xamoeba_4, FALSE, FALSE,
5671 EL_AMOEBA_DRY, ACTION_OTHER, -1
5674 Xamoeba_5, TRUE, FALSE,
5675 EL_AMOEBA_WET, ACTION_OTHER, -1
5678 Xamoeba_6, FALSE, FALSE,
5679 EL_AMOEBA_WET, ACTION_OTHER, -1
5682 Xamoeba_7, FALSE, FALSE,
5683 EL_AMOEBA_WET, ACTION_OTHER, -1
5686 Xamoeba_8, FALSE, FALSE,
5687 EL_AMOEBA_WET, ACTION_OTHER, -1
5690 Xdoor_1, TRUE, FALSE,
5691 EL_EM_GATE_1, -1, -1
5694 Xdoor_2, TRUE, FALSE,
5695 EL_EM_GATE_2, -1, -1
5698 Xdoor_3, TRUE, FALSE,
5699 EL_EM_GATE_3, -1, -1
5702 Xdoor_4, TRUE, FALSE,
5703 EL_EM_GATE_4, -1, -1
5706 Xdoor_5, TRUE, FALSE,
5707 EL_EMC_GATE_5, -1, -1
5710 Xdoor_6, TRUE, FALSE,
5711 EL_EMC_GATE_6, -1, -1
5714 Xdoor_7, TRUE, FALSE,
5715 EL_EMC_GATE_7, -1, -1
5718 Xdoor_8, TRUE, FALSE,
5719 EL_EMC_GATE_8, -1, -1
5722 Xkey_1, TRUE, FALSE,
5726 Xkey_2, TRUE, FALSE,
5730 Xkey_3, TRUE, FALSE,
5734 Xkey_4, TRUE, FALSE,
5738 Xkey_5, TRUE, FALSE,
5739 EL_EMC_KEY_5, -1, -1
5742 Xkey_6, TRUE, FALSE,
5743 EL_EMC_KEY_6, -1, -1
5746 Xkey_7, TRUE, FALSE,
5747 EL_EMC_KEY_7, -1, -1
5750 Xkey_8, TRUE, FALSE,
5751 EL_EMC_KEY_8, -1, -1
5754 Xwind_n, TRUE, FALSE,
5755 EL_BALLOON_SWITCH_UP, -1, -1
5758 Xwind_e, TRUE, FALSE,
5759 EL_BALLOON_SWITCH_RIGHT, -1, -1
5762 Xwind_s, TRUE, FALSE,
5763 EL_BALLOON_SWITCH_DOWN, -1, -1
5766 Xwind_w, TRUE, FALSE,
5767 EL_BALLOON_SWITCH_LEFT, -1, -1
5770 Xwind_nesw, TRUE, FALSE,
5771 EL_BALLOON_SWITCH_ANY, -1, -1
5774 Xwind_stop, TRUE, FALSE,
5775 EL_BALLOON_SWITCH_NONE, -1, -1
5779 EL_EM_EXIT_CLOSED, -1, -1
5782 Xexit_1, TRUE, FALSE,
5783 EL_EM_EXIT_OPEN, -1, -1
5786 Xexit_2, FALSE, FALSE,
5787 EL_EM_EXIT_OPEN, -1, -1
5790 Xexit_3, FALSE, FALSE,
5791 EL_EM_EXIT_OPEN, -1, -1
5794 Xdynamite, TRUE, FALSE,
5795 EL_EM_DYNAMITE, -1, -1
5798 Ydynamite_eat, FALSE, FALSE,
5799 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5802 Xdynamite_1, TRUE, FALSE,
5803 EL_EM_DYNAMITE_ACTIVE, -1, -1
5806 Xdynamite_2, FALSE, FALSE,
5807 EL_EM_DYNAMITE_ACTIVE, -1, -1
5810 Xdynamite_3, FALSE, FALSE,
5811 EL_EM_DYNAMITE_ACTIVE, -1, -1
5814 Xdynamite_4, FALSE, FALSE,
5815 EL_EM_DYNAMITE_ACTIVE, -1, -1
5818 Xbumper, TRUE, FALSE,
5819 EL_EMC_SPRING_BUMPER, -1, -1
5822 XbumperB, FALSE, FALSE,
5823 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5826 Xwheel, TRUE, FALSE,
5827 EL_ROBOT_WHEEL, -1, -1
5830 XwheelB, FALSE, FALSE,
5831 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5834 Xswitch, TRUE, FALSE,
5835 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5838 XswitchB, FALSE, FALSE,
5839 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5843 EL_QUICKSAND_EMPTY, -1, -1
5846 Xsand_stone, TRUE, FALSE,
5847 EL_QUICKSAND_FULL, -1, -1
5850 Xsand_stonein_1, FALSE, TRUE,
5851 EL_ROCK, ACTION_FILLING, -1
5854 Xsand_stonein_2, FALSE, TRUE,
5855 EL_ROCK, ACTION_FILLING, -1
5858 Xsand_stonein_3, FALSE, TRUE,
5859 EL_ROCK, ACTION_FILLING, -1
5862 Xsand_stonein_4, FALSE, TRUE,
5863 EL_ROCK, ACTION_FILLING, -1
5866 Xsand_stonesand_1, FALSE, FALSE,
5867 EL_QUICKSAND_EMPTYING, -1, -1
5870 Xsand_stonesand_2, FALSE, FALSE,
5871 EL_QUICKSAND_EMPTYING, -1, -1
5874 Xsand_stonesand_3, FALSE, FALSE,
5875 EL_QUICKSAND_EMPTYING, -1, -1
5878 Xsand_stonesand_4, FALSE, FALSE,
5879 EL_QUICKSAND_EMPTYING, -1, -1
5882 Xsand_stonesand_quickout_1, FALSE, FALSE,
5883 EL_QUICKSAND_EMPTYING, -1, -1
5886 Xsand_stonesand_quickout_2, FALSE, FALSE,
5887 EL_QUICKSAND_EMPTYING, -1, -1
5890 Xsand_stoneout_1, FALSE, FALSE,
5891 EL_ROCK, ACTION_EMPTYING, -1
5894 Xsand_stoneout_2, FALSE, FALSE,
5895 EL_ROCK, ACTION_EMPTYING, -1
5898 Xsand_sandstone_1, FALSE, FALSE,
5899 EL_QUICKSAND_FILLING, -1, -1
5902 Xsand_sandstone_2, FALSE, FALSE,
5903 EL_QUICKSAND_FILLING, -1, -1
5906 Xsand_sandstone_3, FALSE, FALSE,
5907 EL_QUICKSAND_FILLING, -1, -1
5910 Xsand_sandstone_4, FALSE, FALSE,
5911 EL_QUICKSAND_FILLING, -1, -1
5914 Xplant, TRUE, FALSE,
5915 EL_EMC_PLANT, -1, -1
5918 Yplant, FALSE, FALSE,
5919 EL_EMC_PLANT, -1, -1
5922 Xlenses, TRUE, FALSE,
5923 EL_EMC_LENSES, -1, -1
5926 Xmagnify, TRUE, FALSE,
5927 EL_EMC_MAGNIFIER, -1, -1
5930 Xdripper, TRUE, FALSE,
5931 EL_EMC_DRIPPER, -1, -1
5934 XdripperB, FALSE, FALSE,
5935 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5938 Xfake_blank, TRUE, FALSE,
5939 EL_INVISIBLE_WALL, -1, -1
5942 Xfake_blankB, FALSE, FALSE,
5943 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5946 Xfake_grass, TRUE, FALSE,
5947 EL_EMC_FAKE_GRASS, -1, -1
5950 Xfake_grassB, FALSE, FALSE,
5951 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5954 Xfake_door_1, TRUE, FALSE,
5955 EL_EM_GATE_1_GRAY, -1, -1
5958 Xfake_door_2, TRUE, FALSE,
5959 EL_EM_GATE_2_GRAY, -1, -1
5962 Xfake_door_3, TRUE, FALSE,
5963 EL_EM_GATE_3_GRAY, -1, -1
5966 Xfake_door_4, TRUE, FALSE,
5967 EL_EM_GATE_4_GRAY, -1, -1
5970 Xfake_door_5, TRUE, FALSE,
5971 EL_EMC_GATE_5_GRAY, -1, -1
5974 Xfake_door_6, TRUE, FALSE,
5975 EL_EMC_GATE_6_GRAY, -1, -1
5978 Xfake_door_7, TRUE, FALSE,
5979 EL_EMC_GATE_7_GRAY, -1, -1
5982 Xfake_door_8, TRUE, FALSE,
5983 EL_EMC_GATE_8_GRAY, -1, -1
5986 Xfake_acid_1, TRUE, FALSE,
5987 EL_EMC_FAKE_ACID, -1, -1
5990 Xfake_acid_2, FALSE, FALSE,
5991 EL_EMC_FAKE_ACID, -1, -1
5994 Xfake_acid_3, FALSE, FALSE,
5995 EL_EMC_FAKE_ACID, -1, -1
5998 Xfake_acid_4, FALSE, FALSE,
5999 EL_EMC_FAKE_ACID, -1, -1
6002 Xfake_acid_5, FALSE, FALSE,
6003 EL_EMC_FAKE_ACID, -1, -1
6006 Xfake_acid_6, FALSE, FALSE,
6007 EL_EMC_FAKE_ACID, -1, -1
6010 Xfake_acid_7, FALSE, FALSE,
6011 EL_EMC_FAKE_ACID, -1, -1
6014 Xfake_acid_8, FALSE, FALSE,
6015 EL_EMC_FAKE_ACID, -1, -1
6018 Xsteel_1, TRUE, FALSE,
6019 EL_STEELWALL, -1, -1
6022 Xsteel_2, TRUE, FALSE,
6023 EL_EMC_STEELWALL_2, -1, -1
6026 Xsteel_3, TRUE, FALSE,
6027 EL_EMC_STEELWALL_3, -1, -1
6030 Xsteel_4, TRUE, FALSE,
6031 EL_EMC_STEELWALL_4, -1, -1
6034 Xwall_1, TRUE, FALSE,
6038 Xwall_2, TRUE, FALSE,
6039 EL_EMC_WALL_14, -1, -1
6042 Xwall_3, TRUE, FALSE,
6043 EL_EMC_WALL_15, -1, -1
6046 Xwall_4, TRUE, FALSE,
6047 EL_EMC_WALL_16, -1, -1
6050 Xround_wall_1, TRUE, FALSE,
6051 EL_WALL_SLIPPERY, -1, -1
6054 Xround_wall_2, TRUE, FALSE,
6055 EL_EMC_WALL_SLIPPERY_2, -1, -1
6058 Xround_wall_3, TRUE, FALSE,
6059 EL_EMC_WALL_SLIPPERY_3, -1, -1
6062 Xround_wall_4, TRUE, FALSE,
6063 EL_EMC_WALL_SLIPPERY_4, -1, -1
6066 Xdecor_1, TRUE, FALSE,
6067 EL_EMC_WALL_8, -1, -1
6070 Xdecor_2, TRUE, FALSE,
6071 EL_EMC_WALL_6, -1, -1
6074 Xdecor_3, TRUE, FALSE,
6075 EL_EMC_WALL_4, -1, -1
6078 Xdecor_4, TRUE, FALSE,
6079 EL_EMC_WALL_7, -1, -1
6082 Xdecor_5, TRUE, FALSE,
6083 EL_EMC_WALL_5, -1, -1
6086 Xdecor_6, TRUE, FALSE,
6087 EL_EMC_WALL_9, -1, -1
6090 Xdecor_7, TRUE, FALSE,
6091 EL_EMC_WALL_10, -1, -1
6094 Xdecor_8, TRUE, FALSE,
6095 EL_EMC_WALL_1, -1, -1
6098 Xdecor_9, TRUE, FALSE,
6099 EL_EMC_WALL_2, -1, -1
6102 Xdecor_10, TRUE, FALSE,
6103 EL_EMC_WALL_3, -1, -1
6106 Xdecor_11, TRUE, FALSE,
6107 EL_EMC_WALL_11, -1, -1
6110 Xdecor_12, TRUE, FALSE,
6111 EL_EMC_WALL_12, -1, -1
6114 Xalpha_0, TRUE, FALSE,
6115 EL_CHAR('0'), -1, -1
6118 Xalpha_1, TRUE, FALSE,
6119 EL_CHAR('1'), -1, -1
6122 Xalpha_2, TRUE, FALSE,
6123 EL_CHAR('2'), -1, -1
6126 Xalpha_3, TRUE, FALSE,
6127 EL_CHAR('3'), -1, -1
6130 Xalpha_4, TRUE, FALSE,
6131 EL_CHAR('4'), -1, -1
6134 Xalpha_5, TRUE, FALSE,
6135 EL_CHAR('5'), -1, -1
6138 Xalpha_6, TRUE, FALSE,
6139 EL_CHAR('6'), -1, -1
6142 Xalpha_7, TRUE, FALSE,
6143 EL_CHAR('7'), -1, -1
6146 Xalpha_8, TRUE, FALSE,
6147 EL_CHAR('8'), -1, -1
6150 Xalpha_9, TRUE, FALSE,
6151 EL_CHAR('9'), -1, -1
6154 Xalpha_excla, TRUE, FALSE,
6155 EL_CHAR('!'), -1, -1
6158 Xalpha_quote, TRUE, FALSE,
6159 EL_CHAR('"'), -1, -1
6162 Xalpha_comma, TRUE, FALSE,
6163 EL_CHAR(','), -1, -1
6166 Xalpha_minus, TRUE, FALSE,
6167 EL_CHAR('-'), -1, -1
6170 Xalpha_perio, TRUE, FALSE,
6171 EL_CHAR('.'), -1, -1
6174 Xalpha_colon, TRUE, FALSE,
6175 EL_CHAR(':'), -1, -1
6178 Xalpha_quest, TRUE, FALSE,
6179 EL_CHAR('?'), -1, -1
6182 Xalpha_a, TRUE, FALSE,
6183 EL_CHAR('A'), -1, -1
6186 Xalpha_b, TRUE, FALSE,
6187 EL_CHAR('B'), -1, -1
6190 Xalpha_c, TRUE, FALSE,
6191 EL_CHAR('C'), -1, -1
6194 Xalpha_d, TRUE, FALSE,
6195 EL_CHAR('D'), -1, -1
6198 Xalpha_e, TRUE, FALSE,
6199 EL_CHAR('E'), -1, -1
6202 Xalpha_f, TRUE, FALSE,
6203 EL_CHAR('F'), -1, -1
6206 Xalpha_g, TRUE, FALSE,
6207 EL_CHAR('G'), -1, -1
6210 Xalpha_h, TRUE, FALSE,
6211 EL_CHAR('H'), -1, -1
6214 Xalpha_i, TRUE, FALSE,
6215 EL_CHAR('I'), -1, -1
6218 Xalpha_j, TRUE, FALSE,
6219 EL_CHAR('J'), -1, -1
6222 Xalpha_k, TRUE, FALSE,
6223 EL_CHAR('K'), -1, -1
6226 Xalpha_l, TRUE, FALSE,
6227 EL_CHAR('L'), -1, -1
6230 Xalpha_m, TRUE, FALSE,
6231 EL_CHAR('M'), -1, -1
6234 Xalpha_n, TRUE, FALSE,
6235 EL_CHAR('N'), -1, -1
6238 Xalpha_o, TRUE, FALSE,
6239 EL_CHAR('O'), -1, -1
6242 Xalpha_p, TRUE, FALSE,
6243 EL_CHAR('P'), -1, -1
6246 Xalpha_q, TRUE, FALSE,
6247 EL_CHAR('Q'), -1, -1
6250 Xalpha_r, TRUE, FALSE,
6251 EL_CHAR('R'), -1, -1
6254 Xalpha_s, TRUE, FALSE,
6255 EL_CHAR('S'), -1, -1
6258 Xalpha_t, TRUE, FALSE,
6259 EL_CHAR('T'), -1, -1
6262 Xalpha_u, TRUE, FALSE,
6263 EL_CHAR('U'), -1, -1
6266 Xalpha_v, TRUE, FALSE,
6267 EL_CHAR('V'), -1, -1
6270 Xalpha_w, TRUE, FALSE,
6271 EL_CHAR('W'), -1, -1
6274 Xalpha_x, TRUE, FALSE,
6275 EL_CHAR('X'), -1, -1
6278 Xalpha_y, TRUE, FALSE,
6279 EL_CHAR('Y'), -1, -1
6282 Xalpha_z, TRUE, FALSE,
6283 EL_CHAR('Z'), -1, -1
6286 Xalpha_arrow_e, TRUE, FALSE,
6287 EL_CHAR('>'), -1, -1
6290 Xalpha_arrow_w, TRUE, FALSE,
6291 EL_CHAR('<'), -1, -1
6294 Xalpha_copyr, TRUE, FALSE,
6295 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6299 Xboom_bug, FALSE, FALSE,
6300 EL_BUG, ACTION_EXPLODING, -1
6303 Xboom_bomb, FALSE, FALSE,
6304 EL_BOMB, ACTION_EXPLODING, -1
6307 Xboom_android, FALSE, FALSE,
6308 EL_EMC_ANDROID, ACTION_OTHER, -1
6311 Xboom_1, FALSE, FALSE,
6312 EL_DEFAULT, ACTION_EXPLODING, -1
6315 Xboom_2, FALSE, FALSE,
6316 EL_DEFAULT, ACTION_EXPLODING, -1
6319 Znormal, FALSE, FALSE,
6323 Zdynamite, FALSE, FALSE,
6327 Zplayer, FALSE, FALSE,
6331 ZBORDER, FALSE, FALSE,
6341 static struct Mapping_EM_to_RND_player
6350 em_player_mapping_list[] =
6354 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6358 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6362 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6366 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6370 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6374 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6378 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6382 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6386 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6390 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6394 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6398 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6402 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6406 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6410 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6414 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6418 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6422 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6426 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6430 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6434 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6438 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6442 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6446 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6450 EL_PLAYER_1, ACTION_DEFAULT, -1,
6454 EL_PLAYER_2, ACTION_DEFAULT, -1,
6458 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6462 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6466 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6470 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6474 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6478 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6482 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6486 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6490 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6494 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6498 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6502 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6506 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6510 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6514 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6518 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6522 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6526 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6530 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6534 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6538 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6542 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6546 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6550 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6554 EL_PLAYER_3, ACTION_DEFAULT, -1,
6558 EL_PLAYER_4, ACTION_DEFAULT, -1,
6567 int map_element_RND_to_EM(int element_rnd)
6569 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6570 static boolean mapping_initialized = FALSE;
6572 if (!mapping_initialized)
6576 /* return "Xalpha_quest" for all undefined elements in mapping array */
6577 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6578 mapping_RND_to_EM[i] = Xalpha_quest;
6580 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6581 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6582 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6583 em_object_mapping_list[i].element_em;
6585 mapping_initialized = TRUE;
6588 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6589 return mapping_RND_to_EM[element_rnd];
6591 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6596 int map_element_EM_to_RND(int element_em)
6598 static unsigned short mapping_EM_to_RND[TILE_MAX];
6599 static boolean mapping_initialized = FALSE;
6601 if (!mapping_initialized)
6605 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6606 for (i = 0; i < TILE_MAX; i++)
6607 mapping_EM_to_RND[i] = EL_UNKNOWN;
6609 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6610 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6611 em_object_mapping_list[i].element_rnd;
6613 mapping_initialized = TRUE;
6616 if (element_em >= 0 && element_em < TILE_MAX)
6617 return mapping_EM_to_RND[element_em];
6619 Error(ERR_WARN, "invalid EM level element %d", element_em);
6624 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6626 struct LevelInfo_EM *level_em = level->native_em_level;
6627 struct LEVEL *lev = level_em->lev;
6630 for (i = 0; i < TILE_MAX; i++)
6631 lev->android_array[i] = Xblank;
6633 for (i = 0; i < level->num_android_clone_elements; i++)
6635 int element_rnd = level->android_clone_element[i];
6636 int element_em = map_element_RND_to_EM(element_rnd);
6638 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6639 if (em_object_mapping_list[j].element_rnd == element_rnd)
6640 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6644 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6646 struct LevelInfo_EM *level_em = level->native_em_level;
6647 struct LEVEL *lev = level_em->lev;
6650 level->num_android_clone_elements = 0;
6652 for (i = 0; i < TILE_MAX; i++)
6654 int element_em = lev->android_array[i];
6656 boolean element_found = FALSE;
6658 if (element_em == Xblank)
6661 element_rnd = map_element_EM_to_RND(element_em);
6663 for (j = 0; j < level->num_android_clone_elements; j++)
6664 if (level->android_clone_element[j] == element_rnd)
6665 element_found = TRUE;
6669 level->android_clone_element[level->num_android_clone_elements++] =
6672 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6677 if (level->num_android_clone_elements == 0)
6679 level->num_android_clone_elements = 1;
6680 level->android_clone_element[0] = EL_EMPTY;
6684 int map_direction_RND_to_EM(int direction)
6686 return (direction == MV_UP ? 0 :
6687 direction == MV_RIGHT ? 1 :
6688 direction == MV_DOWN ? 2 :
6689 direction == MV_LEFT ? 3 :
6693 int map_direction_EM_to_RND(int direction)
6695 return (direction == 0 ? MV_UP :
6696 direction == 1 ? MV_RIGHT :
6697 direction == 2 ? MV_DOWN :
6698 direction == 3 ? MV_LEFT :
6702 int map_element_RND_to_SP(int element_rnd)
6704 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6706 if (element_rnd >= EL_SP_START &&
6707 element_rnd <= EL_SP_END)
6708 element_sp = element_rnd - EL_SP_START;
6709 else if (element_rnd == EL_EMPTY_SPACE)
6711 else if (element_rnd == EL_INVISIBLE_WALL)
6717 int map_element_SP_to_RND(int element_sp)
6719 int element_rnd = EL_UNKNOWN;
6721 if (element_sp >= 0x00 &&
6723 element_rnd = EL_SP_START + element_sp;
6724 else if (element_sp == 0x28)
6725 element_rnd = EL_INVISIBLE_WALL;
6730 int map_action_SP_to_RND(int action_sp)
6734 case actActive: return ACTION_ACTIVE;
6735 case actImpact: return ACTION_IMPACT;
6736 case actExploding: return ACTION_EXPLODING;
6737 case actDigging: return ACTION_DIGGING;
6738 case actSnapping: return ACTION_SNAPPING;
6739 case actCollecting: return ACTION_COLLECTING;
6740 case actPassing: return ACTION_PASSING;
6741 case actPushing: return ACTION_PUSHING;
6742 case actDropping: return ACTION_DROPPING;
6744 default: return ACTION_DEFAULT;
6748 int get_next_element(int element)
6752 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6753 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6754 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6755 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6756 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6757 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6758 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6759 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6760 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6761 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6762 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6764 default: return element;
6768 int el_act_dir2img(int element, int action, int direction)
6770 element = GFX_ELEMENT(element);
6771 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6773 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6774 return element_info[element].direction_graphic[action][direction];
6777 static int el_act_dir2crm(int element, int action, int direction)
6779 element = GFX_ELEMENT(element);
6780 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6782 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6783 return element_info[element].direction_crumbled[action][direction];
6786 int el_act2img(int element, int action)
6788 element = GFX_ELEMENT(element);
6790 return element_info[element].graphic[action];
6793 int el_act2crm(int element, int action)
6795 element = GFX_ELEMENT(element);
6797 return element_info[element].crumbled[action];
6800 int el_dir2img(int element, int direction)
6802 element = GFX_ELEMENT(element);
6804 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6807 int el2baseimg(int element)
6809 return element_info[element].graphic[ACTION_DEFAULT];
6812 int el2img(int element)
6814 element = GFX_ELEMENT(element);
6816 return element_info[element].graphic[ACTION_DEFAULT];
6819 int el2edimg(int element)
6821 element = GFX_ELEMENT(element);
6823 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6826 int el2preimg(int element)
6828 element = GFX_ELEMENT(element);
6830 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6833 int el2panelimg(int element)
6835 element = GFX_ELEMENT(element);
6837 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6840 int font2baseimg(int font_nr)
6842 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6845 int getBeltNrFromBeltElement(int element)
6847 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6848 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6849 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6852 int getBeltNrFromBeltActiveElement(int element)
6854 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6855 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6856 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6859 int getBeltNrFromBeltSwitchElement(int element)
6861 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6862 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6863 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6866 int getBeltDirNrFromBeltElement(int element)
6868 static int belt_base_element[4] =
6870 EL_CONVEYOR_BELT_1_LEFT,
6871 EL_CONVEYOR_BELT_2_LEFT,
6872 EL_CONVEYOR_BELT_3_LEFT,
6873 EL_CONVEYOR_BELT_4_LEFT
6876 int belt_nr = getBeltNrFromBeltElement(element);
6877 int belt_dir_nr = element - belt_base_element[belt_nr];
6879 return (belt_dir_nr % 3);
6882 int getBeltDirNrFromBeltSwitchElement(int element)
6884 static int belt_base_element[4] =
6886 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6887 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6888 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6889 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6892 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6893 int belt_dir_nr = element - belt_base_element[belt_nr];
6895 return (belt_dir_nr % 3);
6898 int getBeltDirFromBeltElement(int element)
6900 static int belt_move_dir[3] =
6907 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6909 return belt_move_dir[belt_dir_nr];
6912 int getBeltDirFromBeltSwitchElement(int element)
6914 static int belt_move_dir[3] =
6921 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6923 return belt_move_dir[belt_dir_nr];
6926 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6928 static int belt_base_element[4] =
6930 EL_CONVEYOR_BELT_1_LEFT,
6931 EL_CONVEYOR_BELT_2_LEFT,
6932 EL_CONVEYOR_BELT_3_LEFT,
6933 EL_CONVEYOR_BELT_4_LEFT
6936 return belt_base_element[belt_nr] + belt_dir_nr;
6939 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6941 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6943 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6946 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6948 static int belt_base_element[4] =
6950 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6951 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6952 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6953 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6956 return belt_base_element[belt_nr] + belt_dir_nr;
6959 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6961 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6963 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6966 boolean getTeamMode_EM()
6968 return game.team_mode;
6971 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6973 int game_frame_delay_value;
6975 game_frame_delay_value =
6976 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6977 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6980 if (tape.playing && tape.warp_forward && !tape.pausing)
6981 game_frame_delay_value = 0;
6983 return game_frame_delay_value;
6986 unsigned int InitRND(int seed)
6988 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6989 return InitEngineRandom_EM(seed);
6990 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6991 return InitEngineRandom_SP(seed);
6993 return InitEngineRandom_RND(seed);
6996 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6997 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6999 inline static int get_effective_element_EM(int tile, int frame_em)
7001 int element = object_mapping[tile].element_rnd;
7002 int action = object_mapping[tile].action;
7003 boolean is_backside = object_mapping[tile].is_backside;
7004 boolean action_removing = (action == ACTION_DIGGING ||
7005 action == ACTION_SNAPPING ||
7006 action == ACTION_COLLECTING);
7012 case Yacid_splash_eB:
7013 case Yacid_splash_wB:
7014 return (frame_em > 5 ? EL_EMPTY : element);
7020 else /* frame_em == 7 */
7024 case Yacid_splash_eB:
7025 case Yacid_splash_wB:
7028 case Yemerald_stone:
7031 case Ydiamond_stone:
7035 case Xdrip_stretchB:
7054 case Xsand_stonein_1:
7055 case Xsand_stonein_2:
7056 case Xsand_stonein_3:
7057 case Xsand_stonein_4:
7061 return (is_backside || action_removing ? EL_EMPTY : element);
7066 inline static boolean check_linear_animation_EM(int tile)
7070 case Xsand_stonesand_1:
7071 case Xsand_stonesand_quickout_1:
7072 case Xsand_sandstone_1:
7073 case Xsand_stonein_1:
7074 case Xsand_stoneout_1:
7093 case Yacid_splash_eB:
7094 case Yacid_splash_wB:
7095 case Yemerald_stone:
7102 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7103 boolean has_crumbled_graphics,
7104 int crumbled, int sync_frame)
7106 /* if element can be crumbled, but certain action graphics are just empty
7107 space (like instantly snapping sand to empty space in 1 frame), do not
7108 treat these empty space graphics as crumbled graphics in EMC engine */
7109 if (crumbled == IMG_EMPTY_SPACE)
7110 has_crumbled_graphics = FALSE;
7112 if (has_crumbled_graphics)
7114 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7115 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7116 g_crumbled->anim_delay,
7117 g_crumbled->anim_mode,
7118 g_crumbled->anim_start_frame,
7121 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7122 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7124 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7126 g_em->has_crumbled_graphics = TRUE;
7130 g_em->crumbled_bitmap = NULL;
7131 g_em->crumbled_src_x = 0;
7132 g_em->crumbled_src_y = 0;
7133 g_em->crumbled_border_size = 0;
7135 g_em->has_crumbled_graphics = FALSE;
7139 void ResetGfxAnimation_EM(int x, int y, int tile)
7144 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7145 int tile, int frame_em, int x, int y)
7147 int action = object_mapping[tile].action;
7148 int direction = object_mapping[tile].direction;
7149 int effective_element = get_effective_element_EM(tile, frame_em);
7150 int graphic = (direction == MV_NONE ?
7151 el_act2img(effective_element, action) :
7152 el_act_dir2img(effective_element, action, direction));
7153 struct GraphicInfo *g = &graphic_info[graphic];
7155 boolean action_removing = (action == ACTION_DIGGING ||
7156 action == ACTION_SNAPPING ||
7157 action == ACTION_COLLECTING);
7158 boolean action_moving = (action == ACTION_FALLING ||
7159 action == ACTION_MOVING ||
7160 action == ACTION_PUSHING ||
7161 action == ACTION_EATING ||
7162 action == ACTION_FILLING ||
7163 action == ACTION_EMPTYING);
7164 boolean action_falling = (action == ACTION_FALLING ||
7165 action == ACTION_FILLING ||
7166 action == ACTION_EMPTYING);
7168 /* special case: graphic uses "2nd movement tile" and has defined
7169 7 frames for movement animation (or less) => use default graphic
7170 for last (8th) frame which ends the movement animation */
7171 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7173 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7174 graphic = (direction == MV_NONE ?
7175 el_act2img(effective_element, action) :
7176 el_act_dir2img(effective_element, action, direction));
7178 g = &graphic_info[graphic];
7181 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7185 else if (action_moving)
7187 boolean is_backside = object_mapping[tile].is_backside;
7191 int direction = object_mapping[tile].direction;
7192 int move_dir = (action_falling ? MV_DOWN : direction);
7197 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7198 if (g->double_movement && frame_em == 0)
7202 if (move_dir == MV_LEFT)
7203 GfxFrame[x - 1][y] = GfxFrame[x][y];
7204 else if (move_dir == MV_RIGHT)
7205 GfxFrame[x + 1][y] = GfxFrame[x][y];
7206 else if (move_dir == MV_UP)
7207 GfxFrame[x][y - 1] = GfxFrame[x][y];
7208 else if (move_dir == MV_DOWN)
7209 GfxFrame[x][y + 1] = GfxFrame[x][y];
7216 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7217 if (tile == Xsand_stonesand_quickout_1 ||
7218 tile == Xsand_stonesand_quickout_2)
7222 if (graphic_info[graphic].anim_global_sync)
7223 sync_frame = FrameCounter;
7224 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7225 sync_frame = GfxFrame[x][y];
7227 sync_frame = 0; /* playfield border (pseudo steel) */
7229 SetRandomAnimationValue(x, y);
7231 int frame = getAnimationFrame(g->anim_frames,
7234 g->anim_start_frame,
7237 g_em->unique_identifier =
7238 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7241 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7242 int tile, int frame_em, int x, int y)
7244 int action = object_mapping[tile].action;
7245 int direction = object_mapping[tile].direction;
7246 boolean is_backside = object_mapping[tile].is_backside;
7247 int effective_element = get_effective_element_EM(tile, frame_em);
7248 int effective_action = action;
7249 int graphic = (direction == MV_NONE ?
7250 el_act2img(effective_element, effective_action) :
7251 el_act_dir2img(effective_element, effective_action,
7253 int crumbled = (direction == MV_NONE ?
7254 el_act2crm(effective_element, effective_action) :
7255 el_act_dir2crm(effective_element, effective_action,
7257 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7258 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7259 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7260 struct GraphicInfo *g = &graphic_info[graphic];
7263 /* special case: graphic uses "2nd movement tile" and has defined
7264 7 frames for movement animation (or less) => use default graphic
7265 for last (8th) frame which ends the movement animation */
7266 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7268 effective_action = ACTION_DEFAULT;
7269 graphic = (direction == MV_NONE ?
7270 el_act2img(effective_element, effective_action) :
7271 el_act_dir2img(effective_element, effective_action,
7273 crumbled = (direction == MV_NONE ?
7274 el_act2crm(effective_element, effective_action) :
7275 el_act_dir2crm(effective_element, effective_action,
7278 g = &graphic_info[graphic];
7281 if (graphic_info[graphic].anim_global_sync)
7282 sync_frame = FrameCounter;
7283 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7284 sync_frame = GfxFrame[x][y];
7286 sync_frame = 0; /* playfield border (pseudo steel) */
7288 SetRandomAnimationValue(x, y);
7290 int frame = getAnimationFrame(g->anim_frames,
7293 g->anim_start_frame,
7296 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7297 g->double_movement && is_backside);
7299 /* (updating the "crumbled" graphic definitions is probably not really needed,
7300 as animations for crumbled graphics can't be longer than one EMC cycle) */
7301 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7305 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7306 int player_nr, int anim, int frame_em)
7308 int element = player_mapping[player_nr][anim].element_rnd;
7309 int action = player_mapping[player_nr][anim].action;
7310 int direction = player_mapping[player_nr][anim].direction;
7311 int graphic = (direction == MV_NONE ?
7312 el_act2img(element, action) :
7313 el_act_dir2img(element, action, direction));
7314 struct GraphicInfo *g = &graphic_info[graphic];
7317 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7319 stored_player[player_nr].StepFrame = frame_em;
7321 sync_frame = stored_player[player_nr].Frame;
7323 int frame = getAnimationFrame(g->anim_frames,
7326 g->anim_start_frame,
7329 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7330 &g_em->src_x, &g_em->src_y, FALSE);
7333 void InitGraphicInfo_EM(void)
7338 int num_em_gfx_errors = 0;
7340 if (graphic_info_em_object[0][0].bitmap == NULL)
7342 /* EM graphics not yet initialized in em_open_all() */
7347 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7350 /* always start with reliable default values */
7351 for (i = 0; i < TILE_MAX; i++)
7353 object_mapping[i].element_rnd = EL_UNKNOWN;
7354 object_mapping[i].is_backside = FALSE;
7355 object_mapping[i].action = ACTION_DEFAULT;
7356 object_mapping[i].direction = MV_NONE;
7359 /* always start with reliable default values */
7360 for (p = 0; p < MAX_PLAYERS; p++)
7362 for (i = 0; i < SPR_MAX; i++)
7364 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7365 player_mapping[p][i].action = ACTION_DEFAULT;
7366 player_mapping[p][i].direction = MV_NONE;
7370 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7372 int e = em_object_mapping_list[i].element_em;
7374 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7375 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7377 if (em_object_mapping_list[i].action != -1)
7378 object_mapping[e].action = em_object_mapping_list[i].action;
7380 if (em_object_mapping_list[i].direction != -1)
7381 object_mapping[e].direction =
7382 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7385 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7387 int a = em_player_mapping_list[i].action_em;
7388 int p = em_player_mapping_list[i].player_nr;
7390 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7392 if (em_player_mapping_list[i].action != -1)
7393 player_mapping[p][a].action = em_player_mapping_list[i].action;
7395 if (em_player_mapping_list[i].direction != -1)
7396 player_mapping[p][a].direction =
7397 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7400 for (i = 0; i < TILE_MAX; i++)
7402 int element = object_mapping[i].element_rnd;
7403 int action = object_mapping[i].action;
7404 int direction = object_mapping[i].direction;
7405 boolean is_backside = object_mapping[i].is_backside;
7406 boolean action_exploding = ((action == ACTION_EXPLODING ||
7407 action == ACTION_SMASHED_BY_ROCK ||
7408 action == ACTION_SMASHED_BY_SPRING) &&
7409 element != EL_DIAMOND);
7410 boolean action_active = (action == ACTION_ACTIVE);
7411 boolean action_other = (action == ACTION_OTHER);
7413 for (j = 0; j < 8; j++)
7415 int effective_element = get_effective_element_EM(i, j);
7416 int effective_action = (j < 7 ? action :
7417 i == Xdrip_stretch ? action :
7418 i == Xdrip_stretchB ? action :
7419 i == Ydrip_s1 ? action :
7420 i == Ydrip_s1B ? action :
7421 i == Xball_1B ? action :
7422 i == Xball_2 ? action :
7423 i == Xball_2B ? action :
7424 i == Yball_eat ? action :
7425 i == Ykey_1_eat ? action :
7426 i == Ykey_2_eat ? action :
7427 i == Ykey_3_eat ? action :
7428 i == Ykey_4_eat ? action :
7429 i == Ykey_5_eat ? action :
7430 i == Ykey_6_eat ? action :
7431 i == Ykey_7_eat ? action :
7432 i == Ykey_8_eat ? action :
7433 i == Ylenses_eat ? action :
7434 i == Ymagnify_eat ? action :
7435 i == Ygrass_eat ? action :
7436 i == Ydirt_eat ? action :
7437 i == Xsand_stonein_1 ? action :
7438 i == Xsand_stonein_2 ? action :
7439 i == Xsand_stonein_3 ? action :
7440 i == Xsand_stonein_4 ? action :
7441 i == Xsand_stoneout_1 ? action :
7442 i == Xsand_stoneout_2 ? action :
7443 i == Xboom_android ? ACTION_EXPLODING :
7444 action_exploding ? ACTION_EXPLODING :
7445 action_active ? action :
7446 action_other ? action :
7448 int graphic = (el_act_dir2img(effective_element, effective_action,
7450 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7452 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7453 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7454 boolean has_action_graphics = (graphic != base_graphic);
7455 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7456 struct GraphicInfo *g = &graphic_info[graphic];
7457 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7460 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7461 boolean special_animation = (action != ACTION_DEFAULT &&
7462 g->anim_frames == 3 &&
7463 g->anim_delay == 2 &&
7464 g->anim_mode & ANIM_LINEAR);
7465 int sync_frame = (i == Xdrip_stretch ? 7 :
7466 i == Xdrip_stretchB ? 7 :
7467 i == Ydrip_s2 ? j + 8 :
7468 i == Ydrip_s2B ? j + 8 :
7477 i == Xfake_acid_1 ? 0 :
7478 i == Xfake_acid_2 ? 10 :
7479 i == Xfake_acid_3 ? 20 :
7480 i == Xfake_acid_4 ? 30 :
7481 i == Xfake_acid_5 ? 40 :
7482 i == Xfake_acid_6 ? 50 :
7483 i == Xfake_acid_7 ? 60 :
7484 i == Xfake_acid_8 ? 70 :
7486 i == Xball_2B ? j + 8 :
7487 i == Yball_eat ? j + 1 :
7488 i == Ykey_1_eat ? j + 1 :
7489 i == Ykey_2_eat ? j + 1 :
7490 i == Ykey_3_eat ? j + 1 :
7491 i == Ykey_4_eat ? j + 1 :
7492 i == Ykey_5_eat ? j + 1 :
7493 i == Ykey_6_eat ? j + 1 :
7494 i == Ykey_7_eat ? j + 1 :
7495 i == Ykey_8_eat ? j + 1 :
7496 i == Ylenses_eat ? j + 1 :
7497 i == Ymagnify_eat ? j + 1 :
7498 i == Ygrass_eat ? j + 1 :
7499 i == Ydirt_eat ? j + 1 :
7500 i == Xamoeba_1 ? 0 :
7501 i == Xamoeba_2 ? 1 :
7502 i == Xamoeba_3 ? 2 :
7503 i == Xamoeba_4 ? 3 :
7504 i == Xamoeba_5 ? 0 :
7505 i == Xamoeba_6 ? 1 :
7506 i == Xamoeba_7 ? 2 :
7507 i == Xamoeba_8 ? 3 :
7508 i == Xexit_2 ? j + 8 :
7509 i == Xexit_3 ? j + 16 :
7510 i == Xdynamite_1 ? 0 :
7511 i == Xdynamite_2 ? 8 :
7512 i == Xdynamite_3 ? 16 :
7513 i == Xdynamite_4 ? 24 :
7514 i == Xsand_stonein_1 ? j + 1 :
7515 i == Xsand_stonein_2 ? j + 9 :
7516 i == Xsand_stonein_3 ? j + 17 :
7517 i == Xsand_stonein_4 ? j + 25 :
7518 i == Xsand_stoneout_1 && j == 0 ? 0 :
7519 i == Xsand_stoneout_1 && j == 1 ? 0 :
7520 i == Xsand_stoneout_1 && j == 2 ? 1 :
7521 i == Xsand_stoneout_1 && j == 3 ? 2 :
7522 i == Xsand_stoneout_1 && j == 4 ? 2 :
7523 i == Xsand_stoneout_1 && j == 5 ? 3 :
7524 i == Xsand_stoneout_1 && j == 6 ? 4 :
7525 i == Xsand_stoneout_1 && j == 7 ? 4 :
7526 i == Xsand_stoneout_2 && j == 0 ? 5 :
7527 i == Xsand_stoneout_2 && j == 1 ? 6 :
7528 i == Xsand_stoneout_2 && j == 2 ? 7 :
7529 i == Xsand_stoneout_2 && j == 3 ? 8 :
7530 i == Xsand_stoneout_2 && j == 4 ? 9 :
7531 i == Xsand_stoneout_2 && j == 5 ? 11 :
7532 i == Xsand_stoneout_2 && j == 6 ? 13 :
7533 i == Xsand_stoneout_2 && j == 7 ? 15 :
7534 i == Xboom_bug && j == 1 ? 2 :
7535 i == Xboom_bug && j == 2 ? 2 :
7536 i == Xboom_bug && j == 3 ? 4 :
7537 i == Xboom_bug && j == 4 ? 4 :
7538 i == Xboom_bug && j == 5 ? 2 :
7539 i == Xboom_bug && j == 6 ? 2 :
7540 i == Xboom_bug && j == 7 ? 0 :
7541 i == Xboom_bomb && j == 1 ? 2 :
7542 i == Xboom_bomb && j == 2 ? 2 :
7543 i == Xboom_bomb && j == 3 ? 4 :
7544 i == Xboom_bomb && j == 4 ? 4 :
7545 i == Xboom_bomb && j == 5 ? 2 :
7546 i == Xboom_bomb && j == 6 ? 2 :
7547 i == Xboom_bomb && j == 7 ? 0 :
7548 i == Xboom_android && j == 7 ? 6 :
7549 i == Xboom_1 && j == 1 ? 2 :
7550 i == Xboom_1 && j == 2 ? 2 :
7551 i == Xboom_1 && j == 3 ? 4 :
7552 i == Xboom_1 && j == 4 ? 4 :
7553 i == Xboom_1 && j == 5 ? 6 :
7554 i == Xboom_1 && j == 6 ? 6 :
7555 i == Xboom_1 && j == 7 ? 8 :
7556 i == Xboom_2 && j == 0 ? 8 :
7557 i == Xboom_2 && j == 1 ? 8 :
7558 i == Xboom_2 && j == 2 ? 10 :
7559 i == Xboom_2 && j == 3 ? 10 :
7560 i == Xboom_2 && j == 4 ? 10 :
7561 i == Xboom_2 && j == 5 ? 12 :
7562 i == Xboom_2 && j == 6 ? 12 :
7563 i == Xboom_2 && j == 7 ? 12 :
7564 special_animation && j == 4 ? 3 :
7565 effective_action != action ? 0 :
7569 Bitmap *debug_bitmap = g_em->bitmap;
7570 int debug_src_x = g_em->src_x;
7571 int debug_src_y = g_em->src_y;
7574 int frame = getAnimationFrame(g->anim_frames,
7577 g->anim_start_frame,
7580 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7581 g->double_movement && is_backside);
7583 g_em->bitmap = src_bitmap;
7584 g_em->src_x = src_x;
7585 g_em->src_y = src_y;
7586 g_em->src_offset_x = 0;
7587 g_em->src_offset_y = 0;
7588 g_em->dst_offset_x = 0;
7589 g_em->dst_offset_y = 0;
7590 g_em->width = TILEX;
7591 g_em->height = TILEY;
7593 g_em->preserve_background = FALSE;
7595 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7598 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7599 effective_action == ACTION_MOVING ||
7600 effective_action == ACTION_PUSHING ||
7601 effective_action == ACTION_EATING)) ||
7602 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7603 effective_action == ACTION_EMPTYING)))
7606 (effective_action == ACTION_FALLING ||
7607 effective_action == ACTION_FILLING ||
7608 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7609 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7610 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7611 int num_steps = (i == Ydrip_s1 ? 16 :
7612 i == Ydrip_s1B ? 16 :
7613 i == Ydrip_s2 ? 16 :
7614 i == Ydrip_s2B ? 16 :
7615 i == Xsand_stonein_1 ? 32 :
7616 i == Xsand_stonein_2 ? 32 :
7617 i == Xsand_stonein_3 ? 32 :
7618 i == Xsand_stonein_4 ? 32 :
7619 i == Xsand_stoneout_1 ? 16 :
7620 i == Xsand_stoneout_2 ? 16 : 8);
7621 int cx = ABS(dx) * (TILEX / num_steps);
7622 int cy = ABS(dy) * (TILEY / num_steps);
7623 int step_frame = (i == Ydrip_s2 ? j + 8 :
7624 i == Ydrip_s2B ? j + 8 :
7625 i == Xsand_stonein_2 ? j + 8 :
7626 i == Xsand_stonein_3 ? j + 16 :
7627 i == Xsand_stonein_4 ? j + 24 :
7628 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7629 int step = (is_backside ? step_frame : num_steps - step_frame);
7631 if (is_backside) /* tile where movement starts */
7633 if (dx < 0 || dy < 0)
7635 g_em->src_offset_x = cx * step;
7636 g_em->src_offset_y = cy * step;
7640 g_em->dst_offset_x = cx * step;
7641 g_em->dst_offset_y = cy * step;
7644 else /* tile where movement ends */
7646 if (dx < 0 || dy < 0)
7648 g_em->dst_offset_x = cx * step;
7649 g_em->dst_offset_y = cy * step;
7653 g_em->src_offset_x = cx * step;
7654 g_em->src_offset_y = cy * step;
7658 g_em->width = TILEX - cx * step;
7659 g_em->height = TILEY - cy * step;
7662 /* create unique graphic identifier to decide if tile must be redrawn */
7663 /* bit 31 - 16 (16 bit): EM style graphic
7664 bit 15 - 12 ( 4 bit): EM style frame
7665 bit 11 - 6 ( 6 bit): graphic width
7666 bit 5 - 0 ( 6 bit): graphic height */
7667 g_em->unique_identifier =
7668 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7672 /* skip check for EMC elements not contained in original EMC artwork */
7673 if (element == EL_EMC_FAKE_ACID)
7676 if (g_em->bitmap != debug_bitmap ||
7677 g_em->src_x != debug_src_x ||
7678 g_em->src_y != debug_src_y ||
7679 g_em->src_offset_x != 0 ||
7680 g_em->src_offset_y != 0 ||
7681 g_em->dst_offset_x != 0 ||
7682 g_em->dst_offset_y != 0 ||
7683 g_em->width != TILEX ||
7684 g_em->height != TILEY)
7686 static int last_i = -1;
7694 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7695 i, element, element_info[element].token_name,
7696 element_action_info[effective_action].suffix, direction);
7698 if (element != effective_element)
7699 printf(" [%d ('%s')]",
7701 element_info[effective_element].token_name);
7705 if (g_em->bitmap != debug_bitmap)
7706 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7707 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7709 if (g_em->src_x != debug_src_x ||
7710 g_em->src_y != debug_src_y)
7711 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7712 j, (is_backside ? 'B' : 'F'),
7713 g_em->src_x, g_em->src_y,
7714 g_em->src_x / 32, g_em->src_y / 32,
7715 debug_src_x, debug_src_y,
7716 debug_src_x / 32, debug_src_y / 32);
7718 if (g_em->src_offset_x != 0 ||
7719 g_em->src_offset_y != 0 ||
7720 g_em->dst_offset_x != 0 ||
7721 g_em->dst_offset_y != 0)
7722 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7724 g_em->src_offset_x, g_em->src_offset_y,
7725 g_em->dst_offset_x, g_em->dst_offset_y);
7727 if (g_em->width != TILEX ||
7728 g_em->height != TILEY)
7729 printf(" %d (%d): size %d,%d should be %d,%d\n",
7731 g_em->width, g_em->height, TILEX, TILEY);
7733 num_em_gfx_errors++;
7740 for (i = 0; i < TILE_MAX; i++)
7742 for (j = 0; j < 8; j++)
7744 int element = object_mapping[i].element_rnd;
7745 int action = object_mapping[i].action;
7746 int direction = object_mapping[i].direction;
7747 boolean is_backside = object_mapping[i].is_backside;
7748 int graphic_action = el_act_dir2img(element, action, direction);
7749 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7751 if ((action == ACTION_SMASHED_BY_ROCK ||
7752 action == ACTION_SMASHED_BY_SPRING ||
7753 action == ACTION_EATING) &&
7754 graphic_action == graphic_default)
7756 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7757 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7758 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7759 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7762 /* no separate animation for "smashed by rock" -- use rock instead */
7763 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7764 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7766 g_em->bitmap = g_xx->bitmap;
7767 g_em->src_x = g_xx->src_x;
7768 g_em->src_y = g_xx->src_y;
7769 g_em->src_offset_x = g_xx->src_offset_x;
7770 g_em->src_offset_y = g_xx->src_offset_y;
7771 g_em->dst_offset_x = g_xx->dst_offset_x;
7772 g_em->dst_offset_y = g_xx->dst_offset_y;
7773 g_em->width = g_xx->width;
7774 g_em->height = g_xx->height;
7775 g_em->unique_identifier = g_xx->unique_identifier;
7778 g_em->preserve_background = TRUE;
7783 for (p = 0; p < MAX_PLAYERS; p++)
7785 for (i = 0; i < SPR_MAX; i++)
7787 int element = player_mapping[p][i].element_rnd;
7788 int action = player_mapping[p][i].action;
7789 int direction = player_mapping[p][i].direction;
7791 for (j = 0; j < 8; j++)
7793 int effective_element = element;
7794 int effective_action = action;
7795 int graphic = (direction == MV_NONE ?
7796 el_act2img(effective_element, effective_action) :
7797 el_act_dir2img(effective_element, effective_action,
7799 struct GraphicInfo *g = &graphic_info[graphic];
7800 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7806 Bitmap *debug_bitmap = g_em->bitmap;
7807 int debug_src_x = g_em->src_x;
7808 int debug_src_y = g_em->src_y;
7811 int frame = getAnimationFrame(g->anim_frames,
7814 g->anim_start_frame,
7817 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7819 g_em->bitmap = src_bitmap;
7820 g_em->src_x = src_x;
7821 g_em->src_y = src_y;
7822 g_em->src_offset_x = 0;
7823 g_em->src_offset_y = 0;
7824 g_em->dst_offset_x = 0;
7825 g_em->dst_offset_y = 0;
7826 g_em->width = TILEX;
7827 g_em->height = TILEY;
7831 /* skip check for EMC elements not contained in original EMC artwork */
7832 if (element == EL_PLAYER_3 ||
7833 element == EL_PLAYER_4)
7836 if (g_em->bitmap != debug_bitmap ||
7837 g_em->src_x != debug_src_x ||
7838 g_em->src_y != debug_src_y)
7840 static int last_i = -1;
7848 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7849 p, i, element, element_info[element].token_name,
7850 element_action_info[effective_action].suffix, direction);
7852 if (element != effective_element)
7853 printf(" [%d ('%s')]",
7855 element_info[effective_element].token_name);
7859 if (g_em->bitmap != debug_bitmap)
7860 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7861 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7863 if (g_em->src_x != debug_src_x ||
7864 g_em->src_y != debug_src_y)
7865 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7867 g_em->src_x, g_em->src_y,
7868 g_em->src_x / 32, g_em->src_y / 32,
7869 debug_src_x, debug_src_y,
7870 debug_src_x / 32, debug_src_y / 32);
7872 num_em_gfx_errors++;
7882 printf("::: [%d errors found]\n", num_em_gfx_errors);
7888 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7889 boolean any_player_moving,
7890 boolean any_player_snapping,
7891 boolean any_player_dropping)
7893 static boolean player_was_waiting = TRUE;
7895 if (frame == 0 && !any_player_dropping)
7897 if (!player_was_waiting)
7899 if (!SaveEngineSnapshotToList())
7902 player_was_waiting = TRUE;
7905 else if (any_player_moving || any_player_snapping || any_player_dropping)
7907 player_was_waiting = FALSE;
7911 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7912 boolean murphy_is_dropping)
7914 static boolean player_was_waiting = TRUE;
7916 if (murphy_is_waiting)
7918 if (!player_was_waiting)
7920 if (!SaveEngineSnapshotToList())
7923 player_was_waiting = TRUE;
7928 player_was_waiting = FALSE;
7932 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7933 boolean any_player_moving,
7934 boolean any_player_snapping,
7935 boolean any_player_dropping)
7937 if (tape.single_step && tape.recording && !tape.pausing)
7938 if (frame == 0 && !any_player_dropping)
7939 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7941 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7942 any_player_snapping, any_player_dropping);
7945 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7946 boolean murphy_is_dropping)
7948 if (tape.single_step && tape.recording && !tape.pausing)
7949 if (murphy_is_waiting)
7950 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7952 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7955 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7956 int graphic, int sync_frame, int x, int y)
7958 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7960 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7963 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7965 return (IS_NEXT_FRAME(sync_frame, graphic));
7968 int getGraphicInfo_Delay(int graphic)
7970 return graphic_info[graphic].anim_delay;
7973 void PlayMenuSoundExt(int sound)
7975 if (sound == SND_UNDEFINED)
7978 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7979 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7982 if (IS_LOOP_SOUND(sound))
7983 PlaySoundLoop(sound);
7988 void PlayMenuSound()
7990 PlayMenuSoundExt(menu.sound[game_status]);
7993 void PlayMenuSoundStereo(int sound, int stereo_position)
7995 if (sound == SND_UNDEFINED)
7998 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7999 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8002 if (IS_LOOP_SOUND(sound))
8003 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8005 PlaySoundStereo(sound, stereo_position);
8008 void PlayMenuSoundIfLoopExt(int sound)
8010 if (sound == SND_UNDEFINED)
8013 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8014 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8017 if (IS_LOOP_SOUND(sound))
8018 PlaySoundLoop(sound);
8021 void PlayMenuSoundIfLoop()
8023 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8026 void PlayMenuMusicExt(int music)
8028 if (music == MUS_UNDEFINED)
8031 if (!setup.sound_music)
8037 void PlayMenuMusic()
8039 PlayMenuMusicExt(menu.music[game_status]);
8042 void PlaySoundActivating()
8045 PlaySound(SND_MENU_ITEM_ACTIVATING);
8049 void PlaySoundSelecting()
8052 PlaySound(SND_MENU_ITEM_SELECTING);
8056 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8058 boolean change_fullscreen = (setup.fullscreen !=
8059 video.fullscreen_enabled);
8060 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8061 !strEqual(setup.fullscreen_mode,
8062 video.fullscreen_mode_current));
8063 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8064 setup.window_scaling_percent !=
8065 video.window_scaling_percent);
8067 if (change_window_scaling_percent && video.fullscreen_enabled)
8070 if (!change_window_scaling_percent && !video.fullscreen_available)
8073 #if defined(TARGET_SDL2)
8074 if (change_window_scaling_percent)
8076 SDLSetWindowScaling(setup.window_scaling_percent);
8080 else if (change_fullscreen)
8082 SDLSetWindowFullscreen(setup.fullscreen);
8084 /* set setup value according to successfully changed fullscreen mode */
8085 setup.fullscreen = video.fullscreen_enabled;
8091 if (change_fullscreen ||
8092 change_fullscreen_mode ||
8093 change_window_scaling_percent)
8095 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8097 /* save backbuffer content which gets lost when toggling fullscreen mode */
8098 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8100 if (change_fullscreen_mode)
8102 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8103 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8106 if (change_window_scaling_percent)
8108 /* keep window mode, but change window scaling */
8109 video.fullscreen_enabled = TRUE; /* force new window scaling */
8112 /* toggle fullscreen */
8113 ChangeVideoModeIfNeeded(setup.fullscreen);
8115 /* set setup value according to successfully changed fullscreen mode */
8116 setup.fullscreen = video.fullscreen_enabled;
8118 /* restore backbuffer content from temporary backbuffer backup bitmap */
8119 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8121 FreeBitmap(tmp_backbuffer);
8123 /* update visible window/screen */
8124 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8128 void JoinRectangles(int *x, int *y, int *width, int *height,
8129 int x2, int y2, int width2, int height2)
8131 // do not join with "off-screen" rectangle
8132 if (x2 == -1 || y2 == -1)
8137 *width = MAX(*width, width2);
8138 *height = MAX(*height, height2);
8141 void ChangeViewportPropertiesIfNeeded()
8143 int gfx_game_mode = game_status;
8144 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8146 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8147 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8148 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8149 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8150 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8151 int new_win_xsize = vp_window->width;
8152 int new_win_ysize = vp_window->height;
8153 int border_size = vp_playfield->border_size;
8154 int new_sx = vp_playfield->x + border_size;
8155 int new_sy = vp_playfield->y + border_size;
8156 int new_sxsize = vp_playfield->width - 2 * border_size;
8157 int new_sysize = vp_playfield->height - 2 * border_size;
8158 int new_real_sx = vp_playfield->x;
8159 int new_real_sy = vp_playfield->y;
8160 int new_full_sxsize = vp_playfield->width;
8161 int new_full_sysize = vp_playfield->height;
8162 int new_dx = vp_door_1->x;
8163 int new_dy = vp_door_1->y;
8164 int new_dxsize = vp_door_1->width;
8165 int new_dysize = vp_door_1->height;
8166 int new_vx = vp_door_2->x;
8167 int new_vy = vp_door_2->y;
8168 int new_vxsize = vp_door_2->width;
8169 int new_vysize = vp_door_2->height;
8170 int new_ex = vp_door_3->x;
8171 int new_ey = vp_door_3->y;
8172 int new_exsize = vp_door_3->width;
8173 int new_eysize = vp_door_3->height;
8174 int new_tilesize_var =
8175 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8177 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8178 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8179 int new_scr_fieldx = new_sxsize / tilesize;
8180 int new_scr_fieldy = new_sysize / tilesize;
8181 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8182 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8183 boolean init_gfx_buffers = FALSE;
8184 boolean init_video_buffer = FALSE;
8185 boolean init_gadgets_and_toons = FALSE;
8186 boolean init_em_graphics = FALSE;
8188 if (new_win_xsize != WIN_XSIZE ||
8189 new_win_ysize != WIN_YSIZE)
8191 WIN_XSIZE = new_win_xsize;
8192 WIN_YSIZE = new_win_ysize;
8194 init_video_buffer = TRUE;
8195 init_gfx_buffers = TRUE;
8197 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8200 if (new_scr_fieldx != SCR_FIELDX ||
8201 new_scr_fieldy != SCR_FIELDY)
8203 /* this always toggles between MAIN and GAME when using small tile size */
8205 SCR_FIELDX = new_scr_fieldx;
8206 SCR_FIELDY = new_scr_fieldy;
8208 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8219 new_sxsize != SXSIZE ||
8220 new_sysize != SYSIZE ||
8221 new_dxsize != DXSIZE ||
8222 new_dysize != DYSIZE ||
8223 new_vxsize != VXSIZE ||
8224 new_vysize != VYSIZE ||
8225 new_exsize != EXSIZE ||
8226 new_eysize != EYSIZE ||
8227 new_real_sx != REAL_SX ||
8228 new_real_sy != REAL_SY ||
8229 new_full_sxsize != FULL_SXSIZE ||
8230 new_full_sysize != FULL_SYSIZE ||
8231 new_tilesize_var != TILESIZE_VAR
8234 // ------------------------------------------------------------------------
8235 // determine next fading area for changed viewport definitions
8236 // ------------------------------------------------------------------------
8238 // start with current playfield area (default fading area)
8241 FADE_SXSIZE = FULL_SXSIZE;
8242 FADE_SYSIZE = FULL_SYSIZE;
8244 // add new playfield area if position or size has changed
8245 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8246 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8248 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8249 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8252 // add current and new door 1 area if position or size has changed
8253 if (new_dx != DX || new_dy != DY ||
8254 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8256 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8257 DX, DY, DXSIZE, DYSIZE);
8258 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8259 new_dx, new_dy, new_dxsize, new_dysize);
8262 // add current and new door 2 area if position or size has changed
8263 if (new_dx != VX || new_dy != VY ||
8264 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8266 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8267 VX, VY, VXSIZE, VYSIZE);
8268 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8269 new_vx, new_vy, new_vxsize, new_vysize);
8272 // ------------------------------------------------------------------------
8273 // handle changed tile size
8274 // ------------------------------------------------------------------------
8276 if (new_tilesize_var != TILESIZE_VAR)
8278 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8280 // changing tile size invalidates scroll values of engine snapshots
8281 FreeEngineSnapshotSingle();
8283 // changing tile size requires update of graphic mapping for EM engine
8284 init_em_graphics = TRUE;
8295 SXSIZE = new_sxsize;
8296 SYSIZE = new_sysize;
8297 DXSIZE = new_dxsize;
8298 DYSIZE = new_dysize;
8299 VXSIZE = new_vxsize;
8300 VYSIZE = new_vysize;
8301 EXSIZE = new_exsize;
8302 EYSIZE = new_eysize;
8303 REAL_SX = new_real_sx;
8304 REAL_SY = new_real_sy;
8305 FULL_SXSIZE = new_full_sxsize;
8306 FULL_SYSIZE = new_full_sysize;
8307 TILESIZE_VAR = new_tilesize_var;
8309 init_gfx_buffers = TRUE;
8310 init_gadgets_and_toons = TRUE;
8312 // printf("::: viewports: init_gfx_buffers\n");
8313 // printf("::: viewports: init_gadgets_and_toons\n");
8316 if (init_gfx_buffers)
8318 // printf("::: init_gfx_buffers\n");
8320 SCR_FIELDX = new_scr_fieldx_buffers;
8321 SCR_FIELDY = new_scr_fieldy_buffers;
8325 SCR_FIELDX = new_scr_fieldx;
8326 SCR_FIELDY = new_scr_fieldy;
8328 SetDrawDeactivationMask(REDRAW_NONE);
8329 SetDrawBackgroundMask(REDRAW_FIELD);
8332 if (init_video_buffer)
8334 // printf("::: init_video_buffer\n");
8336 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8339 if (init_gadgets_and_toons)
8341 // printf("::: init_gadgets_and_toons\n");
8347 if (init_em_graphics)
8349 InitGraphicInfo_EM();