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_BUFFERED && setup.soft_scrolling)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
247 drawto_field = fieldbuffer;
249 else /* DRAW_BACKBUFFER */
255 BX2 = SCR_FIELDX - 1;
256 BY2 = SCR_FIELDY - 1;
260 drawto_field = backbuffer;
264 static void RedrawPlayfield_RND()
266 if (game.envelope_active)
269 DrawLevel(REDRAW_ALL);
273 void RedrawPlayfield()
275 if (game_status != GAME_MODE_PLAYING)
278 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
279 RedrawPlayfield_EM(TRUE);
280 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
281 RedrawPlayfield_SP(TRUE);
282 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
283 RedrawPlayfield_RND();
285 BlitScreenToBitmap(backbuffer);
287 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
291 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
293 Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
295 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
298 void DrawMaskedBorder_FIELD()
300 if (global.border_status >= GAME_MODE_TITLE &&
301 global.border_status <= GAME_MODE_PLAYING &&
302 border.draw_masked[global.border_status])
303 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
306 void DrawMaskedBorder_DOOR_1()
308 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
309 (global.border_status != GAME_MODE_EDITOR ||
310 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
311 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
314 void DrawMaskedBorder_DOOR_2()
316 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
317 global.border_status != GAME_MODE_EDITOR)
318 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
321 void DrawMaskedBorder_DOOR_3()
323 /* currently not available */
326 void DrawMaskedBorder_ALL()
328 DrawMaskedBorder_FIELD();
329 DrawMaskedBorder_DOOR_1();
330 DrawMaskedBorder_DOOR_2();
331 DrawMaskedBorder_DOOR_3();
334 void DrawMaskedBorder(int redraw_mask)
336 /* never draw masked screen borders on borderless screens */
337 if (effectiveGameStatus() == GAME_MODE_LOADING ||
338 effectiveGameStatus() == GAME_MODE_TITLE)
341 /* never draw masked screen borders when displaying request outside door */
342 if (effectiveGameStatus() == GAME_MODE_PSEUDO_DOOR &&
343 global.use_envelope_request)
346 if (redraw_mask & REDRAW_ALL)
347 DrawMaskedBorder_ALL();
350 if (redraw_mask & REDRAW_FIELD)
351 DrawMaskedBorder_FIELD();
352 if (redraw_mask & REDRAW_DOOR_1)
353 DrawMaskedBorder_DOOR_1();
354 if (redraw_mask & REDRAW_DOOR_2)
355 DrawMaskedBorder_DOOR_2();
356 if (redraw_mask & REDRAW_DOOR_3)
357 DrawMaskedBorder_DOOR_3();
361 static void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
363 DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
364 int fx = FX, fy = FY;
365 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
366 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
368 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
369 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
370 int dx_var = dx * TILESIZE_VAR / TILESIZE;
371 int dy_var = dy * TILESIZE_VAR / TILESIZE;
374 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
375 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
377 if (EVEN(SCR_FIELDX))
379 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
380 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
382 fx += (dx_var > 0 ? TILEX_VAR : 0);
389 if (EVEN(SCR_FIELDY))
391 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
392 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
394 fy += (dy_var > 0 ? TILEY_VAR : 0);
401 if (full_lev_fieldx <= SCR_FIELDX)
403 if (EVEN(SCR_FIELDX))
404 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
406 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
409 if (full_lev_fieldy <= SCR_FIELDY)
411 if (EVEN(SCR_FIELDY))
412 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
414 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
417 if (border.draw_masked[GAME_MODE_PLAYING])
419 if (buffer != backbuffer)
421 /* copy playfield buffer to backbuffer to add masked border */
422 BlitBitmap(buffer, backbuffer, fx, fy, SXSIZE, SYSIZE, SX, SY);
423 DrawMaskedBorder(REDRAW_FIELD);
426 BlitBitmap(backbuffer, target_bitmap,
427 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
432 BlitBitmap(buffer, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
436 void BlitScreenToBitmap(Bitmap *target_bitmap)
438 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
439 BlitScreenToBitmap_EM(target_bitmap);
440 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
441 BlitScreenToBitmap_SP(target_bitmap);
442 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
443 BlitScreenToBitmap_RND(target_bitmap);
449 DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
451 if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
452 redraw_mask |= REDRAW_FIELD;
455 // never redraw single tiles, always redraw the whole field
456 // (redrawing single tiles up to a certain threshold was faster on old,
457 // now legacy graphics, but slows things down on modern graphics now)
458 // UPDATE: this is now globally defined by value of REDRAWTILES_THRESHOLD
459 if (redraw_mask & REDRAW_TILES)
460 redraw_mask |= REDRAW_FIELD;
464 /* !!! TEST ONLY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
465 /* (force full redraw) */
466 if (game_status == GAME_MODE_PLAYING)
467 redraw_mask |= REDRAW_FIELD;
470 if (redraw_mask & REDRAW_FIELD)
471 redraw_mask &= ~REDRAW_TILES;
473 if (redraw_mask == REDRAW_NONE)
478 if (redraw_mask & REDRAW_ALL)
479 printf("[REDRAW_ALL]");
480 if (redraw_mask & REDRAW_FIELD)
481 printf("[REDRAW_FIELD]");
482 if (redraw_mask & REDRAW_TILES)
483 printf("[REDRAW_TILES]");
484 if (redraw_mask & REDRAW_DOOR_1)
485 printf("[REDRAW_DOOR_1]");
486 if (redraw_mask & REDRAW_DOOR_2)
487 printf("[REDRAW_DOOR_2]");
488 if (redraw_mask & REDRAW_FROM_BACKBUFFER)
489 printf("[REDRAW_FROM_BACKBUFFER]");
490 printf(" [%d]\n", FrameCounter);
493 if (redraw_mask & REDRAW_TILES &&
494 game_status == GAME_MODE_PLAYING &&
495 border.draw_masked[GAME_MODE_PLAYING])
496 redraw_mask |= REDRAW_FIELD;
498 if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
500 static boolean last_frame_skipped = FALSE;
501 boolean skip_even_when_not_scrolling = TRUE;
502 boolean just_scrolling = (ScreenMovDir != 0);
503 boolean verbose = FALSE;
505 if (global.fps_slowdown_factor > 1 &&
506 (FrameCounter % global.fps_slowdown_factor) &&
507 (just_scrolling || skip_even_when_not_scrolling))
509 redraw_mask &= ~REDRAW_MAIN;
511 last_frame_skipped = TRUE;
514 printf("FRAME SKIPPED\n");
518 if (last_frame_skipped)
519 redraw_mask |= REDRAW_FIELD;
521 last_frame_skipped = FALSE;
524 printf("frame not skipped\n");
528 /* synchronize X11 graphics at this point; if we would synchronize the
529 display immediately after the buffer switching (after the XFlush),
530 this could mean that we have to wait for the graphics to complete,
531 although we could go on doing calculations for the next frame */
535 /* never draw masked border to backbuffer when using playfield buffer */
536 if (game_status != GAME_MODE_PLAYING ||
537 redraw_mask & REDRAW_FROM_BACKBUFFER ||
538 buffer == backbuffer)
539 DrawMaskedBorder(redraw_mask);
541 DrawMaskedBorder(redraw_mask & REDRAW_DOORS);
543 if (redraw_mask & REDRAW_ALL)
545 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
547 redraw_mask = REDRAW_NONE;
550 if (redraw_mask & REDRAW_FIELD)
552 if (game_status != GAME_MODE_PLAYING ||
553 redraw_mask & REDRAW_FROM_BACKBUFFER)
555 BlitBitmap(backbuffer, window,
556 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
560 BlitScreenToBitmap_RND(window);
563 redraw_mask &= ~REDRAW_MAIN;
566 if (redraw_mask & REDRAW_DOORS)
568 if (redraw_mask & REDRAW_DOOR_1)
569 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
571 if (redraw_mask & REDRAW_DOOR_2)
572 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
574 if (redraw_mask & REDRAW_DOOR_3)
575 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
577 redraw_mask &= ~REDRAW_DOORS;
580 if (redraw_mask & REDRAW_MICROLEVEL)
582 BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
583 SX, SY + 10 * TILEY);
585 redraw_mask &= ~REDRAW_MICROLEVEL;
588 if (redraw_mask & REDRAW_TILES)
594 int dx_var = dx * TILESIZE_VAR / TILESIZE;
595 int dy_var = dy * TILESIZE_VAR / TILESIZE;
597 int fx = FX, fy = FY;
599 int scr_fieldx = SCR_FIELDX + (EVEN(SCR_FIELDX) ? 2 : 0);
600 int scr_fieldy = SCR_FIELDY + (EVEN(SCR_FIELDY) ? 2 : 0);
602 InitGfxClipRegion(TRUE, SX, SY, SXSIZE, SYSIZE);
604 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
605 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
607 if (EVEN(SCR_FIELDX))
609 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
611 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
620 fx += (dx_var > 0 ? TILEX_VAR : 0);
624 if (EVEN(SCR_FIELDY))
626 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
628 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
637 fy += (dy_var > 0 ? TILEY_VAR : 0);
641 for (x = 0; x < scr_fieldx; x++)
642 for (y = 0 ; y < scr_fieldy; y++)
643 if (redraw[redraw_x1 + x][redraw_y1 + y])
644 BlitBitmap(buffer, window,
645 FX + x * TILEX_VAR, FY + y * TILEY_VAR,
646 TILEX_VAR, TILEY_VAR,
647 sx + x * TILEX_VAR, sy + y * TILEY_VAR);
649 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
652 if (redraw_mask & REDRAW_FPS) /* display frames per second */
657 sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
658 if (!global.fps_slowdown)
661 sprintf(text, "%04.1f fps%s", global.frames_per_second, info1);
663 DrawTextExt(window, SX + SXSIZE + SX, 0, text, FONT_TEXT_2, BLIT_OPAQUE);
666 for (x = 0; x < MAX_BUF_XSIZE; x++)
667 for (y = 0; y < MAX_BUF_YSIZE; y++)
670 redraw_mask = REDRAW_NONE;
673 static void FadeCrossSaveBackbuffer()
675 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
678 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
680 static int fade_type_skip = FADE_TYPE_NONE;
681 void (*draw_border_function)(void) = NULL;
682 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
683 int x, y, width, height;
684 int fade_delay, post_delay;
686 if (fade_type == FADE_TYPE_FADE_OUT)
688 if (fade_type_skip != FADE_TYPE_NONE)
690 /* skip all fade operations until specified fade operation */
691 if (fade_type & fade_type_skip)
692 fade_type_skip = FADE_TYPE_NONE;
697 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
699 FadeCrossSaveBackbuffer();
705 redraw_mask |= fade_mask;
707 if (fade_type == FADE_TYPE_SKIP)
709 fade_type_skip = fade_mode;
714 fade_delay = fading.fade_delay;
715 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
717 if (fade_type_skip != FADE_TYPE_NONE)
719 /* skip all fade operations until specified fade operation */
720 if (fade_type & fade_type_skip)
721 fade_type_skip = FADE_TYPE_NONE;
726 if (global.autoplay_leveldir)
731 if (fade_mask == REDRAW_FIELD)
736 height = FULL_SYSIZE;
738 if (border.draw_masked_when_fading)
739 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
741 DrawMaskedBorder_FIELD(); /* draw once */
743 else /* REDRAW_ALL */
751 if (!setup.fade_screens ||
753 fading.fade_mode == FADE_MODE_NONE)
755 if (fade_mode == FADE_MODE_FADE_OUT)
758 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
760 redraw_mask &= ~fade_mask;
765 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
766 draw_border_function);
768 redraw_mask &= ~fade_mask;
771 void FadeIn(int fade_mask)
773 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
774 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
776 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
779 void FadeOut(int fade_mask)
781 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
782 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
784 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
786 global.border_status = game_status;
789 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
791 static struct TitleFadingInfo fading_leave_stored;
794 fading_leave_stored = fading_leave;
796 fading = fading_leave_stored;
799 void FadeSetEnterMenu()
801 fading = menu.enter_menu;
803 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
806 void FadeSetLeaveMenu()
808 fading = menu.leave_menu;
810 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
813 void FadeSetEnterScreen()
815 fading = menu.enter_screen[game_status];
817 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
820 void FadeSetNextScreen()
822 fading = menu.next_screen;
824 // (do not overwrite fade mode set by FadeSetEnterScreen)
825 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
828 void FadeSetLeaveScreen()
830 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
833 void FadeSetFromType(int type)
835 if (type & TYPE_ENTER_SCREEN)
836 FadeSetEnterScreen();
837 else if (type & TYPE_ENTER)
839 else if (type & TYPE_LEAVE)
843 void FadeSetDisabled()
845 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
847 fading = fading_none;
850 void FadeSkipNextFadeIn()
852 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
855 void FadeSkipNextFadeOut()
857 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
860 void SetWindowBackgroundImageIfDefined(int graphic)
862 if (graphic_info[graphic].bitmap)
863 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
866 void SetMainBackgroundImageIfDefined(int graphic)
868 if (graphic_info[graphic].bitmap)
869 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
872 void SetDoorBackgroundImageIfDefined(int graphic)
874 if (graphic_info[graphic].bitmap)
875 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
878 void SetWindowBackgroundImage(int graphic)
880 SetWindowBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
881 graphic_info[graphic].bitmap ?
882 graphic_info[graphic].bitmap :
883 graphic_info[IMG_BACKGROUND].bitmap);
886 void SetMainBackgroundImage(int graphic)
888 SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
889 graphic_info[graphic].bitmap ?
890 graphic_info[graphic].bitmap :
891 graphic_info[IMG_BACKGROUND].bitmap);
894 void SetDoorBackgroundImage(int graphic)
896 SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
897 graphic_info[graphic].bitmap ?
898 graphic_info[graphic].bitmap :
899 graphic_info[IMG_BACKGROUND].bitmap);
902 void SetPanelBackground()
904 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
906 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
907 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
909 SetDoorBackgroundBitmap(bitmap_db_panel);
912 void DrawBackground(int x, int y, int width, int height)
914 /* "drawto" might still point to playfield buffer here (hall of fame) */
915 ClearRectangleOnBackground(backbuffer, x, y, width, height);
917 if (IN_GFX_FIELD_FULL(x, y))
918 redraw_mask |= REDRAW_FIELD;
919 else if (IN_GFX_DOOR_1(x, y))
920 redraw_mask |= REDRAW_DOOR_1;
921 else if (IN_GFX_DOOR_2(x, y))
922 redraw_mask |= REDRAW_DOOR_2;
923 else if (IN_GFX_DOOR_3(x, y))
924 redraw_mask |= REDRAW_DOOR_3;
927 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
929 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
931 if (font->bitmap == NULL)
934 DrawBackground(x, y, width, height);
937 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
939 struct GraphicInfo *g = &graphic_info[graphic];
941 if (g->bitmap == NULL)
944 DrawBackground(x, y, width, height);
949 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
950 /* (when entering hall of fame after playing) */
951 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
953 /* !!! maybe this should be done before clearing the background !!! */
954 if (setup.soft_scrolling && game_status == GAME_MODE_PLAYING)
956 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
957 SetDrawtoField(DRAW_BUFFERED);
960 SetDrawtoField(DRAW_BACKBUFFER);
963 void MarkTileDirty(int x, int y)
965 int xx = redraw_x1 + x;
966 int yy = redraw_y1 + y;
971 redraw[xx][yy] = TRUE;
972 redraw_mask |= REDRAW_TILES;
975 void SetBorderElement()
979 BorderElement = EL_EMPTY;
981 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
983 for (x = 0; x < lev_fieldx; x++)
985 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
986 BorderElement = EL_STEELWALL;
988 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
994 void FloodFillLevel(int from_x, int from_y, int fill_element,
995 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
996 int max_fieldx, int max_fieldy)
1000 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1001 static int safety = 0;
1003 /* check if starting field still has the desired content */
1004 if (field[from_x][from_y] == fill_element)
1009 if (safety > max_fieldx * max_fieldy)
1010 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1012 old_element = field[from_x][from_y];
1013 field[from_x][from_y] = fill_element;
1015 for (i = 0; i < 4; i++)
1017 x = from_x + check[i][0];
1018 y = from_y + check[i][1];
1020 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1021 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1027 void SetRandomAnimationValue(int x, int y)
1029 gfx.anim_random_frame = GfxRandom[x][y];
1032 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
1034 /* animation synchronized with global frame counter, not move position */
1035 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1036 sync_frame = FrameCounter;
1038 return getAnimationFrame(graphic_info[graphic].anim_frames,
1039 graphic_info[graphic].anim_delay,
1040 graphic_info[graphic].anim_mode,
1041 graphic_info[graphic].anim_start_frame,
1045 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1046 Bitmap **bitmap, int *x, int *y,
1047 boolean get_backside)
1049 struct GraphicInfo *g = &graphic_info[graphic];
1050 Bitmap *src_bitmap = g->bitmap;
1051 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1052 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1053 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1055 // if no in-game graphics defined, always use standard graphic size
1056 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1057 tilesize = TILESIZE;
1059 if (tilesize == gfx.standard_tile_size)
1060 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1061 else if (tilesize == game.tile_size)
1062 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1064 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1066 if (g->offset_y == 0) /* frames are ordered horizontally */
1068 int max_width = g->anim_frames_per_line * g->width;
1069 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1071 src_x = pos % max_width;
1072 src_y = src_y % g->height + pos / max_width * g->height;
1074 else if (g->offset_x == 0) /* frames are ordered vertically */
1076 int max_height = g->anim_frames_per_line * g->height;
1077 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1079 src_x = src_x % g->width + pos / max_height * g->width;
1080 src_y = pos % max_height;
1082 else /* frames are ordered diagonally */
1084 src_x = src_x + frame * g->offset_x;
1085 src_y = src_y + frame * g->offset_y;
1088 *bitmap = src_bitmap;
1089 *x = src_x * tilesize / TILESIZE;
1090 *y = src_y * tilesize / TILESIZE;
1093 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1094 int *x, int *y, boolean get_backside)
1096 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1100 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1101 Bitmap **bitmap, int *x, int *y)
1103 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1106 void getFixedGraphicSource(int graphic, int frame,
1107 Bitmap **bitmap, int *x, int *y)
1109 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1112 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1114 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1117 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1118 int *x, int *y, boolean get_backside)
1120 struct GraphicInfo *g = &graphic_info[graphic];
1121 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1122 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1124 if (TILESIZE_VAR != TILESIZE)
1125 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1128 *bitmap = g->bitmap;
1130 if (g->offset_y == 0) /* frames are ordered horizontally */
1132 int max_width = g->anim_frames_per_line * g->width;
1133 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1135 *x = pos % max_width;
1136 *y = src_y % g->height + pos / max_width * g->height;
1138 else if (g->offset_x == 0) /* frames are ordered vertically */
1140 int max_height = g->anim_frames_per_line * g->height;
1141 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1143 *x = src_x % g->width + pos / max_height * g->width;
1144 *y = pos % max_height;
1146 else /* frames are ordered diagonally */
1148 *x = src_x + frame * g->offset_x;
1149 *y = src_y + frame * g->offset_y;
1153 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1155 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1158 void DrawGraphic(int x, int y, int graphic, int frame)
1161 if (!IN_SCR_FIELD(x, y))
1163 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1164 printf("DrawGraphic(): This should never happen!\n");
1169 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1172 MarkTileDirty(x, y);
1175 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1178 if (!IN_SCR_FIELD(x, y))
1180 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1181 printf("DrawGraphic(): This should never happen!\n");
1186 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1188 MarkTileDirty(x, y);
1191 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1197 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1199 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1202 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1208 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1209 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1212 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1215 if (!IN_SCR_FIELD(x, y))
1217 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1218 printf("DrawGraphicThruMask(): This should never happen!\n");
1223 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1226 MarkTileDirty(x, y);
1229 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1232 if (!IN_SCR_FIELD(x, y))
1234 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1235 printf("DrawGraphicThruMask(): This should never happen!\n");
1240 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1242 MarkTileDirty(x, y);
1245 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1251 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1253 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1257 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1258 int graphic, int frame)
1260 struct GraphicInfo *g = &graphic_info[graphic];
1264 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1266 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1270 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1272 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1274 MarkTileDirty(x / tilesize, y / tilesize);
1277 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1283 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1284 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1287 void DrawMiniGraphic(int x, int y, int graphic)
1289 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1290 MarkTileDirty(x / 2, y / 2);
1293 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1298 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1299 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1302 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1303 int graphic, int frame,
1304 int cut_mode, int mask_mode)
1309 int width = TILEX, height = TILEY;
1312 if (dx || dy) /* shifted graphic */
1314 if (x < BX1) /* object enters playfield from the left */
1321 else if (x > BX2) /* object enters playfield from the right */
1327 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1333 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1335 else if (dx) /* general horizontal movement */
1336 MarkTileDirty(x + SIGN(dx), y);
1338 if (y < BY1) /* object enters playfield from the top */
1340 if (cut_mode==CUT_BELOW) /* object completely above top border */
1348 else if (y > BY2) /* object enters playfield from the bottom */
1354 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1360 else if (dy > 0 && cut_mode == CUT_ABOVE)
1362 if (y == BY2) /* object completely above bottom border */
1368 MarkTileDirty(x, y + 1);
1369 } /* object leaves playfield to the bottom */
1370 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1372 else if (dy) /* general vertical movement */
1373 MarkTileDirty(x, y + SIGN(dy));
1377 if (!IN_SCR_FIELD(x, y))
1379 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1380 printf("DrawGraphicShifted(): This should never happen!\n");
1385 width = width * TILESIZE_VAR / TILESIZE;
1386 height = height * TILESIZE_VAR / TILESIZE;
1387 cx = cx * TILESIZE_VAR / TILESIZE;
1388 cy = cy * TILESIZE_VAR / TILESIZE;
1389 dx = dx * TILESIZE_VAR / TILESIZE;
1390 dy = dy * TILESIZE_VAR / TILESIZE;
1392 if (width > 0 && height > 0)
1394 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1399 dst_x = FX + x * TILEX_VAR + dx;
1400 dst_y = FY + y * TILEY_VAR + dy;
1402 if (mask_mode == USE_MASKING)
1403 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1406 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1409 MarkTileDirty(x, y);
1413 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1414 int graphic, int frame,
1415 int cut_mode, int mask_mode)
1420 int width = TILEX_VAR, height = TILEY_VAR;
1423 int x2 = x + SIGN(dx);
1424 int y2 = y + SIGN(dy);
1426 /* movement with two-tile animations must be sync'ed with movement position,
1427 not with current GfxFrame (which can be higher when using slow movement) */
1428 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1429 int anim_frames = graphic_info[graphic].anim_frames;
1431 /* (we also need anim_delay here for movement animations with less frames) */
1432 int anim_delay = graphic_info[graphic].anim_delay;
1433 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1435 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1436 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1438 /* re-calculate animation frame for two-tile movement animation */
1439 frame = getGraphicAnimationFrame(graphic, sync_frame);
1441 /* check if movement start graphic inside screen area and should be drawn */
1442 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1444 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1446 dst_x = FX + x1 * TILEX_VAR;
1447 dst_y = FY + y1 * TILEY_VAR;
1449 if (mask_mode == USE_MASKING)
1450 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1453 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1456 MarkTileDirty(x1, y1);
1459 /* check if movement end graphic inside screen area and should be drawn */
1460 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1462 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1464 dst_x = FX + x2 * TILEX_VAR;
1465 dst_y = FY + y2 * TILEY_VAR;
1467 if (mask_mode == USE_MASKING)
1468 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1471 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1474 MarkTileDirty(x2, y2);
1478 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1479 int graphic, int frame,
1480 int cut_mode, int mask_mode)
1484 DrawGraphic(x, y, graphic, frame);
1489 if (graphic_info[graphic].double_movement) /* EM style movement images */
1490 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1492 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1495 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1496 int frame, int cut_mode)
1498 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1501 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1502 int cut_mode, int mask_mode)
1504 int lx = LEVELX(x), ly = LEVELY(y);
1508 if (IN_LEV_FIELD(lx, ly))
1510 SetRandomAnimationValue(lx, ly);
1512 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1513 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1515 /* do not use double (EM style) movement graphic when not moving */
1516 if (graphic_info[graphic].double_movement && !dx && !dy)
1518 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1519 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1522 else /* border element */
1524 graphic = el2img(element);
1525 frame = getGraphicAnimationFrame(graphic, -1);
1528 if (element == EL_EXPANDABLE_WALL)
1530 boolean left_stopped = FALSE, right_stopped = FALSE;
1532 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1533 left_stopped = TRUE;
1534 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1535 right_stopped = TRUE;
1537 if (left_stopped && right_stopped)
1539 else if (left_stopped)
1541 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1542 frame = graphic_info[graphic].anim_frames - 1;
1544 else if (right_stopped)
1546 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1547 frame = graphic_info[graphic].anim_frames - 1;
1552 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1553 else if (mask_mode == USE_MASKING)
1554 DrawGraphicThruMask(x, y, graphic, frame);
1556 DrawGraphic(x, y, graphic, frame);
1559 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1560 int cut_mode, int mask_mode)
1562 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1563 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1564 cut_mode, mask_mode);
1567 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1570 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1573 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1576 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1579 void DrawLevelElementThruMask(int x, int y, int element)
1581 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1584 void DrawLevelFieldThruMask(int x, int y)
1586 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1589 /* !!! implementation of quicksand is totally broken !!! */
1590 #define IS_CRUMBLED_TILE(x, y, e) \
1591 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1592 !IS_MOVING(x, y) || \
1593 (e) == EL_QUICKSAND_EMPTYING || \
1594 (e) == EL_QUICKSAND_FAST_EMPTYING))
1596 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1601 int width, height, cx, cy;
1602 int sx = SCREENX(x), sy = SCREENY(y);
1603 int crumbled_border_size = graphic_info[graphic].border_size;
1606 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1608 for (i = 1; i < 4; i++)
1610 int dxx = (i & 1 ? dx : 0);
1611 int dyy = (i & 2 ? dy : 0);
1614 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1617 /* check if neighbour field is of same crumble type */
1618 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1619 graphic_info[graphic].class ==
1620 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1622 /* return if check prevents inner corner */
1623 if (same == (dxx == dx && dyy == dy))
1627 /* if we reach this point, we have an inner corner */
1629 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1631 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1632 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1633 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1634 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1636 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1637 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1640 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1645 int width, height, bx, by, cx, cy;
1646 int sx = SCREENX(x), sy = SCREENY(y);
1647 int crumbled_border_size = graphic_info[graphic].border_size;
1648 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1649 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1652 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1654 /* draw simple, sloppy, non-corner-accurate crumbled border */
1656 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1657 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1658 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1659 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1661 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1662 FX + sx * TILEX_VAR + cx,
1663 FY + sy * TILEY_VAR + cy);
1665 /* (remaining middle border part must be at least as big as corner part) */
1666 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1667 crumbled_border_size >= TILESIZE / 3)
1670 /* correct corners of crumbled border, if needed */
1672 for (i = -1; i <= 1; i += 2)
1674 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1675 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1676 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1679 /* check if neighbour field is of same crumble type */
1680 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1681 graphic_info[graphic].class ==
1682 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1684 /* no crumbled corner, but continued crumbled border */
1686 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1687 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1688 int b1 = (i == 1 ? crumbled_border_size_var :
1689 TILESIZE_VAR - 2 * crumbled_border_size_var);
1691 width = crumbled_border_size_var;
1692 height = crumbled_border_size_var;
1694 if (dir == 1 || dir == 2)
1709 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1711 FX + sx * TILEX_VAR + cx,
1712 FY + sy * TILEY_VAR + cy);
1717 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1719 int sx = SCREENX(x), sy = SCREENY(y);
1722 static int xy[4][2] =
1730 if (!IN_LEV_FIELD(x, y))
1733 element = TILE_GFX_ELEMENT(x, y);
1735 /* crumble field itself */
1736 if (IS_CRUMBLED_TILE(x, y, element))
1738 if (!IN_SCR_FIELD(sx, sy))
1741 for (i = 0; i < 4; i++)
1743 int xx = x + xy[i][0];
1744 int yy = y + xy[i][1];
1746 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1749 /* check if neighbour field is of same crumble type */
1750 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1751 graphic_info[graphic].class ==
1752 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1755 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1758 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1759 graphic_info[graphic].anim_frames == 2)
1761 for (i = 0; i < 4; i++)
1763 int dx = (i & 1 ? +1 : -1);
1764 int dy = (i & 2 ? +1 : -1);
1766 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1770 MarkTileDirty(sx, sy);
1772 else /* center field not crumbled -- crumble neighbour fields */
1774 for (i = 0; i < 4; i++)
1776 int xx = x + xy[i][0];
1777 int yy = y + xy[i][1];
1778 int sxx = sx + xy[i][0];
1779 int syy = sy + xy[i][1];
1781 if (!IN_LEV_FIELD(xx, yy) ||
1782 !IN_SCR_FIELD(sxx, syy))
1785 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1788 element = TILE_GFX_ELEMENT(xx, yy);
1790 if (!IS_CRUMBLED_TILE(xx, yy, element))
1793 graphic = el_act2crm(element, ACTION_DEFAULT);
1795 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1797 MarkTileDirty(sxx, syy);
1802 void DrawLevelFieldCrumbled(int x, int y)
1806 if (!IN_LEV_FIELD(x, y))
1809 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1810 GfxElement[x][y] != EL_UNDEFINED &&
1811 GFX_CRUMBLED(GfxElement[x][y]))
1813 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1818 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1820 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1823 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1826 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1827 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1828 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1829 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1830 int sx = SCREENX(x), sy = SCREENY(y);
1832 DrawGraphic(sx, sy, graphic1, frame1);
1833 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1836 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1838 int sx = SCREENX(x), sy = SCREENY(y);
1839 static int xy[4][2] =
1848 for (i = 0; i < 4; i++)
1850 int xx = x + xy[i][0];
1851 int yy = y + xy[i][1];
1852 int sxx = sx + xy[i][0];
1853 int syy = sy + xy[i][1];
1855 if (!IN_LEV_FIELD(xx, yy) ||
1856 !IN_SCR_FIELD(sxx, syy) ||
1857 !GFX_CRUMBLED(Feld[xx][yy]) ||
1861 DrawLevelField(xx, yy);
1865 static int getBorderElement(int x, int y)
1869 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1870 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1871 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1872 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1873 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1874 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1875 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1877 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1878 int steel_position = (x == -1 && y == -1 ? 0 :
1879 x == lev_fieldx && y == -1 ? 1 :
1880 x == -1 && y == lev_fieldy ? 2 :
1881 x == lev_fieldx && y == lev_fieldy ? 3 :
1882 x == -1 || x == lev_fieldx ? 4 :
1883 y == -1 || y == lev_fieldy ? 5 : 6);
1885 return border[steel_position][steel_type];
1888 void DrawScreenElement(int x, int y, int element)
1890 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1891 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1894 void DrawLevelElement(int x, int y, int element)
1896 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1897 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1900 void DrawScreenField(int x, int y)
1902 int lx = LEVELX(x), ly = LEVELY(y);
1903 int element, content;
1905 if (!IN_LEV_FIELD(lx, ly))
1907 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1910 element = getBorderElement(lx, ly);
1912 DrawScreenElement(x, y, element);
1917 element = Feld[lx][ly];
1918 content = Store[lx][ly];
1920 if (IS_MOVING(lx, ly))
1922 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1923 boolean cut_mode = NO_CUTTING;
1925 if (element == EL_QUICKSAND_EMPTYING ||
1926 element == EL_QUICKSAND_FAST_EMPTYING ||
1927 element == EL_MAGIC_WALL_EMPTYING ||
1928 element == EL_BD_MAGIC_WALL_EMPTYING ||
1929 element == EL_DC_MAGIC_WALL_EMPTYING ||
1930 element == EL_AMOEBA_DROPPING)
1931 cut_mode = CUT_ABOVE;
1932 else if (element == EL_QUICKSAND_FILLING ||
1933 element == EL_QUICKSAND_FAST_FILLING ||
1934 element == EL_MAGIC_WALL_FILLING ||
1935 element == EL_BD_MAGIC_WALL_FILLING ||
1936 element == EL_DC_MAGIC_WALL_FILLING)
1937 cut_mode = CUT_BELOW;
1939 if (cut_mode == CUT_ABOVE)
1940 DrawScreenElement(x, y, element);
1942 DrawScreenElement(x, y, EL_EMPTY);
1945 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1946 else if (cut_mode == NO_CUTTING)
1947 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1950 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1952 if (cut_mode == CUT_BELOW &&
1953 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1954 DrawLevelElement(lx, ly + 1, element);
1957 if (content == EL_ACID)
1959 int dir = MovDir[lx][ly];
1960 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1961 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1963 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1966 else if (IS_BLOCKED(lx, ly))
1971 boolean cut_mode = NO_CUTTING;
1972 int element_old, content_old;
1974 Blocked2Moving(lx, ly, &oldx, &oldy);
1977 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1978 MovDir[oldx][oldy] == MV_RIGHT);
1980 element_old = Feld[oldx][oldy];
1981 content_old = Store[oldx][oldy];
1983 if (element_old == EL_QUICKSAND_EMPTYING ||
1984 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1985 element_old == EL_MAGIC_WALL_EMPTYING ||
1986 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1987 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1988 element_old == EL_AMOEBA_DROPPING)
1989 cut_mode = CUT_ABOVE;
1991 DrawScreenElement(x, y, EL_EMPTY);
1994 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1996 else if (cut_mode == NO_CUTTING)
1997 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2000 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2003 else if (IS_DRAWABLE(element))
2004 DrawScreenElement(x, y, element);
2006 DrawScreenElement(x, y, EL_EMPTY);
2009 void DrawLevelField(int x, int y)
2011 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2012 DrawScreenField(SCREENX(x), SCREENY(y));
2013 else if (IS_MOVING(x, y))
2017 Moving2Blocked(x, y, &newx, &newy);
2018 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2019 DrawScreenField(SCREENX(newx), SCREENY(newy));
2021 else if (IS_BLOCKED(x, y))
2025 Blocked2Moving(x, y, &oldx, &oldy);
2026 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2027 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2031 void DrawMiniElement(int x, int y, int element)
2035 graphic = el2edimg(element);
2036 DrawMiniGraphic(x, y, graphic);
2039 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2041 int x = sx + scroll_x, y = sy + scroll_y;
2043 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2044 DrawMiniElement(sx, sy, EL_EMPTY);
2045 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2046 DrawMiniElement(sx, sy, Feld[x][y]);
2048 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2051 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2052 int x, int y, int xsize, int ysize,
2053 int tile_width, int tile_height)
2057 int dst_x = startx + x * tile_width;
2058 int dst_y = starty + y * tile_height;
2059 int width = graphic_info[graphic].width;
2060 int height = graphic_info[graphic].height;
2061 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2062 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2063 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2064 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2065 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2066 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2067 boolean draw_masked = graphic_info[graphic].draw_masked;
2069 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2071 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2073 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2077 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2078 inner_sx + (x - 1) * tile_width % inner_width);
2079 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2080 inner_sy + (y - 1) * tile_height % inner_height);
2083 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2086 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2090 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2091 int x, int y, int xsize, int ysize, int font_nr)
2093 int font_width = getFontWidth(font_nr);
2094 int font_height = getFontHeight(font_nr);
2096 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2097 font_width, font_height);
2100 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2102 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2103 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2104 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2105 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2106 boolean no_delay = (tape.warp_forward);
2107 unsigned int anim_delay = 0;
2108 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2109 int anim_delay_value = (no_delay ? 0 : frame_delay_value);
2110 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2111 int font_width = getFontWidth(font_nr);
2112 int font_height = getFontHeight(font_nr);
2113 int max_xsize = level.envelope[envelope_nr].xsize;
2114 int max_ysize = level.envelope[envelope_nr].ysize;
2115 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2116 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2117 int xend = max_xsize;
2118 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2119 int xstep = (xstart < xend ? 1 : 0);
2120 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2123 for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2125 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2126 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2127 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2128 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2131 SetDrawtoField(DRAW_BUFFERED);
2133 BlitScreenToBitmap(backbuffer);
2135 SetDrawtoField(DRAW_BACKBUFFER);
2137 for (yy = 0; yy < ysize; yy++)
2138 for (xx = 0; xx < xsize; xx++)
2139 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2141 DrawTextBuffer(sx + font_width, sy + font_height,
2142 level.envelope[envelope_nr].text, font_nr, max_xsize,
2143 xsize - 2, ysize - 2, 0, mask_mode,
2144 level.envelope[envelope_nr].autowrap,
2145 level.envelope[envelope_nr].centered, FALSE);
2147 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2150 WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2154 void ShowEnvelope(int envelope_nr)
2156 int element = EL_ENVELOPE_1 + envelope_nr;
2157 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2158 int sound_opening = element_info[element].sound[ACTION_OPENING];
2159 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2160 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2161 boolean no_delay = (tape.warp_forward);
2162 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2163 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2164 int anim_mode = graphic_info[graphic].anim_mode;
2165 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2166 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2168 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2170 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2172 if (anim_mode == ANIM_DEFAULT)
2173 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2175 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2178 Delay(wait_delay_value);
2180 WaitForEventToContinue();
2182 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2184 if (anim_mode != ANIM_NONE)
2185 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2187 if (anim_mode == ANIM_DEFAULT)
2188 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2190 game.envelope_active = FALSE;
2192 SetDrawtoField(DRAW_BUFFERED);
2194 redraw_mask |= REDRAW_FIELD;
2198 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2200 int border_size = request.border_size;
2201 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2202 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2203 int sx = sx_center - request.width / 2;
2204 int sy = sy_center - request.height / 2;
2206 if (add_border_size)
2216 void DrawEnvelopeRequest(char *text)
2218 char *text_final = text;
2219 char *text_door_style = NULL;
2220 int graphic = IMG_BACKGROUND_REQUEST;
2221 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2222 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2223 int font_nr = FONT_REQUEST;
2224 int font_width = getFontWidth(font_nr);
2225 int font_height = getFontHeight(font_nr);
2226 int border_size = request.border_size;
2227 int line_spacing = request.line_spacing;
2228 int line_height = font_height + line_spacing;
2229 int text_width = request.width - 2 * border_size;
2230 int text_height = request.height - 2 * border_size;
2231 int line_length = text_width / font_width;
2232 int max_lines = text_height / line_height;
2233 int width = request.width;
2234 int height = request.height;
2235 int tile_size = request.step_offset;
2236 int x_steps = width / tile_size;
2237 int y_steps = height / tile_size;
2241 if (request.wrap_single_words)
2243 char *src_text_ptr, *dst_text_ptr;
2245 text_door_style = checked_malloc(2 * strlen(text) + 1);
2247 src_text_ptr = text;
2248 dst_text_ptr = text_door_style;
2250 while (*src_text_ptr)
2252 if (*src_text_ptr == ' ' ||
2253 *src_text_ptr == '?' ||
2254 *src_text_ptr == '!')
2255 *dst_text_ptr++ = '\n';
2257 if (*src_text_ptr != ' ')
2258 *dst_text_ptr++ = *src_text_ptr;
2263 *dst_text_ptr = '\0';
2265 text_final = text_door_style;
2268 setRequestPosition(&sx, &sy, FALSE);
2270 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2272 for (y = 0; y < y_steps; y++)
2273 for (x = 0; x < x_steps; x++)
2274 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2275 x, y, x_steps, y_steps,
2276 tile_size, tile_size);
2278 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2279 line_length, -1, max_lines, line_spacing, mask_mode,
2280 request.autowrap, request.centered, FALSE);
2282 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2283 RedrawGadget(tool_gadget[i]);
2285 // store readily prepared envelope request for later use when animating
2286 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2288 if (text_door_style)
2289 free(text_door_style);
2292 void AnimateEnvelopeRequest(int anim_mode, int action)
2294 int graphic = IMG_BACKGROUND_REQUEST;
2295 boolean draw_masked = graphic_info[graphic].draw_masked;
2296 int delay_value_normal = request.step_delay;
2297 int delay_value_fast = delay_value_normal / 2;
2298 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2299 boolean no_delay = (tape.warp_forward);
2300 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2301 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0);
2302 unsigned int anim_delay = 0;
2304 int width = request.width;
2305 int height = request.height;
2306 int tile_size = request.step_offset;
2307 int max_xsize = width / tile_size;
2308 int max_ysize = height / tile_size;
2309 int max_xsize_inner = max_xsize - 2;
2310 int max_ysize_inner = max_ysize - 2;
2312 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2313 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2314 int xend = max_xsize_inner;
2315 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2316 int xstep = (xstart < xend ? 1 : 0);
2317 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2320 if (setup.quick_doors)
2327 if (action == ACTION_OPENING)
2328 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2329 else if (action == ACTION_CLOSING)
2330 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2333 for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2335 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2336 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2337 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2338 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2339 int src_x = sx_center - width / 2;
2340 int src_y = sy_center - height / 2;
2341 int dst_x = sx_center - xsize * tile_size / 2;
2342 int dst_y = sy_center - ysize * tile_size / 2;
2343 int xsize_size_left = (xsize - 1) * tile_size;
2344 int ysize_size_top = (ysize - 1) * tile_size;
2345 int max_xsize_pos = (max_xsize - 1) * tile_size;
2346 int max_ysize_pos = (max_ysize - 1) * tile_size;
2349 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2351 for (yy = 0; yy < 2; yy++)
2353 for (xx = 0; xx < 2; xx++)
2355 int src_xx = src_x + xx * max_xsize_pos;
2356 int src_yy = src_y + yy * max_ysize_pos;
2357 int dst_xx = dst_x + xx * xsize_size_left;
2358 int dst_yy = dst_y + yy * ysize_size_top;
2359 int xx_size = (xx ? tile_size : xsize_size_left);
2360 int yy_size = (yy ? tile_size : ysize_size_top);
2363 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2364 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2366 BlitBitmap(bitmap_db_cross, backbuffer,
2367 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2371 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2376 WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2381 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2383 int last_game_status = game_status; /* save current game status */
2384 int graphic = IMG_BACKGROUND_REQUEST;
2385 int sound_opening = SND_REQUEST_OPENING;
2386 int sound_closing = SND_REQUEST_CLOSING;
2387 int anim_mode = graphic_info[graphic].anim_mode;
2388 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2389 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2391 if (game_status == GAME_MODE_PLAYING)
2392 BlitScreenToBitmap(backbuffer);
2394 SetDrawtoField(DRAW_BACKBUFFER);
2396 // SetDrawBackgroundMask(REDRAW_NONE);
2398 if (action == ACTION_OPENING)
2400 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2402 if (req_state & REQ_ASK)
2404 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2405 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2407 else if (req_state & REQ_CONFIRM)
2409 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2411 else if (req_state & REQ_PLAYER)
2413 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2414 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2415 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2416 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2419 DrawEnvelopeRequest(text);
2421 if (game_status != GAME_MODE_MAIN)
2425 /* force DOOR font inside door area */
2426 game_status = GAME_MODE_PSEUDO_DOOR;
2428 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2430 if (action == ACTION_OPENING)
2432 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2434 if (anim_mode == ANIM_DEFAULT)
2435 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2437 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2442 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2444 if (anim_mode != ANIM_NONE)
2445 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2447 if (anim_mode == ANIM_DEFAULT)
2448 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2451 game.envelope_active = FALSE;
2453 game_status = last_game_status; /* restore current game status */
2455 if (action == ACTION_CLOSING)
2457 if (game_status != GAME_MODE_MAIN)
2460 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2463 // SetDrawBackgroundMask(last_draw_background_mask);
2465 redraw_mask |= REDRAW_FIELD;
2467 if (game_status == GAME_MODE_MAIN)
2472 if (action == ACTION_CLOSING &&
2473 game_status == GAME_MODE_PLAYING &&
2474 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2475 SetDrawtoField(DRAW_BUFFERED);
2478 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2482 int graphic = el2preimg(element);
2484 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2485 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2488 void DrawLevel(int draw_background_mask)
2492 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2493 SetDrawBackgroundMask(draw_background_mask);
2497 for (x = BX1; x <= BX2; x++)
2498 for (y = BY1; y <= BY2; y++)
2499 DrawScreenField(x, y);
2501 redraw_mask |= REDRAW_FIELD;
2504 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2508 for (x = 0; x < size_x; x++)
2509 for (y = 0; y < size_y; y++)
2510 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2512 redraw_mask |= REDRAW_FIELD;
2515 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2517 boolean show_level_border = (BorderElement != EL_EMPTY);
2518 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2519 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2520 int tile_size = preview.tile_size;
2521 int preview_width = preview.xsize * tile_size;
2522 int preview_height = preview.ysize * tile_size;
2523 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2524 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2525 int real_preview_width = real_preview_xsize * tile_size;
2526 int real_preview_height = real_preview_ysize * tile_size;
2527 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2528 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2531 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2534 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2536 dst_x += (preview_width - real_preview_width) / 2;
2537 dst_y += (preview_height - real_preview_height) / 2;
2539 for (x = 0; x < real_preview_xsize; x++)
2541 for (y = 0; y < real_preview_ysize; y++)
2543 int lx = from_x + x + (show_level_border ? -1 : 0);
2544 int ly = from_y + y + (show_level_border ? -1 : 0);
2545 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2546 getBorderElement(lx, ly));
2548 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2549 element, tile_size);
2553 redraw_mask |= REDRAW_MICROLEVEL;
2556 #define MICROLABEL_EMPTY 0
2557 #define MICROLABEL_LEVEL_NAME 1
2558 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2559 #define MICROLABEL_LEVEL_AUTHOR 3
2560 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2561 #define MICROLABEL_IMPORTED_FROM 5
2562 #define MICROLABEL_IMPORTED_BY_HEAD 6
2563 #define MICROLABEL_IMPORTED_BY 7
2565 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2567 int max_text_width = SXSIZE;
2568 int font_width = getFontWidth(font_nr);
2570 if (pos->align == ALIGN_CENTER)
2571 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2572 else if (pos->align == ALIGN_RIGHT)
2573 max_text_width = pos->x;
2575 max_text_width = SXSIZE - pos->x;
2577 return max_text_width / font_width;
2580 static void DrawPreviewLevelLabelExt(int mode)
2582 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2583 char label_text[MAX_OUTPUT_LINESIZE + 1];
2584 int max_len_label_text;
2585 int font_nr = pos->font;
2588 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2591 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2592 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2593 mode == MICROLABEL_IMPORTED_BY_HEAD)
2594 font_nr = pos->font_alt;
2596 max_len_label_text = getMaxTextLength(pos, font_nr);
2598 if (pos->size != -1)
2599 max_len_label_text = pos->size;
2601 for (i = 0; i < max_len_label_text; i++)
2602 label_text[i] = ' ';
2603 label_text[max_len_label_text] = '\0';
2605 if (strlen(label_text) > 0)
2606 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2609 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2610 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2611 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2612 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2613 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2614 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2615 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2616 max_len_label_text);
2617 label_text[max_len_label_text] = '\0';
2619 if (strlen(label_text) > 0)
2620 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2622 redraw_mask |= REDRAW_MICROLEVEL;
2625 static void DrawPreviewLevelExt(boolean restart)
2627 static unsigned int scroll_delay = 0;
2628 static unsigned int label_delay = 0;
2629 static int from_x, from_y, scroll_direction;
2630 static int label_state, label_counter;
2631 unsigned int scroll_delay_value = preview.step_delay;
2632 boolean show_level_border = (BorderElement != EL_EMPTY);
2633 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2634 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2635 int last_game_status = game_status; /* save current game status */
2642 if (preview.anim_mode == ANIM_CENTERED)
2644 if (level_xsize > preview.xsize)
2645 from_x = (level_xsize - preview.xsize) / 2;
2646 if (level_ysize > preview.ysize)
2647 from_y = (level_ysize - preview.ysize) / 2;
2650 from_x += preview.xoffset;
2651 from_y += preview.yoffset;
2653 scroll_direction = MV_RIGHT;
2657 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2658 DrawPreviewLevelLabelExt(label_state);
2660 /* initialize delay counters */
2661 DelayReached(&scroll_delay, 0);
2662 DelayReached(&label_delay, 0);
2664 if (leveldir_current->name)
2666 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2667 char label_text[MAX_OUTPUT_LINESIZE + 1];
2668 int font_nr = pos->font;
2669 int max_len_label_text = getMaxTextLength(pos, font_nr);
2671 if (pos->size != -1)
2672 max_len_label_text = pos->size;
2674 strncpy(label_text, leveldir_current->name, max_len_label_text);
2675 label_text[max_len_label_text] = '\0';
2677 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2678 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2681 game_status = last_game_status; /* restore current game status */
2686 /* scroll preview level, if needed */
2687 if (preview.anim_mode != ANIM_NONE &&
2688 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2689 DelayReached(&scroll_delay, scroll_delay_value))
2691 switch (scroll_direction)
2696 from_x -= preview.step_offset;
2697 from_x = (from_x < 0 ? 0 : from_x);
2700 scroll_direction = MV_UP;
2704 if (from_x < level_xsize - preview.xsize)
2706 from_x += preview.step_offset;
2707 from_x = (from_x > level_xsize - preview.xsize ?
2708 level_xsize - preview.xsize : from_x);
2711 scroll_direction = MV_DOWN;
2717 from_y -= preview.step_offset;
2718 from_y = (from_y < 0 ? 0 : from_y);
2721 scroll_direction = MV_RIGHT;
2725 if (from_y < level_ysize - preview.ysize)
2727 from_y += preview.step_offset;
2728 from_y = (from_y > level_ysize - preview.ysize ?
2729 level_ysize - preview.ysize : from_y);
2732 scroll_direction = MV_LEFT;
2739 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2742 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2743 /* redraw micro level label, if needed */
2744 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2745 !strEqual(level.author, ANONYMOUS_NAME) &&
2746 !strEqual(level.author, leveldir_current->name) &&
2747 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2749 int max_label_counter = 23;
2751 if (leveldir_current->imported_from != NULL &&
2752 strlen(leveldir_current->imported_from) > 0)
2753 max_label_counter += 14;
2754 if (leveldir_current->imported_by != NULL &&
2755 strlen(leveldir_current->imported_by) > 0)
2756 max_label_counter += 14;
2758 label_counter = (label_counter + 1) % max_label_counter;
2759 label_state = (label_counter >= 0 && label_counter <= 7 ?
2760 MICROLABEL_LEVEL_NAME :
2761 label_counter >= 9 && label_counter <= 12 ?
2762 MICROLABEL_LEVEL_AUTHOR_HEAD :
2763 label_counter >= 14 && label_counter <= 21 ?
2764 MICROLABEL_LEVEL_AUTHOR :
2765 label_counter >= 23 && label_counter <= 26 ?
2766 MICROLABEL_IMPORTED_FROM_HEAD :
2767 label_counter >= 28 && label_counter <= 35 ?
2768 MICROLABEL_IMPORTED_FROM :
2769 label_counter >= 37 && label_counter <= 40 ?
2770 MICROLABEL_IMPORTED_BY_HEAD :
2771 label_counter >= 42 && label_counter <= 49 ?
2772 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2774 if (leveldir_current->imported_from == NULL &&
2775 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2776 label_state == MICROLABEL_IMPORTED_FROM))
2777 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2778 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2780 DrawPreviewLevelLabelExt(label_state);
2783 game_status = last_game_status; /* restore current game status */
2786 void DrawPreviewLevelInitial()
2788 DrawPreviewLevelExt(TRUE);
2791 void DrawPreviewLevelAnimation()
2793 DrawPreviewLevelExt(FALSE);
2796 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2797 int graphic, int sync_frame, int mask_mode)
2799 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2801 if (mask_mode == USE_MASKING)
2802 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2804 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2807 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2808 int graphic, int sync_frame,
2811 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2813 if (mask_mode == USE_MASKING)
2814 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2816 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2819 inline void DrawGraphicAnimation(int x, int y, int graphic)
2821 int lx = LEVELX(x), ly = LEVELY(y);
2823 if (!IN_SCR_FIELD(x, y))
2826 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2827 graphic, GfxFrame[lx][ly], NO_MASKING);
2829 MarkTileDirty(x, y);
2832 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2834 int lx = LEVELX(x), ly = LEVELY(y);
2836 if (!IN_SCR_FIELD(x, y))
2839 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2840 graphic, GfxFrame[lx][ly], NO_MASKING);
2841 MarkTileDirty(x, y);
2844 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2846 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2849 void DrawLevelElementAnimation(int x, int y, int element)
2851 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2853 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2856 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2858 int sx = SCREENX(x), sy = SCREENY(y);
2860 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2863 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2866 DrawGraphicAnimation(sx, sy, graphic);
2869 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2870 DrawLevelFieldCrumbled(x, y);
2872 if (GFX_CRUMBLED(Feld[x][y]))
2873 DrawLevelFieldCrumbled(x, y);
2877 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2879 int sx = SCREENX(x), sy = SCREENY(y);
2882 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2885 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2887 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2890 DrawGraphicAnimation(sx, sy, graphic);
2892 if (GFX_CRUMBLED(element))
2893 DrawLevelFieldCrumbled(x, y);
2896 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2898 if (player->use_murphy)
2900 /* this works only because currently only one player can be "murphy" ... */
2901 static int last_horizontal_dir = MV_LEFT;
2902 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2904 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2905 last_horizontal_dir = move_dir;
2907 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2909 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2911 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2917 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2920 static boolean equalGraphics(int graphic1, int graphic2)
2922 struct GraphicInfo *g1 = &graphic_info[graphic1];
2923 struct GraphicInfo *g2 = &graphic_info[graphic2];
2925 return (g1->bitmap == g2->bitmap &&
2926 g1->src_x == g2->src_x &&
2927 g1->src_y == g2->src_y &&
2928 g1->anim_frames == g2->anim_frames &&
2929 g1->anim_delay == g2->anim_delay &&
2930 g1->anim_mode == g2->anim_mode);
2933 void DrawAllPlayers()
2937 for (i = 0; i < MAX_PLAYERS; i++)
2938 if (stored_player[i].active)
2939 DrawPlayer(&stored_player[i]);
2942 void DrawPlayerField(int x, int y)
2944 if (!IS_PLAYER(x, y))
2947 DrawPlayer(PLAYERINFO(x, y));
2950 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
2952 void DrawPlayer(struct PlayerInfo *player)
2954 int jx = player->jx;
2955 int jy = player->jy;
2956 int move_dir = player->MovDir;
2957 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
2958 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
2959 int last_jx = (player->is_moving ? jx - dx : jx);
2960 int last_jy = (player->is_moving ? jy - dy : jy);
2961 int next_jx = jx + dx;
2962 int next_jy = jy + dy;
2963 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
2964 boolean player_is_opaque = FALSE;
2965 int sx = SCREENX(jx), sy = SCREENY(jy);
2966 int sxx = 0, syy = 0;
2967 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
2969 int action = ACTION_DEFAULT;
2970 int last_player_graphic = getPlayerGraphic(player, move_dir);
2971 int last_player_frame = player->Frame;
2974 /* GfxElement[][] is set to the element the player is digging or collecting;
2975 remove also for off-screen player if the player is not moving anymore */
2976 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
2977 GfxElement[jx][jy] = EL_UNDEFINED;
2979 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
2983 if (!IN_LEV_FIELD(jx, jy))
2985 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
2986 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
2987 printf("DrawPlayerField(): This should never happen!\n");
2992 if (element == EL_EXPLOSION)
2995 action = (player->is_pushing ? ACTION_PUSHING :
2996 player->is_digging ? ACTION_DIGGING :
2997 player->is_collecting ? ACTION_COLLECTING :
2998 player->is_moving ? ACTION_MOVING :
2999 player->is_snapping ? ACTION_SNAPPING :
3000 player->is_dropping ? ACTION_DROPPING :
3001 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3003 if (player->is_waiting)
3004 move_dir = player->dir_waiting;
3006 InitPlayerGfxAnimation(player, action, move_dir);
3008 /* ----------------------------------------------------------------------- */
3009 /* draw things in the field the player is leaving, if needed */
3010 /* ----------------------------------------------------------------------- */
3012 if (player->is_moving)
3014 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3016 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3018 if (last_element == EL_DYNAMITE_ACTIVE ||
3019 last_element == EL_EM_DYNAMITE_ACTIVE ||
3020 last_element == EL_SP_DISK_RED_ACTIVE)
3021 DrawDynamite(last_jx, last_jy);
3023 DrawLevelFieldThruMask(last_jx, last_jy);
3025 else if (last_element == EL_DYNAMITE_ACTIVE ||
3026 last_element == EL_EM_DYNAMITE_ACTIVE ||
3027 last_element == EL_SP_DISK_RED_ACTIVE)
3028 DrawDynamite(last_jx, last_jy);
3030 /* !!! this is not enough to prevent flickering of players which are
3031 moving next to each others without a free tile between them -- this
3032 can only be solved by drawing all players layer by layer (first the
3033 background, then the foreground etc.) !!! => TODO */
3034 else if (!IS_PLAYER(last_jx, last_jy))
3035 DrawLevelField(last_jx, last_jy);
3038 DrawLevelField(last_jx, last_jy);
3041 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3042 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3045 if (!IN_SCR_FIELD(sx, sy))
3048 /* ----------------------------------------------------------------------- */
3049 /* draw things behind the player, if needed */
3050 /* ----------------------------------------------------------------------- */
3053 DrawLevelElement(jx, jy, Back[jx][jy]);
3054 else if (IS_ACTIVE_BOMB(element))
3055 DrawLevelElement(jx, jy, EL_EMPTY);
3058 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3060 int old_element = GfxElement[jx][jy];
3061 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3062 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3064 if (GFX_CRUMBLED(old_element))
3065 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3067 DrawGraphic(sx, sy, old_graphic, frame);
3069 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3070 player_is_opaque = TRUE;
3074 GfxElement[jx][jy] = EL_UNDEFINED;
3076 /* make sure that pushed elements are drawn with correct frame rate */
3077 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3079 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3080 GfxFrame[jx][jy] = player->StepFrame;
3082 DrawLevelField(jx, jy);
3086 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3087 /* ----------------------------------------------------------------------- */
3088 /* draw player himself */
3089 /* ----------------------------------------------------------------------- */
3091 graphic = getPlayerGraphic(player, move_dir);
3093 /* in the case of changed player action or direction, prevent the current
3094 animation frame from being restarted for identical animations */
3095 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3096 player->Frame = last_player_frame;
3098 frame = getGraphicAnimationFrame(graphic, player->Frame);
3102 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3103 sxx = player->GfxPos;
3105 syy = player->GfxPos;
3108 if (!setup.soft_scrolling && ScreenMovPos)
3111 if (player_is_opaque)
3112 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3114 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3116 if (SHIELD_ON(player))
3118 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3119 IMG_SHIELD_NORMAL_ACTIVE);
3120 int frame = getGraphicAnimationFrame(graphic, -1);
3122 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3126 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3129 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3130 sxx = player->GfxPos;
3132 syy = player->GfxPos;
3136 /* ----------------------------------------------------------------------- */
3137 /* draw things the player is pushing, if needed */
3138 /* ----------------------------------------------------------------------- */
3140 if (player->is_pushing && player->is_moving)
3142 int px = SCREENX(jx), py = SCREENY(jy);
3143 int pxx = (TILEX - ABS(sxx)) * dx;
3144 int pyy = (TILEY - ABS(syy)) * dy;
3145 int gfx_frame = GfxFrame[jx][jy];
3151 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3153 element = Feld[next_jx][next_jy];
3154 gfx_frame = GfxFrame[next_jx][next_jy];
3157 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3159 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3160 frame = getGraphicAnimationFrame(graphic, sync_frame);
3162 /* draw background element under pushed element (like the Sokoban field) */
3163 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3165 /* this allows transparent pushing animation over non-black background */
3168 DrawLevelElement(jx, jy, Back[jx][jy]);
3170 DrawLevelElement(jx, jy, EL_EMPTY);
3172 if (Back[next_jx][next_jy])
3173 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3175 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3177 else if (Back[next_jx][next_jy])
3178 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3181 /* do not draw (EM style) pushing animation when pushing is finished */
3182 /* (two-tile animations usually do not contain start and end frame) */
3183 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3184 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3186 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3188 /* masked drawing is needed for EMC style (double) movement graphics */
3189 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3190 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3194 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3195 /* ----------------------------------------------------------------------- */
3196 /* draw player himself */
3197 /* ----------------------------------------------------------------------- */
3199 graphic = getPlayerGraphic(player, move_dir);
3201 /* in the case of changed player action or direction, prevent the current
3202 animation frame from being restarted for identical animations */
3203 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3204 player->Frame = last_player_frame;
3206 frame = getGraphicAnimationFrame(graphic, player->Frame);
3210 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3211 sxx = player->GfxPos;
3213 syy = player->GfxPos;
3216 if (!setup.soft_scrolling && ScreenMovPos)
3219 if (player_is_opaque)
3220 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3222 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3224 if (SHIELD_ON(player))
3226 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3227 IMG_SHIELD_NORMAL_ACTIVE);
3228 int frame = getGraphicAnimationFrame(graphic, -1);
3230 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3234 /* ----------------------------------------------------------------------- */
3235 /* draw things in front of player (active dynamite or dynabombs) */
3236 /* ----------------------------------------------------------------------- */
3238 if (IS_ACTIVE_BOMB(element))
3240 graphic = el2img(element);
3241 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3243 if (game.emulation == EMU_SUPAPLEX)
3244 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3246 DrawGraphicThruMask(sx, sy, graphic, frame);
3249 if (player_is_moving && last_element == EL_EXPLOSION)
3251 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3252 GfxElement[last_jx][last_jy] : EL_EMPTY);
3253 int graphic = el_act2img(element, ACTION_EXPLODING);
3254 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3255 int phase = ExplodePhase[last_jx][last_jy] - 1;
3256 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3259 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3262 /* ----------------------------------------------------------------------- */
3263 /* draw elements the player is just walking/passing through/under */
3264 /* ----------------------------------------------------------------------- */
3266 if (player_is_moving)
3268 /* handle the field the player is leaving ... */
3269 if (IS_ACCESSIBLE_INSIDE(last_element))
3270 DrawLevelField(last_jx, last_jy);
3271 else if (IS_ACCESSIBLE_UNDER(last_element))
3272 DrawLevelFieldThruMask(last_jx, last_jy);
3275 /* do not redraw accessible elements if the player is just pushing them */
3276 if (!player_is_moving || !player->is_pushing)
3278 /* ... and the field the player is entering */
3279 if (IS_ACCESSIBLE_INSIDE(element))
3280 DrawLevelField(jx, jy);
3281 else if (IS_ACCESSIBLE_UNDER(element))
3282 DrawLevelFieldThruMask(jx, jy);
3285 MarkTileDirty(sx, sy);
3288 /* ------------------------------------------------------------------------- */
3290 void WaitForEventToContinue()
3292 boolean still_wait = TRUE;
3294 /* simulate releasing mouse button over last gadget, if still pressed */
3296 HandleGadgets(-1, -1, 0);
3298 button_status = MB_RELEASED;
3312 case EVENT_BUTTONPRESS:
3313 case EVENT_KEYPRESS:
3317 case EVENT_KEYRELEASE:
3318 ClearPlayerAction();
3322 HandleOtherEvents(&event);
3326 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3333 /* don't eat all CPU time */
3338 #define MAX_REQUEST_LINES 13
3339 #define MAX_REQUEST_LINE_FONT1_LEN 7
3340 #define MAX_REQUEST_LINE_FONT2_LEN 10
3342 static int RequestHandleEvents(unsigned int req_state)
3344 int last_game_status = game_status; /* save current game status */
3348 button_status = MB_RELEASED;
3350 request_gadget_id = -1;
3363 case EVENT_BUTTONPRESS:
3364 case EVENT_BUTTONRELEASE:
3365 case EVENT_MOTIONNOTIFY:
3367 if (event.type == EVENT_MOTIONNOTIFY)
3369 if (!PointerInWindow(window))
3370 continue; /* window and pointer are on different screens */
3375 motion_status = TRUE;
3376 mx = ((MotionEvent *) &event)->x;
3377 my = ((MotionEvent *) &event)->y;
3381 motion_status = FALSE;
3382 mx = ((ButtonEvent *) &event)->x;
3383 my = ((ButtonEvent *) &event)->y;
3384 if (event.type == EVENT_BUTTONPRESS)
3385 button_status = ((ButtonEvent *) &event)->button;
3387 button_status = MB_RELEASED;
3390 /* this sets 'request_gadget_id' */
3391 HandleGadgets(mx, my, button_status);
3393 switch (request_gadget_id)
3395 case TOOL_CTRL_ID_YES:
3398 case TOOL_CTRL_ID_NO:
3401 case TOOL_CTRL_ID_CONFIRM:
3402 result = TRUE | FALSE;
3405 case TOOL_CTRL_ID_PLAYER_1:
3408 case TOOL_CTRL_ID_PLAYER_2:
3411 case TOOL_CTRL_ID_PLAYER_3:
3414 case TOOL_CTRL_ID_PLAYER_4:
3425 case EVENT_KEYPRESS:
3426 switch (GetEventKey((KeyEvent *)&event, TRUE))
3429 if (req_state & REQ_CONFIRM)
3434 #if defined(TARGET_SDL2)
3441 #if defined(TARGET_SDL2)
3451 if (req_state & REQ_PLAYER)
3455 case EVENT_KEYRELEASE:
3456 ClearPlayerAction();
3460 HandleOtherEvents(&event);
3464 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3466 int joy = AnyJoystick();
3468 if (joy & JOY_BUTTON_1)
3470 else if (joy & JOY_BUTTON_2)
3474 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3476 HandleGameActions();
3482 if (!PendingEvent()) /* delay only if no pending events */
3486 game_status = GAME_MODE_PSEUDO_DOOR;
3490 game_status = last_game_status; /* restore current game status */
3496 static boolean RequestDoor(char *text, unsigned int req_state)
3498 unsigned int old_door_state;
3499 int last_game_status = game_status; /* save current game status */
3500 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3501 int font_nr = FONT_TEXT_2;
3506 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3508 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3509 font_nr = FONT_TEXT_1;
3512 if (game_status == GAME_MODE_PLAYING)
3513 BlitScreenToBitmap(backbuffer);
3515 /* disable deactivated drawing when quick-loading level tape recording */
3516 if (tape.playing && tape.deactivate_display)
3517 TapeDeactivateDisplayOff(TRUE);
3519 SetMouseCursor(CURSOR_DEFAULT);
3521 #if defined(NETWORK_AVALIABLE)
3522 /* pause network game while waiting for request to answer */
3523 if (options.network &&
3524 game_status == GAME_MODE_PLAYING &&
3525 req_state & REQUEST_WAIT_FOR_INPUT)
3526 SendToServer_PausePlaying();
3529 old_door_state = GetDoorState();
3531 /* simulate releasing mouse button over last gadget, if still pressed */
3533 HandleGadgets(-1, -1, 0);
3537 /* draw released gadget before proceeding */
3540 if (old_door_state & DOOR_OPEN_1)
3542 CloseDoor(DOOR_CLOSE_1);
3544 /* save old door content */
3545 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3546 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3549 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3550 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3552 /* clear door drawing field */
3553 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3555 /* force DOOR font inside door area */
3556 game_status = GAME_MODE_PSEUDO_DOOR;
3558 /* write text for request */
3559 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3561 char text_line[max_request_line_len + 1];
3567 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3569 tc = *(text_ptr + tx);
3570 // if (!tc || tc == ' ')
3571 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3575 if ((tc == '?' || tc == '!') && tl == 0)
3585 strncpy(text_line, text_ptr, tl);
3588 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3589 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3590 text_line, font_nr);
3592 text_ptr += tl + (tc == ' ' ? 1 : 0);
3593 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3596 game_status = last_game_status; /* restore current game status */
3598 if (req_state & REQ_ASK)
3600 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3601 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3603 else if (req_state & REQ_CONFIRM)
3605 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3607 else if (req_state & REQ_PLAYER)
3609 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3610 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3611 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3612 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3615 /* copy request gadgets to door backbuffer */
3616 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3618 OpenDoor(DOOR_OPEN_1);
3620 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3622 if (game_status == GAME_MODE_PLAYING)
3624 SetPanelBackground();
3625 SetDrawBackgroundMask(REDRAW_DOOR_1);
3629 SetDrawBackgroundMask(REDRAW_FIELD);
3635 if (game_status != GAME_MODE_MAIN)
3638 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3640 // ---------- handle request buttons ----------
3641 result = RequestHandleEvents(req_state);
3643 if (game_status != GAME_MODE_MAIN)
3648 if (!(req_state & REQ_STAY_OPEN))
3650 CloseDoor(DOOR_CLOSE_1);
3652 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3653 (req_state & REQ_REOPEN))
3654 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3659 if (game_status == GAME_MODE_PLAYING)
3661 SetPanelBackground();
3662 SetDrawBackgroundMask(REDRAW_DOOR_1);
3666 SetDrawBackgroundMask(REDRAW_FIELD);
3669 #if defined(NETWORK_AVALIABLE)
3670 /* continue network game after request */
3671 if (options.network &&
3672 game_status == GAME_MODE_PLAYING &&
3673 req_state & REQUEST_WAIT_FOR_INPUT)
3674 SendToServer_ContinuePlaying();
3677 /* restore deactivated drawing when quick-loading level tape recording */
3678 if (tape.playing && tape.deactivate_display)
3679 TapeDeactivateDisplayOn();
3684 static boolean RequestEnvelope(char *text, unsigned int req_state)
3688 if (game_status == GAME_MODE_PLAYING)
3689 BlitScreenToBitmap(backbuffer);
3691 /* disable deactivated drawing when quick-loading level tape recording */
3692 if (tape.playing && tape.deactivate_display)
3693 TapeDeactivateDisplayOff(TRUE);
3695 SetMouseCursor(CURSOR_DEFAULT);
3697 #if defined(NETWORK_AVALIABLE)
3698 /* pause network game while waiting for request to answer */
3699 if (options.network &&
3700 game_status == GAME_MODE_PLAYING &&
3701 req_state & REQUEST_WAIT_FOR_INPUT)
3702 SendToServer_PausePlaying();
3705 /* simulate releasing mouse button over last gadget, if still pressed */
3707 HandleGadgets(-1, -1, 0);
3711 // (replace with setting corresponding request background)
3712 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3713 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3715 /* clear door drawing field */
3716 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3718 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3720 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3722 if (game_status == GAME_MODE_PLAYING)
3724 SetPanelBackground();
3725 SetDrawBackgroundMask(REDRAW_DOOR_1);
3729 SetDrawBackgroundMask(REDRAW_FIELD);
3735 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3737 // ---------- handle request buttons ----------
3738 result = RequestHandleEvents(req_state);
3740 if (game_status != GAME_MODE_MAIN)
3745 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3749 if (game_status == GAME_MODE_PLAYING)
3751 SetPanelBackground();
3752 SetDrawBackgroundMask(REDRAW_DOOR_1);
3756 SetDrawBackgroundMask(REDRAW_FIELD);
3759 #if defined(NETWORK_AVALIABLE)
3760 /* continue network game after request */
3761 if (options.network &&
3762 game_status == GAME_MODE_PLAYING &&
3763 req_state & REQUEST_WAIT_FOR_INPUT)
3764 SendToServer_ContinuePlaying();
3767 /* restore deactivated drawing when quick-loading level tape recording */
3768 if (tape.playing && tape.deactivate_display)
3769 TapeDeactivateDisplayOn();
3774 boolean Request(char *text, unsigned int req_state)
3776 if (global.use_envelope_request)
3777 return RequestEnvelope(text, req_state);
3779 return RequestDoor(text, req_state);
3782 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3784 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3785 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3788 if (dpo1->sort_priority != dpo2->sort_priority)
3789 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3791 compare_result = dpo1->nr - dpo2->nr;
3793 return compare_result;
3796 void InitGraphicCompatibilityInfo_Doors()
3802 struct DoorInfo *door;
3806 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3807 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3809 { -1, -1, -1, NULL }
3811 struct Rect door_rect_list[] =
3813 { DX, DY, DXSIZE, DYSIZE },
3814 { VX, VY, VXSIZE, VYSIZE }
3818 for (i = 0; doors[i].door_token != -1; i++)
3820 int door_token = doors[i].door_token;
3821 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3822 int part_1 = doors[i].part_1;
3823 int part_8 = doors[i].part_8;
3824 int part_2 = part_1 + 1;
3825 int part_3 = part_1 + 2;
3826 struct DoorInfo *door = doors[i].door;
3827 struct Rect *door_rect = &door_rect_list[door_index];
3828 boolean door_gfx_redefined = FALSE;
3830 /* check if any door part graphic definitions have been redefined */
3832 for (j = 0; door_part_controls[j].door_token != -1; j++)
3834 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3835 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3837 if (dpc->door_token == door_token && fi->redefined)
3838 door_gfx_redefined = TRUE;
3841 /* check for old-style door graphic/animation modifications */
3843 if (!door_gfx_redefined)
3845 if (door->anim_mode & ANIM_STATIC_PANEL)
3847 door->panel.step_xoffset = 0;
3848 door->panel.step_yoffset = 0;
3851 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3853 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3854 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3855 int num_door_steps, num_panel_steps;
3857 /* remove door part graphics other than the two default wings */
3859 for (j = 0; door_part_controls[j].door_token != -1; j++)
3861 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3862 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3864 if (dpc->graphic >= part_3 &&
3865 dpc->graphic <= part_8)
3869 /* set graphics and screen positions of the default wings */
3871 g_part_1->width = door_rect->width;
3872 g_part_1->height = door_rect->height;
3873 g_part_2->width = door_rect->width;
3874 g_part_2->height = door_rect->height;
3875 g_part_2->src_x = door_rect->width;
3876 g_part_2->src_y = g_part_1->src_y;
3878 door->part_2.x = door->part_1.x;
3879 door->part_2.y = door->part_1.y;
3881 if (door->width != -1)
3883 g_part_1->width = door->width;
3884 g_part_2->width = door->width;
3886 // special treatment for graphics and screen position of right wing
3887 g_part_2->src_x += door_rect->width - door->width;
3888 door->part_2.x += door_rect->width - door->width;
3891 if (door->height != -1)
3893 g_part_1->height = door->height;
3894 g_part_2->height = door->height;
3896 // special treatment for graphics and screen position of bottom wing
3897 g_part_2->src_y += door_rect->height - door->height;
3898 door->part_2.y += door_rect->height - door->height;
3901 /* set animation delays for the default wings and panels */
3903 door->part_1.step_delay = door->step_delay;
3904 door->part_2.step_delay = door->step_delay;
3905 door->panel.step_delay = door->step_delay;
3907 /* set animation draw order for the default wings */
3909 door->part_1.sort_priority = 2; /* draw left wing over ... */
3910 door->part_2.sort_priority = 1; /* ... right wing */
3912 /* set animation draw offset for the default wings */
3914 if (door->anim_mode & ANIM_HORIZONTAL)
3916 door->part_1.step_xoffset = door->step_offset;
3917 door->part_1.step_yoffset = 0;
3918 door->part_2.step_xoffset = door->step_offset * -1;
3919 door->part_2.step_yoffset = 0;
3921 num_door_steps = g_part_1->width / door->step_offset;
3923 else // ANIM_VERTICAL
3925 door->part_1.step_xoffset = 0;
3926 door->part_1.step_yoffset = door->step_offset;
3927 door->part_2.step_xoffset = 0;
3928 door->part_2.step_yoffset = door->step_offset * -1;
3930 num_door_steps = g_part_1->height / door->step_offset;
3933 /* set animation draw offset for the default panels */
3935 if (door->step_offset > 1)
3937 num_panel_steps = 2 * door_rect->height / door->step_offset;
3938 door->panel.start_step = num_panel_steps - num_door_steps;
3942 num_panel_steps = door_rect->height / door->step_offset;
3943 door->panel.start_step = num_panel_steps - num_door_steps / 2;
3944 door->panel.step_delay *= 2;
3955 for (i = 0; door_part_controls[i].door_token != -1; i++)
3957 struct DoorPartControlInfo *dpc = &door_part_controls[i];
3958 struct DoorPartOrderInfo *dpo = &door_part_order[i];
3960 /* initialize "start_step_opening" and "start_step_closing", if needed */
3961 if (dpc->pos->start_step_opening == 0 &&
3962 dpc->pos->start_step_closing == 0)
3964 // dpc->pos->start_step_opening = dpc->pos->start_step;
3965 dpc->pos->start_step_closing = dpc->pos->start_step;
3968 /* fill structure for door part draw order (sorted below) */
3970 dpo->sort_priority = dpc->pos->sort_priority;
3973 /* sort door part controls according to sort_priority and graphic number */
3974 qsort(door_part_order, MAX_DOOR_PARTS,
3975 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
3978 unsigned int OpenDoor(unsigned int door_state)
3980 if (door_state & DOOR_COPY_BACK)
3982 if (door_state & DOOR_OPEN_1)
3983 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3984 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
3986 if (door_state & DOOR_OPEN_2)
3987 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
3988 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
3990 door_state &= ~DOOR_COPY_BACK;
3993 return MoveDoor(door_state);
3996 unsigned int CloseDoor(unsigned int door_state)
3998 unsigned int old_door_state = GetDoorState();
4000 if (!(door_state & DOOR_NO_COPY_BACK))
4002 if (old_door_state & DOOR_OPEN_1)
4003 BlitBitmap(backbuffer, bitmap_db_door_1,
4004 DX, DY, DXSIZE, DYSIZE, 0, 0);
4006 if (old_door_state & DOOR_OPEN_2)
4007 BlitBitmap(backbuffer, bitmap_db_door_2,
4008 VX, VY, VXSIZE, VYSIZE, 0, 0);
4010 door_state &= ~DOOR_NO_COPY_BACK;
4013 return MoveDoor(door_state);
4016 unsigned int GetDoorState()
4018 return MoveDoor(DOOR_GET_STATE);
4021 unsigned int SetDoorState(unsigned int door_state)
4023 return MoveDoor(door_state | DOOR_SET_STATE);
4026 int euclid(int a, int b)
4028 return (b ? euclid(b, a % b) : a);
4031 unsigned int MoveDoor(unsigned int door_state)
4033 struct Rect door_rect_list[] =
4035 { DX, DY, DXSIZE, DYSIZE },
4036 { VX, VY, VXSIZE, VYSIZE }
4038 static int door1 = DOOR_OPEN_1;
4039 static int door2 = DOOR_CLOSE_2;
4040 unsigned int door_delay = 0;
4041 unsigned int door_delay_value;
4044 if (door_1.width < 0 || door_1.width > DXSIZE)
4045 door_1.width = DXSIZE;
4046 if (door_1.height < 0 || door_1.height > DYSIZE)
4047 door_1.height = DYSIZE;
4048 if (door_2.width < 0 || door_2.width > VXSIZE)
4049 door_2.width = VXSIZE;
4050 if (door_2.height < 0 || door_2.height > VYSIZE)
4051 door_2.height = VYSIZE;
4053 if (door_state == DOOR_GET_STATE)
4054 return (door1 | door2);
4056 if (door_state & DOOR_SET_STATE)
4058 if (door_state & DOOR_ACTION_1)
4059 door1 = door_state & DOOR_ACTION_1;
4060 if (door_state & DOOR_ACTION_2)
4061 door2 = door_state & DOOR_ACTION_2;
4063 return (door1 | door2);
4066 if (!(door_state & DOOR_FORCE_REDRAW))
4068 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4069 door_state &= ~DOOR_OPEN_1;
4070 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4071 door_state &= ~DOOR_CLOSE_1;
4072 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4073 door_state &= ~DOOR_OPEN_2;
4074 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4075 door_state &= ~DOOR_CLOSE_2;
4078 if (global.autoplay_leveldir)
4080 door_state |= DOOR_NO_DELAY;
4081 door_state &= ~DOOR_CLOSE_ALL;
4084 if (game_status == GAME_MODE_EDITOR)
4085 door_state |= DOOR_NO_DELAY;
4087 if (door_state & DOOR_ACTION)
4089 boolean door_panel_drawn[NUM_DOORS];
4090 boolean panel_has_doors[NUM_DOORS];
4091 boolean door_part_skip[MAX_DOOR_PARTS];
4092 boolean door_part_done[MAX_DOOR_PARTS];
4093 boolean door_part_done_all;
4094 int num_steps[MAX_DOOR_PARTS];
4095 int max_move_delay = 0; // delay for complete animations of all doors
4096 int max_step_delay = 0; // delay (ms) between two animation frames
4097 int num_move_steps = 0; // number of animation steps for all doors
4098 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4099 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4100 int current_move_delay = 0;
4104 for (i = 0; i < NUM_DOORS; i++)
4105 panel_has_doors[i] = FALSE;
4107 for (i = 0; i < MAX_DOOR_PARTS; i++)
4109 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4110 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4111 int door_token = dpc->door_token;
4113 door_part_done[i] = FALSE;
4114 door_part_skip[i] = (!(door_state & door_token) ||
4118 for (i = 0; i < MAX_DOOR_PARTS; i++)
4120 int nr = door_part_order[i].nr;
4121 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4122 struct DoorPartPosInfo *pos = dpc->pos;
4123 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4124 int door_token = dpc->door_token;
4125 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4126 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4127 int step_xoffset = ABS(pos->step_xoffset);
4128 int step_yoffset = ABS(pos->step_yoffset);
4129 int step_delay = pos->step_delay;
4130 int current_door_state = door_state & door_token;
4131 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4132 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4133 boolean part_opening = (is_panel ? door_closing : door_opening);
4134 int start_step = (part_opening ? pos->start_step_opening :
4135 pos->start_step_closing);
4136 float move_xsize = (step_xoffset ? g->width : 0);
4137 float move_ysize = (step_yoffset ? g->height : 0);
4138 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4139 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4140 int move_steps = (move_xsteps && move_ysteps ?
4141 MIN(move_xsteps, move_ysteps) :
4142 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4143 int move_delay = move_steps * step_delay;
4145 if (door_part_skip[nr])
4148 max_move_delay = MAX(max_move_delay, move_delay);
4149 max_step_delay = (max_step_delay == 0 ? step_delay :
4150 euclid(max_step_delay, step_delay));
4151 num_steps[nr] = move_steps;
4155 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4157 panel_has_doors[door_index] = TRUE;
4161 num_move_steps = max_move_delay / max_step_delay;
4162 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4164 door_delay_value = max_step_delay;
4166 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4168 start = num_move_steps - 1;
4172 /* opening door sound has priority over simultaneously closing door */
4173 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4174 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4175 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4176 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4179 for (k = start; k < num_move_steps; k++)
4181 door_part_done_all = TRUE;
4183 for (i = 0; i < NUM_DOORS; i++)
4184 door_panel_drawn[i] = FALSE;
4186 for (i = 0; i < MAX_DOOR_PARTS; i++)
4188 int nr = door_part_order[i].nr;
4189 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4190 struct DoorPartPosInfo *pos = dpc->pos;
4191 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4192 int door_token = dpc->door_token;
4193 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4194 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4195 boolean is_panel_and_door_has_closed = FALSE;
4196 struct Rect *door_rect = &door_rect_list[door_index];
4197 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4199 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4200 int current_door_state = door_state & door_token;
4201 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4202 boolean door_closing = !door_opening;
4203 boolean part_opening = (is_panel ? door_closing : door_opening);
4204 boolean part_closing = !part_opening;
4205 int start_step = (part_opening ? pos->start_step_opening :
4206 pos->start_step_closing);
4207 int step_delay = pos->step_delay;
4208 int step_factor = step_delay / max_step_delay;
4209 int k1 = (step_factor ? k / step_factor + 1 : k);
4210 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4211 int kk = MAX(0, k2);
4214 int src_x, src_y, src_xx, src_yy;
4215 int dst_x, dst_y, dst_xx, dst_yy;
4218 if (door_part_skip[nr])
4221 if (!(door_state & door_token))
4229 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4230 int kk_door = MAX(0, k2_door);
4231 int sync_frame = kk_door * door_delay_value;
4232 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4234 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4239 if (!door_panel_drawn[door_index])
4241 ClearRectangle(drawto, door_rect->x, door_rect->y,
4242 door_rect->width, door_rect->height);
4244 door_panel_drawn[door_index] = TRUE;
4247 // draw opening or closing door parts
4249 if (pos->step_xoffset < 0) // door part on right side
4252 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4255 if (dst_xx + width > door_rect->width)
4256 width = door_rect->width - dst_xx;
4258 else // door part on left side
4261 dst_xx = pos->x - kk * pos->step_xoffset;
4265 src_xx = ABS(dst_xx);
4269 width = g->width - src_xx;
4271 // printf("::: k == %d [%d] \n", k, start_step);
4274 if (pos->step_yoffset < 0) // door part on bottom side
4277 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4280 if (dst_yy + height > door_rect->height)
4281 height = door_rect->height - dst_yy;
4283 else // door part on top side
4286 dst_yy = pos->y - kk * pos->step_yoffset;
4290 src_yy = ABS(dst_yy);
4294 height = g->height - src_yy;
4297 src_x = g_src_x + src_xx;
4298 src_y = g_src_y + src_yy;
4300 dst_x = door_rect->x + dst_xx;
4301 dst_y = door_rect->y + dst_yy;
4303 is_panel_and_door_has_closed =
4306 panel_has_doors[door_index] &&
4307 k >= num_move_steps_doors_only - 1);
4309 if (width >= 0 && width <= g->width &&
4310 height >= 0 && height <= g->height &&
4311 !is_panel_and_door_has_closed)
4313 if (is_panel || !pos->draw_masked)
4314 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4317 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4321 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4323 if ((part_opening && (width < 0 || height < 0)) ||
4324 (part_closing && (width >= g->width && height >= g->height)))
4325 door_part_done[nr] = TRUE;
4327 // continue door part animations, but not panel after door has closed
4328 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4329 door_part_done_all = FALSE;
4332 if (!(door_state & DOOR_NO_DELAY))
4336 if (game_status == GAME_MODE_MAIN)
4339 WaitUntilDelayReached(&door_delay, door_delay_value);
4341 current_move_delay += max_step_delay;
4344 if (door_part_done_all)
4349 if (door_state & DOOR_ACTION_1)
4350 door1 = door_state & DOOR_ACTION_1;
4351 if (door_state & DOOR_ACTION_2)
4352 door2 = door_state & DOOR_ACTION_2;
4354 return (door1 | door2);
4357 void DrawSpecialEditorDoor()
4359 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4360 int top_border_width = gfx1->width;
4361 int top_border_height = gfx1->height;
4362 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4363 int ex = EX - outer_border;
4364 int ey = EY - outer_border;
4365 int vy = VY - outer_border;
4366 int exsize = EXSIZE + 2 * outer_border;
4368 CloseDoor(DOOR_CLOSE_2);
4370 /* draw bigger level editor toolbox window */
4371 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4372 top_border_width, top_border_height, ex, ey - top_border_height);
4373 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4374 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4376 redraw_mask |= REDRAW_ALL;
4379 void UndrawSpecialEditorDoor()
4381 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4382 int top_border_width = gfx1->width;
4383 int top_border_height = gfx1->height;
4384 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4385 int ex = EX - outer_border;
4386 int ey = EY - outer_border;
4387 int ey_top = ey - top_border_height;
4388 int exsize = EXSIZE + 2 * outer_border;
4389 int eysize = EYSIZE + 2 * outer_border;
4391 /* draw normal tape recorder window */
4392 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4394 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4395 ex, ey_top, top_border_width, top_border_height,
4397 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4398 ex, ey, exsize, eysize, ex, ey);
4402 // if screen background is set to "[NONE]", clear editor toolbox window
4403 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4404 ClearRectangle(drawto, ex, ey, exsize, eysize);
4407 redraw_mask |= REDRAW_ALL;
4411 /* ---------- new tool button stuff ---------------------------------------- */
4416 struct TextPosInfo *pos;
4419 } toolbutton_info[NUM_TOOL_BUTTONS] =
4422 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4423 TOOL_CTRL_ID_YES, "yes"
4426 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4427 TOOL_CTRL_ID_NO, "no"
4430 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4431 TOOL_CTRL_ID_CONFIRM, "confirm"
4434 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4435 TOOL_CTRL_ID_PLAYER_1, "player 1"
4438 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4439 TOOL_CTRL_ID_PLAYER_2, "player 2"
4442 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4443 TOOL_CTRL_ID_PLAYER_3, "player 3"
4446 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4447 TOOL_CTRL_ID_PLAYER_4, "player 4"
4451 void CreateToolButtons()
4455 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4457 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4458 struct TextPosInfo *pos = toolbutton_info[i].pos;
4459 struct GadgetInfo *gi;
4460 Bitmap *deco_bitmap = None;
4461 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4462 unsigned int event_mask = GD_EVENT_RELEASED;
4465 int gd_x = gfx->src_x;
4466 int gd_y = gfx->src_y;
4467 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4468 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4471 if (global.use_envelope_request)
4472 setRequestPosition(&dx, &dy, TRUE);
4474 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4476 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4478 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4479 pos->size, &deco_bitmap, &deco_x, &deco_y);
4480 deco_xpos = (gfx->width - pos->size) / 2;
4481 deco_ypos = (gfx->height - pos->size) / 2;
4484 gi = CreateGadget(GDI_CUSTOM_ID, id,
4485 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4486 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4487 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4488 GDI_WIDTH, gfx->width,
4489 GDI_HEIGHT, gfx->height,
4490 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4491 GDI_STATE, GD_BUTTON_UNPRESSED,
4492 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4493 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4494 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4495 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4496 GDI_DECORATION_SIZE, pos->size, pos->size,
4497 GDI_DECORATION_SHIFTING, 1, 1,
4498 GDI_DIRECT_DRAW, FALSE,
4499 GDI_EVENT_MASK, event_mask,
4500 GDI_CALLBACK_ACTION, HandleToolButtons,
4504 Error(ERR_EXIT, "cannot create gadget");
4506 tool_gadget[id] = gi;
4510 void FreeToolButtons()
4514 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4515 FreeGadget(tool_gadget[i]);
4518 static void UnmapToolButtons()
4522 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4523 UnmapGadget(tool_gadget[i]);
4526 static void HandleToolButtons(struct GadgetInfo *gi)
4528 request_gadget_id = gi->custom_id;
4531 static struct Mapping_EM_to_RND_object
4534 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4535 boolean is_backside; /* backside of moving element */
4541 em_object_mapping_list[] =
4544 Xblank, TRUE, FALSE,
4548 Yacid_splash_eB, FALSE, FALSE,
4549 EL_ACID_SPLASH_RIGHT, -1, -1
4552 Yacid_splash_wB, FALSE, FALSE,
4553 EL_ACID_SPLASH_LEFT, -1, -1
4556 #ifdef EM_ENGINE_BAD_ROLL
4558 Xstone_force_e, FALSE, FALSE,
4559 EL_ROCK, -1, MV_BIT_RIGHT
4562 Xstone_force_w, FALSE, FALSE,
4563 EL_ROCK, -1, MV_BIT_LEFT
4566 Xnut_force_e, FALSE, FALSE,
4567 EL_NUT, -1, MV_BIT_RIGHT
4570 Xnut_force_w, FALSE, FALSE,
4571 EL_NUT, -1, MV_BIT_LEFT
4574 Xspring_force_e, FALSE, FALSE,
4575 EL_SPRING, -1, MV_BIT_RIGHT
4578 Xspring_force_w, FALSE, FALSE,
4579 EL_SPRING, -1, MV_BIT_LEFT
4582 Xemerald_force_e, FALSE, FALSE,
4583 EL_EMERALD, -1, MV_BIT_RIGHT
4586 Xemerald_force_w, FALSE, FALSE,
4587 EL_EMERALD, -1, MV_BIT_LEFT
4590 Xdiamond_force_e, FALSE, FALSE,
4591 EL_DIAMOND, -1, MV_BIT_RIGHT
4594 Xdiamond_force_w, FALSE, FALSE,
4595 EL_DIAMOND, -1, MV_BIT_LEFT
4598 Xbomb_force_e, FALSE, FALSE,
4599 EL_BOMB, -1, MV_BIT_RIGHT
4602 Xbomb_force_w, FALSE, FALSE,
4603 EL_BOMB, -1, MV_BIT_LEFT
4605 #endif /* EM_ENGINE_BAD_ROLL */
4608 Xstone, TRUE, FALSE,
4612 Xstone_pause, FALSE, FALSE,
4616 Xstone_fall, FALSE, FALSE,
4620 Ystone_s, FALSE, FALSE,
4621 EL_ROCK, ACTION_FALLING, -1
4624 Ystone_sB, FALSE, TRUE,
4625 EL_ROCK, ACTION_FALLING, -1
4628 Ystone_e, FALSE, FALSE,
4629 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4632 Ystone_eB, FALSE, TRUE,
4633 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4636 Ystone_w, FALSE, FALSE,
4637 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4640 Ystone_wB, FALSE, TRUE,
4641 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4648 Xnut_pause, FALSE, FALSE,
4652 Xnut_fall, FALSE, FALSE,
4656 Ynut_s, FALSE, FALSE,
4657 EL_NUT, ACTION_FALLING, -1
4660 Ynut_sB, FALSE, TRUE,
4661 EL_NUT, ACTION_FALLING, -1
4664 Ynut_e, FALSE, FALSE,
4665 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4668 Ynut_eB, FALSE, TRUE,
4669 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4672 Ynut_w, FALSE, FALSE,
4673 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4676 Ynut_wB, FALSE, TRUE,
4677 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4680 Xbug_n, TRUE, FALSE,
4684 Xbug_e, TRUE, FALSE,
4685 EL_BUG_RIGHT, -1, -1
4688 Xbug_s, TRUE, FALSE,
4692 Xbug_w, TRUE, FALSE,
4696 Xbug_gon, FALSE, FALSE,
4700 Xbug_goe, FALSE, FALSE,
4701 EL_BUG_RIGHT, -1, -1
4704 Xbug_gos, FALSE, FALSE,
4708 Xbug_gow, FALSE, FALSE,
4712 Ybug_n, FALSE, FALSE,
4713 EL_BUG, ACTION_MOVING, MV_BIT_UP
4716 Ybug_nB, FALSE, TRUE,
4717 EL_BUG, ACTION_MOVING, MV_BIT_UP
4720 Ybug_e, FALSE, FALSE,
4721 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4724 Ybug_eB, FALSE, TRUE,
4725 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4728 Ybug_s, FALSE, FALSE,
4729 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4732 Ybug_sB, FALSE, TRUE,
4733 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4736 Ybug_w, FALSE, FALSE,
4737 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4740 Ybug_wB, FALSE, TRUE,
4741 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4744 Ybug_w_n, FALSE, FALSE,
4745 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4748 Ybug_n_e, FALSE, FALSE,
4749 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4752 Ybug_e_s, FALSE, FALSE,
4753 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4756 Ybug_s_w, FALSE, FALSE,
4757 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4760 Ybug_e_n, FALSE, FALSE,
4761 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4764 Ybug_s_e, FALSE, FALSE,
4765 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4768 Ybug_w_s, FALSE, FALSE,
4769 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4772 Ybug_n_w, FALSE, FALSE,
4773 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4776 Ybug_stone, FALSE, FALSE,
4777 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4780 Ybug_spring, FALSE, FALSE,
4781 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4784 Xtank_n, TRUE, FALSE,
4785 EL_SPACESHIP_UP, -1, -1
4788 Xtank_e, TRUE, FALSE,
4789 EL_SPACESHIP_RIGHT, -1, -1
4792 Xtank_s, TRUE, FALSE,
4793 EL_SPACESHIP_DOWN, -1, -1
4796 Xtank_w, TRUE, FALSE,
4797 EL_SPACESHIP_LEFT, -1, -1
4800 Xtank_gon, FALSE, FALSE,
4801 EL_SPACESHIP_UP, -1, -1
4804 Xtank_goe, FALSE, FALSE,
4805 EL_SPACESHIP_RIGHT, -1, -1
4808 Xtank_gos, FALSE, FALSE,
4809 EL_SPACESHIP_DOWN, -1, -1
4812 Xtank_gow, FALSE, FALSE,
4813 EL_SPACESHIP_LEFT, -1, -1
4816 Ytank_n, FALSE, FALSE,
4817 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4820 Ytank_nB, FALSE, TRUE,
4821 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4824 Ytank_e, FALSE, FALSE,
4825 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4828 Ytank_eB, FALSE, TRUE,
4829 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4832 Ytank_s, FALSE, FALSE,
4833 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4836 Ytank_sB, FALSE, TRUE,
4837 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4840 Ytank_w, FALSE, FALSE,
4841 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4844 Ytank_wB, FALSE, TRUE,
4845 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4848 Ytank_w_n, FALSE, FALSE,
4849 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4852 Ytank_n_e, FALSE, FALSE,
4853 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4856 Ytank_e_s, FALSE, FALSE,
4857 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4860 Ytank_s_w, FALSE, FALSE,
4861 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4864 Ytank_e_n, FALSE, FALSE,
4865 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4868 Ytank_s_e, FALSE, FALSE,
4869 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4872 Ytank_w_s, FALSE, FALSE,
4873 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4876 Ytank_n_w, FALSE, FALSE,
4877 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4880 Ytank_stone, FALSE, FALSE,
4881 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4884 Ytank_spring, FALSE, FALSE,
4885 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4888 Xandroid, TRUE, FALSE,
4889 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4892 Xandroid_1_n, FALSE, FALSE,
4893 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4896 Xandroid_2_n, FALSE, FALSE,
4897 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4900 Xandroid_1_e, FALSE, FALSE,
4901 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4904 Xandroid_2_e, FALSE, FALSE,
4905 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4908 Xandroid_1_w, FALSE, FALSE,
4909 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4912 Xandroid_2_w, FALSE, FALSE,
4913 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4916 Xandroid_1_s, FALSE, FALSE,
4917 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4920 Xandroid_2_s, FALSE, FALSE,
4921 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4924 Yandroid_n, FALSE, FALSE,
4925 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4928 Yandroid_nB, FALSE, TRUE,
4929 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4932 Yandroid_ne, FALSE, FALSE,
4933 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4936 Yandroid_neB, FALSE, TRUE,
4937 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4940 Yandroid_e, FALSE, FALSE,
4941 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4944 Yandroid_eB, FALSE, TRUE,
4945 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4948 Yandroid_se, FALSE, FALSE,
4949 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4952 Yandroid_seB, FALSE, TRUE,
4953 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4956 Yandroid_s, FALSE, FALSE,
4957 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4960 Yandroid_sB, FALSE, TRUE,
4961 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4964 Yandroid_sw, FALSE, FALSE,
4965 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
4968 Yandroid_swB, FALSE, TRUE,
4969 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
4972 Yandroid_w, FALSE, FALSE,
4973 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
4976 Yandroid_wB, FALSE, TRUE,
4977 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
4980 Yandroid_nw, FALSE, FALSE,
4981 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
4984 Yandroid_nwB, FALSE, TRUE,
4985 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
4988 Xspring, TRUE, FALSE,
4992 Xspring_pause, FALSE, FALSE,
4996 Xspring_e, FALSE, FALSE,
5000 Xspring_w, FALSE, FALSE,
5004 Xspring_fall, FALSE, FALSE,
5008 Yspring_s, FALSE, FALSE,
5009 EL_SPRING, ACTION_FALLING, -1
5012 Yspring_sB, FALSE, TRUE,
5013 EL_SPRING, ACTION_FALLING, -1
5016 Yspring_e, FALSE, FALSE,
5017 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5020 Yspring_eB, FALSE, TRUE,
5021 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5024 Yspring_w, FALSE, FALSE,
5025 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5028 Yspring_wB, FALSE, TRUE,
5029 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5032 Yspring_kill_e, FALSE, FALSE,
5033 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5036 Yspring_kill_eB, FALSE, TRUE,
5037 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5040 Yspring_kill_w, FALSE, FALSE,
5041 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5044 Yspring_kill_wB, FALSE, TRUE,
5045 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5048 Xeater_n, TRUE, FALSE,
5049 EL_YAMYAM_UP, -1, -1
5052 Xeater_e, TRUE, FALSE,
5053 EL_YAMYAM_RIGHT, -1, -1
5056 Xeater_w, TRUE, FALSE,
5057 EL_YAMYAM_LEFT, -1, -1
5060 Xeater_s, TRUE, FALSE,
5061 EL_YAMYAM_DOWN, -1, -1
5064 Yeater_n, FALSE, FALSE,
5065 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5068 Yeater_nB, FALSE, TRUE,
5069 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5072 Yeater_e, FALSE, FALSE,
5073 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5076 Yeater_eB, FALSE, TRUE,
5077 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5080 Yeater_s, FALSE, FALSE,
5081 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5084 Yeater_sB, FALSE, TRUE,
5085 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5088 Yeater_w, FALSE, FALSE,
5089 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5092 Yeater_wB, FALSE, TRUE,
5093 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5096 Yeater_stone, FALSE, FALSE,
5097 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5100 Yeater_spring, FALSE, FALSE,
5101 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5104 Xalien, TRUE, FALSE,
5108 Xalien_pause, FALSE, FALSE,
5112 Yalien_n, FALSE, FALSE,
5113 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5116 Yalien_nB, FALSE, TRUE,
5117 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5120 Yalien_e, FALSE, FALSE,
5121 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5124 Yalien_eB, FALSE, TRUE,
5125 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5128 Yalien_s, FALSE, FALSE,
5129 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5132 Yalien_sB, FALSE, TRUE,
5133 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5136 Yalien_w, FALSE, FALSE,
5137 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5140 Yalien_wB, FALSE, TRUE,
5141 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5144 Yalien_stone, FALSE, FALSE,
5145 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5148 Yalien_spring, FALSE, FALSE,
5149 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5152 Xemerald, TRUE, FALSE,
5156 Xemerald_pause, FALSE, FALSE,
5160 Xemerald_fall, FALSE, FALSE,
5164 Xemerald_shine, FALSE, FALSE,
5165 EL_EMERALD, ACTION_TWINKLING, -1
5168 Yemerald_s, FALSE, FALSE,
5169 EL_EMERALD, ACTION_FALLING, -1
5172 Yemerald_sB, FALSE, TRUE,
5173 EL_EMERALD, ACTION_FALLING, -1
5176 Yemerald_e, FALSE, FALSE,
5177 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5180 Yemerald_eB, FALSE, TRUE,
5181 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5184 Yemerald_w, FALSE, FALSE,
5185 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5188 Yemerald_wB, FALSE, TRUE,
5189 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5192 Yemerald_eat, FALSE, FALSE,
5193 EL_EMERALD, ACTION_COLLECTING, -1
5196 Yemerald_stone, FALSE, FALSE,
5197 EL_NUT, ACTION_BREAKING, -1
5200 Xdiamond, TRUE, FALSE,
5204 Xdiamond_pause, FALSE, FALSE,
5208 Xdiamond_fall, FALSE, FALSE,
5212 Xdiamond_shine, FALSE, FALSE,
5213 EL_DIAMOND, ACTION_TWINKLING, -1
5216 Ydiamond_s, FALSE, FALSE,
5217 EL_DIAMOND, ACTION_FALLING, -1
5220 Ydiamond_sB, FALSE, TRUE,
5221 EL_DIAMOND, ACTION_FALLING, -1
5224 Ydiamond_e, FALSE, FALSE,
5225 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5228 Ydiamond_eB, FALSE, TRUE,
5229 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5232 Ydiamond_w, FALSE, FALSE,
5233 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5236 Ydiamond_wB, FALSE, TRUE,
5237 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5240 Ydiamond_eat, FALSE, FALSE,
5241 EL_DIAMOND, ACTION_COLLECTING, -1
5244 Ydiamond_stone, FALSE, FALSE,
5245 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5248 Xdrip_fall, TRUE, FALSE,
5249 EL_AMOEBA_DROP, -1, -1
5252 Xdrip_stretch, FALSE, FALSE,
5253 EL_AMOEBA_DROP, ACTION_FALLING, -1
5256 Xdrip_stretchB, FALSE, TRUE,
5257 EL_AMOEBA_DROP, ACTION_FALLING, -1
5260 Xdrip_eat, FALSE, FALSE,
5261 EL_AMOEBA_DROP, ACTION_GROWING, -1
5264 Ydrip_s1, FALSE, FALSE,
5265 EL_AMOEBA_DROP, ACTION_FALLING, -1
5268 Ydrip_s1B, FALSE, TRUE,
5269 EL_AMOEBA_DROP, ACTION_FALLING, -1
5272 Ydrip_s2, FALSE, FALSE,
5273 EL_AMOEBA_DROP, ACTION_FALLING, -1
5276 Ydrip_s2B, FALSE, TRUE,
5277 EL_AMOEBA_DROP, ACTION_FALLING, -1
5284 Xbomb_pause, FALSE, FALSE,
5288 Xbomb_fall, FALSE, FALSE,
5292 Ybomb_s, FALSE, FALSE,
5293 EL_BOMB, ACTION_FALLING, -1
5296 Ybomb_sB, FALSE, TRUE,
5297 EL_BOMB, ACTION_FALLING, -1
5300 Ybomb_e, FALSE, FALSE,
5301 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5304 Ybomb_eB, FALSE, TRUE,
5305 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5308 Ybomb_w, FALSE, FALSE,
5309 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5312 Ybomb_wB, FALSE, TRUE,
5313 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5316 Ybomb_eat, FALSE, FALSE,
5317 EL_BOMB, ACTION_ACTIVATING, -1
5320 Xballoon, TRUE, FALSE,
5324 Yballoon_n, FALSE, FALSE,
5325 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5328 Yballoon_nB, FALSE, TRUE,
5329 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5332 Yballoon_e, FALSE, FALSE,
5333 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5336 Yballoon_eB, FALSE, TRUE,
5337 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5340 Yballoon_s, FALSE, FALSE,
5341 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5344 Yballoon_sB, FALSE, TRUE,
5345 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5348 Yballoon_w, FALSE, FALSE,
5349 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5352 Yballoon_wB, FALSE, TRUE,
5353 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5356 Xgrass, TRUE, FALSE,
5357 EL_EMC_GRASS, -1, -1
5360 Ygrass_nB, FALSE, FALSE,
5361 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5364 Ygrass_eB, FALSE, FALSE,
5365 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5368 Ygrass_sB, FALSE, FALSE,
5369 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5372 Ygrass_wB, FALSE, FALSE,
5373 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5380 Ydirt_nB, FALSE, FALSE,
5381 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5384 Ydirt_eB, FALSE, FALSE,
5385 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5388 Ydirt_sB, FALSE, FALSE,
5389 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5392 Ydirt_wB, FALSE, FALSE,
5393 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5396 Xacid_ne, TRUE, FALSE,
5397 EL_ACID_POOL_TOPRIGHT, -1, -1
5400 Xacid_se, TRUE, FALSE,
5401 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5404 Xacid_s, TRUE, FALSE,
5405 EL_ACID_POOL_BOTTOM, -1, -1
5408 Xacid_sw, TRUE, FALSE,
5409 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5412 Xacid_nw, TRUE, FALSE,
5413 EL_ACID_POOL_TOPLEFT, -1, -1
5416 Xacid_1, TRUE, FALSE,
5420 Xacid_2, FALSE, FALSE,
5424 Xacid_3, FALSE, FALSE,
5428 Xacid_4, FALSE, FALSE,
5432 Xacid_5, FALSE, FALSE,
5436 Xacid_6, FALSE, FALSE,
5440 Xacid_7, FALSE, FALSE,
5444 Xacid_8, FALSE, FALSE,
5448 Xball_1, TRUE, FALSE,
5449 EL_EMC_MAGIC_BALL, -1, -1
5452 Xball_1B, FALSE, FALSE,
5453 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5456 Xball_2, FALSE, FALSE,
5457 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5460 Xball_2B, FALSE, FALSE,
5461 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5464 Yball_eat, FALSE, FALSE,
5465 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5468 Ykey_1_eat, FALSE, FALSE,
5469 EL_EM_KEY_1, ACTION_COLLECTING, -1
5472 Ykey_2_eat, FALSE, FALSE,
5473 EL_EM_KEY_2, ACTION_COLLECTING, -1
5476 Ykey_3_eat, FALSE, FALSE,
5477 EL_EM_KEY_3, ACTION_COLLECTING, -1
5480 Ykey_4_eat, FALSE, FALSE,
5481 EL_EM_KEY_4, ACTION_COLLECTING, -1
5484 Ykey_5_eat, FALSE, FALSE,
5485 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5488 Ykey_6_eat, FALSE, FALSE,
5489 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5492 Ykey_7_eat, FALSE, FALSE,
5493 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5496 Ykey_8_eat, FALSE, FALSE,
5497 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5500 Ylenses_eat, FALSE, FALSE,
5501 EL_EMC_LENSES, ACTION_COLLECTING, -1
5504 Ymagnify_eat, FALSE, FALSE,
5505 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5508 Ygrass_eat, FALSE, FALSE,
5509 EL_EMC_GRASS, ACTION_SNAPPING, -1
5512 Ydirt_eat, FALSE, FALSE,
5513 EL_SAND, ACTION_SNAPPING, -1
5516 Xgrow_ns, TRUE, FALSE,
5517 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5520 Ygrow_ns_eat, FALSE, FALSE,
5521 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5524 Xgrow_ew, TRUE, FALSE,
5525 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5528 Ygrow_ew_eat, FALSE, FALSE,
5529 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5532 Xwonderwall, TRUE, FALSE,
5533 EL_MAGIC_WALL, -1, -1
5536 XwonderwallB, FALSE, FALSE,
5537 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5540 Xamoeba_1, TRUE, FALSE,
5541 EL_AMOEBA_DRY, ACTION_OTHER, -1
5544 Xamoeba_2, FALSE, FALSE,
5545 EL_AMOEBA_DRY, ACTION_OTHER, -1
5548 Xamoeba_3, FALSE, FALSE,
5549 EL_AMOEBA_DRY, ACTION_OTHER, -1
5552 Xamoeba_4, FALSE, FALSE,
5553 EL_AMOEBA_DRY, ACTION_OTHER, -1
5556 Xamoeba_5, TRUE, FALSE,
5557 EL_AMOEBA_WET, ACTION_OTHER, -1
5560 Xamoeba_6, FALSE, FALSE,
5561 EL_AMOEBA_WET, ACTION_OTHER, -1
5564 Xamoeba_7, FALSE, FALSE,
5565 EL_AMOEBA_WET, ACTION_OTHER, -1
5568 Xamoeba_8, FALSE, FALSE,
5569 EL_AMOEBA_WET, ACTION_OTHER, -1
5572 Xdoor_1, TRUE, FALSE,
5573 EL_EM_GATE_1, -1, -1
5576 Xdoor_2, TRUE, FALSE,
5577 EL_EM_GATE_2, -1, -1
5580 Xdoor_3, TRUE, FALSE,
5581 EL_EM_GATE_3, -1, -1
5584 Xdoor_4, TRUE, FALSE,
5585 EL_EM_GATE_4, -1, -1
5588 Xdoor_5, TRUE, FALSE,
5589 EL_EMC_GATE_5, -1, -1
5592 Xdoor_6, TRUE, FALSE,
5593 EL_EMC_GATE_6, -1, -1
5596 Xdoor_7, TRUE, FALSE,
5597 EL_EMC_GATE_7, -1, -1
5600 Xdoor_8, TRUE, FALSE,
5601 EL_EMC_GATE_8, -1, -1
5604 Xkey_1, TRUE, FALSE,
5608 Xkey_2, TRUE, FALSE,
5612 Xkey_3, TRUE, FALSE,
5616 Xkey_4, TRUE, FALSE,
5620 Xkey_5, TRUE, FALSE,
5621 EL_EMC_KEY_5, -1, -1
5624 Xkey_6, TRUE, FALSE,
5625 EL_EMC_KEY_6, -1, -1
5628 Xkey_7, TRUE, FALSE,
5629 EL_EMC_KEY_7, -1, -1
5632 Xkey_8, TRUE, FALSE,
5633 EL_EMC_KEY_8, -1, -1
5636 Xwind_n, TRUE, FALSE,
5637 EL_BALLOON_SWITCH_UP, -1, -1
5640 Xwind_e, TRUE, FALSE,
5641 EL_BALLOON_SWITCH_RIGHT, -1, -1
5644 Xwind_s, TRUE, FALSE,
5645 EL_BALLOON_SWITCH_DOWN, -1, -1
5648 Xwind_w, TRUE, FALSE,
5649 EL_BALLOON_SWITCH_LEFT, -1, -1
5652 Xwind_nesw, TRUE, FALSE,
5653 EL_BALLOON_SWITCH_ANY, -1, -1
5656 Xwind_stop, TRUE, FALSE,
5657 EL_BALLOON_SWITCH_NONE, -1, -1
5661 EL_EM_EXIT_CLOSED, -1, -1
5664 Xexit_1, TRUE, FALSE,
5665 EL_EM_EXIT_OPEN, -1, -1
5668 Xexit_2, FALSE, FALSE,
5669 EL_EM_EXIT_OPEN, -1, -1
5672 Xexit_3, FALSE, FALSE,
5673 EL_EM_EXIT_OPEN, -1, -1
5676 Xdynamite, TRUE, FALSE,
5677 EL_EM_DYNAMITE, -1, -1
5680 Ydynamite_eat, FALSE, FALSE,
5681 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5684 Xdynamite_1, TRUE, FALSE,
5685 EL_EM_DYNAMITE_ACTIVE, -1, -1
5688 Xdynamite_2, FALSE, FALSE,
5689 EL_EM_DYNAMITE_ACTIVE, -1, -1
5692 Xdynamite_3, FALSE, FALSE,
5693 EL_EM_DYNAMITE_ACTIVE, -1, -1
5696 Xdynamite_4, FALSE, FALSE,
5697 EL_EM_DYNAMITE_ACTIVE, -1, -1
5700 Xbumper, TRUE, FALSE,
5701 EL_EMC_SPRING_BUMPER, -1, -1
5704 XbumperB, FALSE, FALSE,
5705 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5708 Xwheel, TRUE, FALSE,
5709 EL_ROBOT_WHEEL, -1, -1
5712 XwheelB, FALSE, FALSE,
5713 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5716 Xswitch, TRUE, FALSE,
5717 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5720 XswitchB, FALSE, FALSE,
5721 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5725 EL_QUICKSAND_EMPTY, -1, -1
5728 Xsand_stone, TRUE, FALSE,
5729 EL_QUICKSAND_FULL, -1, -1
5732 Xsand_stonein_1, FALSE, TRUE,
5733 EL_ROCK, ACTION_FILLING, -1
5736 Xsand_stonein_2, FALSE, TRUE,
5737 EL_ROCK, ACTION_FILLING, -1
5740 Xsand_stonein_3, FALSE, TRUE,
5741 EL_ROCK, ACTION_FILLING, -1
5744 Xsand_stonein_4, FALSE, TRUE,
5745 EL_ROCK, ACTION_FILLING, -1
5748 Xsand_stonesand_1, FALSE, FALSE,
5749 EL_QUICKSAND_EMPTYING, -1, -1
5752 Xsand_stonesand_2, FALSE, FALSE,
5753 EL_QUICKSAND_EMPTYING, -1, -1
5756 Xsand_stonesand_3, FALSE, FALSE,
5757 EL_QUICKSAND_EMPTYING, -1, -1
5760 Xsand_stonesand_4, FALSE, FALSE,
5761 EL_QUICKSAND_EMPTYING, -1, -1
5764 Xsand_stonesand_quickout_1, FALSE, FALSE,
5765 EL_QUICKSAND_EMPTYING, -1, -1
5768 Xsand_stonesand_quickout_2, FALSE, FALSE,
5769 EL_QUICKSAND_EMPTYING, -1, -1
5772 Xsand_stoneout_1, FALSE, FALSE,
5773 EL_ROCK, ACTION_EMPTYING, -1
5776 Xsand_stoneout_2, FALSE, FALSE,
5777 EL_ROCK, ACTION_EMPTYING, -1
5780 Xsand_sandstone_1, FALSE, FALSE,
5781 EL_QUICKSAND_FILLING, -1, -1
5784 Xsand_sandstone_2, FALSE, FALSE,
5785 EL_QUICKSAND_FILLING, -1, -1
5788 Xsand_sandstone_3, FALSE, FALSE,
5789 EL_QUICKSAND_FILLING, -1, -1
5792 Xsand_sandstone_4, FALSE, FALSE,
5793 EL_QUICKSAND_FILLING, -1, -1
5796 Xplant, TRUE, FALSE,
5797 EL_EMC_PLANT, -1, -1
5800 Yplant, FALSE, FALSE,
5801 EL_EMC_PLANT, -1, -1
5804 Xlenses, TRUE, FALSE,
5805 EL_EMC_LENSES, -1, -1
5808 Xmagnify, TRUE, FALSE,
5809 EL_EMC_MAGNIFIER, -1, -1
5812 Xdripper, TRUE, FALSE,
5813 EL_EMC_DRIPPER, -1, -1
5816 XdripperB, FALSE, FALSE,
5817 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5820 Xfake_blank, TRUE, FALSE,
5821 EL_INVISIBLE_WALL, -1, -1
5824 Xfake_blankB, FALSE, FALSE,
5825 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5828 Xfake_grass, TRUE, FALSE,
5829 EL_EMC_FAKE_GRASS, -1, -1
5832 Xfake_grassB, FALSE, FALSE,
5833 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5836 Xfake_door_1, TRUE, FALSE,
5837 EL_EM_GATE_1_GRAY, -1, -1
5840 Xfake_door_2, TRUE, FALSE,
5841 EL_EM_GATE_2_GRAY, -1, -1
5844 Xfake_door_3, TRUE, FALSE,
5845 EL_EM_GATE_3_GRAY, -1, -1
5848 Xfake_door_4, TRUE, FALSE,
5849 EL_EM_GATE_4_GRAY, -1, -1
5852 Xfake_door_5, TRUE, FALSE,
5853 EL_EMC_GATE_5_GRAY, -1, -1
5856 Xfake_door_6, TRUE, FALSE,
5857 EL_EMC_GATE_6_GRAY, -1, -1
5860 Xfake_door_7, TRUE, FALSE,
5861 EL_EMC_GATE_7_GRAY, -1, -1
5864 Xfake_door_8, TRUE, FALSE,
5865 EL_EMC_GATE_8_GRAY, -1, -1
5868 Xfake_acid_1, TRUE, FALSE,
5869 EL_EMC_FAKE_ACID, -1, -1
5872 Xfake_acid_2, FALSE, FALSE,
5873 EL_EMC_FAKE_ACID, -1, -1
5876 Xfake_acid_3, FALSE, FALSE,
5877 EL_EMC_FAKE_ACID, -1, -1
5880 Xfake_acid_4, FALSE, FALSE,
5881 EL_EMC_FAKE_ACID, -1, -1
5884 Xfake_acid_5, FALSE, FALSE,
5885 EL_EMC_FAKE_ACID, -1, -1
5888 Xfake_acid_6, FALSE, FALSE,
5889 EL_EMC_FAKE_ACID, -1, -1
5892 Xfake_acid_7, FALSE, FALSE,
5893 EL_EMC_FAKE_ACID, -1, -1
5896 Xfake_acid_8, FALSE, FALSE,
5897 EL_EMC_FAKE_ACID, -1, -1
5900 Xsteel_1, TRUE, FALSE,
5901 EL_STEELWALL, -1, -1
5904 Xsteel_2, TRUE, FALSE,
5905 EL_EMC_STEELWALL_2, -1, -1
5908 Xsteel_3, TRUE, FALSE,
5909 EL_EMC_STEELWALL_3, -1, -1
5912 Xsteel_4, TRUE, FALSE,
5913 EL_EMC_STEELWALL_4, -1, -1
5916 Xwall_1, TRUE, FALSE,
5920 Xwall_2, TRUE, FALSE,
5921 EL_EMC_WALL_14, -1, -1
5924 Xwall_3, TRUE, FALSE,
5925 EL_EMC_WALL_15, -1, -1
5928 Xwall_4, TRUE, FALSE,
5929 EL_EMC_WALL_16, -1, -1
5932 Xround_wall_1, TRUE, FALSE,
5933 EL_WALL_SLIPPERY, -1, -1
5936 Xround_wall_2, TRUE, FALSE,
5937 EL_EMC_WALL_SLIPPERY_2, -1, -1
5940 Xround_wall_3, TRUE, FALSE,
5941 EL_EMC_WALL_SLIPPERY_3, -1, -1
5944 Xround_wall_4, TRUE, FALSE,
5945 EL_EMC_WALL_SLIPPERY_4, -1, -1
5948 Xdecor_1, TRUE, FALSE,
5949 EL_EMC_WALL_8, -1, -1
5952 Xdecor_2, TRUE, FALSE,
5953 EL_EMC_WALL_6, -1, -1
5956 Xdecor_3, TRUE, FALSE,
5957 EL_EMC_WALL_4, -1, -1
5960 Xdecor_4, TRUE, FALSE,
5961 EL_EMC_WALL_7, -1, -1
5964 Xdecor_5, TRUE, FALSE,
5965 EL_EMC_WALL_5, -1, -1
5968 Xdecor_6, TRUE, FALSE,
5969 EL_EMC_WALL_9, -1, -1
5972 Xdecor_7, TRUE, FALSE,
5973 EL_EMC_WALL_10, -1, -1
5976 Xdecor_8, TRUE, FALSE,
5977 EL_EMC_WALL_1, -1, -1
5980 Xdecor_9, TRUE, FALSE,
5981 EL_EMC_WALL_2, -1, -1
5984 Xdecor_10, TRUE, FALSE,
5985 EL_EMC_WALL_3, -1, -1
5988 Xdecor_11, TRUE, FALSE,
5989 EL_EMC_WALL_11, -1, -1
5992 Xdecor_12, TRUE, FALSE,
5993 EL_EMC_WALL_12, -1, -1
5996 Xalpha_0, TRUE, FALSE,
5997 EL_CHAR('0'), -1, -1
6000 Xalpha_1, TRUE, FALSE,
6001 EL_CHAR('1'), -1, -1
6004 Xalpha_2, TRUE, FALSE,
6005 EL_CHAR('2'), -1, -1
6008 Xalpha_3, TRUE, FALSE,
6009 EL_CHAR('3'), -1, -1
6012 Xalpha_4, TRUE, FALSE,
6013 EL_CHAR('4'), -1, -1
6016 Xalpha_5, TRUE, FALSE,
6017 EL_CHAR('5'), -1, -1
6020 Xalpha_6, TRUE, FALSE,
6021 EL_CHAR('6'), -1, -1
6024 Xalpha_7, TRUE, FALSE,
6025 EL_CHAR('7'), -1, -1
6028 Xalpha_8, TRUE, FALSE,
6029 EL_CHAR('8'), -1, -1
6032 Xalpha_9, TRUE, FALSE,
6033 EL_CHAR('9'), -1, -1
6036 Xalpha_excla, TRUE, FALSE,
6037 EL_CHAR('!'), -1, -1
6040 Xalpha_quote, TRUE, FALSE,
6041 EL_CHAR('"'), -1, -1
6044 Xalpha_comma, TRUE, FALSE,
6045 EL_CHAR(','), -1, -1
6048 Xalpha_minus, TRUE, FALSE,
6049 EL_CHAR('-'), -1, -1
6052 Xalpha_perio, TRUE, FALSE,
6053 EL_CHAR('.'), -1, -1
6056 Xalpha_colon, TRUE, FALSE,
6057 EL_CHAR(':'), -1, -1
6060 Xalpha_quest, TRUE, FALSE,
6061 EL_CHAR('?'), -1, -1
6064 Xalpha_a, TRUE, FALSE,
6065 EL_CHAR('A'), -1, -1
6068 Xalpha_b, TRUE, FALSE,
6069 EL_CHAR('B'), -1, -1
6072 Xalpha_c, TRUE, FALSE,
6073 EL_CHAR('C'), -1, -1
6076 Xalpha_d, TRUE, FALSE,
6077 EL_CHAR('D'), -1, -1
6080 Xalpha_e, TRUE, FALSE,
6081 EL_CHAR('E'), -1, -1
6084 Xalpha_f, TRUE, FALSE,
6085 EL_CHAR('F'), -1, -1
6088 Xalpha_g, TRUE, FALSE,
6089 EL_CHAR('G'), -1, -1
6092 Xalpha_h, TRUE, FALSE,
6093 EL_CHAR('H'), -1, -1
6096 Xalpha_i, TRUE, FALSE,
6097 EL_CHAR('I'), -1, -1
6100 Xalpha_j, TRUE, FALSE,
6101 EL_CHAR('J'), -1, -1
6104 Xalpha_k, TRUE, FALSE,
6105 EL_CHAR('K'), -1, -1
6108 Xalpha_l, TRUE, FALSE,
6109 EL_CHAR('L'), -1, -1
6112 Xalpha_m, TRUE, FALSE,
6113 EL_CHAR('M'), -1, -1
6116 Xalpha_n, TRUE, FALSE,
6117 EL_CHAR('N'), -1, -1
6120 Xalpha_o, TRUE, FALSE,
6121 EL_CHAR('O'), -1, -1
6124 Xalpha_p, TRUE, FALSE,
6125 EL_CHAR('P'), -1, -1
6128 Xalpha_q, TRUE, FALSE,
6129 EL_CHAR('Q'), -1, -1
6132 Xalpha_r, TRUE, FALSE,
6133 EL_CHAR('R'), -1, -1
6136 Xalpha_s, TRUE, FALSE,
6137 EL_CHAR('S'), -1, -1
6140 Xalpha_t, TRUE, FALSE,
6141 EL_CHAR('T'), -1, -1
6144 Xalpha_u, TRUE, FALSE,
6145 EL_CHAR('U'), -1, -1
6148 Xalpha_v, TRUE, FALSE,
6149 EL_CHAR('V'), -1, -1
6152 Xalpha_w, TRUE, FALSE,
6153 EL_CHAR('W'), -1, -1
6156 Xalpha_x, TRUE, FALSE,
6157 EL_CHAR('X'), -1, -1
6160 Xalpha_y, TRUE, FALSE,
6161 EL_CHAR('Y'), -1, -1
6164 Xalpha_z, TRUE, FALSE,
6165 EL_CHAR('Z'), -1, -1
6168 Xalpha_arrow_e, TRUE, FALSE,
6169 EL_CHAR('>'), -1, -1
6172 Xalpha_arrow_w, TRUE, FALSE,
6173 EL_CHAR('<'), -1, -1
6176 Xalpha_copyr, TRUE, FALSE,
6177 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6181 Xboom_bug, FALSE, FALSE,
6182 EL_BUG, ACTION_EXPLODING, -1
6185 Xboom_bomb, FALSE, FALSE,
6186 EL_BOMB, ACTION_EXPLODING, -1
6189 Xboom_android, FALSE, FALSE,
6190 EL_EMC_ANDROID, ACTION_OTHER, -1
6193 Xboom_1, FALSE, FALSE,
6194 EL_DEFAULT, ACTION_EXPLODING, -1
6197 Xboom_2, FALSE, FALSE,
6198 EL_DEFAULT, ACTION_EXPLODING, -1
6201 Znormal, FALSE, FALSE,
6205 Zdynamite, FALSE, FALSE,
6209 Zplayer, FALSE, FALSE,
6213 ZBORDER, FALSE, FALSE,
6223 static struct Mapping_EM_to_RND_player
6232 em_player_mapping_list[] =
6236 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6240 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6244 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6248 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6252 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6256 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6260 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6264 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6268 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6272 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6276 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6280 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6284 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6288 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6292 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6296 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6300 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6304 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6308 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6312 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6316 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6320 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6324 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6328 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6332 EL_PLAYER_1, ACTION_DEFAULT, -1,
6336 EL_PLAYER_2, ACTION_DEFAULT, -1,
6340 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6344 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6348 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6352 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6356 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6360 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6364 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6368 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6372 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6376 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6380 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6384 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6388 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6392 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6396 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6400 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6404 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6408 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6412 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6416 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6420 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6424 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6428 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6432 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6436 EL_PLAYER_3, ACTION_DEFAULT, -1,
6440 EL_PLAYER_4, ACTION_DEFAULT, -1,
6449 int map_element_RND_to_EM(int element_rnd)
6451 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6452 static boolean mapping_initialized = FALSE;
6454 if (!mapping_initialized)
6458 /* return "Xalpha_quest" for all undefined elements in mapping array */
6459 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6460 mapping_RND_to_EM[i] = Xalpha_quest;
6462 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6463 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6464 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6465 em_object_mapping_list[i].element_em;
6467 mapping_initialized = TRUE;
6470 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6471 return mapping_RND_to_EM[element_rnd];
6473 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6478 int map_element_EM_to_RND(int element_em)
6480 static unsigned short mapping_EM_to_RND[TILE_MAX];
6481 static boolean mapping_initialized = FALSE;
6483 if (!mapping_initialized)
6487 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6488 for (i = 0; i < TILE_MAX; i++)
6489 mapping_EM_to_RND[i] = EL_UNKNOWN;
6491 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6492 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6493 em_object_mapping_list[i].element_rnd;
6495 mapping_initialized = TRUE;
6498 if (element_em >= 0 && element_em < TILE_MAX)
6499 return mapping_EM_to_RND[element_em];
6501 Error(ERR_WARN, "invalid EM level element %d", element_em);
6506 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6508 struct LevelInfo_EM *level_em = level->native_em_level;
6509 struct LEVEL *lev = level_em->lev;
6512 for (i = 0; i < TILE_MAX; i++)
6513 lev->android_array[i] = Xblank;
6515 for (i = 0; i < level->num_android_clone_elements; i++)
6517 int element_rnd = level->android_clone_element[i];
6518 int element_em = map_element_RND_to_EM(element_rnd);
6520 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6521 if (em_object_mapping_list[j].element_rnd == element_rnd)
6522 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6526 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6528 struct LevelInfo_EM *level_em = level->native_em_level;
6529 struct LEVEL *lev = level_em->lev;
6532 level->num_android_clone_elements = 0;
6534 for (i = 0; i < TILE_MAX; i++)
6536 int element_em = lev->android_array[i];
6538 boolean element_found = FALSE;
6540 if (element_em == Xblank)
6543 element_rnd = map_element_EM_to_RND(element_em);
6545 for (j = 0; j < level->num_android_clone_elements; j++)
6546 if (level->android_clone_element[j] == element_rnd)
6547 element_found = TRUE;
6551 level->android_clone_element[level->num_android_clone_elements++] =
6554 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6559 if (level->num_android_clone_elements == 0)
6561 level->num_android_clone_elements = 1;
6562 level->android_clone_element[0] = EL_EMPTY;
6566 int map_direction_RND_to_EM(int direction)
6568 return (direction == MV_UP ? 0 :
6569 direction == MV_RIGHT ? 1 :
6570 direction == MV_DOWN ? 2 :
6571 direction == MV_LEFT ? 3 :
6575 int map_direction_EM_to_RND(int direction)
6577 return (direction == 0 ? MV_UP :
6578 direction == 1 ? MV_RIGHT :
6579 direction == 2 ? MV_DOWN :
6580 direction == 3 ? MV_LEFT :
6584 int map_element_RND_to_SP(int element_rnd)
6586 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6588 if (element_rnd >= EL_SP_START &&
6589 element_rnd <= EL_SP_END)
6590 element_sp = element_rnd - EL_SP_START;
6591 else if (element_rnd == EL_EMPTY_SPACE)
6593 else if (element_rnd == EL_INVISIBLE_WALL)
6599 int map_element_SP_to_RND(int element_sp)
6601 int element_rnd = EL_UNKNOWN;
6603 if (element_sp >= 0x00 &&
6605 element_rnd = EL_SP_START + element_sp;
6606 else if (element_sp == 0x28)
6607 element_rnd = EL_INVISIBLE_WALL;
6612 int map_action_SP_to_RND(int action_sp)
6616 case actActive: return ACTION_ACTIVE;
6617 case actImpact: return ACTION_IMPACT;
6618 case actExploding: return ACTION_EXPLODING;
6619 case actDigging: return ACTION_DIGGING;
6620 case actSnapping: return ACTION_SNAPPING;
6621 case actCollecting: return ACTION_COLLECTING;
6622 case actPassing: return ACTION_PASSING;
6623 case actPushing: return ACTION_PUSHING;
6624 case actDropping: return ACTION_DROPPING;
6626 default: return ACTION_DEFAULT;
6630 int get_next_element(int element)
6634 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6635 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6636 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6637 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6638 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6639 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6640 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6641 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6642 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6643 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6644 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6646 default: return element;
6650 int el_act_dir2img(int element, int action, int direction)
6652 element = GFX_ELEMENT(element);
6653 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6655 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6656 return element_info[element].direction_graphic[action][direction];
6659 static int el_act_dir2crm(int element, int action, int direction)
6661 element = GFX_ELEMENT(element);
6662 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6664 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6665 return element_info[element].direction_crumbled[action][direction];
6668 int el_act2img(int element, int action)
6670 element = GFX_ELEMENT(element);
6672 return element_info[element].graphic[action];
6675 int el_act2crm(int element, int action)
6677 element = GFX_ELEMENT(element);
6679 return element_info[element].crumbled[action];
6682 int el_dir2img(int element, int direction)
6684 element = GFX_ELEMENT(element);
6686 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6689 int el2baseimg(int element)
6691 return element_info[element].graphic[ACTION_DEFAULT];
6694 int el2img(int element)
6696 element = GFX_ELEMENT(element);
6698 return element_info[element].graphic[ACTION_DEFAULT];
6701 int el2edimg(int element)
6703 element = GFX_ELEMENT(element);
6705 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6708 int el2preimg(int element)
6710 element = GFX_ELEMENT(element);
6712 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6715 int el2panelimg(int element)
6717 element = GFX_ELEMENT(element);
6719 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6722 int font2baseimg(int font_nr)
6724 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6727 int getBeltNrFromBeltElement(int element)
6729 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6730 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6731 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6734 int getBeltNrFromBeltActiveElement(int element)
6736 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6737 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6738 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6741 int getBeltNrFromBeltSwitchElement(int element)
6743 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6744 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6745 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6748 int getBeltDirNrFromBeltElement(int element)
6750 static int belt_base_element[4] =
6752 EL_CONVEYOR_BELT_1_LEFT,
6753 EL_CONVEYOR_BELT_2_LEFT,
6754 EL_CONVEYOR_BELT_3_LEFT,
6755 EL_CONVEYOR_BELT_4_LEFT
6758 int belt_nr = getBeltNrFromBeltElement(element);
6759 int belt_dir_nr = element - belt_base_element[belt_nr];
6761 return (belt_dir_nr % 3);
6764 int getBeltDirNrFromBeltSwitchElement(int element)
6766 static int belt_base_element[4] =
6768 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6769 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6770 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6771 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6774 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6775 int belt_dir_nr = element - belt_base_element[belt_nr];
6777 return (belt_dir_nr % 3);
6780 int getBeltDirFromBeltElement(int element)
6782 static int belt_move_dir[3] =
6789 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6791 return belt_move_dir[belt_dir_nr];
6794 int getBeltDirFromBeltSwitchElement(int element)
6796 static int belt_move_dir[3] =
6803 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6805 return belt_move_dir[belt_dir_nr];
6808 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6810 static int belt_base_element[4] =
6812 EL_CONVEYOR_BELT_1_LEFT,
6813 EL_CONVEYOR_BELT_2_LEFT,
6814 EL_CONVEYOR_BELT_3_LEFT,
6815 EL_CONVEYOR_BELT_4_LEFT
6818 return belt_base_element[belt_nr] + belt_dir_nr;
6821 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6823 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6825 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6828 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6830 static int belt_base_element[4] =
6832 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6833 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6834 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6835 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6838 return belt_base_element[belt_nr] + belt_dir_nr;
6841 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6843 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6845 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6848 boolean getTeamMode_EM()
6850 return game.team_mode;
6853 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6855 int game_frame_delay_value;
6857 game_frame_delay_value =
6858 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6859 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6862 if (tape.playing && tape.warp_forward && !tape.pausing)
6863 game_frame_delay_value = 0;
6865 return game_frame_delay_value;
6868 unsigned int InitRND(int seed)
6870 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6871 return InitEngineRandom_EM(seed);
6872 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6873 return InitEngineRandom_SP(seed);
6875 return InitEngineRandom_RND(seed);
6878 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6879 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6881 inline static int get_effective_element_EM(int tile, int frame_em)
6883 int element = object_mapping[tile].element_rnd;
6884 int action = object_mapping[tile].action;
6885 boolean is_backside = object_mapping[tile].is_backside;
6886 boolean action_removing = (action == ACTION_DIGGING ||
6887 action == ACTION_SNAPPING ||
6888 action == ACTION_COLLECTING);
6894 case Yacid_splash_eB:
6895 case Yacid_splash_wB:
6896 return (frame_em > 5 ? EL_EMPTY : element);
6902 else /* frame_em == 7 */
6906 case Yacid_splash_eB:
6907 case Yacid_splash_wB:
6910 case Yemerald_stone:
6913 case Ydiamond_stone:
6917 case Xdrip_stretchB:
6936 case Xsand_stonein_1:
6937 case Xsand_stonein_2:
6938 case Xsand_stonein_3:
6939 case Xsand_stonein_4:
6943 return (is_backside || action_removing ? EL_EMPTY : element);
6948 inline static boolean check_linear_animation_EM(int tile)
6952 case Xsand_stonesand_1:
6953 case Xsand_stonesand_quickout_1:
6954 case Xsand_sandstone_1:
6955 case Xsand_stonein_1:
6956 case Xsand_stoneout_1:
6975 case Yacid_splash_eB:
6976 case Yacid_splash_wB:
6977 case Yemerald_stone:
6984 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
6985 boolean has_crumbled_graphics,
6986 int crumbled, int sync_frame)
6988 /* if element can be crumbled, but certain action graphics are just empty
6989 space (like instantly snapping sand to empty space in 1 frame), do not
6990 treat these empty space graphics as crumbled graphics in EMC engine */
6991 if (crumbled == IMG_EMPTY_SPACE)
6992 has_crumbled_graphics = FALSE;
6994 if (has_crumbled_graphics)
6996 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
6997 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
6998 g_crumbled->anim_delay,
6999 g_crumbled->anim_mode,
7000 g_crumbled->anim_start_frame,
7003 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7004 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7006 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7008 g_em->has_crumbled_graphics = TRUE;
7012 g_em->crumbled_bitmap = NULL;
7013 g_em->crumbled_src_x = 0;
7014 g_em->crumbled_src_y = 0;
7015 g_em->crumbled_border_size = 0;
7017 g_em->has_crumbled_graphics = FALSE;
7021 void ResetGfxAnimation_EM(int x, int y, int tile)
7026 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7027 int tile, int frame_em, int x, int y)
7029 int action = object_mapping[tile].action;
7030 int direction = object_mapping[tile].direction;
7031 int effective_element = get_effective_element_EM(tile, frame_em);
7032 int graphic = (direction == MV_NONE ?
7033 el_act2img(effective_element, action) :
7034 el_act_dir2img(effective_element, action, direction));
7035 struct GraphicInfo *g = &graphic_info[graphic];
7037 boolean action_removing = (action == ACTION_DIGGING ||
7038 action == ACTION_SNAPPING ||
7039 action == ACTION_COLLECTING);
7040 boolean action_moving = (action == ACTION_FALLING ||
7041 action == ACTION_MOVING ||
7042 action == ACTION_PUSHING ||
7043 action == ACTION_EATING ||
7044 action == ACTION_FILLING ||
7045 action == ACTION_EMPTYING);
7046 boolean action_falling = (action == ACTION_FALLING ||
7047 action == ACTION_FILLING ||
7048 action == ACTION_EMPTYING);
7050 /* special case: graphic uses "2nd movement tile" and has defined
7051 7 frames for movement animation (or less) => use default graphic
7052 for last (8th) frame which ends the movement animation */
7053 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7055 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7056 graphic = (direction == MV_NONE ?
7057 el_act2img(effective_element, action) :
7058 el_act_dir2img(effective_element, action, direction));
7060 g = &graphic_info[graphic];
7063 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7067 else if (action_moving)
7069 boolean is_backside = object_mapping[tile].is_backside;
7073 int direction = object_mapping[tile].direction;
7074 int move_dir = (action_falling ? MV_DOWN : direction);
7079 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7080 if (g->double_movement && frame_em == 0)
7084 if (move_dir == MV_LEFT)
7085 GfxFrame[x - 1][y] = GfxFrame[x][y];
7086 else if (move_dir == MV_RIGHT)
7087 GfxFrame[x + 1][y] = GfxFrame[x][y];
7088 else if (move_dir == MV_UP)
7089 GfxFrame[x][y - 1] = GfxFrame[x][y];
7090 else if (move_dir == MV_DOWN)
7091 GfxFrame[x][y + 1] = GfxFrame[x][y];
7098 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7099 if (tile == Xsand_stonesand_quickout_1 ||
7100 tile == Xsand_stonesand_quickout_2)
7104 if (graphic_info[graphic].anim_global_sync)
7105 sync_frame = FrameCounter;
7106 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7107 sync_frame = GfxFrame[x][y];
7109 sync_frame = 0; /* playfield border (pseudo steel) */
7111 SetRandomAnimationValue(x, y);
7113 int frame = getAnimationFrame(g->anim_frames,
7116 g->anim_start_frame,
7119 g_em->unique_identifier =
7120 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7123 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7124 int tile, int frame_em, int x, int y)
7126 int action = object_mapping[tile].action;
7127 int direction = object_mapping[tile].direction;
7128 boolean is_backside = object_mapping[tile].is_backside;
7129 int effective_element = get_effective_element_EM(tile, frame_em);
7130 int effective_action = action;
7131 int graphic = (direction == MV_NONE ?
7132 el_act2img(effective_element, effective_action) :
7133 el_act_dir2img(effective_element, effective_action,
7135 int crumbled = (direction == MV_NONE ?
7136 el_act2crm(effective_element, effective_action) :
7137 el_act_dir2crm(effective_element, effective_action,
7139 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7140 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7141 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7142 struct GraphicInfo *g = &graphic_info[graphic];
7145 /* special case: graphic uses "2nd movement tile" and has defined
7146 7 frames for movement animation (or less) => use default graphic
7147 for last (8th) frame which ends the movement animation */
7148 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7150 effective_action = ACTION_DEFAULT;
7151 graphic = (direction == MV_NONE ?
7152 el_act2img(effective_element, effective_action) :
7153 el_act_dir2img(effective_element, effective_action,
7155 crumbled = (direction == MV_NONE ?
7156 el_act2crm(effective_element, effective_action) :
7157 el_act_dir2crm(effective_element, effective_action,
7160 g = &graphic_info[graphic];
7163 if (graphic_info[graphic].anim_global_sync)
7164 sync_frame = FrameCounter;
7165 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7166 sync_frame = GfxFrame[x][y];
7168 sync_frame = 0; /* playfield border (pseudo steel) */
7170 SetRandomAnimationValue(x, y);
7172 int frame = getAnimationFrame(g->anim_frames,
7175 g->anim_start_frame,
7178 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7179 g->double_movement && is_backside);
7181 /* (updating the "crumbled" graphic definitions is probably not really needed,
7182 as animations for crumbled graphics can't be longer than one EMC cycle) */
7183 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7187 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7188 int player_nr, int anim, int frame_em)
7190 int element = player_mapping[player_nr][anim].element_rnd;
7191 int action = player_mapping[player_nr][anim].action;
7192 int direction = player_mapping[player_nr][anim].direction;
7193 int graphic = (direction == MV_NONE ?
7194 el_act2img(element, action) :
7195 el_act_dir2img(element, action, direction));
7196 struct GraphicInfo *g = &graphic_info[graphic];
7199 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7201 stored_player[player_nr].StepFrame = frame_em;
7203 sync_frame = stored_player[player_nr].Frame;
7205 int frame = getAnimationFrame(g->anim_frames,
7208 g->anim_start_frame,
7211 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7212 &g_em->src_x, &g_em->src_y, FALSE);
7215 void InitGraphicInfo_EM(void)
7220 int num_em_gfx_errors = 0;
7222 if (graphic_info_em_object[0][0].bitmap == NULL)
7224 /* EM graphics not yet initialized in em_open_all() */
7229 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7232 /* always start with reliable default values */
7233 for (i = 0; i < TILE_MAX; i++)
7235 object_mapping[i].element_rnd = EL_UNKNOWN;
7236 object_mapping[i].is_backside = FALSE;
7237 object_mapping[i].action = ACTION_DEFAULT;
7238 object_mapping[i].direction = MV_NONE;
7241 /* always start with reliable default values */
7242 for (p = 0; p < MAX_PLAYERS; p++)
7244 for (i = 0; i < SPR_MAX; i++)
7246 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7247 player_mapping[p][i].action = ACTION_DEFAULT;
7248 player_mapping[p][i].direction = MV_NONE;
7252 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7254 int e = em_object_mapping_list[i].element_em;
7256 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7257 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7259 if (em_object_mapping_list[i].action != -1)
7260 object_mapping[e].action = em_object_mapping_list[i].action;
7262 if (em_object_mapping_list[i].direction != -1)
7263 object_mapping[e].direction =
7264 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7267 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7269 int a = em_player_mapping_list[i].action_em;
7270 int p = em_player_mapping_list[i].player_nr;
7272 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7274 if (em_player_mapping_list[i].action != -1)
7275 player_mapping[p][a].action = em_player_mapping_list[i].action;
7277 if (em_player_mapping_list[i].direction != -1)
7278 player_mapping[p][a].direction =
7279 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7282 for (i = 0; i < TILE_MAX; i++)
7284 int element = object_mapping[i].element_rnd;
7285 int action = object_mapping[i].action;
7286 int direction = object_mapping[i].direction;
7287 boolean is_backside = object_mapping[i].is_backside;
7288 boolean action_exploding = ((action == ACTION_EXPLODING ||
7289 action == ACTION_SMASHED_BY_ROCK ||
7290 action == ACTION_SMASHED_BY_SPRING) &&
7291 element != EL_DIAMOND);
7292 boolean action_active = (action == ACTION_ACTIVE);
7293 boolean action_other = (action == ACTION_OTHER);
7295 for (j = 0; j < 8; j++)
7297 int effective_element = get_effective_element_EM(i, j);
7298 int effective_action = (j < 7 ? action :
7299 i == Xdrip_stretch ? action :
7300 i == Xdrip_stretchB ? action :
7301 i == Ydrip_s1 ? action :
7302 i == Ydrip_s1B ? action :
7303 i == Xball_1B ? action :
7304 i == Xball_2 ? action :
7305 i == Xball_2B ? action :
7306 i == Yball_eat ? action :
7307 i == Ykey_1_eat ? action :
7308 i == Ykey_2_eat ? action :
7309 i == Ykey_3_eat ? action :
7310 i == Ykey_4_eat ? action :
7311 i == Ykey_5_eat ? action :
7312 i == Ykey_6_eat ? action :
7313 i == Ykey_7_eat ? action :
7314 i == Ykey_8_eat ? action :
7315 i == Ylenses_eat ? action :
7316 i == Ymagnify_eat ? action :
7317 i == Ygrass_eat ? action :
7318 i == Ydirt_eat ? action :
7319 i == Xsand_stonein_1 ? action :
7320 i == Xsand_stonein_2 ? action :
7321 i == Xsand_stonein_3 ? action :
7322 i == Xsand_stonein_4 ? action :
7323 i == Xsand_stoneout_1 ? action :
7324 i == Xsand_stoneout_2 ? action :
7325 i == Xboom_android ? ACTION_EXPLODING :
7326 action_exploding ? ACTION_EXPLODING :
7327 action_active ? action :
7328 action_other ? action :
7330 int graphic = (el_act_dir2img(effective_element, effective_action,
7332 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7334 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7335 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7336 boolean has_action_graphics = (graphic != base_graphic);
7337 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7338 struct GraphicInfo *g = &graphic_info[graphic];
7339 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7342 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7343 boolean special_animation = (action != ACTION_DEFAULT &&
7344 g->anim_frames == 3 &&
7345 g->anim_delay == 2 &&
7346 g->anim_mode & ANIM_LINEAR);
7347 int sync_frame = (i == Xdrip_stretch ? 7 :
7348 i == Xdrip_stretchB ? 7 :
7349 i == Ydrip_s2 ? j + 8 :
7350 i == Ydrip_s2B ? j + 8 :
7359 i == Xfake_acid_1 ? 0 :
7360 i == Xfake_acid_2 ? 10 :
7361 i == Xfake_acid_3 ? 20 :
7362 i == Xfake_acid_4 ? 30 :
7363 i == Xfake_acid_5 ? 40 :
7364 i == Xfake_acid_6 ? 50 :
7365 i == Xfake_acid_7 ? 60 :
7366 i == Xfake_acid_8 ? 70 :
7368 i == Xball_2B ? j + 8 :
7369 i == Yball_eat ? j + 1 :
7370 i == Ykey_1_eat ? j + 1 :
7371 i == Ykey_2_eat ? j + 1 :
7372 i == Ykey_3_eat ? j + 1 :
7373 i == Ykey_4_eat ? j + 1 :
7374 i == Ykey_5_eat ? j + 1 :
7375 i == Ykey_6_eat ? j + 1 :
7376 i == Ykey_7_eat ? j + 1 :
7377 i == Ykey_8_eat ? j + 1 :
7378 i == Ylenses_eat ? j + 1 :
7379 i == Ymagnify_eat ? j + 1 :
7380 i == Ygrass_eat ? j + 1 :
7381 i == Ydirt_eat ? j + 1 :
7382 i == Xamoeba_1 ? 0 :
7383 i == Xamoeba_2 ? 1 :
7384 i == Xamoeba_3 ? 2 :
7385 i == Xamoeba_4 ? 3 :
7386 i == Xamoeba_5 ? 0 :
7387 i == Xamoeba_6 ? 1 :
7388 i == Xamoeba_7 ? 2 :
7389 i == Xamoeba_8 ? 3 :
7390 i == Xexit_2 ? j + 8 :
7391 i == Xexit_3 ? j + 16 :
7392 i == Xdynamite_1 ? 0 :
7393 i == Xdynamite_2 ? 8 :
7394 i == Xdynamite_3 ? 16 :
7395 i == Xdynamite_4 ? 24 :
7396 i == Xsand_stonein_1 ? j + 1 :
7397 i == Xsand_stonein_2 ? j + 9 :
7398 i == Xsand_stonein_3 ? j + 17 :
7399 i == Xsand_stonein_4 ? j + 25 :
7400 i == Xsand_stoneout_1 && j == 0 ? 0 :
7401 i == Xsand_stoneout_1 && j == 1 ? 0 :
7402 i == Xsand_stoneout_1 && j == 2 ? 1 :
7403 i == Xsand_stoneout_1 && j == 3 ? 2 :
7404 i == Xsand_stoneout_1 && j == 4 ? 2 :
7405 i == Xsand_stoneout_1 && j == 5 ? 3 :
7406 i == Xsand_stoneout_1 && j == 6 ? 4 :
7407 i == Xsand_stoneout_1 && j == 7 ? 4 :
7408 i == Xsand_stoneout_2 && j == 0 ? 5 :
7409 i == Xsand_stoneout_2 && j == 1 ? 6 :
7410 i == Xsand_stoneout_2 && j == 2 ? 7 :
7411 i == Xsand_stoneout_2 && j == 3 ? 8 :
7412 i == Xsand_stoneout_2 && j == 4 ? 9 :
7413 i == Xsand_stoneout_2 && j == 5 ? 11 :
7414 i == Xsand_stoneout_2 && j == 6 ? 13 :
7415 i == Xsand_stoneout_2 && j == 7 ? 15 :
7416 i == Xboom_bug && j == 1 ? 2 :
7417 i == Xboom_bug && j == 2 ? 2 :
7418 i == Xboom_bug && j == 3 ? 4 :
7419 i == Xboom_bug && j == 4 ? 4 :
7420 i == Xboom_bug && j == 5 ? 2 :
7421 i == Xboom_bug && j == 6 ? 2 :
7422 i == Xboom_bug && j == 7 ? 0 :
7423 i == Xboom_bomb && j == 1 ? 2 :
7424 i == Xboom_bomb && j == 2 ? 2 :
7425 i == Xboom_bomb && j == 3 ? 4 :
7426 i == Xboom_bomb && j == 4 ? 4 :
7427 i == Xboom_bomb && j == 5 ? 2 :
7428 i == Xboom_bomb && j == 6 ? 2 :
7429 i == Xboom_bomb && j == 7 ? 0 :
7430 i == Xboom_android && j == 7 ? 6 :
7431 i == Xboom_1 && j == 1 ? 2 :
7432 i == Xboom_1 && j == 2 ? 2 :
7433 i == Xboom_1 && j == 3 ? 4 :
7434 i == Xboom_1 && j == 4 ? 4 :
7435 i == Xboom_1 && j == 5 ? 6 :
7436 i == Xboom_1 && j == 6 ? 6 :
7437 i == Xboom_1 && j == 7 ? 8 :
7438 i == Xboom_2 && j == 0 ? 8 :
7439 i == Xboom_2 && j == 1 ? 8 :
7440 i == Xboom_2 && j == 2 ? 10 :
7441 i == Xboom_2 && j == 3 ? 10 :
7442 i == Xboom_2 && j == 4 ? 10 :
7443 i == Xboom_2 && j == 5 ? 12 :
7444 i == Xboom_2 && j == 6 ? 12 :
7445 i == Xboom_2 && j == 7 ? 12 :
7446 special_animation && j == 4 ? 3 :
7447 effective_action != action ? 0 :
7451 Bitmap *debug_bitmap = g_em->bitmap;
7452 int debug_src_x = g_em->src_x;
7453 int debug_src_y = g_em->src_y;
7456 int frame = getAnimationFrame(g->anim_frames,
7459 g->anim_start_frame,
7462 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7463 g->double_movement && is_backside);
7465 g_em->bitmap = src_bitmap;
7466 g_em->src_x = src_x;
7467 g_em->src_y = src_y;
7468 g_em->src_offset_x = 0;
7469 g_em->src_offset_y = 0;
7470 g_em->dst_offset_x = 0;
7471 g_em->dst_offset_y = 0;
7472 g_em->width = TILEX;
7473 g_em->height = TILEY;
7475 g_em->preserve_background = FALSE;
7477 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7480 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7481 effective_action == ACTION_MOVING ||
7482 effective_action == ACTION_PUSHING ||
7483 effective_action == ACTION_EATING)) ||
7484 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7485 effective_action == ACTION_EMPTYING)))
7488 (effective_action == ACTION_FALLING ||
7489 effective_action == ACTION_FILLING ||
7490 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7491 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7492 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7493 int num_steps = (i == Ydrip_s1 ? 16 :
7494 i == Ydrip_s1B ? 16 :
7495 i == Ydrip_s2 ? 16 :
7496 i == Ydrip_s2B ? 16 :
7497 i == Xsand_stonein_1 ? 32 :
7498 i == Xsand_stonein_2 ? 32 :
7499 i == Xsand_stonein_3 ? 32 :
7500 i == Xsand_stonein_4 ? 32 :
7501 i == Xsand_stoneout_1 ? 16 :
7502 i == Xsand_stoneout_2 ? 16 : 8);
7503 int cx = ABS(dx) * (TILEX / num_steps);
7504 int cy = ABS(dy) * (TILEY / num_steps);
7505 int step_frame = (i == Ydrip_s2 ? j + 8 :
7506 i == Ydrip_s2B ? j + 8 :
7507 i == Xsand_stonein_2 ? j + 8 :
7508 i == Xsand_stonein_3 ? j + 16 :
7509 i == Xsand_stonein_4 ? j + 24 :
7510 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7511 int step = (is_backside ? step_frame : num_steps - step_frame);
7513 if (is_backside) /* tile where movement starts */
7515 if (dx < 0 || dy < 0)
7517 g_em->src_offset_x = cx * step;
7518 g_em->src_offset_y = cy * step;
7522 g_em->dst_offset_x = cx * step;
7523 g_em->dst_offset_y = cy * step;
7526 else /* tile where movement ends */
7528 if (dx < 0 || dy < 0)
7530 g_em->dst_offset_x = cx * step;
7531 g_em->dst_offset_y = cy * step;
7535 g_em->src_offset_x = cx * step;
7536 g_em->src_offset_y = cy * step;
7540 g_em->width = TILEX - cx * step;
7541 g_em->height = TILEY - cy * step;
7544 /* create unique graphic identifier to decide if tile must be redrawn */
7545 /* bit 31 - 16 (16 bit): EM style graphic
7546 bit 15 - 12 ( 4 bit): EM style frame
7547 bit 11 - 6 ( 6 bit): graphic width
7548 bit 5 - 0 ( 6 bit): graphic height */
7549 g_em->unique_identifier =
7550 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7554 /* skip check for EMC elements not contained in original EMC artwork */
7555 if (element == EL_EMC_FAKE_ACID)
7558 if (g_em->bitmap != debug_bitmap ||
7559 g_em->src_x != debug_src_x ||
7560 g_em->src_y != debug_src_y ||
7561 g_em->src_offset_x != 0 ||
7562 g_em->src_offset_y != 0 ||
7563 g_em->dst_offset_x != 0 ||
7564 g_em->dst_offset_y != 0 ||
7565 g_em->width != TILEX ||
7566 g_em->height != TILEY)
7568 static int last_i = -1;
7576 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7577 i, element, element_info[element].token_name,
7578 element_action_info[effective_action].suffix, direction);
7580 if (element != effective_element)
7581 printf(" [%d ('%s')]",
7583 element_info[effective_element].token_name);
7587 if (g_em->bitmap != debug_bitmap)
7588 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7589 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7591 if (g_em->src_x != debug_src_x ||
7592 g_em->src_y != debug_src_y)
7593 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7594 j, (is_backside ? 'B' : 'F'),
7595 g_em->src_x, g_em->src_y,
7596 g_em->src_x / 32, g_em->src_y / 32,
7597 debug_src_x, debug_src_y,
7598 debug_src_x / 32, debug_src_y / 32);
7600 if (g_em->src_offset_x != 0 ||
7601 g_em->src_offset_y != 0 ||
7602 g_em->dst_offset_x != 0 ||
7603 g_em->dst_offset_y != 0)
7604 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7606 g_em->src_offset_x, g_em->src_offset_y,
7607 g_em->dst_offset_x, g_em->dst_offset_y);
7609 if (g_em->width != TILEX ||
7610 g_em->height != TILEY)
7611 printf(" %d (%d): size %d,%d should be %d,%d\n",
7613 g_em->width, g_em->height, TILEX, TILEY);
7615 num_em_gfx_errors++;
7622 for (i = 0; i < TILE_MAX; i++)
7624 for (j = 0; j < 8; j++)
7626 int element = object_mapping[i].element_rnd;
7627 int action = object_mapping[i].action;
7628 int direction = object_mapping[i].direction;
7629 boolean is_backside = object_mapping[i].is_backside;
7630 int graphic_action = el_act_dir2img(element, action, direction);
7631 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7633 if ((action == ACTION_SMASHED_BY_ROCK ||
7634 action == ACTION_SMASHED_BY_SPRING ||
7635 action == ACTION_EATING) &&
7636 graphic_action == graphic_default)
7638 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7639 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7640 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7641 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7644 /* no separate animation for "smashed by rock" -- use rock instead */
7645 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7646 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7648 g_em->bitmap = g_xx->bitmap;
7649 g_em->src_x = g_xx->src_x;
7650 g_em->src_y = g_xx->src_y;
7651 g_em->src_offset_x = g_xx->src_offset_x;
7652 g_em->src_offset_y = g_xx->src_offset_y;
7653 g_em->dst_offset_x = g_xx->dst_offset_x;
7654 g_em->dst_offset_y = g_xx->dst_offset_y;
7655 g_em->width = g_xx->width;
7656 g_em->height = g_xx->height;
7657 g_em->unique_identifier = g_xx->unique_identifier;
7660 g_em->preserve_background = TRUE;
7665 for (p = 0; p < MAX_PLAYERS; p++)
7667 for (i = 0; i < SPR_MAX; i++)
7669 int element = player_mapping[p][i].element_rnd;
7670 int action = player_mapping[p][i].action;
7671 int direction = player_mapping[p][i].direction;
7673 for (j = 0; j < 8; j++)
7675 int effective_element = element;
7676 int effective_action = action;
7677 int graphic = (direction == MV_NONE ?
7678 el_act2img(effective_element, effective_action) :
7679 el_act_dir2img(effective_element, effective_action,
7681 struct GraphicInfo *g = &graphic_info[graphic];
7682 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7688 Bitmap *debug_bitmap = g_em->bitmap;
7689 int debug_src_x = g_em->src_x;
7690 int debug_src_y = g_em->src_y;
7693 int frame = getAnimationFrame(g->anim_frames,
7696 g->anim_start_frame,
7699 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7701 g_em->bitmap = src_bitmap;
7702 g_em->src_x = src_x;
7703 g_em->src_y = src_y;
7704 g_em->src_offset_x = 0;
7705 g_em->src_offset_y = 0;
7706 g_em->dst_offset_x = 0;
7707 g_em->dst_offset_y = 0;
7708 g_em->width = TILEX;
7709 g_em->height = TILEY;
7713 /* skip check for EMC elements not contained in original EMC artwork */
7714 if (element == EL_PLAYER_3 ||
7715 element == EL_PLAYER_4)
7718 if (g_em->bitmap != debug_bitmap ||
7719 g_em->src_x != debug_src_x ||
7720 g_em->src_y != debug_src_y)
7722 static int last_i = -1;
7730 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7731 p, i, element, element_info[element].token_name,
7732 element_action_info[effective_action].suffix, direction);
7734 if (element != effective_element)
7735 printf(" [%d ('%s')]",
7737 element_info[effective_element].token_name);
7741 if (g_em->bitmap != debug_bitmap)
7742 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7743 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7745 if (g_em->src_x != debug_src_x ||
7746 g_em->src_y != debug_src_y)
7747 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7749 g_em->src_x, g_em->src_y,
7750 g_em->src_x / 32, g_em->src_y / 32,
7751 debug_src_x, debug_src_y,
7752 debug_src_x / 32, debug_src_y / 32);
7754 num_em_gfx_errors++;
7764 printf("::: [%d errors found]\n", num_em_gfx_errors);
7770 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7771 boolean any_player_moving,
7772 boolean player_is_dropping)
7774 if (tape.single_step && tape.recording && !tape.pausing)
7775 if (frame == 0 && !player_is_dropping)
7776 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7779 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7780 boolean murphy_is_dropping)
7782 if (tape.single_step && tape.recording && !tape.pausing)
7783 if (murphy_is_waiting)
7784 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7787 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7788 int graphic, int sync_frame, int x, int y)
7790 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7792 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7795 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7797 return (IS_NEXT_FRAME(sync_frame, graphic));
7800 int getGraphicInfo_Delay(int graphic)
7802 return graphic_info[graphic].anim_delay;
7805 void PlayMenuSoundExt(int sound)
7807 if (sound == SND_UNDEFINED)
7810 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7811 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7814 if (IS_LOOP_SOUND(sound))
7815 PlaySoundLoop(sound);
7820 void PlayMenuSound()
7822 PlayMenuSoundExt(menu.sound[game_status]);
7825 void PlayMenuSoundStereo(int sound, int stereo_position)
7827 if (sound == SND_UNDEFINED)
7830 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7831 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7834 if (IS_LOOP_SOUND(sound))
7835 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7837 PlaySoundStereo(sound, stereo_position);
7840 void PlayMenuSoundIfLoopExt(int sound)
7842 if (sound == SND_UNDEFINED)
7845 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7846 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7849 if (IS_LOOP_SOUND(sound))
7850 PlaySoundLoop(sound);
7853 void PlayMenuSoundIfLoop()
7855 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7858 void PlayMenuMusicExt(int music)
7860 if (music == MUS_UNDEFINED)
7863 if (!setup.sound_music)
7869 void PlayMenuMusic()
7871 PlayMenuMusicExt(menu.music[game_status]);
7874 void PlaySoundActivating()
7877 PlaySound(SND_MENU_ITEM_ACTIVATING);
7881 void PlaySoundSelecting()
7884 PlaySound(SND_MENU_ITEM_SELECTING);
7888 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7890 boolean change_fullscreen = (setup.fullscreen !=
7891 video.fullscreen_enabled);
7892 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7893 !strEqual(setup.fullscreen_mode,
7894 video.fullscreen_mode_current));
7895 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7896 setup.window_scaling_percent !=
7897 video.window_scaling_percent);
7899 if (change_window_scaling_percent && video.fullscreen_enabled)
7902 if (!change_window_scaling_percent && !video.fullscreen_available)
7905 #if defined(TARGET_SDL2)
7906 if (change_window_scaling_percent)
7908 SDLSetWindowScaling(setup.window_scaling_percent);
7912 else if (change_fullscreen)
7914 SDLSetWindowFullscreen(setup.fullscreen);
7916 /* set setup value according to successfully changed fullscreen mode */
7917 setup.fullscreen = video.fullscreen_enabled;
7923 if (change_fullscreen ||
7924 change_fullscreen_mode ||
7925 change_window_scaling_percent)
7927 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
7929 /* save backbuffer content which gets lost when toggling fullscreen mode */
7930 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7932 if (change_fullscreen_mode)
7934 /* keep fullscreen, but change fullscreen mode (screen resolution) */
7935 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
7938 if (change_window_scaling_percent)
7940 /* keep window mode, but change window scaling */
7941 video.fullscreen_enabled = TRUE; /* force new window scaling */
7944 /* toggle fullscreen */
7945 ChangeVideoModeIfNeeded(setup.fullscreen);
7947 /* set setup value according to successfully changed fullscreen mode */
7948 setup.fullscreen = video.fullscreen_enabled;
7950 /* restore backbuffer content from temporary backbuffer backup bitmap */
7951 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7953 FreeBitmap(tmp_backbuffer);
7955 /* update visible window/screen */
7956 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7960 void ChangeViewportPropertiesIfNeeded()
7962 int gfx_game_mode = game_status;
7963 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
7965 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
7966 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
7967 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
7968 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
7969 int border_size = vp_playfield->border_size;
7970 int new_sx = vp_playfield->x + border_size;
7971 int new_sy = vp_playfield->y + border_size;
7972 int new_sxsize = vp_playfield->width - 2 * border_size;
7973 int new_sysize = vp_playfield->height - 2 * border_size;
7974 int new_real_sx = vp_playfield->x;
7975 int new_real_sy = vp_playfield->y;
7976 int new_full_sxsize = vp_playfield->width;
7977 int new_full_sysize = vp_playfield->height;
7978 int new_dx = vp_door_1->x;
7979 int new_dy = vp_door_1->y;
7980 int new_dxsize = vp_door_1->width;
7981 int new_dysize = vp_door_1->height;
7982 int new_vx = vp_door_2->x;
7983 int new_vy = vp_door_2->y;
7984 int new_vxsize = vp_door_2->width;
7985 int new_vysize = vp_door_2->height;
7986 int new_ex = vp_door_3->x;
7987 int new_ey = vp_door_3->y;
7988 int new_exsize = vp_door_3->width;
7989 int new_eysize = vp_door_3->height;
7990 int new_tilesize_var =
7991 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
7993 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
7994 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
7995 int new_scr_fieldx = new_sxsize / tilesize;
7996 int new_scr_fieldy = new_sysize / tilesize;
7997 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
7998 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
7999 boolean init_gfx_buffers = FALSE;
8000 boolean init_video_buffer = FALSE;
8001 boolean init_gadgets_and_toons = FALSE;
8002 boolean init_em_graphics = FALSE;
8004 if (viewport.window.width != WIN_XSIZE ||
8005 viewport.window.height != WIN_YSIZE)
8007 WIN_XSIZE = viewport.window.width;
8008 WIN_YSIZE = viewport.window.height;
8010 init_video_buffer = TRUE;
8011 init_gfx_buffers = TRUE;
8013 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8016 if (new_scr_fieldx != SCR_FIELDX ||
8017 new_scr_fieldy != SCR_FIELDY)
8019 /* this always toggles between MAIN and GAME when using small tile size */
8021 SCR_FIELDX = new_scr_fieldx;
8022 SCR_FIELDY = new_scr_fieldy;
8024 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8035 new_sxsize != SXSIZE ||
8036 new_sysize != SYSIZE ||
8037 new_dxsize != DXSIZE ||
8038 new_dysize != DYSIZE ||
8039 new_vxsize != VXSIZE ||
8040 new_vysize != VYSIZE ||
8041 new_exsize != EXSIZE ||
8042 new_eysize != EYSIZE ||
8043 new_real_sx != REAL_SX ||
8044 new_real_sy != REAL_SY ||
8045 new_full_sxsize != FULL_SXSIZE ||
8046 new_full_sysize != FULL_SYSIZE ||
8047 new_tilesize_var != TILESIZE_VAR
8050 if (new_tilesize_var != TILESIZE_VAR)
8052 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8054 // changing tile size invalidates scroll values of engine snapshots
8055 FreeEngineSnapshot();
8057 // changing tile size requires update of graphic mapping for EM engine
8058 init_em_graphics = TRUE;
8069 SXSIZE = new_sxsize;
8070 SYSIZE = new_sysize;
8071 DXSIZE = new_dxsize;
8072 DYSIZE = new_dysize;
8073 VXSIZE = new_vxsize;
8074 VYSIZE = new_vysize;
8075 EXSIZE = new_exsize;
8076 EYSIZE = new_eysize;
8077 REAL_SX = new_real_sx;
8078 REAL_SY = new_real_sy;
8079 FULL_SXSIZE = new_full_sxsize;
8080 FULL_SYSIZE = new_full_sysize;
8081 TILESIZE_VAR = new_tilesize_var;
8083 init_gfx_buffers = TRUE;
8084 init_gadgets_and_toons = TRUE;
8086 // printf("::: viewports: init_gfx_buffers\n");
8087 // printf("::: viewports: init_gadgets_and_toons\n");
8090 if (init_gfx_buffers)
8092 // printf("::: init_gfx_buffers\n");
8094 SCR_FIELDX = new_scr_fieldx_buffers;
8095 SCR_FIELDY = new_scr_fieldy_buffers;
8099 SCR_FIELDX = new_scr_fieldx;
8100 SCR_FIELDY = new_scr_fieldy;
8103 if (init_video_buffer)
8105 // printf("::: init_video_buffer\n");
8107 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8109 SetDrawDeactivationMask(REDRAW_NONE);
8110 SetDrawBackgroundMask(REDRAW_FIELD);
8113 if (init_gadgets_and_toons)
8115 // printf("::: init_gadgets_and_toons\n");
8121 if (init_em_graphics)
8123 InitGraphicInfo_EM();