1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static char *print_if_not_empty(int element)
178 static char *s = NULL;
179 char *token_name = element_info[element].token_name;
184 s = checked_malloc(strlen(token_name) + 10 + 1);
186 if (element != EL_EMPTY)
187 sprintf(s, "%d\t['%s']", element, token_name);
189 sprintf(s, "%d", element);
194 void DumpTile(int x, int y)
199 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
205 printf_line("-", 79);
206 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
207 printf_line("-", 79);
209 if (!IN_LEV_FIELD(x, y))
211 printf("(not in level field)\n");
217 printf(" Feld: %d\t['%s']\n", Feld[x][y],
218 element_info[Feld[x][y]].token_name);
219 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
220 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
221 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
222 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
223 printf(" MovPos: %d\n", MovPos[x][y]);
224 printf(" MovDir: %d\n", MovDir[x][y]);
225 printf(" MovDelay: %d\n", MovDelay[x][y]);
226 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
227 printf(" CustomValue: %d\n", CustomValue[x][y]);
228 printf(" GfxElement: %d\n", GfxElement[x][y]);
229 printf(" GfxAction: %d\n", GfxAction[x][y]);
230 printf(" GfxFrame: %d\n", GfxFrame[x][y]);
234 void SetDrawtoField(int mode)
236 if (mode == DRAW_FIELDBUFFER)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
245 drawto_field = fieldbuffer;
247 else /* DRAW_BACKBUFFER */
253 BX2 = SCR_FIELDX - 1;
254 BY2 = SCR_FIELDY - 1;
256 drawto_field = backbuffer;
260 static void RedrawPlayfield_RND()
262 if (game.envelope_active)
265 DrawLevel(REDRAW_ALL);
269 void RedrawPlayfield()
271 if (game_status != GAME_MODE_PLAYING)
274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
275 RedrawPlayfield_EM(TRUE);
276 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
277 RedrawPlayfield_SP(TRUE);
278 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
279 RedrawPlayfield_RND();
281 BlitScreenToBitmap(backbuffer);
283 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
287 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
289 Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
291 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
294 void DrawMaskedBorder_FIELD()
296 if (global.border_status >= GAME_MODE_TITLE &&
297 global.border_status <= GAME_MODE_PLAYING &&
298 border.draw_masked[global.border_status])
299 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
302 void DrawMaskedBorder_DOOR_1()
304 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
305 (global.border_status != GAME_MODE_EDITOR ||
306 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
307 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
310 void DrawMaskedBorder_DOOR_2()
312 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
313 global.border_status != GAME_MODE_EDITOR)
314 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
317 void DrawMaskedBorder_DOOR_3()
319 /* currently not available */
322 void DrawMaskedBorder_ALL()
324 DrawMaskedBorder_FIELD();
325 DrawMaskedBorder_DOOR_1();
326 DrawMaskedBorder_DOOR_2();
327 DrawMaskedBorder_DOOR_3();
330 void DrawMaskedBorder(int redraw_mask)
332 /* never draw masked screen borders on borderless screens */
333 if (effectiveGameStatus() == GAME_MODE_LOADING ||
334 effectiveGameStatus() == GAME_MODE_TITLE)
337 if (redraw_mask & REDRAW_ALL)
338 DrawMaskedBorder_ALL();
341 if (redraw_mask & REDRAW_FIELD)
342 DrawMaskedBorder_FIELD();
343 if (redraw_mask & REDRAW_DOOR_1)
344 DrawMaskedBorder_DOOR_1();
345 if (redraw_mask & REDRAW_DOOR_2)
346 DrawMaskedBorder_DOOR_2();
347 if (redraw_mask & REDRAW_DOOR_3)
348 DrawMaskedBorder_DOOR_3();
352 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
354 int fx = FX, fy = FY;
355 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
356 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
358 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
359 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
360 int dx_var = dx * TILESIZE_VAR / TILESIZE;
361 int dy_var = dy * TILESIZE_VAR / TILESIZE;
364 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
365 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
367 if (EVEN(SCR_FIELDX))
369 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
370 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
372 fx += (dx_var > 0 ? TILEX_VAR : 0);
379 if (EVEN(SCR_FIELDY))
381 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
382 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
384 fy += (dy_var > 0 ? TILEY_VAR : 0);
391 if (full_lev_fieldx <= SCR_FIELDX)
393 if (EVEN(SCR_FIELDX))
394 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
396 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
399 if (full_lev_fieldy <= SCR_FIELDY)
401 if (EVEN(SCR_FIELDY))
402 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
404 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
407 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
410 void BlitScreenToBitmap(Bitmap *target_bitmap)
412 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
413 BlitScreenToBitmap_EM(target_bitmap);
414 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
415 BlitScreenToBitmap_SP(target_bitmap);
416 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
417 BlitScreenToBitmap_RND(target_bitmap);
419 redraw_mask |= REDRAW_FIELD;
422 void DrawFramesPerSecond()
425 int font_nr = FONT_TEXT_2;
426 int font_width = getFontWidth(font_nr);
428 sprintf(text, "%04.1f fps", global.frames_per_second);
430 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
431 font_nr, BLIT_OPAQUE);
436 if (redraw_mask == REDRAW_NONE)
439 // draw masked border to all viewports, if defined
440 DrawMaskedBorder(redraw_mask);
442 // draw frames per second (only if debug mode is enabled)
443 if (redraw_mask & REDRAW_FPS)
444 DrawFramesPerSecond();
446 // redraw complete window if both playfield and (some) doors need redraw
447 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
448 redraw_mask = REDRAW_ALL;
450 /* although redrawing the whole window would be fine for normal gameplay,
451 being able to only redraw the playfield is required for deactivating
452 certain drawing areas (mainly playfield) to work, which is needed for
453 warp-forward to be fast enough (by skipping redraw of most frames) */
455 if (redraw_mask & REDRAW_ALL)
457 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
459 else if (redraw_mask & REDRAW_FIELD)
461 BlitBitmap(backbuffer, window,
462 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
464 else if (redraw_mask & REDRAW_DOORS)
466 if (redraw_mask & REDRAW_DOOR_1)
467 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
469 if (redraw_mask & REDRAW_DOOR_2)
470 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
472 if (redraw_mask & REDRAW_DOOR_3)
473 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
476 redraw_mask = REDRAW_NONE;
479 static void FadeCrossSaveBackbuffer()
481 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
484 static void FadeCrossRestoreBackbuffer()
486 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
489 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
491 static int fade_type_skip = FADE_TYPE_NONE;
492 void (*draw_border_function)(void) = NULL;
493 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
494 int x, y, width, height;
495 int fade_delay, post_delay;
497 if (fade_type == FADE_TYPE_FADE_OUT)
499 if (fade_type_skip != FADE_TYPE_NONE)
501 /* skip all fade operations until specified fade operation */
502 if (fade_type & fade_type_skip)
503 fade_type_skip = FADE_TYPE_NONE;
509 FadeCrossSaveBackbuffer();
512 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
515 FadeCrossSaveBackbuffer();
522 redraw_mask |= fade_mask;
524 if (fade_type == FADE_TYPE_SKIP)
526 fade_type_skip = fade_mode;
531 fade_delay = fading.fade_delay;
532 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
534 if (fade_type_skip != FADE_TYPE_NONE)
536 /* skip all fade operations until specified fade operation */
537 if (fade_type & fade_type_skip)
538 fade_type_skip = FADE_TYPE_NONE;
543 if (global.autoplay_leveldir)
548 if (fade_mask == REDRAW_FIELD)
553 height = FULL_SYSIZE;
555 if (border.draw_masked_when_fading)
556 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
558 DrawMaskedBorder_FIELD(); /* draw once */
560 else /* REDRAW_ALL */
568 if (!setup.fade_screens ||
570 fading.fade_mode == FADE_MODE_NONE)
572 if (fade_mode == FADE_MODE_FADE_OUT)
575 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
577 redraw_mask &= ~fade_mask;
582 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
583 draw_border_function);
585 if (fade_type == FADE_TYPE_FADE_OUT)
586 FadeCrossRestoreBackbuffer();
588 redraw_mask &= ~fade_mask;
591 void FadeIn(int fade_mask)
593 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
594 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
596 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
599 void FadeOut(int fade_mask)
601 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
602 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
604 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
606 global.border_status = game_status;
609 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
611 static struct TitleFadingInfo fading_leave_stored;
614 fading_leave_stored = fading_leave;
616 fading = fading_leave_stored;
619 void FadeSetEnterMenu()
621 fading = menu.enter_menu;
623 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
626 void FadeSetLeaveMenu()
628 fading = menu.leave_menu;
630 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
633 void FadeSetEnterScreen()
635 fading = menu.enter_screen[game_status];
637 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
640 void FadeSetNextScreen()
642 fading = menu.next_screen;
644 // (do not overwrite fade mode set by FadeSetEnterScreen)
645 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
648 void FadeSetLeaveScreen()
650 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
653 void FadeSetFromType(int type)
655 if (type & TYPE_ENTER_SCREEN)
656 FadeSetEnterScreen();
657 else if (type & TYPE_ENTER)
659 else if (type & TYPE_LEAVE)
663 void FadeSetDisabled()
665 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
667 fading = fading_none;
670 void FadeSkipNextFadeIn()
672 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
675 void FadeSkipNextFadeOut()
677 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
680 void SetWindowBackgroundImageIfDefined(int graphic)
682 if (graphic_info[graphic].bitmap)
683 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
686 void SetMainBackgroundImageIfDefined(int graphic)
688 if (graphic_info[graphic].bitmap)
689 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
692 void SetDoorBackgroundImageIfDefined(int graphic)
694 if (graphic_info[graphic].bitmap)
695 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
698 void SetWindowBackgroundImage(int graphic)
700 SetWindowBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
701 graphic_info[graphic].bitmap ?
702 graphic_info[graphic].bitmap :
703 graphic_info[IMG_BACKGROUND].bitmap);
706 void SetMainBackgroundImage(int graphic)
708 SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
709 graphic_info[graphic].bitmap ?
710 graphic_info[graphic].bitmap :
711 graphic_info[IMG_BACKGROUND].bitmap);
714 void SetDoorBackgroundImage(int graphic)
716 SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
717 graphic_info[graphic].bitmap ?
718 graphic_info[graphic].bitmap :
719 graphic_info[IMG_BACKGROUND].bitmap);
722 void SetPanelBackground()
724 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
726 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
727 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
729 SetDoorBackgroundBitmap(bitmap_db_panel);
732 void DrawBackground(int x, int y, int width, int height)
734 /* "drawto" might still point to playfield buffer here (hall of fame) */
735 ClearRectangleOnBackground(backbuffer, x, y, width, height);
737 if (IN_GFX_FIELD_FULL(x, y))
738 redraw_mask |= REDRAW_FIELD;
739 else if (IN_GFX_DOOR_1(x, y))
740 redraw_mask |= REDRAW_DOOR_1;
741 else if (IN_GFX_DOOR_2(x, y))
742 redraw_mask |= REDRAW_DOOR_2;
743 else if (IN_GFX_DOOR_3(x, y))
744 redraw_mask |= REDRAW_DOOR_3;
747 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
749 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
751 if (font->bitmap == NULL)
754 DrawBackground(x, y, width, height);
757 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
759 struct GraphicInfo *g = &graphic_info[graphic];
761 if (g->bitmap == NULL)
764 DrawBackground(x, y, width, height);
767 static int game_status_last = -1;
768 static Bitmap *global_border_bitmap_last = NULL;
769 static Bitmap *global_border_bitmap = NULL;
770 static int real_sx_last = -1, real_sy_last = -1;
771 static int full_sxsize_last = -1, full_sysize_last = -1;
772 static int dx_last = -1, dy_last = -1;
773 static int dxsize_last = -1, dysize_last = -1;
774 static int vx_last = -1, vy_last = -1;
775 static int vxsize_last = -1, vysize_last = -1;
777 boolean CheckIfRedrawGlobalBorderIsNeeded()
779 int global_border_graphic;
781 if (game_status == game_status_last)
784 global_border_graphic =
785 (game_status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
786 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
787 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
788 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
791 global_border_bitmap =
792 (graphic_info[global_border_graphic].bitmap ?
793 graphic_info[global_border_graphic].bitmap :
794 graphic_info[IMG_GLOBAL_BORDER].bitmap);
796 // redraw if global screen border has changed
797 if (global_border_bitmap_last != global_border_bitmap)
800 // redraw if position or size of playfield area has changed
801 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
802 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
805 // redraw if position or size of door area has changed
806 if (dx_last != DX || dy_last != DY ||
807 dxsize_last != DXSIZE || dysize_last != DYSIZE)
810 // redraw if position or size of tape area has changed
811 if (vx_last != VX || vy_last != VY ||
812 vxsize_last != VXSIZE || vysize_last != VYSIZE)
818 static void RedrawGlobalBorderIfNeeded()
820 if (game_status == game_status_last)
823 // copy current draw buffer to later copy back areas that have not changed
824 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
826 if (CheckIfRedrawGlobalBorderIsNeeded())
828 // redraw global screen border (or clear, if defined to be empty)
830 if (global_border_bitmap)
831 BlitBitmap(global_border_bitmap, backbuffer,
832 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
834 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
836 // copy previous playfield and door areas, if they are defined on both
837 // previous and current screen and if they still have the same size
839 if (real_sx_last != -1 && real_sy_last != -1 &&
840 REAL_SX != -1 && REAL_SY != -1 &&
841 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
842 BlitBitmap(bitmap_db_store, backbuffer,
843 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
846 if (dx_last != -1 && dy_last != -1 &&
847 DX != -1 && DY != -1 &&
848 dxsize_last == DXSIZE && dysize_last == DYSIZE)
849 BlitBitmap(bitmap_db_store, backbuffer,
850 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
852 if (vx_last != -1 && vy_last != -1 &&
853 VX != -1 && VY != -1 &&
854 vxsize_last == VXSIZE && vysize_last == VYSIZE)
855 BlitBitmap(bitmap_db_store, backbuffer,
856 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
858 redraw_mask = REDRAW_ALL;
861 game_status_last = game_status;
863 global_border_bitmap_last = global_border_bitmap;
865 real_sx_last = REAL_SX;
866 real_sy_last = REAL_SY;
867 full_sxsize_last = FULL_SXSIZE;
868 full_sysize_last = FULL_SYSIZE;
871 dxsize_last = DXSIZE;
872 dysize_last = DYSIZE;
875 vxsize_last = VXSIZE;
876 vysize_last = VYSIZE;
881 RedrawGlobalBorderIfNeeded();
883 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
884 /* (when entering hall of fame after playing) */
885 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
887 /* !!! maybe this should be done before clearing the background !!! */
888 if (game_status == GAME_MODE_PLAYING)
890 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
891 SetDrawtoField(DRAW_FIELDBUFFER);
895 SetDrawtoField(DRAW_BACKBUFFER);
899 void MarkTileDirty(int x, int y)
901 redraw_mask |= REDRAW_FIELD;
904 void SetBorderElement()
908 BorderElement = EL_EMPTY;
910 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
912 for (x = 0; x < lev_fieldx; x++)
914 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
915 BorderElement = EL_STEELWALL;
917 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
923 void FloodFillLevel(int from_x, int from_y, int fill_element,
924 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
925 int max_fieldx, int max_fieldy)
929 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
930 static int safety = 0;
932 /* check if starting field still has the desired content */
933 if (field[from_x][from_y] == fill_element)
938 if (safety > max_fieldx * max_fieldy)
939 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
941 old_element = field[from_x][from_y];
942 field[from_x][from_y] = fill_element;
944 for (i = 0; i < 4; i++)
946 x = from_x + check[i][0];
947 y = from_y + check[i][1];
949 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
950 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
956 void SetRandomAnimationValue(int x, int y)
958 gfx.anim_random_frame = GfxRandom[x][y];
961 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
963 /* animation synchronized with global frame counter, not move position */
964 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
965 sync_frame = FrameCounter;
967 return getAnimationFrame(graphic_info[graphic].anim_frames,
968 graphic_info[graphic].anim_delay,
969 graphic_info[graphic].anim_mode,
970 graphic_info[graphic].anim_start_frame,
974 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
975 Bitmap **bitmap, int *x, int *y,
976 boolean get_backside)
978 struct GraphicInfo *g = &graphic_info[graphic];
979 Bitmap *src_bitmap = g->bitmap;
980 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
981 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
982 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
984 // if no in-game graphics defined, always use standard graphic size
985 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
988 if (tilesize == gfx.standard_tile_size)
989 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
990 else if (tilesize == game.tile_size)
991 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
993 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
995 if (g->offset_y == 0) /* frames are ordered horizontally */
997 int max_width = g->anim_frames_per_line * g->width;
998 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1000 src_x = pos % max_width;
1001 src_y = src_y % g->height + pos / max_width * g->height;
1003 else if (g->offset_x == 0) /* frames are ordered vertically */
1005 int max_height = g->anim_frames_per_line * g->height;
1006 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1008 src_x = src_x % g->width + pos / max_height * g->width;
1009 src_y = pos % max_height;
1011 else /* frames are ordered diagonally */
1013 src_x = src_x + frame * g->offset_x;
1014 src_y = src_y + frame * g->offset_y;
1017 *bitmap = src_bitmap;
1018 *x = src_x * tilesize / TILESIZE;
1019 *y = src_y * tilesize / TILESIZE;
1022 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1023 int *x, int *y, boolean get_backside)
1025 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1029 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1030 Bitmap **bitmap, int *x, int *y)
1032 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1035 void getFixedGraphicSource(int graphic, int frame,
1036 Bitmap **bitmap, int *x, int *y)
1038 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1041 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1043 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1046 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1047 int *x, int *y, boolean get_backside)
1049 struct GraphicInfo *g = &graphic_info[graphic];
1050 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1051 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1053 if (TILESIZE_VAR != TILESIZE)
1054 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1057 *bitmap = g->bitmap;
1059 if (g->offset_y == 0) /* frames are ordered horizontally */
1061 int max_width = g->anim_frames_per_line * g->width;
1062 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1064 *x = pos % max_width;
1065 *y = src_y % g->height + pos / max_width * g->height;
1067 else if (g->offset_x == 0) /* frames are ordered vertically */
1069 int max_height = g->anim_frames_per_line * g->height;
1070 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1072 *x = src_x % g->width + pos / max_height * g->width;
1073 *y = pos % max_height;
1075 else /* frames are ordered diagonally */
1077 *x = src_x + frame * g->offset_x;
1078 *y = src_y + frame * g->offset_y;
1082 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1084 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1087 void DrawGraphic(int x, int y, int graphic, int frame)
1090 if (!IN_SCR_FIELD(x, y))
1092 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1093 printf("DrawGraphic(): This should never happen!\n");
1098 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1101 MarkTileDirty(x, y);
1104 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1107 if (!IN_SCR_FIELD(x, y))
1109 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1110 printf("DrawGraphic(): This should never happen!\n");
1115 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1117 MarkTileDirty(x, y);
1120 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1126 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1128 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1131 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1137 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1138 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1141 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1144 if (!IN_SCR_FIELD(x, y))
1146 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1147 printf("DrawGraphicThruMask(): This should never happen!\n");
1152 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1155 MarkTileDirty(x, y);
1158 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1161 if (!IN_SCR_FIELD(x, y))
1163 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1164 printf("DrawGraphicThruMask(): This should never happen!\n");
1169 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1171 MarkTileDirty(x, y);
1174 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1180 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1182 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1186 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1187 int graphic, int frame)
1189 struct GraphicInfo *g = &graphic_info[graphic];
1193 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1195 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1199 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1201 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1203 MarkTileDirty(x / tilesize, y / tilesize);
1206 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1212 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1213 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1216 void DrawMiniGraphic(int x, int y, int graphic)
1218 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1219 MarkTileDirty(x / 2, y / 2);
1222 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1227 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1228 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1231 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1232 int graphic, int frame,
1233 int cut_mode, int mask_mode)
1238 int width = TILEX, height = TILEY;
1241 if (dx || dy) /* shifted graphic */
1243 if (x < BX1) /* object enters playfield from the left */
1250 else if (x > BX2) /* object enters playfield from the right */
1256 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1262 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1264 else if (dx) /* general horizontal movement */
1265 MarkTileDirty(x + SIGN(dx), y);
1267 if (y < BY1) /* object enters playfield from the top */
1269 if (cut_mode==CUT_BELOW) /* object completely above top border */
1277 else if (y > BY2) /* object enters playfield from the bottom */
1283 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1289 else if (dy > 0 && cut_mode == CUT_ABOVE)
1291 if (y == BY2) /* object completely above bottom border */
1297 MarkTileDirty(x, y + 1);
1298 } /* object leaves playfield to the bottom */
1299 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1301 else if (dy) /* general vertical movement */
1302 MarkTileDirty(x, y + SIGN(dy));
1306 if (!IN_SCR_FIELD(x, y))
1308 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1309 printf("DrawGraphicShifted(): This should never happen!\n");
1314 width = width * TILESIZE_VAR / TILESIZE;
1315 height = height * TILESIZE_VAR / TILESIZE;
1316 cx = cx * TILESIZE_VAR / TILESIZE;
1317 cy = cy * TILESIZE_VAR / TILESIZE;
1318 dx = dx * TILESIZE_VAR / TILESIZE;
1319 dy = dy * TILESIZE_VAR / TILESIZE;
1321 if (width > 0 && height > 0)
1323 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1328 dst_x = FX + x * TILEX_VAR + dx;
1329 dst_y = FY + y * TILEY_VAR + dy;
1331 if (mask_mode == USE_MASKING)
1332 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1335 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1338 MarkTileDirty(x, y);
1342 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1343 int graphic, int frame,
1344 int cut_mode, int mask_mode)
1349 int width = TILEX_VAR, height = TILEY_VAR;
1352 int x2 = x + SIGN(dx);
1353 int y2 = y + SIGN(dy);
1355 /* movement with two-tile animations must be sync'ed with movement position,
1356 not with current GfxFrame (which can be higher when using slow movement) */
1357 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1358 int anim_frames = graphic_info[graphic].anim_frames;
1360 /* (we also need anim_delay here for movement animations with less frames) */
1361 int anim_delay = graphic_info[graphic].anim_delay;
1362 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1364 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1365 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1367 /* re-calculate animation frame for two-tile movement animation */
1368 frame = getGraphicAnimationFrame(graphic, sync_frame);
1370 /* check if movement start graphic inside screen area and should be drawn */
1371 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1373 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1375 dst_x = FX + x1 * TILEX_VAR;
1376 dst_y = FY + y1 * TILEY_VAR;
1378 if (mask_mode == USE_MASKING)
1379 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1382 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1385 MarkTileDirty(x1, y1);
1388 /* check if movement end graphic inside screen area and should be drawn */
1389 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1391 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1393 dst_x = FX + x2 * TILEX_VAR;
1394 dst_y = FY + y2 * TILEY_VAR;
1396 if (mask_mode == USE_MASKING)
1397 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1400 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1403 MarkTileDirty(x2, y2);
1407 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1408 int graphic, int frame,
1409 int cut_mode, int mask_mode)
1413 DrawGraphic(x, y, graphic, frame);
1418 if (graphic_info[graphic].double_movement) /* EM style movement images */
1419 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1421 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1424 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1425 int frame, int cut_mode)
1427 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1430 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1431 int cut_mode, int mask_mode)
1433 int lx = LEVELX(x), ly = LEVELY(y);
1437 if (IN_LEV_FIELD(lx, ly))
1439 SetRandomAnimationValue(lx, ly);
1441 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1442 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1444 /* do not use double (EM style) movement graphic when not moving */
1445 if (graphic_info[graphic].double_movement && !dx && !dy)
1447 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1448 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1451 else /* border element */
1453 graphic = el2img(element);
1454 frame = getGraphicAnimationFrame(graphic, -1);
1457 if (element == EL_EXPANDABLE_WALL)
1459 boolean left_stopped = FALSE, right_stopped = FALSE;
1461 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1462 left_stopped = TRUE;
1463 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1464 right_stopped = TRUE;
1466 if (left_stopped && right_stopped)
1468 else if (left_stopped)
1470 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1471 frame = graphic_info[graphic].anim_frames - 1;
1473 else if (right_stopped)
1475 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1476 frame = graphic_info[graphic].anim_frames - 1;
1481 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1482 else if (mask_mode == USE_MASKING)
1483 DrawGraphicThruMask(x, y, graphic, frame);
1485 DrawGraphic(x, y, graphic, frame);
1488 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1489 int cut_mode, int mask_mode)
1491 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1492 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1493 cut_mode, mask_mode);
1496 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1499 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1502 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1505 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1508 void DrawLevelElementThruMask(int x, int y, int element)
1510 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1513 void DrawLevelFieldThruMask(int x, int y)
1515 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1518 /* !!! implementation of quicksand is totally broken !!! */
1519 #define IS_CRUMBLED_TILE(x, y, e) \
1520 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1521 !IS_MOVING(x, y) || \
1522 (e) == EL_QUICKSAND_EMPTYING || \
1523 (e) == EL_QUICKSAND_FAST_EMPTYING))
1525 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1530 int width, height, cx, cy;
1531 int sx = SCREENX(x), sy = SCREENY(y);
1532 int crumbled_border_size = graphic_info[graphic].border_size;
1535 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1537 for (i = 1; i < 4; i++)
1539 int dxx = (i & 1 ? dx : 0);
1540 int dyy = (i & 2 ? dy : 0);
1543 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1546 /* check if neighbour field is of same crumble type */
1547 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1548 graphic_info[graphic].class ==
1549 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1551 /* return if check prevents inner corner */
1552 if (same == (dxx == dx && dyy == dy))
1556 /* if we reach this point, we have an inner corner */
1558 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1560 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1561 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1562 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1563 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1565 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1566 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1569 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1574 int width, height, bx, by, cx, cy;
1575 int sx = SCREENX(x), sy = SCREENY(y);
1576 int crumbled_border_size = graphic_info[graphic].border_size;
1577 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1578 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1581 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1583 /* draw simple, sloppy, non-corner-accurate crumbled border */
1585 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1586 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1587 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1588 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1590 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1591 FX + sx * TILEX_VAR + cx,
1592 FY + sy * TILEY_VAR + cy);
1594 /* (remaining middle border part must be at least as big as corner part) */
1595 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1596 crumbled_border_size >= TILESIZE / 3)
1599 /* correct corners of crumbled border, if needed */
1601 for (i = -1; i <= 1; i += 2)
1603 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1604 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1605 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1608 /* check if neighbour field is of same crumble type */
1609 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1610 graphic_info[graphic].class ==
1611 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1613 /* no crumbled corner, but continued crumbled border */
1615 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1616 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1617 int b1 = (i == 1 ? crumbled_border_size_var :
1618 TILESIZE_VAR - 2 * crumbled_border_size_var);
1620 width = crumbled_border_size_var;
1621 height = crumbled_border_size_var;
1623 if (dir == 1 || dir == 2)
1638 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1640 FX + sx * TILEX_VAR + cx,
1641 FY + sy * TILEY_VAR + cy);
1646 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1648 int sx = SCREENX(x), sy = SCREENY(y);
1651 static int xy[4][2] =
1659 if (!IN_LEV_FIELD(x, y))
1662 element = TILE_GFX_ELEMENT(x, y);
1664 /* crumble field itself */
1665 if (IS_CRUMBLED_TILE(x, y, element))
1667 if (!IN_SCR_FIELD(sx, sy))
1670 for (i = 0; i < 4; i++)
1672 int xx = x + xy[i][0];
1673 int yy = y + xy[i][1];
1675 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1678 /* check if neighbour field is of same crumble type */
1679 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1680 graphic_info[graphic].class ==
1681 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1684 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1687 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1688 graphic_info[graphic].anim_frames == 2)
1690 for (i = 0; i < 4; i++)
1692 int dx = (i & 1 ? +1 : -1);
1693 int dy = (i & 2 ? +1 : -1);
1695 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1699 MarkTileDirty(sx, sy);
1701 else /* center field not crumbled -- crumble neighbour fields */
1703 for (i = 0; i < 4; i++)
1705 int xx = x + xy[i][0];
1706 int yy = y + xy[i][1];
1707 int sxx = sx + xy[i][0];
1708 int syy = sy + xy[i][1];
1710 if (!IN_LEV_FIELD(xx, yy) ||
1711 !IN_SCR_FIELD(sxx, syy))
1714 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1717 element = TILE_GFX_ELEMENT(xx, yy);
1719 if (!IS_CRUMBLED_TILE(xx, yy, element))
1722 graphic = el_act2crm(element, ACTION_DEFAULT);
1724 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1726 MarkTileDirty(sxx, syy);
1731 void DrawLevelFieldCrumbled(int x, int y)
1735 if (!IN_LEV_FIELD(x, y))
1738 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1739 GfxElement[x][y] != EL_UNDEFINED &&
1740 GFX_CRUMBLED(GfxElement[x][y]))
1742 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1747 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1749 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1752 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1755 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1756 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1757 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1758 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1759 int sx = SCREENX(x), sy = SCREENY(y);
1761 DrawGraphic(sx, sy, graphic1, frame1);
1762 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1765 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1767 int sx = SCREENX(x), sy = SCREENY(y);
1768 static int xy[4][2] =
1777 for (i = 0; i < 4; i++)
1779 int xx = x + xy[i][0];
1780 int yy = y + xy[i][1];
1781 int sxx = sx + xy[i][0];
1782 int syy = sy + xy[i][1];
1784 if (!IN_LEV_FIELD(xx, yy) ||
1785 !IN_SCR_FIELD(sxx, syy) ||
1786 !GFX_CRUMBLED(Feld[xx][yy]) ||
1790 DrawLevelField(xx, yy);
1794 static int getBorderElement(int x, int y)
1798 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1799 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1800 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1801 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1802 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1803 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1804 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1806 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1807 int steel_position = (x == -1 && y == -1 ? 0 :
1808 x == lev_fieldx && y == -1 ? 1 :
1809 x == -1 && y == lev_fieldy ? 2 :
1810 x == lev_fieldx && y == lev_fieldy ? 3 :
1811 x == -1 || x == lev_fieldx ? 4 :
1812 y == -1 || y == lev_fieldy ? 5 : 6);
1814 return border[steel_position][steel_type];
1817 void DrawScreenElement(int x, int y, int element)
1819 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1820 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1823 void DrawLevelElement(int x, int y, int element)
1825 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1826 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1829 void DrawScreenField(int x, int y)
1831 int lx = LEVELX(x), ly = LEVELY(y);
1832 int element, content;
1834 if (!IN_LEV_FIELD(lx, ly))
1836 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1839 element = getBorderElement(lx, ly);
1841 DrawScreenElement(x, y, element);
1846 element = Feld[lx][ly];
1847 content = Store[lx][ly];
1849 if (IS_MOVING(lx, ly))
1851 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1852 boolean cut_mode = NO_CUTTING;
1854 if (element == EL_QUICKSAND_EMPTYING ||
1855 element == EL_QUICKSAND_FAST_EMPTYING ||
1856 element == EL_MAGIC_WALL_EMPTYING ||
1857 element == EL_BD_MAGIC_WALL_EMPTYING ||
1858 element == EL_DC_MAGIC_WALL_EMPTYING ||
1859 element == EL_AMOEBA_DROPPING)
1860 cut_mode = CUT_ABOVE;
1861 else if (element == EL_QUICKSAND_FILLING ||
1862 element == EL_QUICKSAND_FAST_FILLING ||
1863 element == EL_MAGIC_WALL_FILLING ||
1864 element == EL_BD_MAGIC_WALL_FILLING ||
1865 element == EL_DC_MAGIC_WALL_FILLING)
1866 cut_mode = CUT_BELOW;
1868 if (cut_mode == CUT_ABOVE)
1869 DrawScreenElement(x, y, element);
1871 DrawScreenElement(x, y, EL_EMPTY);
1874 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1875 else if (cut_mode == NO_CUTTING)
1876 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1879 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1881 if (cut_mode == CUT_BELOW &&
1882 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1883 DrawLevelElement(lx, ly + 1, element);
1886 if (content == EL_ACID)
1888 int dir = MovDir[lx][ly];
1889 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1890 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1892 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1895 else if (IS_BLOCKED(lx, ly))
1900 boolean cut_mode = NO_CUTTING;
1901 int element_old, content_old;
1903 Blocked2Moving(lx, ly, &oldx, &oldy);
1906 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1907 MovDir[oldx][oldy] == MV_RIGHT);
1909 element_old = Feld[oldx][oldy];
1910 content_old = Store[oldx][oldy];
1912 if (element_old == EL_QUICKSAND_EMPTYING ||
1913 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1914 element_old == EL_MAGIC_WALL_EMPTYING ||
1915 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1916 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1917 element_old == EL_AMOEBA_DROPPING)
1918 cut_mode = CUT_ABOVE;
1920 DrawScreenElement(x, y, EL_EMPTY);
1923 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1925 else if (cut_mode == NO_CUTTING)
1926 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1929 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1932 else if (IS_DRAWABLE(element))
1933 DrawScreenElement(x, y, element);
1935 DrawScreenElement(x, y, EL_EMPTY);
1938 void DrawLevelField(int x, int y)
1940 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1941 DrawScreenField(SCREENX(x), SCREENY(y));
1942 else if (IS_MOVING(x, y))
1946 Moving2Blocked(x, y, &newx, &newy);
1947 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1948 DrawScreenField(SCREENX(newx), SCREENY(newy));
1950 else if (IS_BLOCKED(x, y))
1954 Blocked2Moving(x, y, &oldx, &oldy);
1955 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
1956 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
1960 void DrawSizedElement(int x, int y, int element, int tilesize)
1964 graphic = el2edimg(element);
1965 DrawSizedGraphic(x, y, graphic, 0, tilesize);
1968 void DrawMiniElement(int x, int y, int element)
1972 graphic = el2edimg(element);
1973 DrawMiniGraphic(x, y, graphic);
1976 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
1979 int x = sx + scroll_x, y = sy + scroll_y;
1981 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
1982 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
1983 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
1984 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
1986 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
1989 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
1991 int x = sx + scroll_x, y = sy + scroll_y;
1993 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
1994 DrawMiniElement(sx, sy, EL_EMPTY);
1995 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
1996 DrawMiniElement(sx, sy, Feld[x][y]);
1998 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2001 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2002 int x, int y, int xsize, int ysize,
2003 int tile_width, int tile_height)
2007 int dst_x = startx + x * tile_width;
2008 int dst_y = starty + y * tile_height;
2009 int width = graphic_info[graphic].width;
2010 int height = graphic_info[graphic].height;
2011 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2012 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2013 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2014 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2015 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2016 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2017 boolean draw_masked = graphic_info[graphic].draw_masked;
2019 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2021 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2023 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2027 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2028 inner_sx + (x - 1) * tile_width % inner_width);
2029 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2030 inner_sy + (y - 1) * tile_height % inner_height);
2033 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2036 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2040 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2041 int x, int y, int xsize, int ysize, int font_nr)
2043 int font_width = getFontWidth(font_nr);
2044 int font_height = getFontHeight(font_nr);
2046 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2047 font_width, font_height);
2050 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2052 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2053 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2054 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2055 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2056 boolean no_delay = (tape.warp_forward);
2057 unsigned int anim_delay = 0;
2058 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2059 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2060 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2061 int font_width = getFontWidth(font_nr);
2062 int font_height = getFontHeight(font_nr);
2063 int max_xsize = level.envelope[envelope_nr].xsize;
2064 int max_ysize = level.envelope[envelope_nr].ysize;
2065 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2066 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2067 int xend = max_xsize;
2068 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2069 int xstep = (xstart < xend ? 1 : 0);
2070 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2072 int end = MAX(xend - xstart, yend - ystart);
2075 for (i = start; i <= end; i++)
2077 int last_frame = end; // last frame of this "for" loop
2078 int x = xstart + i * xstep;
2079 int y = ystart + i * ystep;
2080 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2081 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2082 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2083 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2086 SetDrawtoField(DRAW_FIELDBUFFER);
2088 BlitScreenToBitmap(backbuffer);
2090 SetDrawtoField(DRAW_BACKBUFFER);
2092 for (yy = 0; yy < ysize; yy++)
2093 for (xx = 0; xx < xsize; xx++)
2094 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2096 DrawTextBuffer(sx + font_width, sy + font_height,
2097 level.envelope[envelope_nr].text, font_nr, max_xsize,
2098 xsize - 2, ysize - 2, 0, mask_mode,
2099 level.envelope[envelope_nr].autowrap,
2100 level.envelope[envelope_nr].centered, FALSE);
2102 redraw_mask |= REDRAW_FIELD;
2105 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2109 void ShowEnvelope(int envelope_nr)
2111 int element = EL_ENVELOPE_1 + envelope_nr;
2112 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2113 int sound_opening = element_info[element].sound[ACTION_OPENING];
2114 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2115 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2116 boolean no_delay = (tape.warp_forward);
2117 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2118 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2119 int anim_mode = graphic_info[graphic].anim_mode;
2120 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2121 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2123 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2125 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2127 if (anim_mode == ANIM_DEFAULT)
2128 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2130 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2133 Delay(wait_delay_value);
2135 WaitForEventToContinue();
2137 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2139 if (anim_mode != ANIM_NONE)
2140 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2142 if (anim_mode == ANIM_DEFAULT)
2143 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2145 game.envelope_active = FALSE;
2147 SetDrawtoField(DRAW_FIELDBUFFER);
2149 redraw_mask |= REDRAW_FIELD;
2153 static void setRequestBasePosition(int *x, int *y)
2155 int sx_base, sy_base;
2157 if (request.x != -1)
2158 sx_base = request.x;
2159 else if (request.align == ALIGN_LEFT)
2161 else if (request.align == ALIGN_RIGHT)
2162 sx_base = SX + SXSIZE;
2164 sx_base = SX + SXSIZE / 2;
2166 if (request.y != -1)
2167 sy_base = request.y;
2168 else if (request.valign == VALIGN_TOP)
2170 else if (request.valign == VALIGN_BOTTOM)
2171 sy_base = SY + SYSIZE;
2173 sy_base = SY + SYSIZE / 2;
2179 static void setRequestPositionExt(int *x, int *y, int width, int height,
2180 boolean add_border_size)
2182 int border_size = request.border_size;
2183 int sx_base, sy_base;
2186 setRequestBasePosition(&sx_base, &sy_base);
2188 if (request.align == ALIGN_LEFT)
2190 else if (request.align == ALIGN_RIGHT)
2191 sx = sx_base - width;
2193 sx = sx_base - width / 2;
2195 if (request.valign == VALIGN_TOP)
2197 else if (request.valign == VALIGN_BOTTOM)
2198 sy = sy_base - height;
2200 sy = sy_base - height / 2;
2202 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2203 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2205 if (add_border_size)
2215 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2217 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2220 void DrawEnvelopeRequest(char *text)
2222 char *text_final = text;
2223 char *text_door_style = NULL;
2224 int graphic = IMG_BACKGROUND_REQUEST;
2225 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2226 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2227 int font_nr = FONT_REQUEST;
2228 int font_width = getFontWidth(font_nr);
2229 int font_height = getFontHeight(font_nr);
2230 int border_size = request.border_size;
2231 int line_spacing = request.line_spacing;
2232 int line_height = font_height + line_spacing;
2233 int text_width = request.width - 2 * border_size;
2234 int text_height = request.height - 2 * border_size;
2235 int line_length = text_width / font_width;
2236 int max_lines = text_height / line_height;
2237 int width = request.width;
2238 int height = request.height;
2239 int tile_size = request.step_offset;
2240 int x_steps = width / tile_size;
2241 int y_steps = height / tile_size;
2245 if (request.wrap_single_words)
2247 char *src_text_ptr, *dst_text_ptr;
2249 text_door_style = checked_malloc(2 * strlen(text) + 1);
2251 src_text_ptr = text;
2252 dst_text_ptr = text_door_style;
2254 while (*src_text_ptr)
2256 if (*src_text_ptr == ' ' ||
2257 *src_text_ptr == '?' ||
2258 *src_text_ptr == '!')
2259 *dst_text_ptr++ = '\n';
2261 if (*src_text_ptr != ' ')
2262 *dst_text_ptr++ = *src_text_ptr;
2267 *dst_text_ptr = '\0';
2269 text_final = text_door_style;
2272 setRequestPosition(&sx, &sy, FALSE);
2274 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2276 for (y = 0; y < y_steps; y++)
2277 for (x = 0; x < x_steps; x++)
2278 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2279 x, y, x_steps, y_steps,
2280 tile_size, tile_size);
2282 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2283 line_length, -1, max_lines, line_spacing, mask_mode,
2284 request.autowrap, request.centered, FALSE);
2286 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2287 RedrawGadget(tool_gadget[i]);
2289 // store readily prepared envelope request for later use when animating
2290 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2292 if (text_door_style)
2293 free(text_door_style);
2296 void AnimateEnvelopeRequest(int anim_mode, int action)
2298 int graphic = IMG_BACKGROUND_REQUEST;
2299 boolean draw_masked = graphic_info[graphic].draw_masked;
2300 int delay_value_normal = request.step_delay;
2301 int delay_value_fast = delay_value_normal / 2;
2302 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2303 boolean no_delay = (tape.warp_forward);
2304 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2305 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2306 unsigned int anim_delay = 0;
2308 int tile_size = request.step_offset;
2309 int max_xsize = request.width / tile_size;
2310 int max_ysize = request.height / tile_size;
2311 int max_xsize_inner = max_xsize - 2;
2312 int max_ysize_inner = max_ysize - 2;
2314 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2315 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2316 int xend = max_xsize_inner;
2317 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2318 int xstep = (xstart < xend ? 1 : 0);
2319 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2321 int end = MAX(xend - xstart, yend - ystart);
2324 if (setup.quick_doors)
2332 if (action == ACTION_OPENING)
2333 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2334 else if (action == ACTION_CLOSING)
2335 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2338 for (i = start; i <= end; i++)
2340 int last_frame = end; // last frame of this "for" loop
2341 int x = xstart + i * xstep;
2342 int y = ystart + i * ystep;
2343 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2344 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2345 int xsize_size_left = (xsize - 1) * tile_size;
2346 int ysize_size_top = (ysize - 1) * tile_size;
2347 int max_xsize_pos = (max_xsize - 1) * tile_size;
2348 int max_ysize_pos = (max_ysize - 1) * tile_size;
2349 int width = xsize * tile_size;
2350 int height = ysize * tile_size;
2355 setRequestPosition(&src_x, &src_y, FALSE);
2356 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2358 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2360 for (yy = 0; yy < 2; yy++)
2362 for (xx = 0; xx < 2; xx++)
2364 int src_xx = src_x + xx * max_xsize_pos;
2365 int src_yy = src_y + yy * max_ysize_pos;
2366 int dst_xx = dst_x + xx * xsize_size_left;
2367 int dst_yy = dst_y + yy * ysize_size_top;
2368 int xx_size = (xx ? tile_size : xsize_size_left);
2369 int yy_size = (yy ? tile_size : ysize_size_top);
2372 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2373 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2375 BlitBitmap(bitmap_db_cross, backbuffer,
2376 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2380 redraw_mask |= REDRAW_FIELD;
2385 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2389 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2391 int last_game_status = game_status; /* save current game status */
2392 int graphic = IMG_BACKGROUND_REQUEST;
2393 int sound_opening = SND_REQUEST_OPENING;
2394 int sound_closing = SND_REQUEST_CLOSING;
2395 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2396 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2397 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2398 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2399 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2401 if (game_status == GAME_MODE_PLAYING)
2402 BlitScreenToBitmap(backbuffer);
2404 SetDrawtoField(DRAW_BACKBUFFER);
2406 // SetDrawBackgroundMask(REDRAW_NONE);
2408 if (action == ACTION_OPENING)
2410 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2412 if (req_state & REQ_ASK)
2414 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2415 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2417 else if (req_state & REQ_CONFIRM)
2419 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2421 else if (req_state & REQ_PLAYER)
2423 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2424 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2425 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2426 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2429 DrawEnvelopeRequest(text);
2431 if (game_status != GAME_MODE_MAIN)
2435 /* force DOOR font inside door area */
2436 game_status = GAME_MODE_PSEUDO_DOOR;
2438 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2440 if (action == ACTION_OPENING)
2442 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2444 if (anim_mode == ANIM_DEFAULT)
2445 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2447 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2451 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2453 if (anim_mode != ANIM_NONE)
2454 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2456 if (anim_mode == ANIM_DEFAULT)
2457 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2460 game.envelope_active = FALSE;
2462 game_status = last_game_status; /* restore current game status */
2464 if (action == ACTION_CLOSING)
2466 if (game_status != GAME_MODE_MAIN)
2469 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2472 // SetDrawBackgroundMask(last_draw_background_mask);
2474 redraw_mask |= REDRAW_FIELD;
2476 if (game_status == GAME_MODE_MAIN)
2481 if (action == ACTION_CLOSING &&
2482 game_status == GAME_MODE_PLAYING &&
2483 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2484 SetDrawtoField(DRAW_FIELDBUFFER);
2487 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2491 int graphic = el2preimg(element);
2493 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2494 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2497 void DrawLevel(int draw_background_mask)
2501 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2502 SetDrawBackgroundMask(draw_background_mask);
2506 for (x = BX1; x <= BX2; x++)
2507 for (y = BY1; y <= BY2; y++)
2508 DrawScreenField(x, y);
2510 redraw_mask |= REDRAW_FIELD;
2513 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2518 for (x = 0; x < size_x; x++)
2519 for (y = 0; y < size_y; y++)
2520 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2522 redraw_mask |= REDRAW_FIELD;
2525 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2529 for (x = 0; x < size_x; x++)
2530 for (y = 0; y < size_y; y++)
2531 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2533 redraw_mask |= REDRAW_FIELD;
2536 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2538 boolean show_level_border = (BorderElement != EL_EMPTY);
2539 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2540 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2541 int tile_size = preview.tile_size;
2542 int preview_width = preview.xsize * tile_size;
2543 int preview_height = preview.ysize * tile_size;
2544 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2545 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2546 int real_preview_width = real_preview_xsize * tile_size;
2547 int real_preview_height = real_preview_ysize * tile_size;
2548 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2549 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2552 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2555 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2557 dst_x += (preview_width - real_preview_width) / 2;
2558 dst_y += (preview_height - real_preview_height) / 2;
2560 for (x = 0; x < real_preview_xsize; x++)
2562 for (y = 0; y < real_preview_ysize; y++)
2564 int lx = from_x + x + (show_level_border ? -1 : 0);
2565 int ly = from_y + y + (show_level_border ? -1 : 0);
2566 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2567 getBorderElement(lx, ly));
2569 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2570 element, tile_size);
2574 redraw_mask |= REDRAW_FIELD;
2577 #define MICROLABEL_EMPTY 0
2578 #define MICROLABEL_LEVEL_NAME 1
2579 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2580 #define MICROLABEL_LEVEL_AUTHOR 3
2581 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2582 #define MICROLABEL_IMPORTED_FROM 5
2583 #define MICROLABEL_IMPORTED_BY_HEAD 6
2584 #define MICROLABEL_IMPORTED_BY 7
2586 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2588 int max_text_width = SXSIZE;
2589 int font_width = getFontWidth(font_nr);
2591 if (pos->align == ALIGN_CENTER)
2592 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2593 else if (pos->align == ALIGN_RIGHT)
2594 max_text_width = pos->x;
2596 max_text_width = SXSIZE - pos->x;
2598 return max_text_width / font_width;
2601 static void DrawPreviewLevelLabelExt(int mode)
2603 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2604 char label_text[MAX_OUTPUT_LINESIZE + 1];
2605 int max_len_label_text;
2606 int font_nr = pos->font;
2609 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2612 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2613 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2614 mode == MICROLABEL_IMPORTED_BY_HEAD)
2615 font_nr = pos->font_alt;
2617 max_len_label_text = getMaxTextLength(pos, font_nr);
2619 if (pos->size != -1)
2620 max_len_label_text = pos->size;
2622 for (i = 0; i < max_len_label_text; i++)
2623 label_text[i] = ' ';
2624 label_text[max_len_label_text] = '\0';
2626 if (strlen(label_text) > 0)
2627 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2630 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2631 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2632 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2633 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2634 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2635 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2636 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2637 max_len_label_text);
2638 label_text[max_len_label_text] = '\0';
2640 if (strlen(label_text) > 0)
2641 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2643 redraw_mask |= REDRAW_FIELD;
2646 static void DrawPreviewLevelExt(boolean restart)
2648 static unsigned int scroll_delay = 0;
2649 static unsigned int label_delay = 0;
2650 static int from_x, from_y, scroll_direction;
2651 static int label_state, label_counter;
2652 unsigned int scroll_delay_value = preview.step_delay;
2653 boolean show_level_border = (BorderElement != EL_EMPTY);
2654 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2655 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2656 int last_game_status = game_status; /* save current game status */
2663 if (preview.anim_mode == ANIM_CENTERED)
2665 if (level_xsize > preview.xsize)
2666 from_x = (level_xsize - preview.xsize) / 2;
2667 if (level_ysize > preview.ysize)
2668 from_y = (level_ysize - preview.ysize) / 2;
2671 from_x += preview.xoffset;
2672 from_y += preview.yoffset;
2674 scroll_direction = MV_RIGHT;
2678 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2679 DrawPreviewLevelLabelExt(label_state);
2681 /* initialize delay counters */
2682 DelayReached(&scroll_delay, 0);
2683 DelayReached(&label_delay, 0);
2685 if (leveldir_current->name)
2687 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2688 char label_text[MAX_OUTPUT_LINESIZE + 1];
2689 int font_nr = pos->font;
2690 int max_len_label_text = getMaxTextLength(pos, font_nr);
2692 if (pos->size != -1)
2693 max_len_label_text = pos->size;
2695 strncpy(label_text, leveldir_current->name, max_len_label_text);
2696 label_text[max_len_label_text] = '\0';
2698 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2699 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2702 game_status = last_game_status; /* restore current game status */
2707 /* scroll preview level, if needed */
2708 if (preview.anim_mode != ANIM_NONE &&
2709 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2710 DelayReached(&scroll_delay, scroll_delay_value))
2712 switch (scroll_direction)
2717 from_x -= preview.step_offset;
2718 from_x = (from_x < 0 ? 0 : from_x);
2721 scroll_direction = MV_UP;
2725 if (from_x < level_xsize - preview.xsize)
2727 from_x += preview.step_offset;
2728 from_x = (from_x > level_xsize - preview.xsize ?
2729 level_xsize - preview.xsize : from_x);
2732 scroll_direction = MV_DOWN;
2738 from_y -= preview.step_offset;
2739 from_y = (from_y < 0 ? 0 : from_y);
2742 scroll_direction = MV_RIGHT;
2746 if (from_y < level_ysize - preview.ysize)
2748 from_y += preview.step_offset;
2749 from_y = (from_y > level_ysize - preview.ysize ?
2750 level_ysize - preview.ysize : from_y);
2753 scroll_direction = MV_LEFT;
2760 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2763 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2764 /* redraw micro level label, if needed */
2765 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2766 !strEqual(level.author, ANONYMOUS_NAME) &&
2767 !strEqual(level.author, leveldir_current->name) &&
2768 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2770 int max_label_counter = 23;
2772 if (leveldir_current->imported_from != NULL &&
2773 strlen(leveldir_current->imported_from) > 0)
2774 max_label_counter += 14;
2775 if (leveldir_current->imported_by != NULL &&
2776 strlen(leveldir_current->imported_by) > 0)
2777 max_label_counter += 14;
2779 label_counter = (label_counter + 1) % max_label_counter;
2780 label_state = (label_counter >= 0 && label_counter <= 7 ?
2781 MICROLABEL_LEVEL_NAME :
2782 label_counter >= 9 && label_counter <= 12 ?
2783 MICROLABEL_LEVEL_AUTHOR_HEAD :
2784 label_counter >= 14 && label_counter <= 21 ?
2785 MICROLABEL_LEVEL_AUTHOR :
2786 label_counter >= 23 && label_counter <= 26 ?
2787 MICROLABEL_IMPORTED_FROM_HEAD :
2788 label_counter >= 28 && label_counter <= 35 ?
2789 MICROLABEL_IMPORTED_FROM :
2790 label_counter >= 37 && label_counter <= 40 ?
2791 MICROLABEL_IMPORTED_BY_HEAD :
2792 label_counter >= 42 && label_counter <= 49 ?
2793 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2795 if (leveldir_current->imported_from == NULL &&
2796 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2797 label_state == MICROLABEL_IMPORTED_FROM))
2798 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2799 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2801 DrawPreviewLevelLabelExt(label_state);
2804 game_status = last_game_status; /* restore current game status */
2807 void DrawPreviewLevelInitial()
2809 DrawPreviewLevelExt(TRUE);
2812 void DrawPreviewLevelAnimation()
2814 DrawPreviewLevelExt(FALSE);
2817 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2818 int graphic, int sync_frame, int mask_mode)
2820 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2822 if (mask_mode == USE_MASKING)
2823 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2825 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2828 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2829 int graphic, int sync_frame,
2832 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2834 if (mask_mode == USE_MASKING)
2835 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2837 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2840 inline void DrawGraphicAnimation(int x, int y, int graphic)
2842 int lx = LEVELX(x), ly = LEVELY(y);
2844 if (!IN_SCR_FIELD(x, y))
2847 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2848 graphic, GfxFrame[lx][ly], NO_MASKING);
2850 MarkTileDirty(x, y);
2853 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2855 int lx = LEVELX(x), ly = LEVELY(y);
2857 if (!IN_SCR_FIELD(x, y))
2860 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2861 graphic, GfxFrame[lx][ly], NO_MASKING);
2862 MarkTileDirty(x, y);
2865 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2867 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2870 void DrawLevelElementAnimation(int x, int y, int element)
2872 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2874 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2877 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2879 int sx = SCREENX(x), sy = SCREENY(y);
2881 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2884 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2887 DrawGraphicAnimation(sx, sy, graphic);
2890 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2891 DrawLevelFieldCrumbled(x, y);
2893 if (GFX_CRUMBLED(Feld[x][y]))
2894 DrawLevelFieldCrumbled(x, y);
2898 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2900 int sx = SCREENX(x), sy = SCREENY(y);
2903 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2906 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2908 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2911 DrawGraphicAnimation(sx, sy, graphic);
2913 if (GFX_CRUMBLED(element))
2914 DrawLevelFieldCrumbled(x, y);
2917 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2919 if (player->use_murphy)
2921 /* this works only because currently only one player can be "murphy" ... */
2922 static int last_horizontal_dir = MV_LEFT;
2923 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2925 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2926 last_horizontal_dir = move_dir;
2928 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2930 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2932 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2938 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2941 static boolean equalGraphics(int graphic1, int graphic2)
2943 struct GraphicInfo *g1 = &graphic_info[graphic1];
2944 struct GraphicInfo *g2 = &graphic_info[graphic2];
2946 return (g1->bitmap == g2->bitmap &&
2947 g1->src_x == g2->src_x &&
2948 g1->src_y == g2->src_y &&
2949 g1->anim_frames == g2->anim_frames &&
2950 g1->anim_delay == g2->anim_delay &&
2951 g1->anim_mode == g2->anim_mode);
2954 void DrawAllPlayers()
2958 for (i = 0; i < MAX_PLAYERS; i++)
2959 if (stored_player[i].active)
2960 DrawPlayer(&stored_player[i]);
2963 void DrawPlayerField(int x, int y)
2965 if (!IS_PLAYER(x, y))
2968 DrawPlayer(PLAYERINFO(x, y));
2971 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
2973 void DrawPlayer(struct PlayerInfo *player)
2975 int jx = player->jx;
2976 int jy = player->jy;
2977 int move_dir = player->MovDir;
2978 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
2979 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
2980 int last_jx = (player->is_moving ? jx - dx : jx);
2981 int last_jy = (player->is_moving ? jy - dy : jy);
2982 int next_jx = jx + dx;
2983 int next_jy = jy + dy;
2984 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
2985 boolean player_is_opaque = FALSE;
2986 int sx = SCREENX(jx), sy = SCREENY(jy);
2987 int sxx = 0, syy = 0;
2988 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
2990 int action = ACTION_DEFAULT;
2991 int last_player_graphic = getPlayerGraphic(player, move_dir);
2992 int last_player_frame = player->Frame;
2995 /* GfxElement[][] is set to the element the player is digging or collecting;
2996 remove also for off-screen player if the player is not moving anymore */
2997 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
2998 GfxElement[jx][jy] = EL_UNDEFINED;
3000 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3004 if (!IN_LEV_FIELD(jx, jy))
3006 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3007 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3008 printf("DrawPlayerField(): This should never happen!\n");
3013 if (element == EL_EXPLOSION)
3016 action = (player->is_pushing ? ACTION_PUSHING :
3017 player->is_digging ? ACTION_DIGGING :
3018 player->is_collecting ? ACTION_COLLECTING :
3019 player->is_moving ? ACTION_MOVING :
3020 player->is_snapping ? ACTION_SNAPPING :
3021 player->is_dropping ? ACTION_DROPPING :
3022 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3024 if (player->is_waiting)
3025 move_dir = player->dir_waiting;
3027 InitPlayerGfxAnimation(player, action, move_dir);
3029 /* ----------------------------------------------------------------------- */
3030 /* draw things in the field the player is leaving, if needed */
3031 /* ----------------------------------------------------------------------- */
3033 if (player->is_moving)
3035 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3037 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3039 if (last_element == EL_DYNAMITE_ACTIVE ||
3040 last_element == EL_EM_DYNAMITE_ACTIVE ||
3041 last_element == EL_SP_DISK_RED_ACTIVE)
3042 DrawDynamite(last_jx, last_jy);
3044 DrawLevelFieldThruMask(last_jx, last_jy);
3046 else if (last_element == EL_DYNAMITE_ACTIVE ||
3047 last_element == EL_EM_DYNAMITE_ACTIVE ||
3048 last_element == EL_SP_DISK_RED_ACTIVE)
3049 DrawDynamite(last_jx, last_jy);
3051 /* !!! this is not enough to prevent flickering of players which are
3052 moving next to each others without a free tile between them -- this
3053 can only be solved by drawing all players layer by layer (first the
3054 background, then the foreground etc.) !!! => TODO */
3055 else if (!IS_PLAYER(last_jx, last_jy))
3056 DrawLevelField(last_jx, last_jy);
3059 DrawLevelField(last_jx, last_jy);
3062 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3063 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3066 if (!IN_SCR_FIELD(sx, sy))
3069 /* ----------------------------------------------------------------------- */
3070 /* draw things behind the player, if needed */
3071 /* ----------------------------------------------------------------------- */
3074 DrawLevelElement(jx, jy, Back[jx][jy]);
3075 else if (IS_ACTIVE_BOMB(element))
3076 DrawLevelElement(jx, jy, EL_EMPTY);
3079 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3081 int old_element = GfxElement[jx][jy];
3082 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3083 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3085 if (GFX_CRUMBLED(old_element))
3086 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3088 DrawGraphic(sx, sy, old_graphic, frame);
3090 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3091 player_is_opaque = TRUE;
3095 GfxElement[jx][jy] = EL_UNDEFINED;
3097 /* make sure that pushed elements are drawn with correct frame rate */
3098 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3100 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3101 GfxFrame[jx][jy] = player->StepFrame;
3103 DrawLevelField(jx, jy);
3107 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3108 /* ----------------------------------------------------------------------- */
3109 /* draw player himself */
3110 /* ----------------------------------------------------------------------- */
3112 graphic = getPlayerGraphic(player, move_dir);
3114 /* in the case of changed player action or direction, prevent the current
3115 animation frame from being restarted for identical animations */
3116 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3117 player->Frame = last_player_frame;
3119 frame = getGraphicAnimationFrame(graphic, player->Frame);
3123 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3124 sxx = player->GfxPos;
3126 syy = player->GfxPos;
3129 if (player_is_opaque)
3130 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3132 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3134 if (SHIELD_ON(player))
3136 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3137 IMG_SHIELD_NORMAL_ACTIVE);
3138 int frame = getGraphicAnimationFrame(graphic, -1);
3140 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3144 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3147 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3148 sxx = player->GfxPos;
3150 syy = player->GfxPos;
3154 /* ----------------------------------------------------------------------- */
3155 /* draw things the player is pushing, if needed */
3156 /* ----------------------------------------------------------------------- */
3158 if (player->is_pushing && player->is_moving)
3160 int px = SCREENX(jx), py = SCREENY(jy);
3161 int pxx = (TILEX - ABS(sxx)) * dx;
3162 int pyy = (TILEY - ABS(syy)) * dy;
3163 int gfx_frame = GfxFrame[jx][jy];
3169 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3171 element = Feld[next_jx][next_jy];
3172 gfx_frame = GfxFrame[next_jx][next_jy];
3175 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3177 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3178 frame = getGraphicAnimationFrame(graphic, sync_frame);
3180 /* draw background element under pushed element (like the Sokoban field) */
3181 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3183 /* this allows transparent pushing animation over non-black background */
3186 DrawLevelElement(jx, jy, Back[jx][jy]);
3188 DrawLevelElement(jx, jy, EL_EMPTY);
3190 if (Back[next_jx][next_jy])
3191 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3193 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3195 else if (Back[next_jx][next_jy])
3196 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3199 /* do not draw (EM style) pushing animation when pushing is finished */
3200 /* (two-tile animations usually do not contain start and end frame) */
3201 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3202 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3204 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3206 /* masked drawing is needed for EMC style (double) movement graphics */
3207 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3208 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3212 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3213 /* ----------------------------------------------------------------------- */
3214 /* draw player himself */
3215 /* ----------------------------------------------------------------------- */
3217 graphic = getPlayerGraphic(player, move_dir);
3219 /* in the case of changed player action or direction, prevent the current
3220 animation frame from being restarted for identical animations */
3221 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3222 player->Frame = last_player_frame;
3224 frame = getGraphicAnimationFrame(graphic, player->Frame);
3228 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3229 sxx = player->GfxPos;
3231 syy = player->GfxPos;
3234 if (player_is_opaque)
3235 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3237 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3239 if (SHIELD_ON(player))
3241 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3242 IMG_SHIELD_NORMAL_ACTIVE);
3243 int frame = getGraphicAnimationFrame(graphic, -1);
3245 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3249 /* ----------------------------------------------------------------------- */
3250 /* draw things in front of player (active dynamite or dynabombs) */
3251 /* ----------------------------------------------------------------------- */
3253 if (IS_ACTIVE_BOMB(element))
3255 graphic = el2img(element);
3256 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3258 if (game.emulation == EMU_SUPAPLEX)
3259 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3261 DrawGraphicThruMask(sx, sy, graphic, frame);
3264 if (player_is_moving && last_element == EL_EXPLOSION)
3266 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3267 GfxElement[last_jx][last_jy] : EL_EMPTY);
3268 int graphic = el_act2img(element, ACTION_EXPLODING);
3269 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3270 int phase = ExplodePhase[last_jx][last_jy] - 1;
3271 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3274 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3277 /* ----------------------------------------------------------------------- */
3278 /* draw elements the player is just walking/passing through/under */
3279 /* ----------------------------------------------------------------------- */
3281 if (player_is_moving)
3283 /* handle the field the player is leaving ... */
3284 if (IS_ACCESSIBLE_INSIDE(last_element))
3285 DrawLevelField(last_jx, last_jy);
3286 else if (IS_ACCESSIBLE_UNDER(last_element))
3287 DrawLevelFieldThruMask(last_jx, last_jy);
3290 /* do not redraw accessible elements if the player is just pushing them */
3291 if (!player_is_moving || !player->is_pushing)
3293 /* ... and the field the player is entering */
3294 if (IS_ACCESSIBLE_INSIDE(element))
3295 DrawLevelField(jx, jy);
3296 else if (IS_ACCESSIBLE_UNDER(element))
3297 DrawLevelFieldThruMask(jx, jy);
3300 MarkTileDirty(sx, sy);
3303 /* ------------------------------------------------------------------------- */
3305 void WaitForEventToContinue()
3307 boolean still_wait = TRUE;
3309 /* simulate releasing mouse button over last gadget, if still pressed */
3311 HandleGadgets(-1, -1, 0);
3313 button_status = MB_RELEASED;
3327 case EVENT_BUTTONPRESS:
3328 case EVENT_KEYPRESS:
3332 case EVENT_KEYRELEASE:
3333 ClearPlayerAction();
3337 HandleOtherEvents(&event);
3341 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3348 /* don't eat all CPU time */
3353 #define MAX_REQUEST_LINES 13
3354 #define MAX_REQUEST_LINE_FONT1_LEN 7
3355 #define MAX_REQUEST_LINE_FONT2_LEN 10
3357 static int RequestHandleEvents(unsigned int req_state)
3359 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3360 local_player->LevelSolved_GameEnd);
3361 int last_game_status = game_status; /* save current game status */
3362 int width = request.width;
3363 int height = request.height;
3367 setRequestPosition(&sx, &sy, FALSE);
3369 button_status = MB_RELEASED;
3371 request_gadget_id = -1;
3378 SetDrawtoField(DRAW_FIELDBUFFER);
3380 HandleGameActions();
3382 SetDrawtoField(DRAW_BACKBUFFER);
3384 if (global.use_envelope_request)
3386 /* copy current state of request area to middle of playfield area */
3387 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3395 while (NextValidEvent(&event))
3399 case EVENT_BUTTONPRESS:
3400 case EVENT_BUTTONRELEASE:
3401 case EVENT_MOTIONNOTIFY:
3405 if (event.type == EVENT_MOTIONNOTIFY)
3410 motion_status = TRUE;
3411 mx = ((MotionEvent *) &event)->x;
3412 my = ((MotionEvent *) &event)->y;
3416 motion_status = FALSE;
3417 mx = ((ButtonEvent *) &event)->x;
3418 my = ((ButtonEvent *) &event)->y;
3419 if (event.type == EVENT_BUTTONPRESS)
3420 button_status = ((ButtonEvent *) &event)->button;
3422 button_status = MB_RELEASED;
3425 /* this sets 'request_gadget_id' */
3426 HandleGadgets(mx, my, button_status);
3428 switch (request_gadget_id)
3430 case TOOL_CTRL_ID_YES:
3433 case TOOL_CTRL_ID_NO:
3436 case TOOL_CTRL_ID_CONFIRM:
3437 result = TRUE | FALSE;
3440 case TOOL_CTRL_ID_PLAYER_1:
3443 case TOOL_CTRL_ID_PLAYER_2:
3446 case TOOL_CTRL_ID_PLAYER_3:
3449 case TOOL_CTRL_ID_PLAYER_4:
3460 case EVENT_KEYPRESS:
3461 switch (GetEventKey((KeyEvent *)&event, TRUE))
3464 if (req_state & REQ_CONFIRM)
3469 #if defined(TARGET_SDL2)
3476 #if defined(TARGET_SDL2)
3486 if (req_state & REQ_PLAYER)
3490 case EVENT_KEYRELEASE:
3491 ClearPlayerAction();
3495 HandleOtherEvents(&event);
3500 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3502 int joy = AnyJoystick();
3504 if (joy & JOY_BUTTON_1)
3506 else if (joy & JOY_BUTTON_2)
3512 if (global.use_envelope_request)
3514 /* copy back current state of pressed buttons inside request area */
3515 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3522 if (!PendingEvent()) /* delay only if no pending events */
3526 game_status = GAME_MODE_PSEUDO_DOOR;
3530 game_status = last_game_status; /* restore current game status */
3536 static boolean RequestDoor(char *text, unsigned int req_state)
3538 unsigned int old_door_state;
3539 int last_game_status = game_status; /* save current game status */
3540 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3541 int font_nr = FONT_TEXT_2;
3546 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3548 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3549 font_nr = FONT_TEXT_1;
3552 if (game_status == GAME_MODE_PLAYING)
3553 BlitScreenToBitmap(backbuffer);
3555 /* disable deactivated drawing when quick-loading level tape recording */
3556 if (tape.playing && tape.deactivate_display)
3557 TapeDeactivateDisplayOff(TRUE);
3559 SetMouseCursor(CURSOR_DEFAULT);
3561 #if defined(NETWORK_AVALIABLE)
3562 /* pause network game while waiting for request to answer */
3563 if (options.network &&
3564 game_status == GAME_MODE_PLAYING &&
3565 req_state & REQUEST_WAIT_FOR_INPUT)
3566 SendToServer_PausePlaying();
3569 old_door_state = GetDoorState();
3571 /* simulate releasing mouse button over last gadget, if still pressed */
3573 HandleGadgets(-1, -1, 0);
3577 /* draw released gadget before proceeding */
3580 if (old_door_state & DOOR_OPEN_1)
3582 CloseDoor(DOOR_CLOSE_1);
3584 /* save old door content */
3585 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3586 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3589 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3590 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3592 /* clear door drawing field */
3593 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3595 /* force DOOR font inside door area */
3596 game_status = GAME_MODE_PSEUDO_DOOR;
3598 /* write text for request */
3599 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3601 char text_line[max_request_line_len + 1];
3607 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3609 tc = *(text_ptr + tx);
3610 // if (!tc || tc == ' ')
3611 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3615 if ((tc == '?' || tc == '!') && tl == 0)
3625 strncpy(text_line, text_ptr, tl);
3628 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3629 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3630 text_line, font_nr);
3632 text_ptr += tl + (tc == ' ' ? 1 : 0);
3633 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3636 game_status = last_game_status; /* restore current game status */
3638 if (req_state & REQ_ASK)
3640 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3641 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3643 else if (req_state & REQ_CONFIRM)
3645 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3647 else if (req_state & REQ_PLAYER)
3649 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3650 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3651 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3652 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3655 /* copy request gadgets to door backbuffer */
3656 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3658 OpenDoor(DOOR_OPEN_1);
3660 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3662 if (game_status == GAME_MODE_PLAYING)
3664 SetPanelBackground();
3665 SetDrawBackgroundMask(REDRAW_DOOR_1);
3669 SetDrawBackgroundMask(REDRAW_FIELD);
3675 if (game_status != GAME_MODE_MAIN)
3678 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3680 // ---------- handle request buttons ----------
3681 result = RequestHandleEvents(req_state);
3683 if (game_status != GAME_MODE_MAIN)
3688 if (!(req_state & REQ_STAY_OPEN))
3690 CloseDoor(DOOR_CLOSE_1);
3692 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3693 (req_state & REQ_REOPEN))
3694 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3699 if (game_status == GAME_MODE_PLAYING)
3701 SetPanelBackground();
3702 SetDrawBackgroundMask(REDRAW_DOOR_1);
3706 SetDrawBackgroundMask(REDRAW_FIELD);
3709 #if defined(NETWORK_AVALIABLE)
3710 /* continue network game after request */
3711 if (options.network &&
3712 game_status == GAME_MODE_PLAYING &&
3713 req_state & REQUEST_WAIT_FOR_INPUT)
3714 SendToServer_ContinuePlaying();
3717 /* restore deactivated drawing when quick-loading level tape recording */
3718 if (tape.playing && tape.deactivate_display)
3719 TapeDeactivateDisplayOn();
3724 static boolean RequestEnvelope(char *text, unsigned int req_state)
3728 if (game_status == GAME_MODE_PLAYING)
3729 BlitScreenToBitmap(backbuffer);
3731 /* disable deactivated drawing when quick-loading level tape recording */
3732 if (tape.playing && tape.deactivate_display)
3733 TapeDeactivateDisplayOff(TRUE);
3735 SetMouseCursor(CURSOR_DEFAULT);
3737 #if defined(NETWORK_AVALIABLE)
3738 /* pause network game while waiting for request to answer */
3739 if (options.network &&
3740 game_status == GAME_MODE_PLAYING &&
3741 req_state & REQUEST_WAIT_FOR_INPUT)
3742 SendToServer_PausePlaying();
3745 /* simulate releasing mouse button over last gadget, if still pressed */
3747 HandleGadgets(-1, -1, 0);
3751 // (replace with setting corresponding request background)
3752 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3753 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3755 /* clear door drawing field */
3756 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3758 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3760 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3762 if (game_status == GAME_MODE_PLAYING)
3764 SetPanelBackground();
3765 SetDrawBackgroundMask(REDRAW_DOOR_1);
3769 SetDrawBackgroundMask(REDRAW_FIELD);
3775 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3777 // ---------- handle request buttons ----------
3778 result = RequestHandleEvents(req_state);
3780 if (game_status != GAME_MODE_MAIN)
3785 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3789 if (game_status == GAME_MODE_PLAYING)
3791 SetPanelBackground();
3792 SetDrawBackgroundMask(REDRAW_DOOR_1);
3796 SetDrawBackgroundMask(REDRAW_FIELD);
3799 #if defined(NETWORK_AVALIABLE)
3800 /* continue network game after request */
3801 if (options.network &&
3802 game_status == GAME_MODE_PLAYING &&
3803 req_state & REQUEST_WAIT_FOR_INPUT)
3804 SendToServer_ContinuePlaying();
3807 /* restore deactivated drawing when quick-loading level tape recording */
3808 if (tape.playing && tape.deactivate_display)
3809 TapeDeactivateDisplayOn();
3814 boolean Request(char *text, unsigned int req_state)
3816 if (global.use_envelope_request)
3817 return RequestEnvelope(text, req_state);
3819 return RequestDoor(text, req_state);
3822 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3824 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3825 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3828 if (dpo1->sort_priority != dpo2->sort_priority)
3829 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3831 compare_result = dpo1->nr - dpo2->nr;
3833 return compare_result;
3836 void InitGraphicCompatibilityInfo_Doors()
3842 struct DoorInfo *door;
3846 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3847 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3849 { -1, -1, -1, NULL }
3851 struct Rect door_rect_list[] =
3853 { DX, DY, DXSIZE, DYSIZE },
3854 { VX, VY, VXSIZE, VYSIZE }
3858 for (i = 0; doors[i].door_token != -1; i++)
3860 int door_token = doors[i].door_token;
3861 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3862 int part_1 = doors[i].part_1;
3863 int part_8 = doors[i].part_8;
3864 int part_2 = part_1 + 1;
3865 int part_3 = part_1 + 2;
3866 struct DoorInfo *door = doors[i].door;
3867 struct Rect *door_rect = &door_rect_list[door_index];
3868 boolean door_gfx_redefined = FALSE;
3870 /* check if any door part graphic definitions have been redefined */
3872 for (j = 0; door_part_controls[j].door_token != -1; j++)
3874 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3875 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3877 if (dpc->door_token == door_token && fi->redefined)
3878 door_gfx_redefined = TRUE;
3881 /* check for old-style door graphic/animation modifications */
3883 if (!door_gfx_redefined)
3885 if (door->anim_mode & ANIM_STATIC_PANEL)
3887 door->panel.step_xoffset = 0;
3888 door->panel.step_yoffset = 0;
3891 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3893 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3894 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3895 int num_door_steps, num_panel_steps;
3897 /* remove door part graphics other than the two default wings */
3899 for (j = 0; door_part_controls[j].door_token != -1; j++)
3901 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3902 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3904 if (dpc->graphic >= part_3 &&
3905 dpc->graphic <= part_8)
3909 /* set graphics and screen positions of the default wings */
3911 g_part_1->width = door_rect->width;
3912 g_part_1->height = door_rect->height;
3913 g_part_2->width = door_rect->width;
3914 g_part_2->height = door_rect->height;
3915 g_part_2->src_x = door_rect->width;
3916 g_part_2->src_y = g_part_1->src_y;
3918 door->part_2.x = door->part_1.x;
3919 door->part_2.y = door->part_1.y;
3921 if (door->width != -1)
3923 g_part_1->width = door->width;
3924 g_part_2->width = door->width;
3926 // special treatment for graphics and screen position of right wing
3927 g_part_2->src_x += door_rect->width - door->width;
3928 door->part_2.x += door_rect->width - door->width;
3931 if (door->height != -1)
3933 g_part_1->height = door->height;
3934 g_part_2->height = door->height;
3936 // special treatment for graphics and screen position of bottom wing
3937 g_part_2->src_y += door_rect->height - door->height;
3938 door->part_2.y += door_rect->height - door->height;
3941 /* set animation delays for the default wings and panels */
3943 door->part_1.step_delay = door->step_delay;
3944 door->part_2.step_delay = door->step_delay;
3945 door->panel.step_delay = door->step_delay;
3947 /* set animation draw order for the default wings */
3949 door->part_1.sort_priority = 2; /* draw left wing over ... */
3950 door->part_2.sort_priority = 1; /* ... right wing */
3952 /* set animation draw offset for the default wings */
3954 if (door->anim_mode & ANIM_HORIZONTAL)
3956 door->part_1.step_xoffset = door->step_offset;
3957 door->part_1.step_yoffset = 0;
3958 door->part_2.step_xoffset = door->step_offset * -1;
3959 door->part_2.step_yoffset = 0;
3961 num_door_steps = g_part_1->width / door->step_offset;
3963 else // ANIM_VERTICAL
3965 door->part_1.step_xoffset = 0;
3966 door->part_1.step_yoffset = door->step_offset;
3967 door->part_2.step_xoffset = 0;
3968 door->part_2.step_yoffset = door->step_offset * -1;
3970 num_door_steps = g_part_1->height / door->step_offset;
3973 /* set animation draw offset for the default panels */
3975 if (door->step_offset > 1)
3977 num_panel_steps = 2 * door_rect->height / door->step_offset;
3978 door->panel.start_step = num_panel_steps - num_door_steps;
3979 door->panel.start_step_closing = door->panel.start_step;
3983 num_panel_steps = door_rect->height / door->step_offset;
3984 door->panel.start_step = num_panel_steps - num_door_steps / 2;
3985 door->panel.start_step_closing = door->panel.start_step;
3986 door->panel.step_delay *= 2;
3997 for (i = 0; door_part_controls[i].door_token != -1; i++)
3999 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4000 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4002 /* initialize "start_step_opening" and "start_step_closing", if needed */
4003 if (dpc->pos->start_step_opening == 0 &&
4004 dpc->pos->start_step_closing == 0)
4006 // dpc->pos->start_step_opening = dpc->pos->start_step;
4007 dpc->pos->start_step_closing = dpc->pos->start_step;
4010 /* fill structure for door part draw order (sorted below) */
4012 dpo->sort_priority = dpc->pos->sort_priority;
4015 /* sort door part controls according to sort_priority and graphic number */
4016 qsort(door_part_order, MAX_DOOR_PARTS,
4017 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4020 unsigned int OpenDoor(unsigned int door_state)
4022 if (door_state & DOOR_COPY_BACK)
4024 if (door_state & DOOR_OPEN_1)
4025 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4026 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4028 if (door_state & DOOR_OPEN_2)
4029 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4030 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4032 door_state &= ~DOOR_COPY_BACK;
4035 return MoveDoor(door_state);
4038 unsigned int CloseDoor(unsigned int door_state)
4040 unsigned int old_door_state = GetDoorState();
4042 if (!(door_state & DOOR_NO_COPY_BACK))
4044 if (old_door_state & DOOR_OPEN_1)
4045 BlitBitmap(backbuffer, bitmap_db_door_1,
4046 DX, DY, DXSIZE, DYSIZE, 0, 0);
4048 if (old_door_state & DOOR_OPEN_2)
4049 BlitBitmap(backbuffer, bitmap_db_door_2,
4050 VX, VY, VXSIZE, VYSIZE, 0, 0);
4052 door_state &= ~DOOR_NO_COPY_BACK;
4055 return MoveDoor(door_state);
4058 unsigned int GetDoorState()
4060 return MoveDoor(DOOR_GET_STATE);
4063 unsigned int SetDoorState(unsigned int door_state)
4065 return MoveDoor(door_state | DOOR_SET_STATE);
4068 int euclid(int a, int b)
4070 return (b ? euclid(b, a % b) : a);
4073 unsigned int MoveDoor(unsigned int door_state)
4075 struct Rect door_rect_list[] =
4077 { DX, DY, DXSIZE, DYSIZE },
4078 { VX, VY, VXSIZE, VYSIZE }
4080 static int door1 = DOOR_OPEN_1;
4081 static int door2 = DOOR_CLOSE_2;
4082 unsigned int door_delay = 0;
4083 unsigned int door_delay_value;
4086 if (door_state == DOOR_GET_STATE)
4087 return (door1 | door2);
4089 if (door_state & DOOR_SET_STATE)
4091 if (door_state & DOOR_ACTION_1)
4092 door1 = door_state & DOOR_ACTION_1;
4093 if (door_state & DOOR_ACTION_2)
4094 door2 = door_state & DOOR_ACTION_2;
4096 return (door1 | door2);
4099 if (!(door_state & DOOR_FORCE_REDRAW))
4101 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4102 door_state &= ~DOOR_OPEN_1;
4103 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4104 door_state &= ~DOOR_CLOSE_1;
4105 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4106 door_state &= ~DOOR_OPEN_2;
4107 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4108 door_state &= ~DOOR_CLOSE_2;
4111 if (global.autoplay_leveldir)
4113 door_state |= DOOR_NO_DELAY;
4114 door_state &= ~DOOR_CLOSE_ALL;
4117 if (game_status == GAME_MODE_EDITOR)
4118 door_state |= DOOR_NO_DELAY;
4120 if (door_state & DOOR_ACTION)
4122 boolean door_panel_drawn[NUM_DOORS];
4123 boolean panel_has_doors[NUM_DOORS];
4124 boolean door_part_skip[MAX_DOOR_PARTS];
4125 boolean door_part_done[MAX_DOOR_PARTS];
4126 boolean door_part_done_all;
4127 int num_steps[MAX_DOOR_PARTS];
4128 int max_move_delay = 0; // delay for complete animations of all doors
4129 int max_step_delay = 0; // delay (ms) between two animation frames
4130 int num_move_steps = 0; // number of animation steps for all doors
4131 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4132 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4133 int current_move_delay = 0;
4137 for (i = 0; i < NUM_DOORS; i++)
4138 panel_has_doors[i] = FALSE;
4140 for (i = 0; i < MAX_DOOR_PARTS; i++)
4142 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4143 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4144 int door_token = dpc->door_token;
4146 door_part_done[i] = FALSE;
4147 door_part_skip[i] = (!(door_state & door_token) ||
4151 for (i = 0; i < MAX_DOOR_PARTS; i++)
4153 int nr = door_part_order[i].nr;
4154 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4155 struct DoorPartPosInfo *pos = dpc->pos;
4156 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4157 int door_token = dpc->door_token;
4158 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4159 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4160 int step_xoffset = ABS(pos->step_xoffset);
4161 int step_yoffset = ABS(pos->step_yoffset);
4162 int step_delay = pos->step_delay;
4163 int current_door_state = door_state & door_token;
4164 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4165 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4166 boolean part_opening = (is_panel ? door_closing : door_opening);
4167 int start_step = (part_opening ? pos->start_step_opening :
4168 pos->start_step_closing);
4169 float move_xsize = (step_xoffset ? g->width : 0);
4170 float move_ysize = (step_yoffset ? g->height : 0);
4171 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4172 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4173 int move_steps = (move_xsteps && move_ysteps ?
4174 MIN(move_xsteps, move_ysteps) :
4175 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4176 int move_delay = move_steps * step_delay;
4178 if (door_part_skip[nr])
4181 max_move_delay = MAX(max_move_delay, move_delay);
4182 max_step_delay = (max_step_delay == 0 ? step_delay :
4183 euclid(max_step_delay, step_delay));
4184 num_steps[nr] = move_steps;
4188 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4190 panel_has_doors[door_index] = TRUE;
4194 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4196 num_move_steps = max_move_delay / max_step_delay;
4197 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4199 door_delay_value = max_step_delay;
4201 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4203 start = num_move_steps - 1;
4207 /* opening door sound has priority over simultaneously closing door */
4208 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4209 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4210 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4211 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4214 for (k = start; k < num_move_steps; k++)
4216 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4218 door_part_done_all = TRUE;
4220 for (i = 0; i < NUM_DOORS; i++)
4221 door_panel_drawn[i] = FALSE;
4223 for (i = 0; i < MAX_DOOR_PARTS; i++)
4225 int nr = door_part_order[i].nr;
4226 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4227 struct DoorPartPosInfo *pos = dpc->pos;
4228 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4229 int door_token = dpc->door_token;
4230 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4231 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4232 boolean is_panel_and_door_has_closed = FALSE;
4233 struct Rect *door_rect = &door_rect_list[door_index];
4234 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4236 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4237 int current_door_state = door_state & door_token;
4238 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4239 boolean door_closing = !door_opening;
4240 boolean part_opening = (is_panel ? door_closing : door_opening);
4241 boolean part_closing = !part_opening;
4242 int start_step = (part_opening ? pos->start_step_opening :
4243 pos->start_step_closing);
4244 int step_delay = pos->step_delay;
4245 int step_factor = step_delay / max_step_delay;
4246 int k1 = (step_factor ? k / step_factor + 1 : k);
4247 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4248 int kk = MAX(0, k2);
4251 int src_x, src_y, src_xx, src_yy;
4252 int dst_x, dst_y, dst_xx, dst_yy;
4255 if (door_part_skip[nr])
4258 if (!(door_state & door_token))
4266 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4267 int kk_door = MAX(0, k2_door);
4268 int sync_frame = kk_door * door_delay_value;
4269 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4271 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4276 if (!door_panel_drawn[door_index])
4278 ClearRectangle(drawto, door_rect->x, door_rect->y,
4279 door_rect->width, door_rect->height);
4281 door_panel_drawn[door_index] = TRUE;
4284 // draw opening or closing door parts
4286 if (pos->step_xoffset < 0) // door part on right side
4289 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4292 if (dst_xx + width > door_rect->width)
4293 width = door_rect->width - dst_xx;
4295 else // door part on left side
4298 dst_xx = pos->x - kk * pos->step_xoffset;
4302 src_xx = ABS(dst_xx);
4306 width = g->width - src_xx;
4308 // printf("::: k == %d [%d] \n", k, start_step);
4311 if (pos->step_yoffset < 0) // door part on bottom side
4314 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4317 if (dst_yy + height > door_rect->height)
4318 height = door_rect->height - dst_yy;
4320 else // door part on top side
4323 dst_yy = pos->y - kk * pos->step_yoffset;
4327 src_yy = ABS(dst_yy);
4331 height = g->height - src_yy;
4334 src_x = g_src_x + src_xx;
4335 src_y = g_src_y + src_yy;
4337 dst_x = door_rect->x + dst_xx;
4338 dst_y = door_rect->y + dst_yy;
4340 is_panel_and_door_has_closed =
4343 panel_has_doors[door_index] &&
4344 k >= num_move_steps_doors_only - 1);
4346 if (width >= 0 && width <= g->width &&
4347 height >= 0 && height <= g->height &&
4348 !is_panel_and_door_has_closed)
4350 if (is_panel || !pos->draw_masked)
4351 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4354 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4358 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4360 if ((part_opening && (width < 0 || height < 0)) ||
4361 (part_closing && (width >= g->width && height >= g->height)))
4362 door_part_done[nr] = TRUE;
4364 // continue door part animations, but not panel after door has closed
4365 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4366 door_part_done_all = FALSE;
4369 if (!(door_state & DOOR_NO_DELAY))
4373 if (game_status == GAME_MODE_MAIN)
4376 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4378 current_move_delay += max_step_delay;
4381 if (door_part_done_all)
4386 if (door_state & DOOR_ACTION_1)
4387 door1 = door_state & DOOR_ACTION_1;
4388 if (door_state & DOOR_ACTION_2)
4389 door2 = door_state & DOOR_ACTION_2;
4391 return (door1 | door2);
4394 void DrawSpecialEditorDoor()
4396 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4397 int top_border_width = gfx1->width;
4398 int top_border_height = gfx1->height;
4399 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4400 int ex = EX - outer_border;
4401 int ey = EY - outer_border;
4402 int vy = VY - outer_border;
4403 int exsize = EXSIZE + 2 * outer_border;
4405 /* draw bigger level editor toolbox window */
4406 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4407 top_border_width, top_border_height, ex, ey - top_border_height);
4408 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4409 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4411 redraw_mask |= REDRAW_ALL;
4414 void UndrawSpecialEditorDoor()
4416 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4417 int top_border_width = gfx1->width;
4418 int top_border_height = gfx1->height;
4419 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4420 int ex = EX - outer_border;
4421 int ey = EY - outer_border;
4422 int ey_top = ey - top_border_height;
4423 int exsize = EXSIZE + 2 * outer_border;
4424 int eysize = EYSIZE + 2 * outer_border;
4426 /* draw normal tape recorder window */
4427 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4429 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4430 ex, ey_top, top_border_width, top_border_height,
4432 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4433 ex, ey, exsize, eysize, ex, ey);
4437 // if screen background is set to "[NONE]", clear editor toolbox window
4438 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4439 ClearRectangle(drawto, ex, ey, exsize, eysize);
4442 redraw_mask |= REDRAW_ALL;
4446 /* ---------- new tool button stuff ---------------------------------------- */
4451 struct TextPosInfo *pos;
4454 } toolbutton_info[NUM_TOOL_BUTTONS] =
4457 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4458 TOOL_CTRL_ID_YES, "yes"
4461 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4462 TOOL_CTRL_ID_NO, "no"
4465 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4466 TOOL_CTRL_ID_CONFIRM, "confirm"
4469 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4470 TOOL_CTRL_ID_PLAYER_1, "player 1"
4473 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4474 TOOL_CTRL_ID_PLAYER_2, "player 2"
4477 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4478 TOOL_CTRL_ID_PLAYER_3, "player 3"
4481 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4482 TOOL_CTRL_ID_PLAYER_4, "player 4"
4486 void CreateToolButtons()
4490 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4492 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4493 struct TextPosInfo *pos = toolbutton_info[i].pos;
4494 struct GadgetInfo *gi;
4495 Bitmap *deco_bitmap = None;
4496 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4497 unsigned int event_mask = GD_EVENT_RELEASED;
4500 int gd_x = gfx->src_x;
4501 int gd_y = gfx->src_y;
4502 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4503 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4506 if (global.use_envelope_request)
4507 setRequestPosition(&dx, &dy, TRUE);
4509 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4511 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4513 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4514 pos->size, &deco_bitmap, &deco_x, &deco_y);
4515 deco_xpos = (gfx->width - pos->size) / 2;
4516 deco_ypos = (gfx->height - pos->size) / 2;
4519 gi = CreateGadget(GDI_CUSTOM_ID, id,
4520 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4521 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4522 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4523 GDI_WIDTH, gfx->width,
4524 GDI_HEIGHT, gfx->height,
4525 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4526 GDI_STATE, GD_BUTTON_UNPRESSED,
4527 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4528 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4529 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4530 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4531 GDI_DECORATION_SIZE, pos->size, pos->size,
4532 GDI_DECORATION_SHIFTING, 1, 1,
4533 GDI_DIRECT_DRAW, FALSE,
4534 GDI_EVENT_MASK, event_mask,
4535 GDI_CALLBACK_ACTION, HandleToolButtons,
4539 Error(ERR_EXIT, "cannot create gadget");
4541 tool_gadget[id] = gi;
4545 void FreeToolButtons()
4549 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4550 FreeGadget(tool_gadget[i]);
4553 static void UnmapToolButtons()
4557 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4558 UnmapGadget(tool_gadget[i]);
4561 static void HandleToolButtons(struct GadgetInfo *gi)
4563 request_gadget_id = gi->custom_id;
4566 static struct Mapping_EM_to_RND_object
4569 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4570 boolean is_backside; /* backside of moving element */
4576 em_object_mapping_list[] =
4579 Xblank, TRUE, FALSE,
4583 Yacid_splash_eB, FALSE, FALSE,
4584 EL_ACID_SPLASH_RIGHT, -1, -1
4587 Yacid_splash_wB, FALSE, FALSE,
4588 EL_ACID_SPLASH_LEFT, -1, -1
4591 #ifdef EM_ENGINE_BAD_ROLL
4593 Xstone_force_e, FALSE, FALSE,
4594 EL_ROCK, -1, MV_BIT_RIGHT
4597 Xstone_force_w, FALSE, FALSE,
4598 EL_ROCK, -1, MV_BIT_LEFT
4601 Xnut_force_e, FALSE, FALSE,
4602 EL_NUT, -1, MV_BIT_RIGHT
4605 Xnut_force_w, FALSE, FALSE,
4606 EL_NUT, -1, MV_BIT_LEFT
4609 Xspring_force_e, FALSE, FALSE,
4610 EL_SPRING, -1, MV_BIT_RIGHT
4613 Xspring_force_w, FALSE, FALSE,
4614 EL_SPRING, -1, MV_BIT_LEFT
4617 Xemerald_force_e, FALSE, FALSE,
4618 EL_EMERALD, -1, MV_BIT_RIGHT
4621 Xemerald_force_w, FALSE, FALSE,
4622 EL_EMERALD, -1, MV_BIT_LEFT
4625 Xdiamond_force_e, FALSE, FALSE,
4626 EL_DIAMOND, -1, MV_BIT_RIGHT
4629 Xdiamond_force_w, FALSE, FALSE,
4630 EL_DIAMOND, -1, MV_BIT_LEFT
4633 Xbomb_force_e, FALSE, FALSE,
4634 EL_BOMB, -1, MV_BIT_RIGHT
4637 Xbomb_force_w, FALSE, FALSE,
4638 EL_BOMB, -1, MV_BIT_LEFT
4640 #endif /* EM_ENGINE_BAD_ROLL */
4643 Xstone, TRUE, FALSE,
4647 Xstone_pause, FALSE, FALSE,
4651 Xstone_fall, FALSE, FALSE,
4655 Ystone_s, FALSE, FALSE,
4656 EL_ROCK, ACTION_FALLING, -1
4659 Ystone_sB, FALSE, TRUE,
4660 EL_ROCK, ACTION_FALLING, -1
4663 Ystone_e, FALSE, FALSE,
4664 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4667 Ystone_eB, FALSE, TRUE,
4668 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4671 Ystone_w, FALSE, FALSE,
4672 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4675 Ystone_wB, FALSE, TRUE,
4676 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4683 Xnut_pause, FALSE, FALSE,
4687 Xnut_fall, FALSE, FALSE,
4691 Ynut_s, FALSE, FALSE,
4692 EL_NUT, ACTION_FALLING, -1
4695 Ynut_sB, FALSE, TRUE,
4696 EL_NUT, ACTION_FALLING, -1
4699 Ynut_e, FALSE, FALSE,
4700 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4703 Ynut_eB, FALSE, TRUE,
4704 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4707 Ynut_w, FALSE, FALSE,
4708 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4711 Ynut_wB, FALSE, TRUE,
4712 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4715 Xbug_n, TRUE, FALSE,
4719 Xbug_e, TRUE, FALSE,
4720 EL_BUG_RIGHT, -1, -1
4723 Xbug_s, TRUE, FALSE,
4727 Xbug_w, TRUE, FALSE,
4731 Xbug_gon, FALSE, FALSE,
4735 Xbug_goe, FALSE, FALSE,
4736 EL_BUG_RIGHT, -1, -1
4739 Xbug_gos, FALSE, FALSE,
4743 Xbug_gow, FALSE, FALSE,
4747 Ybug_n, FALSE, FALSE,
4748 EL_BUG, ACTION_MOVING, MV_BIT_UP
4751 Ybug_nB, FALSE, TRUE,
4752 EL_BUG, ACTION_MOVING, MV_BIT_UP
4755 Ybug_e, FALSE, FALSE,
4756 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4759 Ybug_eB, FALSE, TRUE,
4760 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4763 Ybug_s, FALSE, FALSE,
4764 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4767 Ybug_sB, FALSE, TRUE,
4768 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4771 Ybug_w, FALSE, FALSE,
4772 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4775 Ybug_wB, FALSE, TRUE,
4776 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4779 Ybug_w_n, FALSE, FALSE,
4780 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4783 Ybug_n_e, FALSE, FALSE,
4784 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4787 Ybug_e_s, FALSE, FALSE,
4788 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4791 Ybug_s_w, FALSE, FALSE,
4792 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4795 Ybug_e_n, FALSE, FALSE,
4796 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4799 Ybug_s_e, FALSE, FALSE,
4800 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4803 Ybug_w_s, FALSE, FALSE,
4804 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4807 Ybug_n_w, FALSE, FALSE,
4808 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4811 Ybug_stone, FALSE, FALSE,
4812 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4815 Ybug_spring, FALSE, FALSE,
4816 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4819 Xtank_n, TRUE, FALSE,
4820 EL_SPACESHIP_UP, -1, -1
4823 Xtank_e, TRUE, FALSE,
4824 EL_SPACESHIP_RIGHT, -1, -1
4827 Xtank_s, TRUE, FALSE,
4828 EL_SPACESHIP_DOWN, -1, -1
4831 Xtank_w, TRUE, FALSE,
4832 EL_SPACESHIP_LEFT, -1, -1
4835 Xtank_gon, FALSE, FALSE,
4836 EL_SPACESHIP_UP, -1, -1
4839 Xtank_goe, FALSE, FALSE,
4840 EL_SPACESHIP_RIGHT, -1, -1
4843 Xtank_gos, FALSE, FALSE,
4844 EL_SPACESHIP_DOWN, -1, -1
4847 Xtank_gow, FALSE, FALSE,
4848 EL_SPACESHIP_LEFT, -1, -1
4851 Ytank_n, FALSE, FALSE,
4852 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4855 Ytank_nB, FALSE, TRUE,
4856 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4859 Ytank_e, FALSE, FALSE,
4860 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4863 Ytank_eB, FALSE, TRUE,
4864 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4867 Ytank_s, FALSE, FALSE,
4868 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4871 Ytank_sB, FALSE, TRUE,
4872 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4875 Ytank_w, FALSE, FALSE,
4876 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4879 Ytank_wB, FALSE, TRUE,
4880 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4883 Ytank_w_n, FALSE, FALSE,
4884 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4887 Ytank_n_e, FALSE, FALSE,
4888 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4891 Ytank_e_s, FALSE, FALSE,
4892 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4895 Ytank_s_w, FALSE, FALSE,
4896 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4899 Ytank_e_n, FALSE, FALSE,
4900 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4903 Ytank_s_e, FALSE, FALSE,
4904 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4907 Ytank_w_s, FALSE, FALSE,
4908 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4911 Ytank_n_w, FALSE, FALSE,
4912 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4915 Ytank_stone, FALSE, FALSE,
4916 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4919 Ytank_spring, FALSE, FALSE,
4920 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4923 Xandroid, TRUE, FALSE,
4924 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4927 Xandroid_1_n, FALSE, FALSE,
4928 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4931 Xandroid_2_n, FALSE, FALSE,
4932 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4935 Xandroid_1_e, FALSE, FALSE,
4936 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4939 Xandroid_2_e, FALSE, FALSE,
4940 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4943 Xandroid_1_w, FALSE, FALSE,
4944 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4947 Xandroid_2_w, FALSE, FALSE,
4948 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4951 Xandroid_1_s, FALSE, FALSE,
4952 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4955 Xandroid_2_s, FALSE, FALSE,
4956 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4959 Yandroid_n, FALSE, FALSE,
4960 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4963 Yandroid_nB, FALSE, TRUE,
4964 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4967 Yandroid_ne, FALSE, FALSE,
4968 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4971 Yandroid_neB, FALSE, TRUE,
4972 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4975 Yandroid_e, FALSE, FALSE,
4976 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4979 Yandroid_eB, FALSE, TRUE,
4980 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4983 Yandroid_se, FALSE, FALSE,
4984 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4987 Yandroid_seB, FALSE, TRUE,
4988 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4991 Yandroid_s, FALSE, FALSE,
4992 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4995 Yandroid_sB, FALSE, TRUE,
4996 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4999 Yandroid_sw, FALSE, FALSE,
5000 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5003 Yandroid_swB, FALSE, TRUE,
5004 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5007 Yandroid_w, FALSE, FALSE,
5008 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5011 Yandroid_wB, FALSE, TRUE,
5012 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5015 Yandroid_nw, FALSE, FALSE,
5016 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5019 Yandroid_nwB, FALSE, TRUE,
5020 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5023 Xspring, TRUE, FALSE,
5027 Xspring_pause, FALSE, FALSE,
5031 Xspring_e, FALSE, FALSE,
5035 Xspring_w, FALSE, FALSE,
5039 Xspring_fall, FALSE, FALSE,
5043 Yspring_s, FALSE, FALSE,
5044 EL_SPRING, ACTION_FALLING, -1
5047 Yspring_sB, FALSE, TRUE,
5048 EL_SPRING, ACTION_FALLING, -1
5051 Yspring_e, FALSE, FALSE,
5052 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5055 Yspring_eB, FALSE, TRUE,
5056 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5059 Yspring_w, FALSE, FALSE,
5060 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5063 Yspring_wB, FALSE, TRUE,
5064 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5067 Yspring_kill_e, FALSE, FALSE,
5068 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5071 Yspring_kill_eB, FALSE, TRUE,
5072 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5075 Yspring_kill_w, FALSE, FALSE,
5076 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5079 Yspring_kill_wB, FALSE, TRUE,
5080 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5083 Xeater_n, TRUE, FALSE,
5084 EL_YAMYAM_UP, -1, -1
5087 Xeater_e, TRUE, FALSE,
5088 EL_YAMYAM_RIGHT, -1, -1
5091 Xeater_w, TRUE, FALSE,
5092 EL_YAMYAM_LEFT, -1, -1
5095 Xeater_s, TRUE, FALSE,
5096 EL_YAMYAM_DOWN, -1, -1
5099 Yeater_n, FALSE, FALSE,
5100 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5103 Yeater_nB, FALSE, TRUE,
5104 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5107 Yeater_e, FALSE, FALSE,
5108 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5111 Yeater_eB, FALSE, TRUE,
5112 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5115 Yeater_s, FALSE, FALSE,
5116 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5119 Yeater_sB, FALSE, TRUE,
5120 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5123 Yeater_w, FALSE, FALSE,
5124 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5127 Yeater_wB, FALSE, TRUE,
5128 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5131 Yeater_stone, FALSE, FALSE,
5132 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5135 Yeater_spring, FALSE, FALSE,
5136 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5139 Xalien, TRUE, FALSE,
5143 Xalien_pause, FALSE, FALSE,
5147 Yalien_n, FALSE, FALSE,
5148 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5151 Yalien_nB, FALSE, TRUE,
5152 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5155 Yalien_e, FALSE, FALSE,
5156 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5159 Yalien_eB, FALSE, TRUE,
5160 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5163 Yalien_s, FALSE, FALSE,
5164 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5167 Yalien_sB, FALSE, TRUE,
5168 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5171 Yalien_w, FALSE, FALSE,
5172 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5175 Yalien_wB, FALSE, TRUE,
5176 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5179 Yalien_stone, FALSE, FALSE,
5180 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5183 Yalien_spring, FALSE, FALSE,
5184 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5187 Xemerald, TRUE, FALSE,
5191 Xemerald_pause, FALSE, FALSE,
5195 Xemerald_fall, FALSE, FALSE,
5199 Xemerald_shine, FALSE, FALSE,
5200 EL_EMERALD, ACTION_TWINKLING, -1
5203 Yemerald_s, FALSE, FALSE,
5204 EL_EMERALD, ACTION_FALLING, -1
5207 Yemerald_sB, FALSE, TRUE,
5208 EL_EMERALD, ACTION_FALLING, -1
5211 Yemerald_e, FALSE, FALSE,
5212 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5215 Yemerald_eB, FALSE, TRUE,
5216 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5219 Yemerald_w, FALSE, FALSE,
5220 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5223 Yemerald_wB, FALSE, TRUE,
5224 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5227 Yemerald_eat, FALSE, FALSE,
5228 EL_EMERALD, ACTION_COLLECTING, -1
5231 Yemerald_stone, FALSE, FALSE,
5232 EL_NUT, ACTION_BREAKING, -1
5235 Xdiamond, TRUE, FALSE,
5239 Xdiamond_pause, FALSE, FALSE,
5243 Xdiamond_fall, FALSE, FALSE,
5247 Xdiamond_shine, FALSE, FALSE,
5248 EL_DIAMOND, ACTION_TWINKLING, -1
5251 Ydiamond_s, FALSE, FALSE,
5252 EL_DIAMOND, ACTION_FALLING, -1
5255 Ydiamond_sB, FALSE, TRUE,
5256 EL_DIAMOND, ACTION_FALLING, -1
5259 Ydiamond_e, FALSE, FALSE,
5260 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5263 Ydiamond_eB, FALSE, TRUE,
5264 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5267 Ydiamond_w, FALSE, FALSE,
5268 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5271 Ydiamond_wB, FALSE, TRUE,
5272 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5275 Ydiamond_eat, FALSE, FALSE,
5276 EL_DIAMOND, ACTION_COLLECTING, -1
5279 Ydiamond_stone, FALSE, FALSE,
5280 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5283 Xdrip_fall, TRUE, FALSE,
5284 EL_AMOEBA_DROP, -1, -1
5287 Xdrip_stretch, FALSE, FALSE,
5288 EL_AMOEBA_DROP, ACTION_FALLING, -1
5291 Xdrip_stretchB, FALSE, TRUE,
5292 EL_AMOEBA_DROP, ACTION_FALLING, -1
5295 Xdrip_eat, FALSE, FALSE,
5296 EL_AMOEBA_DROP, ACTION_GROWING, -1
5299 Ydrip_s1, FALSE, FALSE,
5300 EL_AMOEBA_DROP, ACTION_FALLING, -1
5303 Ydrip_s1B, FALSE, TRUE,
5304 EL_AMOEBA_DROP, ACTION_FALLING, -1
5307 Ydrip_s2, FALSE, FALSE,
5308 EL_AMOEBA_DROP, ACTION_FALLING, -1
5311 Ydrip_s2B, FALSE, TRUE,
5312 EL_AMOEBA_DROP, ACTION_FALLING, -1
5319 Xbomb_pause, FALSE, FALSE,
5323 Xbomb_fall, FALSE, FALSE,
5327 Ybomb_s, FALSE, FALSE,
5328 EL_BOMB, ACTION_FALLING, -1
5331 Ybomb_sB, FALSE, TRUE,
5332 EL_BOMB, ACTION_FALLING, -1
5335 Ybomb_e, FALSE, FALSE,
5336 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5339 Ybomb_eB, FALSE, TRUE,
5340 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5343 Ybomb_w, FALSE, FALSE,
5344 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5347 Ybomb_wB, FALSE, TRUE,
5348 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5351 Ybomb_eat, FALSE, FALSE,
5352 EL_BOMB, ACTION_ACTIVATING, -1
5355 Xballoon, TRUE, FALSE,
5359 Yballoon_n, FALSE, FALSE,
5360 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5363 Yballoon_nB, FALSE, TRUE,
5364 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5367 Yballoon_e, FALSE, FALSE,
5368 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5371 Yballoon_eB, FALSE, TRUE,
5372 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5375 Yballoon_s, FALSE, FALSE,
5376 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5379 Yballoon_sB, FALSE, TRUE,
5380 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5383 Yballoon_w, FALSE, FALSE,
5384 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5387 Yballoon_wB, FALSE, TRUE,
5388 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5391 Xgrass, TRUE, FALSE,
5392 EL_EMC_GRASS, -1, -1
5395 Ygrass_nB, FALSE, FALSE,
5396 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5399 Ygrass_eB, FALSE, FALSE,
5400 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5403 Ygrass_sB, FALSE, FALSE,
5404 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5407 Ygrass_wB, FALSE, FALSE,
5408 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5415 Ydirt_nB, FALSE, FALSE,
5416 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5419 Ydirt_eB, FALSE, FALSE,
5420 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5423 Ydirt_sB, FALSE, FALSE,
5424 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5427 Ydirt_wB, FALSE, FALSE,
5428 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5431 Xacid_ne, TRUE, FALSE,
5432 EL_ACID_POOL_TOPRIGHT, -1, -1
5435 Xacid_se, TRUE, FALSE,
5436 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5439 Xacid_s, TRUE, FALSE,
5440 EL_ACID_POOL_BOTTOM, -1, -1
5443 Xacid_sw, TRUE, FALSE,
5444 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5447 Xacid_nw, TRUE, FALSE,
5448 EL_ACID_POOL_TOPLEFT, -1, -1
5451 Xacid_1, TRUE, FALSE,
5455 Xacid_2, FALSE, FALSE,
5459 Xacid_3, FALSE, FALSE,
5463 Xacid_4, FALSE, FALSE,
5467 Xacid_5, FALSE, FALSE,
5471 Xacid_6, FALSE, FALSE,
5475 Xacid_7, FALSE, FALSE,
5479 Xacid_8, FALSE, FALSE,
5483 Xball_1, TRUE, FALSE,
5484 EL_EMC_MAGIC_BALL, -1, -1
5487 Xball_1B, FALSE, FALSE,
5488 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5491 Xball_2, FALSE, FALSE,
5492 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5495 Xball_2B, FALSE, FALSE,
5496 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5499 Yball_eat, FALSE, FALSE,
5500 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5503 Ykey_1_eat, FALSE, FALSE,
5504 EL_EM_KEY_1, ACTION_COLLECTING, -1
5507 Ykey_2_eat, FALSE, FALSE,
5508 EL_EM_KEY_2, ACTION_COLLECTING, -1
5511 Ykey_3_eat, FALSE, FALSE,
5512 EL_EM_KEY_3, ACTION_COLLECTING, -1
5515 Ykey_4_eat, FALSE, FALSE,
5516 EL_EM_KEY_4, ACTION_COLLECTING, -1
5519 Ykey_5_eat, FALSE, FALSE,
5520 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5523 Ykey_6_eat, FALSE, FALSE,
5524 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5527 Ykey_7_eat, FALSE, FALSE,
5528 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5531 Ykey_8_eat, FALSE, FALSE,
5532 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5535 Ylenses_eat, FALSE, FALSE,
5536 EL_EMC_LENSES, ACTION_COLLECTING, -1
5539 Ymagnify_eat, FALSE, FALSE,
5540 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5543 Ygrass_eat, FALSE, FALSE,
5544 EL_EMC_GRASS, ACTION_SNAPPING, -1
5547 Ydirt_eat, FALSE, FALSE,
5548 EL_SAND, ACTION_SNAPPING, -1
5551 Xgrow_ns, TRUE, FALSE,
5552 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5555 Ygrow_ns_eat, FALSE, FALSE,
5556 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5559 Xgrow_ew, TRUE, FALSE,
5560 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5563 Ygrow_ew_eat, FALSE, FALSE,
5564 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5567 Xwonderwall, TRUE, FALSE,
5568 EL_MAGIC_WALL, -1, -1
5571 XwonderwallB, FALSE, FALSE,
5572 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5575 Xamoeba_1, TRUE, FALSE,
5576 EL_AMOEBA_DRY, ACTION_OTHER, -1
5579 Xamoeba_2, FALSE, FALSE,
5580 EL_AMOEBA_DRY, ACTION_OTHER, -1
5583 Xamoeba_3, FALSE, FALSE,
5584 EL_AMOEBA_DRY, ACTION_OTHER, -1
5587 Xamoeba_4, FALSE, FALSE,
5588 EL_AMOEBA_DRY, ACTION_OTHER, -1
5591 Xamoeba_5, TRUE, FALSE,
5592 EL_AMOEBA_WET, ACTION_OTHER, -1
5595 Xamoeba_6, FALSE, FALSE,
5596 EL_AMOEBA_WET, ACTION_OTHER, -1
5599 Xamoeba_7, FALSE, FALSE,
5600 EL_AMOEBA_WET, ACTION_OTHER, -1
5603 Xamoeba_8, FALSE, FALSE,
5604 EL_AMOEBA_WET, ACTION_OTHER, -1
5607 Xdoor_1, TRUE, FALSE,
5608 EL_EM_GATE_1, -1, -1
5611 Xdoor_2, TRUE, FALSE,
5612 EL_EM_GATE_2, -1, -1
5615 Xdoor_3, TRUE, FALSE,
5616 EL_EM_GATE_3, -1, -1
5619 Xdoor_4, TRUE, FALSE,
5620 EL_EM_GATE_4, -1, -1
5623 Xdoor_5, TRUE, FALSE,
5624 EL_EMC_GATE_5, -1, -1
5627 Xdoor_6, TRUE, FALSE,
5628 EL_EMC_GATE_6, -1, -1
5631 Xdoor_7, TRUE, FALSE,
5632 EL_EMC_GATE_7, -1, -1
5635 Xdoor_8, TRUE, FALSE,
5636 EL_EMC_GATE_8, -1, -1
5639 Xkey_1, TRUE, FALSE,
5643 Xkey_2, TRUE, FALSE,
5647 Xkey_3, TRUE, FALSE,
5651 Xkey_4, TRUE, FALSE,
5655 Xkey_5, TRUE, FALSE,
5656 EL_EMC_KEY_5, -1, -1
5659 Xkey_6, TRUE, FALSE,
5660 EL_EMC_KEY_6, -1, -1
5663 Xkey_7, TRUE, FALSE,
5664 EL_EMC_KEY_7, -1, -1
5667 Xkey_8, TRUE, FALSE,
5668 EL_EMC_KEY_8, -1, -1
5671 Xwind_n, TRUE, FALSE,
5672 EL_BALLOON_SWITCH_UP, -1, -1
5675 Xwind_e, TRUE, FALSE,
5676 EL_BALLOON_SWITCH_RIGHT, -1, -1
5679 Xwind_s, TRUE, FALSE,
5680 EL_BALLOON_SWITCH_DOWN, -1, -1
5683 Xwind_w, TRUE, FALSE,
5684 EL_BALLOON_SWITCH_LEFT, -1, -1
5687 Xwind_nesw, TRUE, FALSE,
5688 EL_BALLOON_SWITCH_ANY, -1, -1
5691 Xwind_stop, TRUE, FALSE,
5692 EL_BALLOON_SWITCH_NONE, -1, -1
5696 EL_EM_EXIT_CLOSED, -1, -1
5699 Xexit_1, TRUE, FALSE,
5700 EL_EM_EXIT_OPEN, -1, -1
5703 Xexit_2, FALSE, FALSE,
5704 EL_EM_EXIT_OPEN, -1, -1
5707 Xexit_3, FALSE, FALSE,
5708 EL_EM_EXIT_OPEN, -1, -1
5711 Xdynamite, TRUE, FALSE,
5712 EL_EM_DYNAMITE, -1, -1
5715 Ydynamite_eat, FALSE, FALSE,
5716 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5719 Xdynamite_1, TRUE, FALSE,
5720 EL_EM_DYNAMITE_ACTIVE, -1, -1
5723 Xdynamite_2, FALSE, FALSE,
5724 EL_EM_DYNAMITE_ACTIVE, -1, -1
5727 Xdynamite_3, FALSE, FALSE,
5728 EL_EM_DYNAMITE_ACTIVE, -1, -1
5731 Xdynamite_4, FALSE, FALSE,
5732 EL_EM_DYNAMITE_ACTIVE, -1, -1
5735 Xbumper, TRUE, FALSE,
5736 EL_EMC_SPRING_BUMPER, -1, -1
5739 XbumperB, FALSE, FALSE,
5740 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5743 Xwheel, TRUE, FALSE,
5744 EL_ROBOT_WHEEL, -1, -1
5747 XwheelB, FALSE, FALSE,
5748 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5751 Xswitch, TRUE, FALSE,
5752 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5755 XswitchB, FALSE, FALSE,
5756 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5760 EL_QUICKSAND_EMPTY, -1, -1
5763 Xsand_stone, TRUE, FALSE,
5764 EL_QUICKSAND_FULL, -1, -1
5767 Xsand_stonein_1, FALSE, TRUE,
5768 EL_ROCK, ACTION_FILLING, -1
5771 Xsand_stonein_2, FALSE, TRUE,
5772 EL_ROCK, ACTION_FILLING, -1
5775 Xsand_stonein_3, FALSE, TRUE,
5776 EL_ROCK, ACTION_FILLING, -1
5779 Xsand_stonein_4, FALSE, TRUE,
5780 EL_ROCK, ACTION_FILLING, -1
5783 Xsand_stonesand_1, FALSE, FALSE,
5784 EL_QUICKSAND_EMPTYING, -1, -1
5787 Xsand_stonesand_2, FALSE, FALSE,
5788 EL_QUICKSAND_EMPTYING, -1, -1
5791 Xsand_stonesand_3, FALSE, FALSE,
5792 EL_QUICKSAND_EMPTYING, -1, -1
5795 Xsand_stonesand_4, FALSE, FALSE,
5796 EL_QUICKSAND_EMPTYING, -1, -1
5799 Xsand_stonesand_quickout_1, FALSE, FALSE,
5800 EL_QUICKSAND_EMPTYING, -1, -1
5803 Xsand_stonesand_quickout_2, FALSE, FALSE,
5804 EL_QUICKSAND_EMPTYING, -1, -1
5807 Xsand_stoneout_1, FALSE, FALSE,
5808 EL_ROCK, ACTION_EMPTYING, -1
5811 Xsand_stoneout_2, FALSE, FALSE,
5812 EL_ROCK, ACTION_EMPTYING, -1
5815 Xsand_sandstone_1, FALSE, FALSE,
5816 EL_QUICKSAND_FILLING, -1, -1
5819 Xsand_sandstone_2, FALSE, FALSE,
5820 EL_QUICKSAND_FILLING, -1, -1
5823 Xsand_sandstone_3, FALSE, FALSE,
5824 EL_QUICKSAND_FILLING, -1, -1
5827 Xsand_sandstone_4, FALSE, FALSE,
5828 EL_QUICKSAND_FILLING, -1, -1
5831 Xplant, TRUE, FALSE,
5832 EL_EMC_PLANT, -1, -1
5835 Yplant, FALSE, FALSE,
5836 EL_EMC_PLANT, -1, -1
5839 Xlenses, TRUE, FALSE,
5840 EL_EMC_LENSES, -1, -1
5843 Xmagnify, TRUE, FALSE,
5844 EL_EMC_MAGNIFIER, -1, -1
5847 Xdripper, TRUE, FALSE,
5848 EL_EMC_DRIPPER, -1, -1
5851 XdripperB, FALSE, FALSE,
5852 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5855 Xfake_blank, TRUE, FALSE,
5856 EL_INVISIBLE_WALL, -1, -1
5859 Xfake_blankB, FALSE, FALSE,
5860 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5863 Xfake_grass, TRUE, FALSE,
5864 EL_EMC_FAKE_GRASS, -1, -1
5867 Xfake_grassB, FALSE, FALSE,
5868 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5871 Xfake_door_1, TRUE, FALSE,
5872 EL_EM_GATE_1_GRAY, -1, -1
5875 Xfake_door_2, TRUE, FALSE,
5876 EL_EM_GATE_2_GRAY, -1, -1
5879 Xfake_door_3, TRUE, FALSE,
5880 EL_EM_GATE_3_GRAY, -1, -1
5883 Xfake_door_4, TRUE, FALSE,
5884 EL_EM_GATE_4_GRAY, -1, -1
5887 Xfake_door_5, TRUE, FALSE,
5888 EL_EMC_GATE_5_GRAY, -1, -1
5891 Xfake_door_6, TRUE, FALSE,
5892 EL_EMC_GATE_6_GRAY, -1, -1
5895 Xfake_door_7, TRUE, FALSE,
5896 EL_EMC_GATE_7_GRAY, -1, -1
5899 Xfake_door_8, TRUE, FALSE,
5900 EL_EMC_GATE_8_GRAY, -1, -1
5903 Xfake_acid_1, TRUE, FALSE,
5904 EL_EMC_FAKE_ACID, -1, -1
5907 Xfake_acid_2, FALSE, FALSE,
5908 EL_EMC_FAKE_ACID, -1, -1
5911 Xfake_acid_3, FALSE, FALSE,
5912 EL_EMC_FAKE_ACID, -1, -1
5915 Xfake_acid_4, FALSE, FALSE,
5916 EL_EMC_FAKE_ACID, -1, -1
5919 Xfake_acid_5, FALSE, FALSE,
5920 EL_EMC_FAKE_ACID, -1, -1
5923 Xfake_acid_6, FALSE, FALSE,
5924 EL_EMC_FAKE_ACID, -1, -1
5927 Xfake_acid_7, FALSE, FALSE,
5928 EL_EMC_FAKE_ACID, -1, -1
5931 Xfake_acid_8, FALSE, FALSE,
5932 EL_EMC_FAKE_ACID, -1, -1
5935 Xsteel_1, TRUE, FALSE,
5936 EL_STEELWALL, -1, -1
5939 Xsteel_2, TRUE, FALSE,
5940 EL_EMC_STEELWALL_2, -1, -1
5943 Xsteel_3, TRUE, FALSE,
5944 EL_EMC_STEELWALL_3, -1, -1
5947 Xsteel_4, TRUE, FALSE,
5948 EL_EMC_STEELWALL_4, -1, -1
5951 Xwall_1, TRUE, FALSE,
5955 Xwall_2, TRUE, FALSE,
5956 EL_EMC_WALL_14, -1, -1
5959 Xwall_3, TRUE, FALSE,
5960 EL_EMC_WALL_15, -1, -1
5963 Xwall_4, TRUE, FALSE,
5964 EL_EMC_WALL_16, -1, -1
5967 Xround_wall_1, TRUE, FALSE,
5968 EL_WALL_SLIPPERY, -1, -1
5971 Xround_wall_2, TRUE, FALSE,
5972 EL_EMC_WALL_SLIPPERY_2, -1, -1
5975 Xround_wall_3, TRUE, FALSE,
5976 EL_EMC_WALL_SLIPPERY_3, -1, -1
5979 Xround_wall_4, TRUE, FALSE,
5980 EL_EMC_WALL_SLIPPERY_4, -1, -1
5983 Xdecor_1, TRUE, FALSE,
5984 EL_EMC_WALL_8, -1, -1
5987 Xdecor_2, TRUE, FALSE,
5988 EL_EMC_WALL_6, -1, -1
5991 Xdecor_3, TRUE, FALSE,
5992 EL_EMC_WALL_4, -1, -1
5995 Xdecor_4, TRUE, FALSE,
5996 EL_EMC_WALL_7, -1, -1
5999 Xdecor_5, TRUE, FALSE,
6000 EL_EMC_WALL_5, -1, -1
6003 Xdecor_6, TRUE, FALSE,
6004 EL_EMC_WALL_9, -1, -1
6007 Xdecor_7, TRUE, FALSE,
6008 EL_EMC_WALL_10, -1, -1
6011 Xdecor_8, TRUE, FALSE,
6012 EL_EMC_WALL_1, -1, -1
6015 Xdecor_9, TRUE, FALSE,
6016 EL_EMC_WALL_2, -1, -1
6019 Xdecor_10, TRUE, FALSE,
6020 EL_EMC_WALL_3, -1, -1
6023 Xdecor_11, TRUE, FALSE,
6024 EL_EMC_WALL_11, -1, -1
6027 Xdecor_12, TRUE, FALSE,
6028 EL_EMC_WALL_12, -1, -1
6031 Xalpha_0, TRUE, FALSE,
6032 EL_CHAR('0'), -1, -1
6035 Xalpha_1, TRUE, FALSE,
6036 EL_CHAR('1'), -1, -1
6039 Xalpha_2, TRUE, FALSE,
6040 EL_CHAR('2'), -1, -1
6043 Xalpha_3, TRUE, FALSE,
6044 EL_CHAR('3'), -1, -1
6047 Xalpha_4, TRUE, FALSE,
6048 EL_CHAR('4'), -1, -1
6051 Xalpha_5, TRUE, FALSE,
6052 EL_CHAR('5'), -1, -1
6055 Xalpha_6, TRUE, FALSE,
6056 EL_CHAR('6'), -1, -1
6059 Xalpha_7, TRUE, FALSE,
6060 EL_CHAR('7'), -1, -1
6063 Xalpha_8, TRUE, FALSE,
6064 EL_CHAR('8'), -1, -1
6067 Xalpha_9, TRUE, FALSE,
6068 EL_CHAR('9'), -1, -1
6071 Xalpha_excla, TRUE, FALSE,
6072 EL_CHAR('!'), -1, -1
6075 Xalpha_quote, TRUE, FALSE,
6076 EL_CHAR('"'), -1, -1
6079 Xalpha_comma, TRUE, FALSE,
6080 EL_CHAR(','), -1, -1
6083 Xalpha_minus, TRUE, FALSE,
6084 EL_CHAR('-'), -1, -1
6087 Xalpha_perio, TRUE, FALSE,
6088 EL_CHAR('.'), -1, -1
6091 Xalpha_colon, TRUE, FALSE,
6092 EL_CHAR(':'), -1, -1
6095 Xalpha_quest, TRUE, FALSE,
6096 EL_CHAR('?'), -1, -1
6099 Xalpha_a, TRUE, FALSE,
6100 EL_CHAR('A'), -1, -1
6103 Xalpha_b, TRUE, FALSE,
6104 EL_CHAR('B'), -1, -1
6107 Xalpha_c, TRUE, FALSE,
6108 EL_CHAR('C'), -1, -1
6111 Xalpha_d, TRUE, FALSE,
6112 EL_CHAR('D'), -1, -1
6115 Xalpha_e, TRUE, FALSE,
6116 EL_CHAR('E'), -1, -1
6119 Xalpha_f, TRUE, FALSE,
6120 EL_CHAR('F'), -1, -1
6123 Xalpha_g, TRUE, FALSE,
6124 EL_CHAR('G'), -1, -1
6127 Xalpha_h, TRUE, FALSE,
6128 EL_CHAR('H'), -1, -1
6131 Xalpha_i, TRUE, FALSE,
6132 EL_CHAR('I'), -1, -1
6135 Xalpha_j, TRUE, FALSE,
6136 EL_CHAR('J'), -1, -1
6139 Xalpha_k, TRUE, FALSE,
6140 EL_CHAR('K'), -1, -1
6143 Xalpha_l, TRUE, FALSE,
6144 EL_CHAR('L'), -1, -1
6147 Xalpha_m, TRUE, FALSE,
6148 EL_CHAR('M'), -1, -1
6151 Xalpha_n, TRUE, FALSE,
6152 EL_CHAR('N'), -1, -1
6155 Xalpha_o, TRUE, FALSE,
6156 EL_CHAR('O'), -1, -1
6159 Xalpha_p, TRUE, FALSE,
6160 EL_CHAR('P'), -1, -1
6163 Xalpha_q, TRUE, FALSE,
6164 EL_CHAR('Q'), -1, -1
6167 Xalpha_r, TRUE, FALSE,
6168 EL_CHAR('R'), -1, -1
6171 Xalpha_s, TRUE, FALSE,
6172 EL_CHAR('S'), -1, -1
6175 Xalpha_t, TRUE, FALSE,
6176 EL_CHAR('T'), -1, -1
6179 Xalpha_u, TRUE, FALSE,
6180 EL_CHAR('U'), -1, -1
6183 Xalpha_v, TRUE, FALSE,
6184 EL_CHAR('V'), -1, -1
6187 Xalpha_w, TRUE, FALSE,
6188 EL_CHAR('W'), -1, -1
6191 Xalpha_x, TRUE, FALSE,
6192 EL_CHAR('X'), -1, -1
6195 Xalpha_y, TRUE, FALSE,
6196 EL_CHAR('Y'), -1, -1
6199 Xalpha_z, TRUE, FALSE,
6200 EL_CHAR('Z'), -1, -1
6203 Xalpha_arrow_e, TRUE, FALSE,
6204 EL_CHAR('>'), -1, -1
6207 Xalpha_arrow_w, TRUE, FALSE,
6208 EL_CHAR('<'), -1, -1
6211 Xalpha_copyr, TRUE, FALSE,
6212 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6216 Xboom_bug, FALSE, FALSE,
6217 EL_BUG, ACTION_EXPLODING, -1
6220 Xboom_bomb, FALSE, FALSE,
6221 EL_BOMB, ACTION_EXPLODING, -1
6224 Xboom_android, FALSE, FALSE,
6225 EL_EMC_ANDROID, ACTION_OTHER, -1
6228 Xboom_1, FALSE, FALSE,
6229 EL_DEFAULT, ACTION_EXPLODING, -1
6232 Xboom_2, FALSE, FALSE,
6233 EL_DEFAULT, ACTION_EXPLODING, -1
6236 Znormal, FALSE, FALSE,
6240 Zdynamite, FALSE, FALSE,
6244 Zplayer, FALSE, FALSE,
6248 ZBORDER, FALSE, FALSE,
6258 static struct Mapping_EM_to_RND_player
6267 em_player_mapping_list[] =
6271 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6275 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6279 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6283 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6287 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6291 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6295 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6299 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6303 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6307 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6311 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6315 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6319 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6323 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6327 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6331 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6335 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6339 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6343 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6347 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6351 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6355 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6359 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6363 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6367 EL_PLAYER_1, ACTION_DEFAULT, -1,
6371 EL_PLAYER_2, ACTION_DEFAULT, -1,
6375 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6379 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6383 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6387 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6391 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6395 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6399 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6403 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6407 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6411 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6415 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6419 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6423 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6427 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6431 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6435 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6439 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6443 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6447 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6451 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6455 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6459 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6463 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6467 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6471 EL_PLAYER_3, ACTION_DEFAULT, -1,
6475 EL_PLAYER_4, ACTION_DEFAULT, -1,
6484 int map_element_RND_to_EM(int element_rnd)
6486 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6487 static boolean mapping_initialized = FALSE;
6489 if (!mapping_initialized)
6493 /* return "Xalpha_quest" for all undefined elements in mapping array */
6494 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6495 mapping_RND_to_EM[i] = Xalpha_quest;
6497 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6498 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6499 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6500 em_object_mapping_list[i].element_em;
6502 mapping_initialized = TRUE;
6505 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6506 return mapping_RND_to_EM[element_rnd];
6508 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6513 int map_element_EM_to_RND(int element_em)
6515 static unsigned short mapping_EM_to_RND[TILE_MAX];
6516 static boolean mapping_initialized = FALSE;
6518 if (!mapping_initialized)
6522 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6523 for (i = 0; i < TILE_MAX; i++)
6524 mapping_EM_to_RND[i] = EL_UNKNOWN;
6526 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6527 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6528 em_object_mapping_list[i].element_rnd;
6530 mapping_initialized = TRUE;
6533 if (element_em >= 0 && element_em < TILE_MAX)
6534 return mapping_EM_to_RND[element_em];
6536 Error(ERR_WARN, "invalid EM level element %d", element_em);
6541 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6543 struct LevelInfo_EM *level_em = level->native_em_level;
6544 struct LEVEL *lev = level_em->lev;
6547 for (i = 0; i < TILE_MAX; i++)
6548 lev->android_array[i] = Xblank;
6550 for (i = 0; i < level->num_android_clone_elements; i++)
6552 int element_rnd = level->android_clone_element[i];
6553 int element_em = map_element_RND_to_EM(element_rnd);
6555 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6556 if (em_object_mapping_list[j].element_rnd == element_rnd)
6557 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6561 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6563 struct LevelInfo_EM *level_em = level->native_em_level;
6564 struct LEVEL *lev = level_em->lev;
6567 level->num_android_clone_elements = 0;
6569 for (i = 0; i < TILE_MAX; i++)
6571 int element_em = lev->android_array[i];
6573 boolean element_found = FALSE;
6575 if (element_em == Xblank)
6578 element_rnd = map_element_EM_to_RND(element_em);
6580 for (j = 0; j < level->num_android_clone_elements; j++)
6581 if (level->android_clone_element[j] == element_rnd)
6582 element_found = TRUE;
6586 level->android_clone_element[level->num_android_clone_elements++] =
6589 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6594 if (level->num_android_clone_elements == 0)
6596 level->num_android_clone_elements = 1;
6597 level->android_clone_element[0] = EL_EMPTY;
6601 int map_direction_RND_to_EM(int direction)
6603 return (direction == MV_UP ? 0 :
6604 direction == MV_RIGHT ? 1 :
6605 direction == MV_DOWN ? 2 :
6606 direction == MV_LEFT ? 3 :
6610 int map_direction_EM_to_RND(int direction)
6612 return (direction == 0 ? MV_UP :
6613 direction == 1 ? MV_RIGHT :
6614 direction == 2 ? MV_DOWN :
6615 direction == 3 ? MV_LEFT :
6619 int map_element_RND_to_SP(int element_rnd)
6621 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6623 if (element_rnd >= EL_SP_START &&
6624 element_rnd <= EL_SP_END)
6625 element_sp = element_rnd - EL_SP_START;
6626 else if (element_rnd == EL_EMPTY_SPACE)
6628 else if (element_rnd == EL_INVISIBLE_WALL)
6634 int map_element_SP_to_RND(int element_sp)
6636 int element_rnd = EL_UNKNOWN;
6638 if (element_sp >= 0x00 &&
6640 element_rnd = EL_SP_START + element_sp;
6641 else if (element_sp == 0x28)
6642 element_rnd = EL_INVISIBLE_WALL;
6647 int map_action_SP_to_RND(int action_sp)
6651 case actActive: return ACTION_ACTIVE;
6652 case actImpact: return ACTION_IMPACT;
6653 case actExploding: return ACTION_EXPLODING;
6654 case actDigging: return ACTION_DIGGING;
6655 case actSnapping: return ACTION_SNAPPING;
6656 case actCollecting: return ACTION_COLLECTING;
6657 case actPassing: return ACTION_PASSING;
6658 case actPushing: return ACTION_PUSHING;
6659 case actDropping: return ACTION_DROPPING;
6661 default: return ACTION_DEFAULT;
6665 int get_next_element(int element)
6669 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6670 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6671 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6672 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6673 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6674 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6675 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6676 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6677 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6678 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6679 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6681 default: return element;
6685 int el_act_dir2img(int element, int action, int direction)
6687 element = GFX_ELEMENT(element);
6688 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6690 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6691 return element_info[element].direction_graphic[action][direction];
6694 static int el_act_dir2crm(int element, int action, int direction)
6696 element = GFX_ELEMENT(element);
6697 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6699 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6700 return element_info[element].direction_crumbled[action][direction];
6703 int el_act2img(int element, int action)
6705 element = GFX_ELEMENT(element);
6707 return element_info[element].graphic[action];
6710 int el_act2crm(int element, int action)
6712 element = GFX_ELEMENT(element);
6714 return element_info[element].crumbled[action];
6717 int el_dir2img(int element, int direction)
6719 element = GFX_ELEMENT(element);
6721 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6724 int el2baseimg(int element)
6726 return element_info[element].graphic[ACTION_DEFAULT];
6729 int el2img(int element)
6731 element = GFX_ELEMENT(element);
6733 return element_info[element].graphic[ACTION_DEFAULT];
6736 int el2edimg(int element)
6738 element = GFX_ELEMENT(element);
6740 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6743 int el2preimg(int element)
6745 element = GFX_ELEMENT(element);
6747 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6750 int el2panelimg(int element)
6752 element = GFX_ELEMENT(element);
6754 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6757 int font2baseimg(int font_nr)
6759 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6762 int getBeltNrFromBeltElement(int element)
6764 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6765 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6766 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6769 int getBeltNrFromBeltActiveElement(int element)
6771 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6772 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6773 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6776 int getBeltNrFromBeltSwitchElement(int element)
6778 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6779 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6780 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6783 int getBeltDirNrFromBeltElement(int element)
6785 static int belt_base_element[4] =
6787 EL_CONVEYOR_BELT_1_LEFT,
6788 EL_CONVEYOR_BELT_2_LEFT,
6789 EL_CONVEYOR_BELT_3_LEFT,
6790 EL_CONVEYOR_BELT_4_LEFT
6793 int belt_nr = getBeltNrFromBeltElement(element);
6794 int belt_dir_nr = element - belt_base_element[belt_nr];
6796 return (belt_dir_nr % 3);
6799 int getBeltDirNrFromBeltSwitchElement(int element)
6801 static int belt_base_element[4] =
6803 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6804 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6805 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6806 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6809 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6810 int belt_dir_nr = element - belt_base_element[belt_nr];
6812 return (belt_dir_nr % 3);
6815 int getBeltDirFromBeltElement(int element)
6817 static int belt_move_dir[3] =
6824 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6826 return belt_move_dir[belt_dir_nr];
6829 int getBeltDirFromBeltSwitchElement(int element)
6831 static int belt_move_dir[3] =
6838 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6840 return belt_move_dir[belt_dir_nr];
6843 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6845 static int belt_base_element[4] =
6847 EL_CONVEYOR_BELT_1_LEFT,
6848 EL_CONVEYOR_BELT_2_LEFT,
6849 EL_CONVEYOR_BELT_3_LEFT,
6850 EL_CONVEYOR_BELT_4_LEFT
6853 return belt_base_element[belt_nr] + belt_dir_nr;
6856 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6858 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6860 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6863 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6865 static int belt_base_element[4] =
6867 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6868 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6869 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6870 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6873 return belt_base_element[belt_nr] + belt_dir_nr;
6876 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6878 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6880 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6883 boolean getTeamMode_EM()
6885 return game.team_mode;
6888 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6890 int game_frame_delay_value;
6892 game_frame_delay_value =
6893 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6894 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6897 if (tape.playing && tape.warp_forward && !tape.pausing)
6898 game_frame_delay_value = 0;
6900 return game_frame_delay_value;
6903 unsigned int InitRND(int seed)
6905 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6906 return InitEngineRandom_EM(seed);
6907 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6908 return InitEngineRandom_SP(seed);
6910 return InitEngineRandom_RND(seed);
6913 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6914 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6916 inline static int get_effective_element_EM(int tile, int frame_em)
6918 int element = object_mapping[tile].element_rnd;
6919 int action = object_mapping[tile].action;
6920 boolean is_backside = object_mapping[tile].is_backside;
6921 boolean action_removing = (action == ACTION_DIGGING ||
6922 action == ACTION_SNAPPING ||
6923 action == ACTION_COLLECTING);
6929 case Yacid_splash_eB:
6930 case Yacid_splash_wB:
6931 return (frame_em > 5 ? EL_EMPTY : element);
6937 else /* frame_em == 7 */
6941 case Yacid_splash_eB:
6942 case Yacid_splash_wB:
6945 case Yemerald_stone:
6948 case Ydiamond_stone:
6952 case Xdrip_stretchB:
6971 case Xsand_stonein_1:
6972 case Xsand_stonein_2:
6973 case Xsand_stonein_3:
6974 case Xsand_stonein_4:
6978 return (is_backside || action_removing ? EL_EMPTY : element);
6983 inline static boolean check_linear_animation_EM(int tile)
6987 case Xsand_stonesand_1:
6988 case Xsand_stonesand_quickout_1:
6989 case Xsand_sandstone_1:
6990 case Xsand_stonein_1:
6991 case Xsand_stoneout_1:
7010 case Yacid_splash_eB:
7011 case Yacid_splash_wB:
7012 case Yemerald_stone:
7019 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7020 boolean has_crumbled_graphics,
7021 int crumbled, int sync_frame)
7023 /* if element can be crumbled, but certain action graphics are just empty
7024 space (like instantly snapping sand to empty space in 1 frame), do not
7025 treat these empty space graphics as crumbled graphics in EMC engine */
7026 if (crumbled == IMG_EMPTY_SPACE)
7027 has_crumbled_graphics = FALSE;
7029 if (has_crumbled_graphics)
7031 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7032 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7033 g_crumbled->anim_delay,
7034 g_crumbled->anim_mode,
7035 g_crumbled->anim_start_frame,
7038 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7039 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7041 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7043 g_em->has_crumbled_graphics = TRUE;
7047 g_em->crumbled_bitmap = NULL;
7048 g_em->crumbled_src_x = 0;
7049 g_em->crumbled_src_y = 0;
7050 g_em->crumbled_border_size = 0;
7052 g_em->has_crumbled_graphics = FALSE;
7056 void ResetGfxAnimation_EM(int x, int y, int tile)
7061 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7062 int tile, int frame_em, int x, int y)
7064 int action = object_mapping[tile].action;
7065 int direction = object_mapping[tile].direction;
7066 int effective_element = get_effective_element_EM(tile, frame_em);
7067 int graphic = (direction == MV_NONE ?
7068 el_act2img(effective_element, action) :
7069 el_act_dir2img(effective_element, action, direction));
7070 struct GraphicInfo *g = &graphic_info[graphic];
7072 boolean action_removing = (action == ACTION_DIGGING ||
7073 action == ACTION_SNAPPING ||
7074 action == ACTION_COLLECTING);
7075 boolean action_moving = (action == ACTION_FALLING ||
7076 action == ACTION_MOVING ||
7077 action == ACTION_PUSHING ||
7078 action == ACTION_EATING ||
7079 action == ACTION_FILLING ||
7080 action == ACTION_EMPTYING);
7081 boolean action_falling = (action == ACTION_FALLING ||
7082 action == ACTION_FILLING ||
7083 action == ACTION_EMPTYING);
7085 /* special case: graphic uses "2nd movement tile" and has defined
7086 7 frames for movement animation (or less) => use default graphic
7087 for last (8th) frame which ends the movement animation */
7088 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7090 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7091 graphic = (direction == MV_NONE ?
7092 el_act2img(effective_element, action) :
7093 el_act_dir2img(effective_element, action, direction));
7095 g = &graphic_info[graphic];
7098 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7102 else if (action_moving)
7104 boolean is_backside = object_mapping[tile].is_backside;
7108 int direction = object_mapping[tile].direction;
7109 int move_dir = (action_falling ? MV_DOWN : direction);
7114 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7115 if (g->double_movement && frame_em == 0)
7119 if (move_dir == MV_LEFT)
7120 GfxFrame[x - 1][y] = GfxFrame[x][y];
7121 else if (move_dir == MV_RIGHT)
7122 GfxFrame[x + 1][y] = GfxFrame[x][y];
7123 else if (move_dir == MV_UP)
7124 GfxFrame[x][y - 1] = GfxFrame[x][y];
7125 else if (move_dir == MV_DOWN)
7126 GfxFrame[x][y + 1] = GfxFrame[x][y];
7133 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7134 if (tile == Xsand_stonesand_quickout_1 ||
7135 tile == Xsand_stonesand_quickout_2)
7139 if (graphic_info[graphic].anim_global_sync)
7140 sync_frame = FrameCounter;
7141 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7142 sync_frame = GfxFrame[x][y];
7144 sync_frame = 0; /* playfield border (pseudo steel) */
7146 SetRandomAnimationValue(x, y);
7148 int frame = getAnimationFrame(g->anim_frames,
7151 g->anim_start_frame,
7154 g_em->unique_identifier =
7155 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7158 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7159 int tile, int frame_em, int x, int y)
7161 int action = object_mapping[tile].action;
7162 int direction = object_mapping[tile].direction;
7163 boolean is_backside = object_mapping[tile].is_backside;
7164 int effective_element = get_effective_element_EM(tile, frame_em);
7165 int effective_action = action;
7166 int graphic = (direction == MV_NONE ?
7167 el_act2img(effective_element, effective_action) :
7168 el_act_dir2img(effective_element, effective_action,
7170 int crumbled = (direction == MV_NONE ?
7171 el_act2crm(effective_element, effective_action) :
7172 el_act_dir2crm(effective_element, effective_action,
7174 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7175 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7176 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7177 struct GraphicInfo *g = &graphic_info[graphic];
7180 /* special case: graphic uses "2nd movement tile" and has defined
7181 7 frames for movement animation (or less) => use default graphic
7182 for last (8th) frame which ends the movement animation */
7183 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7185 effective_action = ACTION_DEFAULT;
7186 graphic = (direction == MV_NONE ?
7187 el_act2img(effective_element, effective_action) :
7188 el_act_dir2img(effective_element, effective_action,
7190 crumbled = (direction == MV_NONE ?
7191 el_act2crm(effective_element, effective_action) :
7192 el_act_dir2crm(effective_element, effective_action,
7195 g = &graphic_info[graphic];
7198 if (graphic_info[graphic].anim_global_sync)
7199 sync_frame = FrameCounter;
7200 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7201 sync_frame = GfxFrame[x][y];
7203 sync_frame = 0; /* playfield border (pseudo steel) */
7205 SetRandomAnimationValue(x, y);
7207 int frame = getAnimationFrame(g->anim_frames,
7210 g->anim_start_frame,
7213 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7214 g->double_movement && is_backside);
7216 /* (updating the "crumbled" graphic definitions is probably not really needed,
7217 as animations for crumbled graphics can't be longer than one EMC cycle) */
7218 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7222 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7223 int player_nr, int anim, int frame_em)
7225 int element = player_mapping[player_nr][anim].element_rnd;
7226 int action = player_mapping[player_nr][anim].action;
7227 int direction = player_mapping[player_nr][anim].direction;
7228 int graphic = (direction == MV_NONE ?
7229 el_act2img(element, action) :
7230 el_act_dir2img(element, action, direction));
7231 struct GraphicInfo *g = &graphic_info[graphic];
7234 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7236 stored_player[player_nr].StepFrame = frame_em;
7238 sync_frame = stored_player[player_nr].Frame;
7240 int frame = getAnimationFrame(g->anim_frames,
7243 g->anim_start_frame,
7246 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7247 &g_em->src_x, &g_em->src_y, FALSE);
7250 void InitGraphicInfo_EM(void)
7255 int num_em_gfx_errors = 0;
7257 if (graphic_info_em_object[0][0].bitmap == NULL)
7259 /* EM graphics not yet initialized in em_open_all() */
7264 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7267 /* always start with reliable default values */
7268 for (i = 0; i < TILE_MAX; i++)
7270 object_mapping[i].element_rnd = EL_UNKNOWN;
7271 object_mapping[i].is_backside = FALSE;
7272 object_mapping[i].action = ACTION_DEFAULT;
7273 object_mapping[i].direction = MV_NONE;
7276 /* always start with reliable default values */
7277 for (p = 0; p < MAX_PLAYERS; p++)
7279 for (i = 0; i < SPR_MAX; i++)
7281 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7282 player_mapping[p][i].action = ACTION_DEFAULT;
7283 player_mapping[p][i].direction = MV_NONE;
7287 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7289 int e = em_object_mapping_list[i].element_em;
7291 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7292 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7294 if (em_object_mapping_list[i].action != -1)
7295 object_mapping[e].action = em_object_mapping_list[i].action;
7297 if (em_object_mapping_list[i].direction != -1)
7298 object_mapping[e].direction =
7299 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7302 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7304 int a = em_player_mapping_list[i].action_em;
7305 int p = em_player_mapping_list[i].player_nr;
7307 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7309 if (em_player_mapping_list[i].action != -1)
7310 player_mapping[p][a].action = em_player_mapping_list[i].action;
7312 if (em_player_mapping_list[i].direction != -1)
7313 player_mapping[p][a].direction =
7314 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7317 for (i = 0; i < TILE_MAX; i++)
7319 int element = object_mapping[i].element_rnd;
7320 int action = object_mapping[i].action;
7321 int direction = object_mapping[i].direction;
7322 boolean is_backside = object_mapping[i].is_backside;
7323 boolean action_exploding = ((action == ACTION_EXPLODING ||
7324 action == ACTION_SMASHED_BY_ROCK ||
7325 action == ACTION_SMASHED_BY_SPRING) &&
7326 element != EL_DIAMOND);
7327 boolean action_active = (action == ACTION_ACTIVE);
7328 boolean action_other = (action == ACTION_OTHER);
7330 for (j = 0; j < 8; j++)
7332 int effective_element = get_effective_element_EM(i, j);
7333 int effective_action = (j < 7 ? action :
7334 i == Xdrip_stretch ? action :
7335 i == Xdrip_stretchB ? action :
7336 i == Ydrip_s1 ? action :
7337 i == Ydrip_s1B ? action :
7338 i == Xball_1B ? action :
7339 i == Xball_2 ? action :
7340 i == Xball_2B ? action :
7341 i == Yball_eat ? action :
7342 i == Ykey_1_eat ? action :
7343 i == Ykey_2_eat ? action :
7344 i == Ykey_3_eat ? action :
7345 i == Ykey_4_eat ? action :
7346 i == Ykey_5_eat ? action :
7347 i == Ykey_6_eat ? action :
7348 i == Ykey_7_eat ? action :
7349 i == Ykey_8_eat ? action :
7350 i == Ylenses_eat ? action :
7351 i == Ymagnify_eat ? action :
7352 i == Ygrass_eat ? action :
7353 i == Ydirt_eat ? action :
7354 i == Xsand_stonein_1 ? action :
7355 i == Xsand_stonein_2 ? action :
7356 i == Xsand_stonein_3 ? action :
7357 i == Xsand_stonein_4 ? action :
7358 i == Xsand_stoneout_1 ? action :
7359 i == Xsand_stoneout_2 ? action :
7360 i == Xboom_android ? ACTION_EXPLODING :
7361 action_exploding ? ACTION_EXPLODING :
7362 action_active ? action :
7363 action_other ? action :
7365 int graphic = (el_act_dir2img(effective_element, effective_action,
7367 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7369 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7370 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7371 boolean has_action_graphics = (graphic != base_graphic);
7372 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7373 struct GraphicInfo *g = &graphic_info[graphic];
7374 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7377 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7378 boolean special_animation = (action != ACTION_DEFAULT &&
7379 g->anim_frames == 3 &&
7380 g->anim_delay == 2 &&
7381 g->anim_mode & ANIM_LINEAR);
7382 int sync_frame = (i == Xdrip_stretch ? 7 :
7383 i == Xdrip_stretchB ? 7 :
7384 i == Ydrip_s2 ? j + 8 :
7385 i == Ydrip_s2B ? j + 8 :
7394 i == Xfake_acid_1 ? 0 :
7395 i == Xfake_acid_2 ? 10 :
7396 i == Xfake_acid_3 ? 20 :
7397 i == Xfake_acid_4 ? 30 :
7398 i == Xfake_acid_5 ? 40 :
7399 i == Xfake_acid_6 ? 50 :
7400 i == Xfake_acid_7 ? 60 :
7401 i == Xfake_acid_8 ? 70 :
7403 i == Xball_2B ? j + 8 :
7404 i == Yball_eat ? j + 1 :
7405 i == Ykey_1_eat ? j + 1 :
7406 i == Ykey_2_eat ? j + 1 :
7407 i == Ykey_3_eat ? j + 1 :
7408 i == Ykey_4_eat ? j + 1 :
7409 i == Ykey_5_eat ? j + 1 :
7410 i == Ykey_6_eat ? j + 1 :
7411 i == Ykey_7_eat ? j + 1 :
7412 i == Ykey_8_eat ? j + 1 :
7413 i == Ylenses_eat ? j + 1 :
7414 i == Ymagnify_eat ? j + 1 :
7415 i == Ygrass_eat ? j + 1 :
7416 i == Ydirt_eat ? j + 1 :
7417 i == Xamoeba_1 ? 0 :
7418 i == Xamoeba_2 ? 1 :
7419 i == Xamoeba_3 ? 2 :
7420 i == Xamoeba_4 ? 3 :
7421 i == Xamoeba_5 ? 0 :
7422 i == Xamoeba_6 ? 1 :
7423 i == Xamoeba_7 ? 2 :
7424 i == Xamoeba_8 ? 3 :
7425 i == Xexit_2 ? j + 8 :
7426 i == Xexit_3 ? j + 16 :
7427 i == Xdynamite_1 ? 0 :
7428 i == Xdynamite_2 ? 8 :
7429 i == Xdynamite_3 ? 16 :
7430 i == Xdynamite_4 ? 24 :
7431 i == Xsand_stonein_1 ? j + 1 :
7432 i == Xsand_stonein_2 ? j + 9 :
7433 i == Xsand_stonein_3 ? j + 17 :
7434 i == Xsand_stonein_4 ? j + 25 :
7435 i == Xsand_stoneout_1 && j == 0 ? 0 :
7436 i == Xsand_stoneout_1 && j == 1 ? 0 :
7437 i == Xsand_stoneout_1 && j == 2 ? 1 :
7438 i == Xsand_stoneout_1 && j == 3 ? 2 :
7439 i == Xsand_stoneout_1 && j == 4 ? 2 :
7440 i == Xsand_stoneout_1 && j == 5 ? 3 :
7441 i == Xsand_stoneout_1 && j == 6 ? 4 :
7442 i == Xsand_stoneout_1 && j == 7 ? 4 :
7443 i == Xsand_stoneout_2 && j == 0 ? 5 :
7444 i == Xsand_stoneout_2 && j == 1 ? 6 :
7445 i == Xsand_stoneout_2 && j == 2 ? 7 :
7446 i == Xsand_stoneout_2 && j == 3 ? 8 :
7447 i == Xsand_stoneout_2 && j == 4 ? 9 :
7448 i == Xsand_stoneout_2 && j == 5 ? 11 :
7449 i == Xsand_stoneout_2 && j == 6 ? 13 :
7450 i == Xsand_stoneout_2 && j == 7 ? 15 :
7451 i == Xboom_bug && j == 1 ? 2 :
7452 i == Xboom_bug && j == 2 ? 2 :
7453 i == Xboom_bug && j == 3 ? 4 :
7454 i == Xboom_bug && j == 4 ? 4 :
7455 i == Xboom_bug && j == 5 ? 2 :
7456 i == Xboom_bug && j == 6 ? 2 :
7457 i == Xboom_bug && j == 7 ? 0 :
7458 i == Xboom_bomb && j == 1 ? 2 :
7459 i == Xboom_bomb && j == 2 ? 2 :
7460 i == Xboom_bomb && j == 3 ? 4 :
7461 i == Xboom_bomb && j == 4 ? 4 :
7462 i == Xboom_bomb && j == 5 ? 2 :
7463 i == Xboom_bomb && j == 6 ? 2 :
7464 i == Xboom_bomb && j == 7 ? 0 :
7465 i == Xboom_android && j == 7 ? 6 :
7466 i == Xboom_1 && j == 1 ? 2 :
7467 i == Xboom_1 && j == 2 ? 2 :
7468 i == Xboom_1 && j == 3 ? 4 :
7469 i == Xboom_1 && j == 4 ? 4 :
7470 i == Xboom_1 && j == 5 ? 6 :
7471 i == Xboom_1 && j == 6 ? 6 :
7472 i == Xboom_1 && j == 7 ? 8 :
7473 i == Xboom_2 && j == 0 ? 8 :
7474 i == Xboom_2 && j == 1 ? 8 :
7475 i == Xboom_2 && j == 2 ? 10 :
7476 i == Xboom_2 && j == 3 ? 10 :
7477 i == Xboom_2 && j == 4 ? 10 :
7478 i == Xboom_2 && j == 5 ? 12 :
7479 i == Xboom_2 && j == 6 ? 12 :
7480 i == Xboom_2 && j == 7 ? 12 :
7481 special_animation && j == 4 ? 3 :
7482 effective_action != action ? 0 :
7486 Bitmap *debug_bitmap = g_em->bitmap;
7487 int debug_src_x = g_em->src_x;
7488 int debug_src_y = g_em->src_y;
7491 int frame = getAnimationFrame(g->anim_frames,
7494 g->anim_start_frame,
7497 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7498 g->double_movement && is_backside);
7500 g_em->bitmap = src_bitmap;
7501 g_em->src_x = src_x;
7502 g_em->src_y = src_y;
7503 g_em->src_offset_x = 0;
7504 g_em->src_offset_y = 0;
7505 g_em->dst_offset_x = 0;
7506 g_em->dst_offset_y = 0;
7507 g_em->width = TILEX;
7508 g_em->height = TILEY;
7510 g_em->preserve_background = FALSE;
7512 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7515 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7516 effective_action == ACTION_MOVING ||
7517 effective_action == ACTION_PUSHING ||
7518 effective_action == ACTION_EATING)) ||
7519 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7520 effective_action == ACTION_EMPTYING)))
7523 (effective_action == ACTION_FALLING ||
7524 effective_action == ACTION_FILLING ||
7525 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7526 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7527 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7528 int num_steps = (i == Ydrip_s1 ? 16 :
7529 i == Ydrip_s1B ? 16 :
7530 i == Ydrip_s2 ? 16 :
7531 i == Ydrip_s2B ? 16 :
7532 i == Xsand_stonein_1 ? 32 :
7533 i == Xsand_stonein_2 ? 32 :
7534 i == Xsand_stonein_3 ? 32 :
7535 i == Xsand_stonein_4 ? 32 :
7536 i == Xsand_stoneout_1 ? 16 :
7537 i == Xsand_stoneout_2 ? 16 : 8);
7538 int cx = ABS(dx) * (TILEX / num_steps);
7539 int cy = ABS(dy) * (TILEY / num_steps);
7540 int step_frame = (i == Ydrip_s2 ? j + 8 :
7541 i == Ydrip_s2B ? j + 8 :
7542 i == Xsand_stonein_2 ? j + 8 :
7543 i == Xsand_stonein_3 ? j + 16 :
7544 i == Xsand_stonein_4 ? j + 24 :
7545 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7546 int step = (is_backside ? step_frame : num_steps - step_frame);
7548 if (is_backside) /* tile where movement starts */
7550 if (dx < 0 || dy < 0)
7552 g_em->src_offset_x = cx * step;
7553 g_em->src_offset_y = cy * step;
7557 g_em->dst_offset_x = cx * step;
7558 g_em->dst_offset_y = cy * step;
7561 else /* tile where movement ends */
7563 if (dx < 0 || dy < 0)
7565 g_em->dst_offset_x = cx * step;
7566 g_em->dst_offset_y = cy * step;
7570 g_em->src_offset_x = cx * step;
7571 g_em->src_offset_y = cy * step;
7575 g_em->width = TILEX - cx * step;
7576 g_em->height = TILEY - cy * step;
7579 /* create unique graphic identifier to decide if tile must be redrawn */
7580 /* bit 31 - 16 (16 bit): EM style graphic
7581 bit 15 - 12 ( 4 bit): EM style frame
7582 bit 11 - 6 ( 6 bit): graphic width
7583 bit 5 - 0 ( 6 bit): graphic height */
7584 g_em->unique_identifier =
7585 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7589 /* skip check for EMC elements not contained in original EMC artwork */
7590 if (element == EL_EMC_FAKE_ACID)
7593 if (g_em->bitmap != debug_bitmap ||
7594 g_em->src_x != debug_src_x ||
7595 g_em->src_y != debug_src_y ||
7596 g_em->src_offset_x != 0 ||
7597 g_em->src_offset_y != 0 ||
7598 g_em->dst_offset_x != 0 ||
7599 g_em->dst_offset_y != 0 ||
7600 g_em->width != TILEX ||
7601 g_em->height != TILEY)
7603 static int last_i = -1;
7611 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7612 i, element, element_info[element].token_name,
7613 element_action_info[effective_action].suffix, direction);
7615 if (element != effective_element)
7616 printf(" [%d ('%s')]",
7618 element_info[effective_element].token_name);
7622 if (g_em->bitmap != debug_bitmap)
7623 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7624 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7626 if (g_em->src_x != debug_src_x ||
7627 g_em->src_y != debug_src_y)
7628 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7629 j, (is_backside ? 'B' : 'F'),
7630 g_em->src_x, g_em->src_y,
7631 g_em->src_x / 32, g_em->src_y / 32,
7632 debug_src_x, debug_src_y,
7633 debug_src_x / 32, debug_src_y / 32);
7635 if (g_em->src_offset_x != 0 ||
7636 g_em->src_offset_y != 0 ||
7637 g_em->dst_offset_x != 0 ||
7638 g_em->dst_offset_y != 0)
7639 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7641 g_em->src_offset_x, g_em->src_offset_y,
7642 g_em->dst_offset_x, g_em->dst_offset_y);
7644 if (g_em->width != TILEX ||
7645 g_em->height != TILEY)
7646 printf(" %d (%d): size %d,%d should be %d,%d\n",
7648 g_em->width, g_em->height, TILEX, TILEY);
7650 num_em_gfx_errors++;
7657 for (i = 0; i < TILE_MAX; i++)
7659 for (j = 0; j < 8; j++)
7661 int element = object_mapping[i].element_rnd;
7662 int action = object_mapping[i].action;
7663 int direction = object_mapping[i].direction;
7664 boolean is_backside = object_mapping[i].is_backside;
7665 int graphic_action = el_act_dir2img(element, action, direction);
7666 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7668 if ((action == ACTION_SMASHED_BY_ROCK ||
7669 action == ACTION_SMASHED_BY_SPRING ||
7670 action == ACTION_EATING) &&
7671 graphic_action == graphic_default)
7673 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7674 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7675 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7676 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7679 /* no separate animation for "smashed by rock" -- use rock instead */
7680 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7681 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7683 g_em->bitmap = g_xx->bitmap;
7684 g_em->src_x = g_xx->src_x;
7685 g_em->src_y = g_xx->src_y;
7686 g_em->src_offset_x = g_xx->src_offset_x;
7687 g_em->src_offset_y = g_xx->src_offset_y;
7688 g_em->dst_offset_x = g_xx->dst_offset_x;
7689 g_em->dst_offset_y = g_xx->dst_offset_y;
7690 g_em->width = g_xx->width;
7691 g_em->height = g_xx->height;
7692 g_em->unique_identifier = g_xx->unique_identifier;
7695 g_em->preserve_background = TRUE;
7700 for (p = 0; p < MAX_PLAYERS; p++)
7702 for (i = 0; i < SPR_MAX; i++)
7704 int element = player_mapping[p][i].element_rnd;
7705 int action = player_mapping[p][i].action;
7706 int direction = player_mapping[p][i].direction;
7708 for (j = 0; j < 8; j++)
7710 int effective_element = element;
7711 int effective_action = action;
7712 int graphic = (direction == MV_NONE ?
7713 el_act2img(effective_element, effective_action) :
7714 el_act_dir2img(effective_element, effective_action,
7716 struct GraphicInfo *g = &graphic_info[graphic];
7717 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7723 Bitmap *debug_bitmap = g_em->bitmap;
7724 int debug_src_x = g_em->src_x;
7725 int debug_src_y = g_em->src_y;
7728 int frame = getAnimationFrame(g->anim_frames,
7731 g->anim_start_frame,
7734 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7736 g_em->bitmap = src_bitmap;
7737 g_em->src_x = src_x;
7738 g_em->src_y = src_y;
7739 g_em->src_offset_x = 0;
7740 g_em->src_offset_y = 0;
7741 g_em->dst_offset_x = 0;
7742 g_em->dst_offset_y = 0;
7743 g_em->width = TILEX;
7744 g_em->height = TILEY;
7748 /* skip check for EMC elements not contained in original EMC artwork */
7749 if (element == EL_PLAYER_3 ||
7750 element == EL_PLAYER_4)
7753 if (g_em->bitmap != debug_bitmap ||
7754 g_em->src_x != debug_src_x ||
7755 g_em->src_y != debug_src_y)
7757 static int last_i = -1;
7765 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7766 p, i, element, element_info[element].token_name,
7767 element_action_info[effective_action].suffix, direction);
7769 if (element != effective_element)
7770 printf(" [%d ('%s')]",
7772 element_info[effective_element].token_name);
7776 if (g_em->bitmap != debug_bitmap)
7777 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7778 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7780 if (g_em->src_x != debug_src_x ||
7781 g_em->src_y != debug_src_y)
7782 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7784 g_em->src_x, g_em->src_y,
7785 g_em->src_x / 32, g_em->src_y / 32,
7786 debug_src_x, debug_src_y,
7787 debug_src_x / 32, debug_src_y / 32);
7789 num_em_gfx_errors++;
7799 printf("::: [%d errors found]\n", num_em_gfx_errors);
7805 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7806 boolean any_player_moving,
7807 boolean any_player_snapping,
7808 boolean any_player_dropping)
7810 static boolean player_was_waiting = TRUE;
7812 if (frame == 0 && !any_player_dropping)
7814 if (!player_was_waiting)
7816 if (!SaveEngineSnapshotToList())
7819 player_was_waiting = TRUE;
7822 else if (any_player_moving || any_player_snapping || any_player_dropping)
7824 player_was_waiting = FALSE;
7828 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7829 boolean murphy_is_dropping)
7831 static boolean player_was_waiting = TRUE;
7833 if (murphy_is_waiting)
7835 if (!player_was_waiting)
7837 if (!SaveEngineSnapshotToList())
7840 player_was_waiting = TRUE;
7845 player_was_waiting = FALSE;
7849 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7850 boolean any_player_moving,
7851 boolean any_player_snapping,
7852 boolean any_player_dropping)
7854 if (tape.single_step && tape.recording && !tape.pausing)
7855 if (frame == 0 && !any_player_dropping)
7856 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7858 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7859 any_player_snapping, any_player_dropping);
7862 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7863 boolean murphy_is_dropping)
7865 if (tape.single_step && tape.recording && !tape.pausing)
7866 if (murphy_is_waiting)
7867 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7869 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7872 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7873 int graphic, int sync_frame, int x, int y)
7875 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7877 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7880 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7882 return (IS_NEXT_FRAME(sync_frame, graphic));
7885 int getGraphicInfo_Delay(int graphic)
7887 return graphic_info[graphic].anim_delay;
7890 void PlayMenuSoundExt(int sound)
7892 if (sound == SND_UNDEFINED)
7895 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7896 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7899 if (IS_LOOP_SOUND(sound))
7900 PlaySoundLoop(sound);
7905 void PlayMenuSound()
7907 PlayMenuSoundExt(menu.sound[game_status]);
7910 void PlayMenuSoundStereo(int sound, int stereo_position)
7912 if (sound == SND_UNDEFINED)
7915 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7916 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7919 if (IS_LOOP_SOUND(sound))
7920 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7922 PlaySoundStereo(sound, stereo_position);
7925 void PlayMenuSoundIfLoopExt(int sound)
7927 if (sound == SND_UNDEFINED)
7930 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7931 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7934 if (IS_LOOP_SOUND(sound))
7935 PlaySoundLoop(sound);
7938 void PlayMenuSoundIfLoop()
7940 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7943 void PlayMenuMusicExt(int music)
7945 if (music == MUS_UNDEFINED)
7948 if (!setup.sound_music)
7954 void PlayMenuMusic()
7956 PlayMenuMusicExt(menu.music[game_status]);
7959 void PlaySoundActivating()
7962 PlaySound(SND_MENU_ITEM_ACTIVATING);
7966 void PlaySoundSelecting()
7969 PlaySound(SND_MENU_ITEM_SELECTING);
7973 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7975 boolean change_fullscreen = (setup.fullscreen !=
7976 video.fullscreen_enabled);
7977 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7978 !strEqual(setup.fullscreen_mode,
7979 video.fullscreen_mode_current));
7980 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7981 setup.window_scaling_percent !=
7982 video.window_scaling_percent);
7984 if (change_window_scaling_percent && video.fullscreen_enabled)
7987 if (!change_window_scaling_percent && !video.fullscreen_available)
7990 #if defined(TARGET_SDL2)
7991 if (change_window_scaling_percent)
7993 SDLSetWindowScaling(setup.window_scaling_percent);
7997 else if (change_fullscreen)
7999 SDLSetWindowFullscreen(setup.fullscreen);
8001 /* set setup value according to successfully changed fullscreen mode */
8002 setup.fullscreen = video.fullscreen_enabled;
8008 if (change_fullscreen ||
8009 change_fullscreen_mode ||
8010 change_window_scaling_percent)
8012 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8014 /* save backbuffer content which gets lost when toggling fullscreen mode */
8015 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8017 if (change_fullscreen_mode)
8019 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8020 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8023 if (change_window_scaling_percent)
8025 /* keep window mode, but change window scaling */
8026 video.fullscreen_enabled = TRUE; /* force new window scaling */
8029 /* toggle fullscreen */
8030 ChangeVideoModeIfNeeded(setup.fullscreen);
8032 /* set setup value according to successfully changed fullscreen mode */
8033 setup.fullscreen = video.fullscreen_enabled;
8035 /* restore backbuffer content from temporary backbuffer backup bitmap */
8036 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8038 FreeBitmap(tmp_backbuffer);
8040 /* update visible window/screen */
8041 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8045 void ChangeViewportPropertiesIfNeeded()
8047 int gfx_game_mode = game_status;
8048 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8050 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8051 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8052 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8053 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8054 int border_size = vp_playfield->border_size;
8055 int new_sx = vp_playfield->x + border_size;
8056 int new_sy = vp_playfield->y + border_size;
8057 int new_sxsize = vp_playfield->width - 2 * border_size;
8058 int new_sysize = vp_playfield->height - 2 * border_size;
8059 int new_real_sx = vp_playfield->x;
8060 int new_real_sy = vp_playfield->y;
8061 int new_full_sxsize = vp_playfield->width;
8062 int new_full_sysize = vp_playfield->height;
8063 int new_dx = vp_door_1->x;
8064 int new_dy = vp_door_1->y;
8065 int new_dxsize = vp_door_1->width;
8066 int new_dysize = vp_door_1->height;
8067 int new_vx = vp_door_2->x;
8068 int new_vy = vp_door_2->y;
8069 int new_vxsize = vp_door_2->width;
8070 int new_vysize = vp_door_2->height;
8071 int new_ex = vp_door_3->x;
8072 int new_ey = vp_door_3->y;
8073 int new_exsize = vp_door_3->width;
8074 int new_eysize = vp_door_3->height;
8075 int new_tilesize_var =
8076 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8078 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8079 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8080 int new_scr_fieldx = new_sxsize / tilesize;
8081 int new_scr_fieldy = new_sysize / tilesize;
8082 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8083 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8084 boolean init_gfx_buffers = FALSE;
8085 boolean init_video_buffer = FALSE;
8086 boolean init_gadgets_and_toons = FALSE;
8087 boolean init_em_graphics = FALSE;
8089 if (viewport.window.width != WIN_XSIZE ||
8090 viewport.window.height != WIN_YSIZE)
8092 WIN_XSIZE = viewport.window.width;
8093 WIN_YSIZE = viewport.window.height;
8095 init_video_buffer = TRUE;
8096 init_gfx_buffers = TRUE;
8098 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8101 if (new_scr_fieldx != SCR_FIELDX ||
8102 new_scr_fieldy != SCR_FIELDY)
8104 /* this always toggles between MAIN and GAME when using small tile size */
8106 SCR_FIELDX = new_scr_fieldx;
8107 SCR_FIELDY = new_scr_fieldy;
8109 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8120 new_sxsize != SXSIZE ||
8121 new_sysize != SYSIZE ||
8122 new_dxsize != DXSIZE ||
8123 new_dysize != DYSIZE ||
8124 new_vxsize != VXSIZE ||
8125 new_vysize != VYSIZE ||
8126 new_exsize != EXSIZE ||
8127 new_eysize != EYSIZE ||
8128 new_real_sx != REAL_SX ||
8129 new_real_sy != REAL_SY ||
8130 new_full_sxsize != FULL_SXSIZE ||
8131 new_full_sysize != FULL_SYSIZE ||
8132 new_tilesize_var != TILESIZE_VAR
8135 if (new_tilesize_var != TILESIZE_VAR)
8137 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8139 // changing tile size invalidates scroll values of engine snapshots
8140 FreeEngineSnapshotSingle();
8142 // changing tile size requires update of graphic mapping for EM engine
8143 init_em_graphics = TRUE;
8154 SXSIZE = new_sxsize;
8155 SYSIZE = new_sysize;
8156 DXSIZE = new_dxsize;
8157 DYSIZE = new_dysize;
8158 VXSIZE = new_vxsize;
8159 VYSIZE = new_vysize;
8160 EXSIZE = new_exsize;
8161 EYSIZE = new_eysize;
8162 REAL_SX = new_real_sx;
8163 REAL_SY = new_real_sy;
8164 FULL_SXSIZE = new_full_sxsize;
8165 FULL_SYSIZE = new_full_sysize;
8166 TILESIZE_VAR = new_tilesize_var;
8168 init_gfx_buffers = TRUE;
8169 init_gadgets_and_toons = TRUE;
8171 // printf("::: viewports: init_gfx_buffers\n");
8172 // printf("::: viewports: init_gadgets_and_toons\n");
8175 if (init_gfx_buffers)
8177 // printf("::: init_gfx_buffers\n");
8179 SCR_FIELDX = new_scr_fieldx_buffers;
8180 SCR_FIELDY = new_scr_fieldy_buffers;
8184 SCR_FIELDX = new_scr_fieldx;
8185 SCR_FIELDY = new_scr_fieldy;
8187 SetDrawDeactivationMask(REDRAW_NONE);
8188 SetDrawBackgroundMask(REDRAW_FIELD);
8191 if (init_video_buffer)
8193 // printf("::: init_video_buffer\n");
8195 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8198 if (init_gadgets_and_toons)
8200 // printf("::: init_gadgets_and_toons\n");
8206 if (init_em_graphics)
8208 InitGraphicInfo_EM();