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;
3939 door->panel.start_step_closing = door->panel.start_step;
3943 num_panel_steps = door_rect->height / door->step_offset;
3944 door->panel.start_step = num_panel_steps - num_door_steps / 2;
3945 door->panel.start_step_closing = door->panel.start_step;
3946 door->panel.step_delay *= 2;
3957 for (i = 0; door_part_controls[i].door_token != -1; i++)
3959 struct DoorPartControlInfo *dpc = &door_part_controls[i];
3960 struct DoorPartOrderInfo *dpo = &door_part_order[i];
3962 /* initialize "start_step_opening" and "start_step_closing", if needed */
3963 if (dpc->pos->start_step_opening == 0 &&
3964 dpc->pos->start_step_closing == 0)
3966 // dpc->pos->start_step_opening = dpc->pos->start_step;
3967 dpc->pos->start_step_closing = dpc->pos->start_step;
3970 /* fill structure for door part draw order (sorted below) */
3972 dpo->sort_priority = dpc->pos->sort_priority;
3975 /* sort door part controls according to sort_priority and graphic number */
3976 qsort(door_part_order, MAX_DOOR_PARTS,
3977 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
3980 unsigned int OpenDoor(unsigned int door_state)
3982 if (door_state & DOOR_COPY_BACK)
3984 if (door_state & DOOR_OPEN_1)
3985 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3986 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
3988 if (door_state & DOOR_OPEN_2)
3989 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
3990 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
3992 door_state &= ~DOOR_COPY_BACK;
3995 return MoveDoor(door_state);
3998 unsigned int CloseDoor(unsigned int door_state)
4000 unsigned int old_door_state = GetDoorState();
4002 if (!(door_state & DOOR_NO_COPY_BACK))
4004 if (old_door_state & DOOR_OPEN_1)
4005 BlitBitmap(backbuffer, bitmap_db_door_1,
4006 DX, DY, DXSIZE, DYSIZE, 0, 0);
4008 if (old_door_state & DOOR_OPEN_2)
4009 BlitBitmap(backbuffer, bitmap_db_door_2,
4010 VX, VY, VXSIZE, VYSIZE, 0, 0);
4012 door_state &= ~DOOR_NO_COPY_BACK;
4015 return MoveDoor(door_state);
4018 unsigned int GetDoorState()
4020 return MoveDoor(DOOR_GET_STATE);
4023 unsigned int SetDoorState(unsigned int door_state)
4025 return MoveDoor(door_state | DOOR_SET_STATE);
4028 int euclid(int a, int b)
4030 return (b ? euclid(b, a % b) : a);
4033 unsigned int MoveDoor(unsigned int door_state)
4035 struct Rect door_rect_list[] =
4037 { DX, DY, DXSIZE, DYSIZE },
4038 { VX, VY, VXSIZE, VYSIZE }
4040 static int door1 = DOOR_OPEN_1;
4041 static int door2 = DOOR_CLOSE_2;
4042 unsigned int door_delay = 0;
4043 unsigned int door_delay_value;
4046 if (door_state == DOOR_GET_STATE)
4047 return (door1 | door2);
4049 if (door_state & DOOR_SET_STATE)
4051 if (door_state & DOOR_ACTION_1)
4052 door1 = door_state & DOOR_ACTION_1;
4053 if (door_state & DOOR_ACTION_2)
4054 door2 = door_state & DOOR_ACTION_2;
4056 return (door1 | door2);
4059 if (!(door_state & DOOR_FORCE_REDRAW))
4061 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4062 door_state &= ~DOOR_OPEN_1;
4063 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4064 door_state &= ~DOOR_CLOSE_1;
4065 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4066 door_state &= ~DOOR_OPEN_2;
4067 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4068 door_state &= ~DOOR_CLOSE_2;
4071 if (global.autoplay_leveldir)
4073 door_state |= DOOR_NO_DELAY;
4074 door_state &= ~DOOR_CLOSE_ALL;
4077 if (game_status == GAME_MODE_EDITOR)
4078 door_state |= DOOR_NO_DELAY;
4080 if (door_state & DOOR_ACTION)
4082 boolean door_panel_drawn[NUM_DOORS];
4083 boolean panel_has_doors[NUM_DOORS];
4084 boolean door_part_skip[MAX_DOOR_PARTS];
4085 boolean door_part_done[MAX_DOOR_PARTS];
4086 boolean door_part_done_all;
4087 int num_steps[MAX_DOOR_PARTS];
4088 int max_move_delay = 0; // delay for complete animations of all doors
4089 int max_step_delay = 0; // delay (ms) between two animation frames
4090 int num_move_steps = 0; // number of animation steps for all doors
4091 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4092 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4093 int current_move_delay = 0;
4097 for (i = 0; i < NUM_DOORS; i++)
4098 panel_has_doors[i] = FALSE;
4100 for (i = 0; i < MAX_DOOR_PARTS; i++)
4102 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4103 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4104 int door_token = dpc->door_token;
4106 door_part_done[i] = FALSE;
4107 door_part_skip[i] = (!(door_state & door_token) ||
4111 for (i = 0; i < MAX_DOOR_PARTS; i++)
4113 int nr = door_part_order[i].nr;
4114 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4115 struct DoorPartPosInfo *pos = dpc->pos;
4116 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4117 int door_token = dpc->door_token;
4118 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4119 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4120 int step_xoffset = ABS(pos->step_xoffset);
4121 int step_yoffset = ABS(pos->step_yoffset);
4122 int step_delay = pos->step_delay;
4123 int current_door_state = door_state & door_token;
4124 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4125 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4126 boolean part_opening = (is_panel ? door_closing : door_opening);
4127 int start_step = (part_opening ? pos->start_step_opening :
4128 pos->start_step_closing);
4129 float move_xsize = (step_xoffset ? g->width : 0);
4130 float move_ysize = (step_yoffset ? g->height : 0);
4131 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4132 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4133 int move_steps = (move_xsteps && move_ysteps ?
4134 MIN(move_xsteps, move_ysteps) :
4135 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4136 int move_delay = move_steps * step_delay;
4138 if (door_part_skip[nr])
4141 max_move_delay = MAX(max_move_delay, move_delay);
4142 max_step_delay = (max_step_delay == 0 ? step_delay :
4143 euclid(max_step_delay, step_delay));
4144 num_steps[nr] = move_steps;
4148 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4150 panel_has_doors[door_index] = TRUE;
4154 num_move_steps = max_move_delay / max_step_delay;
4155 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4157 door_delay_value = max_step_delay;
4159 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4161 start = num_move_steps - 1;
4165 /* opening door sound has priority over simultaneously closing door */
4166 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4167 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4168 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4169 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4172 for (k = start; k < num_move_steps; k++)
4174 door_part_done_all = TRUE;
4176 for (i = 0; i < NUM_DOORS; i++)
4177 door_panel_drawn[i] = FALSE;
4179 for (i = 0; i < MAX_DOOR_PARTS; i++)
4181 int nr = door_part_order[i].nr;
4182 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4183 struct DoorPartPosInfo *pos = dpc->pos;
4184 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4185 int door_token = dpc->door_token;
4186 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4187 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4188 boolean is_panel_and_door_has_closed = FALSE;
4189 struct Rect *door_rect = &door_rect_list[door_index];
4190 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4192 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4193 int current_door_state = door_state & door_token;
4194 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4195 boolean door_closing = !door_opening;
4196 boolean part_opening = (is_panel ? door_closing : door_opening);
4197 boolean part_closing = !part_opening;
4198 int start_step = (part_opening ? pos->start_step_opening :
4199 pos->start_step_closing);
4200 int step_delay = pos->step_delay;
4201 int step_factor = step_delay / max_step_delay;
4202 int k1 = (step_factor ? k / step_factor + 1 : k);
4203 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4204 int kk = MAX(0, k2);
4207 int src_x, src_y, src_xx, src_yy;
4208 int dst_x, dst_y, dst_xx, dst_yy;
4211 if (door_part_skip[nr])
4214 if (!(door_state & door_token))
4222 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4223 int kk_door = MAX(0, k2_door);
4224 int sync_frame = kk_door * door_delay_value;
4225 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4227 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4232 if (!door_panel_drawn[door_index])
4234 ClearRectangle(drawto, door_rect->x, door_rect->y,
4235 door_rect->width, door_rect->height);
4237 door_panel_drawn[door_index] = TRUE;
4240 // draw opening or closing door parts
4242 if (pos->step_xoffset < 0) // door part on right side
4245 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4248 if (dst_xx + width > door_rect->width)
4249 width = door_rect->width - dst_xx;
4251 else // door part on left side
4254 dst_xx = pos->x - kk * pos->step_xoffset;
4258 src_xx = ABS(dst_xx);
4262 width = g->width - src_xx;
4264 // printf("::: k == %d [%d] \n", k, start_step);
4267 if (pos->step_yoffset < 0) // door part on bottom side
4270 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4273 if (dst_yy + height > door_rect->height)
4274 height = door_rect->height - dst_yy;
4276 else // door part on top side
4279 dst_yy = pos->y - kk * pos->step_yoffset;
4283 src_yy = ABS(dst_yy);
4287 height = g->height - src_yy;
4290 src_x = g_src_x + src_xx;
4291 src_y = g_src_y + src_yy;
4293 dst_x = door_rect->x + dst_xx;
4294 dst_y = door_rect->y + dst_yy;
4296 is_panel_and_door_has_closed =
4299 panel_has_doors[door_index] &&
4300 k >= num_move_steps_doors_only - 1);
4302 if (width >= 0 && width <= g->width &&
4303 height >= 0 && height <= g->height &&
4304 !is_panel_and_door_has_closed)
4306 if (is_panel || !pos->draw_masked)
4307 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4310 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4314 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4316 if ((part_opening && (width < 0 || height < 0)) ||
4317 (part_closing && (width >= g->width && height >= g->height)))
4318 door_part_done[nr] = TRUE;
4320 // continue door part animations, but not panel after door has closed
4321 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4322 door_part_done_all = FALSE;
4325 if (!(door_state & DOOR_NO_DELAY))
4329 if (game_status == GAME_MODE_MAIN)
4332 WaitUntilDelayReached(&door_delay, door_delay_value);
4334 current_move_delay += max_step_delay;
4337 if (door_part_done_all)
4342 if (door_state & DOOR_ACTION_1)
4343 door1 = door_state & DOOR_ACTION_1;
4344 if (door_state & DOOR_ACTION_2)
4345 door2 = door_state & DOOR_ACTION_2;
4347 return (door1 | door2);
4350 void DrawSpecialEditorDoor()
4352 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4353 int top_border_width = gfx1->width;
4354 int top_border_height = gfx1->height;
4355 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4356 int ex = EX - outer_border;
4357 int ey = EY - outer_border;
4358 int vy = VY - outer_border;
4359 int exsize = EXSIZE + 2 * outer_border;
4361 /* draw bigger level editor toolbox window */
4362 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4363 top_border_width, top_border_height, ex, ey - top_border_height);
4364 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4365 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4367 redraw_mask |= REDRAW_ALL;
4370 void UndrawSpecialEditorDoor()
4372 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4373 int top_border_width = gfx1->width;
4374 int top_border_height = gfx1->height;
4375 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4376 int ex = EX - outer_border;
4377 int ey = EY - outer_border;
4378 int ey_top = ey - top_border_height;
4379 int exsize = EXSIZE + 2 * outer_border;
4380 int eysize = EYSIZE + 2 * outer_border;
4382 /* draw normal tape recorder window */
4383 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4385 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4386 ex, ey_top, top_border_width, top_border_height,
4388 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4389 ex, ey, exsize, eysize, ex, ey);
4393 // if screen background is set to "[NONE]", clear editor toolbox window
4394 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4395 ClearRectangle(drawto, ex, ey, exsize, eysize);
4398 redraw_mask |= REDRAW_ALL;
4402 /* ---------- new tool button stuff ---------------------------------------- */
4407 struct TextPosInfo *pos;
4410 } toolbutton_info[NUM_TOOL_BUTTONS] =
4413 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4414 TOOL_CTRL_ID_YES, "yes"
4417 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4418 TOOL_CTRL_ID_NO, "no"
4421 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4422 TOOL_CTRL_ID_CONFIRM, "confirm"
4425 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4426 TOOL_CTRL_ID_PLAYER_1, "player 1"
4429 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4430 TOOL_CTRL_ID_PLAYER_2, "player 2"
4433 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4434 TOOL_CTRL_ID_PLAYER_3, "player 3"
4437 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4438 TOOL_CTRL_ID_PLAYER_4, "player 4"
4442 void CreateToolButtons()
4446 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4448 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4449 struct TextPosInfo *pos = toolbutton_info[i].pos;
4450 struct GadgetInfo *gi;
4451 Bitmap *deco_bitmap = None;
4452 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4453 unsigned int event_mask = GD_EVENT_RELEASED;
4456 int gd_x = gfx->src_x;
4457 int gd_y = gfx->src_y;
4458 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4459 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4462 if (global.use_envelope_request)
4463 setRequestPosition(&dx, &dy, TRUE);
4465 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4467 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4469 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4470 pos->size, &deco_bitmap, &deco_x, &deco_y);
4471 deco_xpos = (gfx->width - pos->size) / 2;
4472 deco_ypos = (gfx->height - pos->size) / 2;
4475 gi = CreateGadget(GDI_CUSTOM_ID, id,
4476 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4477 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4478 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4479 GDI_WIDTH, gfx->width,
4480 GDI_HEIGHT, gfx->height,
4481 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4482 GDI_STATE, GD_BUTTON_UNPRESSED,
4483 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4484 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4485 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4486 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4487 GDI_DECORATION_SIZE, pos->size, pos->size,
4488 GDI_DECORATION_SHIFTING, 1, 1,
4489 GDI_DIRECT_DRAW, FALSE,
4490 GDI_EVENT_MASK, event_mask,
4491 GDI_CALLBACK_ACTION, HandleToolButtons,
4495 Error(ERR_EXIT, "cannot create gadget");
4497 tool_gadget[id] = gi;
4501 void FreeToolButtons()
4505 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4506 FreeGadget(tool_gadget[i]);
4509 static void UnmapToolButtons()
4513 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4514 UnmapGadget(tool_gadget[i]);
4517 static void HandleToolButtons(struct GadgetInfo *gi)
4519 request_gadget_id = gi->custom_id;
4522 static struct Mapping_EM_to_RND_object
4525 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4526 boolean is_backside; /* backside of moving element */
4532 em_object_mapping_list[] =
4535 Xblank, TRUE, FALSE,
4539 Yacid_splash_eB, FALSE, FALSE,
4540 EL_ACID_SPLASH_RIGHT, -1, -1
4543 Yacid_splash_wB, FALSE, FALSE,
4544 EL_ACID_SPLASH_LEFT, -1, -1
4547 #ifdef EM_ENGINE_BAD_ROLL
4549 Xstone_force_e, FALSE, FALSE,
4550 EL_ROCK, -1, MV_BIT_RIGHT
4553 Xstone_force_w, FALSE, FALSE,
4554 EL_ROCK, -1, MV_BIT_LEFT
4557 Xnut_force_e, FALSE, FALSE,
4558 EL_NUT, -1, MV_BIT_RIGHT
4561 Xnut_force_w, FALSE, FALSE,
4562 EL_NUT, -1, MV_BIT_LEFT
4565 Xspring_force_e, FALSE, FALSE,
4566 EL_SPRING, -1, MV_BIT_RIGHT
4569 Xspring_force_w, FALSE, FALSE,
4570 EL_SPRING, -1, MV_BIT_LEFT
4573 Xemerald_force_e, FALSE, FALSE,
4574 EL_EMERALD, -1, MV_BIT_RIGHT
4577 Xemerald_force_w, FALSE, FALSE,
4578 EL_EMERALD, -1, MV_BIT_LEFT
4581 Xdiamond_force_e, FALSE, FALSE,
4582 EL_DIAMOND, -1, MV_BIT_RIGHT
4585 Xdiamond_force_w, FALSE, FALSE,
4586 EL_DIAMOND, -1, MV_BIT_LEFT
4589 Xbomb_force_e, FALSE, FALSE,
4590 EL_BOMB, -1, MV_BIT_RIGHT
4593 Xbomb_force_w, FALSE, FALSE,
4594 EL_BOMB, -1, MV_BIT_LEFT
4596 #endif /* EM_ENGINE_BAD_ROLL */
4599 Xstone, TRUE, FALSE,
4603 Xstone_pause, FALSE, FALSE,
4607 Xstone_fall, FALSE, FALSE,
4611 Ystone_s, FALSE, FALSE,
4612 EL_ROCK, ACTION_FALLING, -1
4615 Ystone_sB, FALSE, TRUE,
4616 EL_ROCK, ACTION_FALLING, -1
4619 Ystone_e, FALSE, FALSE,
4620 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4623 Ystone_eB, FALSE, TRUE,
4624 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4627 Ystone_w, FALSE, FALSE,
4628 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4631 Ystone_wB, FALSE, TRUE,
4632 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4639 Xnut_pause, FALSE, FALSE,
4643 Xnut_fall, FALSE, FALSE,
4647 Ynut_s, FALSE, FALSE,
4648 EL_NUT, ACTION_FALLING, -1
4651 Ynut_sB, FALSE, TRUE,
4652 EL_NUT, ACTION_FALLING, -1
4655 Ynut_e, FALSE, FALSE,
4656 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4659 Ynut_eB, FALSE, TRUE,
4660 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4663 Ynut_w, FALSE, FALSE,
4664 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4667 Ynut_wB, FALSE, TRUE,
4668 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4671 Xbug_n, TRUE, FALSE,
4675 Xbug_e, TRUE, FALSE,
4676 EL_BUG_RIGHT, -1, -1
4679 Xbug_s, TRUE, FALSE,
4683 Xbug_w, TRUE, FALSE,
4687 Xbug_gon, FALSE, FALSE,
4691 Xbug_goe, FALSE, FALSE,
4692 EL_BUG_RIGHT, -1, -1
4695 Xbug_gos, FALSE, FALSE,
4699 Xbug_gow, FALSE, FALSE,
4703 Ybug_n, FALSE, FALSE,
4704 EL_BUG, ACTION_MOVING, MV_BIT_UP
4707 Ybug_nB, FALSE, TRUE,
4708 EL_BUG, ACTION_MOVING, MV_BIT_UP
4711 Ybug_e, FALSE, FALSE,
4712 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4715 Ybug_eB, FALSE, TRUE,
4716 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4719 Ybug_s, FALSE, FALSE,
4720 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4723 Ybug_sB, FALSE, TRUE,
4724 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4727 Ybug_w, FALSE, FALSE,
4728 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4731 Ybug_wB, FALSE, TRUE,
4732 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4735 Ybug_w_n, FALSE, FALSE,
4736 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4739 Ybug_n_e, FALSE, FALSE,
4740 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4743 Ybug_e_s, FALSE, FALSE,
4744 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4747 Ybug_s_w, FALSE, FALSE,
4748 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4751 Ybug_e_n, FALSE, FALSE,
4752 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4755 Ybug_s_e, FALSE, FALSE,
4756 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4759 Ybug_w_s, FALSE, FALSE,
4760 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4763 Ybug_n_w, FALSE, FALSE,
4764 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4767 Ybug_stone, FALSE, FALSE,
4768 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4771 Ybug_spring, FALSE, FALSE,
4772 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4775 Xtank_n, TRUE, FALSE,
4776 EL_SPACESHIP_UP, -1, -1
4779 Xtank_e, TRUE, FALSE,
4780 EL_SPACESHIP_RIGHT, -1, -1
4783 Xtank_s, TRUE, FALSE,
4784 EL_SPACESHIP_DOWN, -1, -1
4787 Xtank_w, TRUE, FALSE,
4788 EL_SPACESHIP_LEFT, -1, -1
4791 Xtank_gon, FALSE, FALSE,
4792 EL_SPACESHIP_UP, -1, -1
4795 Xtank_goe, FALSE, FALSE,
4796 EL_SPACESHIP_RIGHT, -1, -1
4799 Xtank_gos, FALSE, FALSE,
4800 EL_SPACESHIP_DOWN, -1, -1
4803 Xtank_gow, FALSE, FALSE,
4804 EL_SPACESHIP_LEFT, -1, -1
4807 Ytank_n, FALSE, FALSE,
4808 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4811 Ytank_nB, FALSE, TRUE,
4812 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4815 Ytank_e, FALSE, FALSE,
4816 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4819 Ytank_eB, FALSE, TRUE,
4820 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4823 Ytank_s, FALSE, FALSE,
4824 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4827 Ytank_sB, FALSE, TRUE,
4828 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4831 Ytank_w, FALSE, FALSE,
4832 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4835 Ytank_wB, FALSE, TRUE,
4836 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4839 Ytank_w_n, FALSE, FALSE,
4840 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4843 Ytank_n_e, FALSE, FALSE,
4844 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4847 Ytank_e_s, FALSE, FALSE,
4848 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4851 Ytank_s_w, FALSE, FALSE,
4852 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4855 Ytank_e_n, FALSE, FALSE,
4856 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4859 Ytank_s_e, FALSE, FALSE,
4860 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4863 Ytank_w_s, FALSE, FALSE,
4864 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4867 Ytank_n_w, FALSE, FALSE,
4868 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4871 Ytank_stone, FALSE, FALSE,
4872 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4875 Ytank_spring, FALSE, FALSE,
4876 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4879 Xandroid, TRUE, FALSE,
4880 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4883 Xandroid_1_n, FALSE, FALSE,
4884 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4887 Xandroid_2_n, FALSE, FALSE,
4888 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4891 Xandroid_1_e, FALSE, FALSE,
4892 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4895 Xandroid_2_e, FALSE, FALSE,
4896 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4899 Xandroid_1_w, FALSE, FALSE,
4900 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4903 Xandroid_2_w, FALSE, FALSE,
4904 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4907 Xandroid_1_s, FALSE, FALSE,
4908 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4911 Xandroid_2_s, FALSE, FALSE,
4912 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4915 Yandroid_n, FALSE, FALSE,
4916 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4919 Yandroid_nB, FALSE, TRUE,
4920 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4923 Yandroid_ne, FALSE, FALSE,
4924 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4927 Yandroid_neB, FALSE, TRUE,
4928 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4931 Yandroid_e, FALSE, FALSE,
4932 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4935 Yandroid_eB, FALSE, TRUE,
4936 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4939 Yandroid_se, FALSE, FALSE,
4940 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4943 Yandroid_seB, FALSE, TRUE,
4944 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4947 Yandroid_s, FALSE, FALSE,
4948 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4951 Yandroid_sB, FALSE, TRUE,
4952 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4955 Yandroid_sw, FALSE, FALSE,
4956 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
4959 Yandroid_swB, FALSE, TRUE,
4960 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
4963 Yandroid_w, FALSE, FALSE,
4964 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
4967 Yandroid_wB, FALSE, TRUE,
4968 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
4971 Yandroid_nw, FALSE, FALSE,
4972 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
4975 Yandroid_nwB, FALSE, TRUE,
4976 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
4979 Xspring, TRUE, FALSE,
4983 Xspring_pause, FALSE, FALSE,
4987 Xspring_e, FALSE, FALSE,
4991 Xspring_w, FALSE, FALSE,
4995 Xspring_fall, FALSE, FALSE,
4999 Yspring_s, FALSE, FALSE,
5000 EL_SPRING, ACTION_FALLING, -1
5003 Yspring_sB, FALSE, TRUE,
5004 EL_SPRING, ACTION_FALLING, -1
5007 Yspring_e, FALSE, FALSE,
5008 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5011 Yspring_eB, FALSE, TRUE,
5012 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5015 Yspring_w, FALSE, FALSE,
5016 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5019 Yspring_wB, FALSE, TRUE,
5020 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5023 Yspring_kill_e, FALSE, FALSE,
5024 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5027 Yspring_kill_eB, FALSE, TRUE,
5028 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5031 Yspring_kill_w, FALSE, FALSE,
5032 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5035 Yspring_kill_wB, FALSE, TRUE,
5036 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5039 Xeater_n, TRUE, FALSE,
5040 EL_YAMYAM_UP, -1, -1
5043 Xeater_e, TRUE, FALSE,
5044 EL_YAMYAM_RIGHT, -1, -1
5047 Xeater_w, TRUE, FALSE,
5048 EL_YAMYAM_LEFT, -1, -1
5051 Xeater_s, TRUE, FALSE,
5052 EL_YAMYAM_DOWN, -1, -1
5055 Yeater_n, FALSE, FALSE,
5056 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5059 Yeater_nB, FALSE, TRUE,
5060 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5063 Yeater_e, FALSE, FALSE,
5064 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5067 Yeater_eB, FALSE, TRUE,
5068 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5071 Yeater_s, FALSE, FALSE,
5072 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5075 Yeater_sB, FALSE, TRUE,
5076 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5079 Yeater_w, FALSE, FALSE,
5080 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5083 Yeater_wB, FALSE, TRUE,
5084 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5087 Yeater_stone, FALSE, FALSE,
5088 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5091 Yeater_spring, FALSE, FALSE,
5092 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5095 Xalien, TRUE, FALSE,
5099 Xalien_pause, FALSE, FALSE,
5103 Yalien_n, FALSE, FALSE,
5104 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5107 Yalien_nB, FALSE, TRUE,
5108 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5111 Yalien_e, FALSE, FALSE,
5112 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5115 Yalien_eB, FALSE, TRUE,
5116 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5119 Yalien_s, FALSE, FALSE,
5120 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5123 Yalien_sB, FALSE, TRUE,
5124 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5127 Yalien_w, FALSE, FALSE,
5128 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5131 Yalien_wB, FALSE, TRUE,
5132 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5135 Yalien_stone, FALSE, FALSE,
5136 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5139 Yalien_spring, FALSE, FALSE,
5140 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5143 Xemerald, TRUE, FALSE,
5147 Xemerald_pause, FALSE, FALSE,
5151 Xemerald_fall, FALSE, FALSE,
5155 Xemerald_shine, FALSE, FALSE,
5156 EL_EMERALD, ACTION_TWINKLING, -1
5159 Yemerald_s, FALSE, FALSE,
5160 EL_EMERALD, ACTION_FALLING, -1
5163 Yemerald_sB, FALSE, TRUE,
5164 EL_EMERALD, ACTION_FALLING, -1
5167 Yemerald_e, FALSE, FALSE,
5168 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5171 Yemerald_eB, FALSE, TRUE,
5172 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5175 Yemerald_w, FALSE, FALSE,
5176 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5179 Yemerald_wB, FALSE, TRUE,
5180 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5183 Yemerald_eat, FALSE, FALSE,
5184 EL_EMERALD, ACTION_COLLECTING, -1
5187 Yemerald_stone, FALSE, FALSE,
5188 EL_NUT, ACTION_BREAKING, -1
5191 Xdiamond, TRUE, FALSE,
5195 Xdiamond_pause, FALSE, FALSE,
5199 Xdiamond_fall, FALSE, FALSE,
5203 Xdiamond_shine, FALSE, FALSE,
5204 EL_DIAMOND, ACTION_TWINKLING, -1
5207 Ydiamond_s, FALSE, FALSE,
5208 EL_DIAMOND, ACTION_FALLING, -1
5211 Ydiamond_sB, FALSE, TRUE,
5212 EL_DIAMOND, ACTION_FALLING, -1
5215 Ydiamond_e, FALSE, FALSE,
5216 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5219 Ydiamond_eB, FALSE, TRUE,
5220 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5223 Ydiamond_w, FALSE, FALSE,
5224 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5227 Ydiamond_wB, FALSE, TRUE,
5228 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5231 Ydiamond_eat, FALSE, FALSE,
5232 EL_DIAMOND, ACTION_COLLECTING, -1
5235 Ydiamond_stone, FALSE, FALSE,
5236 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5239 Xdrip_fall, TRUE, FALSE,
5240 EL_AMOEBA_DROP, -1, -1
5243 Xdrip_stretch, FALSE, FALSE,
5244 EL_AMOEBA_DROP, ACTION_FALLING, -1
5247 Xdrip_stretchB, FALSE, TRUE,
5248 EL_AMOEBA_DROP, ACTION_FALLING, -1
5251 Xdrip_eat, FALSE, FALSE,
5252 EL_AMOEBA_DROP, ACTION_GROWING, -1
5255 Ydrip_s1, FALSE, FALSE,
5256 EL_AMOEBA_DROP, ACTION_FALLING, -1
5259 Ydrip_s1B, FALSE, TRUE,
5260 EL_AMOEBA_DROP, ACTION_FALLING, -1
5263 Ydrip_s2, FALSE, FALSE,
5264 EL_AMOEBA_DROP, ACTION_FALLING, -1
5267 Ydrip_s2B, FALSE, TRUE,
5268 EL_AMOEBA_DROP, ACTION_FALLING, -1
5275 Xbomb_pause, FALSE, FALSE,
5279 Xbomb_fall, FALSE, FALSE,
5283 Ybomb_s, FALSE, FALSE,
5284 EL_BOMB, ACTION_FALLING, -1
5287 Ybomb_sB, FALSE, TRUE,
5288 EL_BOMB, ACTION_FALLING, -1
5291 Ybomb_e, FALSE, FALSE,
5292 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5295 Ybomb_eB, FALSE, TRUE,
5296 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5299 Ybomb_w, FALSE, FALSE,
5300 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5303 Ybomb_wB, FALSE, TRUE,
5304 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5307 Ybomb_eat, FALSE, FALSE,
5308 EL_BOMB, ACTION_ACTIVATING, -1
5311 Xballoon, TRUE, FALSE,
5315 Yballoon_n, FALSE, FALSE,
5316 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5319 Yballoon_nB, FALSE, TRUE,
5320 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5323 Yballoon_e, FALSE, FALSE,
5324 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5327 Yballoon_eB, FALSE, TRUE,
5328 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5331 Yballoon_s, FALSE, FALSE,
5332 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5335 Yballoon_sB, FALSE, TRUE,
5336 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5339 Yballoon_w, FALSE, FALSE,
5340 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5343 Yballoon_wB, FALSE, TRUE,
5344 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5347 Xgrass, TRUE, FALSE,
5348 EL_EMC_GRASS, -1, -1
5351 Ygrass_nB, FALSE, FALSE,
5352 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5355 Ygrass_eB, FALSE, FALSE,
5356 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5359 Ygrass_sB, FALSE, FALSE,
5360 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5363 Ygrass_wB, FALSE, FALSE,
5364 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5371 Ydirt_nB, FALSE, FALSE,
5372 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5375 Ydirt_eB, FALSE, FALSE,
5376 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5379 Ydirt_sB, FALSE, FALSE,
5380 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5383 Ydirt_wB, FALSE, FALSE,
5384 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5387 Xacid_ne, TRUE, FALSE,
5388 EL_ACID_POOL_TOPRIGHT, -1, -1
5391 Xacid_se, TRUE, FALSE,
5392 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5395 Xacid_s, TRUE, FALSE,
5396 EL_ACID_POOL_BOTTOM, -1, -1
5399 Xacid_sw, TRUE, FALSE,
5400 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5403 Xacid_nw, TRUE, FALSE,
5404 EL_ACID_POOL_TOPLEFT, -1, -1
5407 Xacid_1, TRUE, FALSE,
5411 Xacid_2, FALSE, FALSE,
5415 Xacid_3, FALSE, FALSE,
5419 Xacid_4, FALSE, FALSE,
5423 Xacid_5, FALSE, FALSE,
5427 Xacid_6, FALSE, FALSE,
5431 Xacid_7, FALSE, FALSE,
5435 Xacid_8, FALSE, FALSE,
5439 Xball_1, TRUE, FALSE,
5440 EL_EMC_MAGIC_BALL, -1, -1
5443 Xball_1B, FALSE, FALSE,
5444 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5447 Xball_2, FALSE, FALSE,
5448 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5451 Xball_2B, FALSE, FALSE,
5452 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5455 Yball_eat, FALSE, FALSE,
5456 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5459 Ykey_1_eat, FALSE, FALSE,
5460 EL_EM_KEY_1, ACTION_COLLECTING, -1
5463 Ykey_2_eat, FALSE, FALSE,
5464 EL_EM_KEY_2, ACTION_COLLECTING, -1
5467 Ykey_3_eat, FALSE, FALSE,
5468 EL_EM_KEY_3, ACTION_COLLECTING, -1
5471 Ykey_4_eat, FALSE, FALSE,
5472 EL_EM_KEY_4, ACTION_COLLECTING, -1
5475 Ykey_5_eat, FALSE, FALSE,
5476 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5479 Ykey_6_eat, FALSE, FALSE,
5480 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5483 Ykey_7_eat, FALSE, FALSE,
5484 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5487 Ykey_8_eat, FALSE, FALSE,
5488 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5491 Ylenses_eat, FALSE, FALSE,
5492 EL_EMC_LENSES, ACTION_COLLECTING, -1
5495 Ymagnify_eat, FALSE, FALSE,
5496 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5499 Ygrass_eat, FALSE, FALSE,
5500 EL_EMC_GRASS, ACTION_SNAPPING, -1
5503 Ydirt_eat, FALSE, FALSE,
5504 EL_SAND, ACTION_SNAPPING, -1
5507 Xgrow_ns, TRUE, FALSE,
5508 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5511 Ygrow_ns_eat, FALSE, FALSE,
5512 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5515 Xgrow_ew, TRUE, FALSE,
5516 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5519 Ygrow_ew_eat, FALSE, FALSE,
5520 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5523 Xwonderwall, TRUE, FALSE,
5524 EL_MAGIC_WALL, -1, -1
5527 XwonderwallB, FALSE, FALSE,
5528 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5531 Xamoeba_1, TRUE, FALSE,
5532 EL_AMOEBA_DRY, ACTION_OTHER, -1
5535 Xamoeba_2, FALSE, FALSE,
5536 EL_AMOEBA_DRY, ACTION_OTHER, -1
5539 Xamoeba_3, FALSE, FALSE,
5540 EL_AMOEBA_DRY, ACTION_OTHER, -1
5543 Xamoeba_4, FALSE, FALSE,
5544 EL_AMOEBA_DRY, ACTION_OTHER, -1
5547 Xamoeba_5, TRUE, FALSE,
5548 EL_AMOEBA_WET, ACTION_OTHER, -1
5551 Xamoeba_6, FALSE, FALSE,
5552 EL_AMOEBA_WET, ACTION_OTHER, -1
5555 Xamoeba_7, FALSE, FALSE,
5556 EL_AMOEBA_WET, ACTION_OTHER, -1
5559 Xamoeba_8, FALSE, FALSE,
5560 EL_AMOEBA_WET, ACTION_OTHER, -1
5563 Xdoor_1, TRUE, FALSE,
5564 EL_EM_GATE_1, -1, -1
5567 Xdoor_2, TRUE, FALSE,
5568 EL_EM_GATE_2, -1, -1
5571 Xdoor_3, TRUE, FALSE,
5572 EL_EM_GATE_3, -1, -1
5575 Xdoor_4, TRUE, FALSE,
5576 EL_EM_GATE_4, -1, -1
5579 Xdoor_5, TRUE, FALSE,
5580 EL_EMC_GATE_5, -1, -1
5583 Xdoor_6, TRUE, FALSE,
5584 EL_EMC_GATE_6, -1, -1
5587 Xdoor_7, TRUE, FALSE,
5588 EL_EMC_GATE_7, -1, -1
5591 Xdoor_8, TRUE, FALSE,
5592 EL_EMC_GATE_8, -1, -1
5595 Xkey_1, TRUE, FALSE,
5599 Xkey_2, TRUE, FALSE,
5603 Xkey_3, TRUE, FALSE,
5607 Xkey_4, TRUE, FALSE,
5611 Xkey_5, TRUE, FALSE,
5612 EL_EMC_KEY_5, -1, -1
5615 Xkey_6, TRUE, FALSE,
5616 EL_EMC_KEY_6, -1, -1
5619 Xkey_7, TRUE, FALSE,
5620 EL_EMC_KEY_7, -1, -1
5623 Xkey_8, TRUE, FALSE,
5624 EL_EMC_KEY_8, -1, -1
5627 Xwind_n, TRUE, FALSE,
5628 EL_BALLOON_SWITCH_UP, -1, -1
5631 Xwind_e, TRUE, FALSE,
5632 EL_BALLOON_SWITCH_RIGHT, -1, -1
5635 Xwind_s, TRUE, FALSE,
5636 EL_BALLOON_SWITCH_DOWN, -1, -1
5639 Xwind_w, TRUE, FALSE,
5640 EL_BALLOON_SWITCH_LEFT, -1, -1
5643 Xwind_nesw, TRUE, FALSE,
5644 EL_BALLOON_SWITCH_ANY, -1, -1
5647 Xwind_stop, TRUE, FALSE,
5648 EL_BALLOON_SWITCH_NONE, -1, -1
5652 EL_EM_EXIT_CLOSED, -1, -1
5655 Xexit_1, TRUE, FALSE,
5656 EL_EM_EXIT_OPEN, -1, -1
5659 Xexit_2, FALSE, FALSE,
5660 EL_EM_EXIT_OPEN, -1, -1
5663 Xexit_3, FALSE, FALSE,
5664 EL_EM_EXIT_OPEN, -1, -1
5667 Xdynamite, TRUE, FALSE,
5668 EL_EM_DYNAMITE, -1, -1
5671 Ydynamite_eat, FALSE, FALSE,
5672 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5675 Xdynamite_1, TRUE, FALSE,
5676 EL_EM_DYNAMITE_ACTIVE, -1, -1
5679 Xdynamite_2, FALSE, FALSE,
5680 EL_EM_DYNAMITE_ACTIVE, -1, -1
5683 Xdynamite_3, FALSE, FALSE,
5684 EL_EM_DYNAMITE_ACTIVE, -1, -1
5687 Xdynamite_4, FALSE, FALSE,
5688 EL_EM_DYNAMITE_ACTIVE, -1, -1
5691 Xbumper, TRUE, FALSE,
5692 EL_EMC_SPRING_BUMPER, -1, -1
5695 XbumperB, FALSE, FALSE,
5696 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5699 Xwheel, TRUE, FALSE,
5700 EL_ROBOT_WHEEL, -1, -1
5703 XwheelB, FALSE, FALSE,
5704 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5707 Xswitch, TRUE, FALSE,
5708 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5711 XswitchB, FALSE, FALSE,
5712 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5716 EL_QUICKSAND_EMPTY, -1, -1
5719 Xsand_stone, TRUE, FALSE,
5720 EL_QUICKSAND_FULL, -1, -1
5723 Xsand_stonein_1, FALSE, TRUE,
5724 EL_ROCK, ACTION_FILLING, -1
5727 Xsand_stonein_2, FALSE, TRUE,
5728 EL_ROCK, ACTION_FILLING, -1
5731 Xsand_stonein_3, FALSE, TRUE,
5732 EL_ROCK, ACTION_FILLING, -1
5735 Xsand_stonein_4, FALSE, TRUE,
5736 EL_ROCK, ACTION_FILLING, -1
5739 Xsand_stonesand_1, FALSE, FALSE,
5740 EL_QUICKSAND_EMPTYING, -1, -1
5743 Xsand_stonesand_2, FALSE, FALSE,
5744 EL_QUICKSAND_EMPTYING, -1, -1
5747 Xsand_stonesand_3, FALSE, FALSE,
5748 EL_QUICKSAND_EMPTYING, -1, -1
5751 Xsand_stonesand_4, FALSE, FALSE,
5752 EL_QUICKSAND_EMPTYING, -1, -1
5755 Xsand_stonesand_quickout_1, FALSE, FALSE,
5756 EL_QUICKSAND_EMPTYING, -1, -1
5759 Xsand_stonesand_quickout_2, FALSE, FALSE,
5760 EL_QUICKSAND_EMPTYING, -1, -1
5763 Xsand_stoneout_1, FALSE, FALSE,
5764 EL_ROCK, ACTION_EMPTYING, -1
5767 Xsand_stoneout_2, FALSE, FALSE,
5768 EL_ROCK, ACTION_EMPTYING, -1
5771 Xsand_sandstone_1, FALSE, FALSE,
5772 EL_QUICKSAND_FILLING, -1, -1
5775 Xsand_sandstone_2, FALSE, FALSE,
5776 EL_QUICKSAND_FILLING, -1, -1
5779 Xsand_sandstone_3, FALSE, FALSE,
5780 EL_QUICKSAND_FILLING, -1, -1
5783 Xsand_sandstone_4, FALSE, FALSE,
5784 EL_QUICKSAND_FILLING, -1, -1
5787 Xplant, TRUE, FALSE,
5788 EL_EMC_PLANT, -1, -1
5791 Yplant, FALSE, FALSE,
5792 EL_EMC_PLANT, -1, -1
5795 Xlenses, TRUE, FALSE,
5796 EL_EMC_LENSES, -1, -1
5799 Xmagnify, TRUE, FALSE,
5800 EL_EMC_MAGNIFIER, -1, -1
5803 Xdripper, TRUE, FALSE,
5804 EL_EMC_DRIPPER, -1, -1
5807 XdripperB, FALSE, FALSE,
5808 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5811 Xfake_blank, TRUE, FALSE,
5812 EL_INVISIBLE_WALL, -1, -1
5815 Xfake_blankB, FALSE, FALSE,
5816 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5819 Xfake_grass, TRUE, FALSE,
5820 EL_EMC_FAKE_GRASS, -1, -1
5823 Xfake_grassB, FALSE, FALSE,
5824 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5827 Xfake_door_1, TRUE, FALSE,
5828 EL_EM_GATE_1_GRAY, -1, -1
5831 Xfake_door_2, TRUE, FALSE,
5832 EL_EM_GATE_2_GRAY, -1, -1
5835 Xfake_door_3, TRUE, FALSE,
5836 EL_EM_GATE_3_GRAY, -1, -1
5839 Xfake_door_4, TRUE, FALSE,
5840 EL_EM_GATE_4_GRAY, -1, -1
5843 Xfake_door_5, TRUE, FALSE,
5844 EL_EMC_GATE_5_GRAY, -1, -1
5847 Xfake_door_6, TRUE, FALSE,
5848 EL_EMC_GATE_6_GRAY, -1, -1
5851 Xfake_door_7, TRUE, FALSE,
5852 EL_EMC_GATE_7_GRAY, -1, -1
5855 Xfake_door_8, TRUE, FALSE,
5856 EL_EMC_GATE_8_GRAY, -1, -1
5859 Xfake_acid_1, TRUE, FALSE,
5860 EL_EMC_FAKE_ACID, -1, -1
5863 Xfake_acid_2, FALSE, FALSE,
5864 EL_EMC_FAKE_ACID, -1, -1
5867 Xfake_acid_3, FALSE, FALSE,
5868 EL_EMC_FAKE_ACID, -1, -1
5871 Xfake_acid_4, FALSE, FALSE,
5872 EL_EMC_FAKE_ACID, -1, -1
5875 Xfake_acid_5, FALSE, FALSE,
5876 EL_EMC_FAKE_ACID, -1, -1
5879 Xfake_acid_6, FALSE, FALSE,
5880 EL_EMC_FAKE_ACID, -1, -1
5883 Xfake_acid_7, FALSE, FALSE,
5884 EL_EMC_FAKE_ACID, -1, -1
5887 Xfake_acid_8, FALSE, FALSE,
5888 EL_EMC_FAKE_ACID, -1, -1
5891 Xsteel_1, TRUE, FALSE,
5892 EL_STEELWALL, -1, -1
5895 Xsteel_2, TRUE, FALSE,
5896 EL_EMC_STEELWALL_2, -1, -1
5899 Xsteel_3, TRUE, FALSE,
5900 EL_EMC_STEELWALL_3, -1, -1
5903 Xsteel_4, TRUE, FALSE,
5904 EL_EMC_STEELWALL_4, -1, -1
5907 Xwall_1, TRUE, FALSE,
5911 Xwall_2, TRUE, FALSE,
5912 EL_EMC_WALL_14, -1, -1
5915 Xwall_3, TRUE, FALSE,
5916 EL_EMC_WALL_15, -1, -1
5919 Xwall_4, TRUE, FALSE,
5920 EL_EMC_WALL_16, -1, -1
5923 Xround_wall_1, TRUE, FALSE,
5924 EL_WALL_SLIPPERY, -1, -1
5927 Xround_wall_2, TRUE, FALSE,
5928 EL_EMC_WALL_SLIPPERY_2, -1, -1
5931 Xround_wall_3, TRUE, FALSE,
5932 EL_EMC_WALL_SLIPPERY_3, -1, -1
5935 Xround_wall_4, TRUE, FALSE,
5936 EL_EMC_WALL_SLIPPERY_4, -1, -1
5939 Xdecor_1, TRUE, FALSE,
5940 EL_EMC_WALL_8, -1, -1
5943 Xdecor_2, TRUE, FALSE,
5944 EL_EMC_WALL_6, -1, -1
5947 Xdecor_3, TRUE, FALSE,
5948 EL_EMC_WALL_4, -1, -1
5951 Xdecor_4, TRUE, FALSE,
5952 EL_EMC_WALL_7, -1, -1
5955 Xdecor_5, TRUE, FALSE,
5956 EL_EMC_WALL_5, -1, -1
5959 Xdecor_6, TRUE, FALSE,
5960 EL_EMC_WALL_9, -1, -1
5963 Xdecor_7, TRUE, FALSE,
5964 EL_EMC_WALL_10, -1, -1
5967 Xdecor_8, TRUE, FALSE,
5968 EL_EMC_WALL_1, -1, -1
5971 Xdecor_9, TRUE, FALSE,
5972 EL_EMC_WALL_2, -1, -1
5975 Xdecor_10, TRUE, FALSE,
5976 EL_EMC_WALL_3, -1, -1
5979 Xdecor_11, TRUE, FALSE,
5980 EL_EMC_WALL_11, -1, -1
5983 Xdecor_12, TRUE, FALSE,
5984 EL_EMC_WALL_12, -1, -1
5987 Xalpha_0, TRUE, FALSE,
5988 EL_CHAR('0'), -1, -1
5991 Xalpha_1, TRUE, FALSE,
5992 EL_CHAR('1'), -1, -1
5995 Xalpha_2, TRUE, FALSE,
5996 EL_CHAR('2'), -1, -1
5999 Xalpha_3, TRUE, FALSE,
6000 EL_CHAR('3'), -1, -1
6003 Xalpha_4, TRUE, FALSE,
6004 EL_CHAR('4'), -1, -1
6007 Xalpha_5, TRUE, FALSE,
6008 EL_CHAR('5'), -1, -1
6011 Xalpha_6, TRUE, FALSE,
6012 EL_CHAR('6'), -1, -1
6015 Xalpha_7, TRUE, FALSE,
6016 EL_CHAR('7'), -1, -1
6019 Xalpha_8, TRUE, FALSE,
6020 EL_CHAR('8'), -1, -1
6023 Xalpha_9, TRUE, FALSE,
6024 EL_CHAR('9'), -1, -1
6027 Xalpha_excla, TRUE, FALSE,
6028 EL_CHAR('!'), -1, -1
6031 Xalpha_quote, TRUE, FALSE,
6032 EL_CHAR('"'), -1, -1
6035 Xalpha_comma, TRUE, FALSE,
6036 EL_CHAR(','), -1, -1
6039 Xalpha_minus, TRUE, FALSE,
6040 EL_CHAR('-'), -1, -1
6043 Xalpha_perio, TRUE, FALSE,
6044 EL_CHAR('.'), -1, -1
6047 Xalpha_colon, TRUE, FALSE,
6048 EL_CHAR(':'), -1, -1
6051 Xalpha_quest, TRUE, FALSE,
6052 EL_CHAR('?'), -1, -1
6055 Xalpha_a, TRUE, FALSE,
6056 EL_CHAR('A'), -1, -1
6059 Xalpha_b, TRUE, FALSE,
6060 EL_CHAR('B'), -1, -1
6063 Xalpha_c, TRUE, FALSE,
6064 EL_CHAR('C'), -1, -1
6067 Xalpha_d, TRUE, FALSE,
6068 EL_CHAR('D'), -1, -1
6071 Xalpha_e, TRUE, FALSE,
6072 EL_CHAR('E'), -1, -1
6075 Xalpha_f, TRUE, FALSE,
6076 EL_CHAR('F'), -1, -1
6079 Xalpha_g, TRUE, FALSE,
6080 EL_CHAR('G'), -1, -1
6083 Xalpha_h, TRUE, FALSE,
6084 EL_CHAR('H'), -1, -1
6087 Xalpha_i, TRUE, FALSE,
6088 EL_CHAR('I'), -1, -1
6091 Xalpha_j, TRUE, FALSE,
6092 EL_CHAR('J'), -1, -1
6095 Xalpha_k, TRUE, FALSE,
6096 EL_CHAR('K'), -1, -1
6099 Xalpha_l, TRUE, FALSE,
6100 EL_CHAR('L'), -1, -1
6103 Xalpha_m, TRUE, FALSE,
6104 EL_CHAR('M'), -1, -1
6107 Xalpha_n, TRUE, FALSE,
6108 EL_CHAR('N'), -1, -1
6111 Xalpha_o, TRUE, FALSE,
6112 EL_CHAR('O'), -1, -1
6115 Xalpha_p, TRUE, FALSE,
6116 EL_CHAR('P'), -1, -1
6119 Xalpha_q, TRUE, FALSE,
6120 EL_CHAR('Q'), -1, -1
6123 Xalpha_r, TRUE, FALSE,
6124 EL_CHAR('R'), -1, -1
6127 Xalpha_s, TRUE, FALSE,
6128 EL_CHAR('S'), -1, -1
6131 Xalpha_t, TRUE, FALSE,
6132 EL_CHAR('T'), -1, -1
6135 Xalpha_u, TRUE, FALSE,
6136 EL_CHAR('U'), -1, -1
6139 Xalpha_v, TRUE, FALSE,
6140 EL_CHAR('V'), -1, -1
6143 Xalpha_w, TRUE, FALSE,
6144 EL_CHAR('W'), -1, -1
6147 Xalpha_x, TRUE, FALSE,
6148 EL_CHAR('X'), -1, -1
6151 Xalpha_y, TRUE, FALSE,
6152 EL_CHAR('Y'), -1, -1
6155 Xalpha_z, TRUE, FALSE,
6156 EL_CHAR('Z'), -1, -1
6159 Xalpha_arrow_e, TRUE, FALSE,
6160 EL_CHAR('>'), -1, -1
6163 Xalpha_arrow_w, TRUE, FALSE,
6164 EL_CHAR('<'), -1, -1
6167 Xalpha_copyr, TRUE, FALSE,
6168 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6172 Xboom_bug, FALSE, FALSE,
6173 EL_BUG, ACTION_EXPLODING, -1
6176 Xboom_bomb, FALSE, FALSE,
6177 EL_BOMB, ACTION_EXPLODING, -1
6180 Xboom_android, FALSE, FALSE,
6181 EL_EMC_ANDROID, ACTION_OTHER, -1
6184 Xboom_1, FALSE, FALSE,
6185 EL_DEFAULT, ACTION_EXPLODING, -1
6188 Xboom_2, FALSE, FALSE,
6189 EL_DEFAULT, ACTION_EXPLODING, -1
6192 Znormal, FALSE, FALSE,
6196 Zdynamite, FALSE, FALSE,
6200 Zplayer, FALSE, FALSE,
6204 ZBORDER, FALSE, FALSE,
6214 static struct Mapping_EM_to_RND_player
6223 em_player_mapping_list[] =
6227 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6231 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6235 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6239 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6243 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6247 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6251 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6255 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6259 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6263 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6267 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6271 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6275 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6279 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6283 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6287 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6291 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6295 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6299 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6303 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6307 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6311 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6315 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6319 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6323 EL_PLAYER_1, ACTION_DEFAULT, -1,
6327 EL_PLAYER_2, ACTION_DEFAULT, -1,
6331 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6335 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6339 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6343 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6347 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6351 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6355 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6359 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6363 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6367 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6371 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6375 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6379 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6383 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6387 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6391 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6395 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6399 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6403 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6407 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6411 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6415 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6419 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6423 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6427 EL_PLAYER_3, ACTION_DEFAULT, -1,
6431 EL_PLAYER_4, ACTION_DEFAULT, -1,
6440 int map_element_RND_to_EM(int element_rnd)
6442 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6443 static boolean mapping_initialized = FALSE;
6445 if (!mapping_initialized)
6449 /* return "Xalpha_quest" for all undefined elements in mapping array */
6450 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6451 mapping_RND_to_EM[i] = Xalpha_quest;
6453 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6454 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6455 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6456 em_object_mapping_list[i].element_em;
6458 mapping_initialized = TRUE;
6461 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6462 return mapping_RND_to_EM[element_rnd];
6464 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6469 int map_element_EM_to_RND(int element_em)
6471 static unsigned short mapping_EM_to_RND[TILE_MAX];
6472 static boolean mapping_initialized = FALSE;
6474 if (!mapping_initialized)
6478 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6479 for (i = 0; i < TILE_MAX; i++)
6480 mapping_EM_to_RND[i] = EL_UNKNOWN;
6482 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6483 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6484 em_object_mapping_list[i].element_rnd;
6486 mapping_initialized = TRUE;
6489 if (element_em >= 0 && element_em < TILE_MAX)
6490 return mapping_EM_to_RND[element_em];
6492 Error(ERR_WARN, "invalid EM level element %d", element_em);
6497 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6499 struct LevelInfo_EM *level_em = level->native_em_level;
6500 struct LEVEL *lev = level_em->lev;
6503 for (i = 0; i < TILE_MAX; i++)
6504 lev->android_array[i] = Xblank;
6506 for (i = 0; i < level->num_android_clone_elements; i++)
6508 int element_rnd = level->android_clone_element[i];
6509 int element_em = map_element_RND_to_EM(element_rnd);
6511 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6512 if (em_object_mapping_list[j].element_rnd == element_rnd)
6513 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6517 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6519 struct LevelInfo_EM *level_em = level->native_em_level;
6520 struct LEVEL *lev = level_em->lev;
6523 level->num_android_clone_elements = 0;
6525 for (i = 0; i < TILE_MAX; i++)
6527 int element_em = lev->android_array[i];
6529 boolean element_found = FALSE;
6531 if (element_em == Xblank)
6534 element_rnd = map_element_EM_to_RND(element_em);
6536 for (j = 0; j < level->num_android_clone_elements; j++)
6537 if (level->android_clone_element[j] == element_rnd)
6538 element_found = TRUE;
6542 level->android_clone_element[level->num_android_clone_elements++] =
6545 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6550 if (level->num_android_clone_elements == 0)
6552 level->num_android_clone_elements = 1;
6553 level->android_clone_element[0] = EL_EMPTY;
6557 int map_direction_RND_to_EM(int direction)
6559 return (direction == MV_UP ? 0 :
6560 direction == MV_RIGHT ? 1 :
6561 direction == MV_DOWN ? 2 :
6562 direction == MV_LEFT ? 3 :
6566 int map_direction_EM_to_RND(int direction)
6568 return (direction == 0 ? MV_UP :
6569 direction == 1 ? MV_RIGHT :
6570 direction == 2 ? MV_DOWN :
6571 direction == 3 ? MV_LEFT :
6575 int map_element_RND_to_SP(int element_rnd)
6577 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6579 if (element_rnd >= EL_SP_START &&
6580 element_rnd <= EL_SP_END)
6581 element_sp = element_rnd - EL_SP_START;
6582 else if (element_rnd == EL_EMPTY_SPACE)
6584 else if (element_rnd == EL_INVISIBLE_WALL)
6590 int map_element_SP_to_RND(int element_sp)
6592 int element_rnd = EL_UNKNOWN;
6594 if (element_sp >= 0x00 &&
6596 element_rnd = EL_SP_START + element_sp;
6597 else if (element_sp == 0x28)
6598 element_rnd = EL_INVISIBLE_WALL;
6603 int map_action_SP_to_RND(int action_sp)
6607 case actActive: return ACTION_ACTIVE;
6608 case actImpact: return ACTION_IMPACT;
6609 case actExploding: return ACTION_EXPLODING;
6610 case actDigging: return ACTION_DIGGING;
6611 case actSnapping: return ACTION_SNAPPING;
6612 case actCollecting: return ACTION_COLLECTING;
6613 case actPassing: return ACTION_PASSING;
6614 case actPushing: return ACTION_PUSHING;
6615 case actDropping: return ACTION_DROPPING;
6617 default: return ACTION_DEFAULT;
6621 int get_next_element(int element)
6625 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6626 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6627 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6628 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6629 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6630 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6631 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6632 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6633 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6634 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6635 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6637 default: return element;
6641 int el_act_dir2img(int element, int action, int direction)
6643 element = GFX_ELEMENT(element);
6644 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6646 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6647 return element_info[element].direction_graphic[action][direction];
6650 static int el_act_dir2crm(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_crumbled[action][direction];
6659 int el_act2img(int element, int action)
6661 element = GFX_ELEMENT(element);
6663 return element_info[element].graphic[action];
6666 int el_act2crm(int element, int action)
6668 element = GFX_ELEMENT(element);
6670 return element_info[element].crumbled[action];
6673 int el_dir2img(int element, int direction)
6675 element = GFX_ELEMENT(element);
6677 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6680 int el2baseimg(int element)
6682 return element_info[element].graphic[ACTION_DEFAULT];
6685 int el2img(int element)
6687 element = GFX_ELEMENT(element);
6689 return element_info[element].graphic[ACTION_DEFAULT];
6692 int el2edimg(int element)
6694 element = GFX_ELEMENT(element);
6696 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6699 int el2preimg(int element)
6701 element = GFX_ELEMENT(element);
6703 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6706 int el2panelimg(int element)
6708 element = GFX_ELEMENT(element);
6710 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6713 int font2baseimg(int font_nr)
6715 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6718 int getBeltNrFromBeltElement(int element)
6720 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6721 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6722 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6725 int getBeltNrFromBeltActiveElement(int element)
6727 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6728 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6729 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6732 int getBeltNrFromBeltSwitchElement(int element)
6734 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6735 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6736 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6739 int getBeltDirNrFromBeltElement(int element)
6741 static int belt_base_element[4] =
6743 EL_CONVEYOR_BELT_1_LEFT,
6744 EL_CONVEYOR_BELT_2_LEFT,
6745 EL_CONVEYOR_BELT_3_LEFT,
6746 EL_CONVEYOR_BELT_4_LEFT
6749 int belt_nr = getBeltNrFromBeltElement(element);
6750 int belt_dir_nr = element - belt_base_element[belt_nr];
6752 return (belt_dir_nr % 3);
6755 int getBeltDirNrFromBeltSwitchElement(int element)
6757 static int belt_base_element[4] =
6759 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6760 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6761 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6762 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6765 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6766 int belt_dir_nr = element - belt_base_element[belt_nr];
6768 return (belt_dir_nr % 3);
6771 int getBeltDirFromBeltElement(int element)
6773 static int belt_move_dir[3] =
6780 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6782 return belt_move_dir[belt_dir_nr];
6785 int getBeltDirFromBeltSwitchElement(int element)
6787 static int belt_move_dir[3] =
6794 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6796 return belt_move_dir[belt_dir_nr];
6799 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6801 static int belt_base_element[4] =
6803 EL_CONVEYOR_BELT_1_LEFT,
6804 EL_CONVEYOR_BELT_2_LEFT,
6805 EL_CONVEYOR_BELT_3_LEFT,
6806 EL_CONVEYOR_BELT_4_LEFT
6809 return belt_base_element[belt_nr] + belt_dir_nr;
6812 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6814 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6816 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6819 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6821 static int belt_base_element[4] =
6823 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6824 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6825 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6826 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6829 return belt_base_element[belt_nr] + belt_dir_nr;
6832 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6834 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6836 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6839 boolean getTeamMode_EM()
6841 return game.team_mode;
6844 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6846 int game_frame_delay_value;
6848 game_frame_delay_value =
6849 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6850 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6853 if (tape.playing && tape.warp_forward && !tape.pausing)
6854 game_frame_delay_value = 0;
6856 return game_frame_delay_value;
6859 unsigned int InitRND(int seed)
6861 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6862 return InitEngineRandom_EM(seed);
6863 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6864 return InitEngineRandom_SP(seed);
6866 return InitEngineRandom_RND(seed);
6869 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6870 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6872 inline static int get_effective_element_EM(int tile, int frame_em)
6874 int element = object_mapping[tile].element_rnd;
6875 int action = object_mapping[tile].action;
6876 boolean is_backside = object_mapping[tile].is_backside;
6877 boolean action_removing = (action == ACTION_DIGGING ||
6878 action == ACTION_SNAPPING ||
6879 action == ACTION_COLLECTING);
6885 case Yacid_splash_eB:
6886 case Yacid_splash_wB:
6887 return (frame_em > 5 ? EL_EMPTY : element);
6893 else /* frame_em == 7 */
6897 case Yacid_splash_eB:
6898 case Yacid_splash_wB:
6901 case Yemerald_stone:
6904 case Ydiamond_stone:
6908 case Xdrip_stretchB:
6927 case Xsand_stonein_1:
6928 case Xsand_stonein_2:
6929 case Xsand_stonein_3:
6930 case Xsand_stonein_4:
6934 return (is_backside || action_removing ? EL_EMPTY : element);
6939 inline static boolean check_linear_animation_EM(int tile)
6943 case Xsand_stonesand_1:
6944 case Xsand_stonesand_quickout_1:
6945 case Xsand_sandstone_1:
6946 case Xsand_stonein_1:
6947 case Xsand_stoneout_1:
6966 case Yacid_splash_eB:
6967 case Yacid_splash_wB:
6968 case Yemerald_stone:
6975 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
6976 boolean has_crumbled_graphics,
6977 int crumbled, int sync_frame)
6979 /* if element can be crumbled, but certain action graphics are just empty
6980 space (like instantly snapping sand to empty space in 1 frame), do not
6981 treat these empty space graphics as crumbled graphics in EMC engine */
6982 if (crumbled == IMG_EMPTY_SPACE)
6983 has_crumbled_graphics = FALSE;
6985 if (has_crumbled_graphics)
6987 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
6988 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
6989 g_crumbled->anim_delay,
6990 g_crumbled->anim_mode,
6991 g_crumbled->anim_start_frame,
6994 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
6995 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
6997 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
6999 g_em->has_crumbled_graphics = TRUE;
7003 g_em->crumbled_bitmap = NULL;
7004 g_em->crumbled_src_x = 0;
7005 g_em->crumbled_src_y = 0;
7006 g_em->crumbled_border_size = 0;
7008 g_em->has_crumbled_graphics = FALSE;
7012 void ResetGfxAnimation_EM(int x, int y, int tile)
7017 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7018 int tile, int frame_em, int x, int y)
7020 int action = object_mapping[tile].action;
7021 int direction = object_mapping[tile].direction;
7022 int effective_element = get_effective_element_EM(tile, frame_em);
7023 int graphic = (direction == MV_NONE ?
7024 el_act2img(effective_element, action) :
7025 el_act_dir2img(effective_element, action, direction));
7026 struct GraphicInfo *g = &graphic_info[graphic];
7028 boolean action_removing = (action == ACTION_DIGGING ||
7029 action == ACTION_SNAPPING ||
7030 action == ACTION_COLLECTING);
7031 boolean action_moving = (action == ACTION_FALLING ||
7032 action == ACTION_MOVING ||
7033 action == ACTION_PUSHING ||
7034 action == ACTION_EATING ||
7035 action == ACTION_FILLING ||
7036 action == ACTION_EMPTYING);
7037 boolean action_falling = (action == ACTION_FALLING ||
7038 action == ACTION_FILLING ||
7039 action == ACTION_EMPTYING);
7041 /* special case: graphic uses "2nd movement tile" and has defined
7042 7 frames for movement animation (or less) => use default graphic
7043 for last (8th) frame which ends the movement animation */
7044 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7046 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7047 graphic = (direction == MV_NONE ?
7048 el_act2img(effective_element, action) :
7049 el_act_dir2img(effective_element, action, direction));
7051 g = &graphic_info[graphic];
7054 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7058 else if (action_moving)
7060 boolean is_backside = object_mapping[tile].is_backside;
7064 int direction = object_mapping[tile].direction;
7065 int move_dir = (action_falling ? MV_DOWN : direction);
7070 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7071 if (g->double_movement && frame_em == 0)
7075 if (move_dir == MV_LEFT)
7076 GfxFrame[x - 1][y] = GfxFrame[x][y];
7077 else if (move_dir == MV_RIGHT)
7078 GfxFrame[x + 1][y] = GfxFrame[x][y];
7079 else if (move_dir == MV_UP)
7080 GfxFrame[x][y - 1] = GfxFrame[x][y];
7081 else if (move_dir == MV_DOWN)
7082 GfxFrame[x][y + 1] = GfxFrame[x][y];
7089 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7090 if (tile == Xsand_stonesand_quickout_1 ||
7091 tile == Xsand_stonesand_quickout_2)
7095 if (graphic_info[graphic].anim_global_sync)
7096 sync_frame = FrameCounter;
7097 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7098 sync_frame = GfxFrame[x][y];
7100 sync_frame = 0; /* playfield border (pseudo steel) */
7102 SetRandomAnimationValue(x, y);
7104 int frame = getAnimationFrame(g->anim_frames,
7107 g->anim_start_frame,
7110 g_em->unique_identifier =
7111 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7114 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7115 int tile, int frame_em, int x, int y)
7117 int action = object_mapping[tile].action;
7118 int direction = object_mapping[tile].direction;
7119 boolean is_backside = object_mapping[tile].is_backside;
7120 int effective_element = get_effective_element_EM(tile, frame_em);
7121 int effective_action = action;
7122 int graphic = (direction == MV_NONE ?
7123 el_act2img(effective_element, effective_action) :
7124 el_act_dir2img(effective_element, effective_action,
7126 int crumbled = (direction == MV_NONE ?
7127 el_act2crm(effective_element, effective_action) :
7128 el_act_dir2crm(effective_element, effective_action,
7130 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7131 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7132 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7133 struct GraphicInfo *g = &graphic_info[graphic];
7136 /* special case: graphic uses "2nd movement tile" and has defined
7137 7 frames for movement animation (or less) => use default graphic
7138 for last (8th) frame which ends the movement animation */
7139 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7141 effective_action = ACTION_DEFAULT;
7142 graphic = (direction == MV_NONE ?
7143 el_act2img(effective_element, effective_action) :
7144 el_act_dir2img(effective_element, effective_action,
7146 crumbled = (direction == MV_NONE ?
7147 el_act2crm(effective_element, effective_action) :
7148 el_act_dir2crm(effective_element, effective_action,
7151 g = &graphic_info[graphic];
7154 if (graphic_info[graphic].anim_global_sync)
7155 sync_frame = FrameCounter;
7156 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7157 sync_frame = GfxFrame[x][y];
7159 sync_frame = 0; /* playfield border (pseudo steel) */
7161 SetRandomAnimationValue(x, y);
7163 int frame = getAnimationFrame(g->anim_frames,
7166 g->anim_start_frame,
7169 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7170 g->double_movement && is_backside);
7172 /* (updating the "crumbled" graphic definitions is probably not really needed,
7173 as animations for crumbled graphics can't be longer than one EMC cycle) */
7174 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7178 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7179 int player_nr, int anim, int frame_em)
7181 int element = player_mapping[player_nr][anim].element_rnd;
7182 int action = player_mapping[player_nr][anim].action;
7183 int direction = player_mapping[player_nr][anim].direction;
7184 int graphic = (direction == MV_NONE ?
7185 el_act2img(element, action) :
7186 el_act_dir2img(element, action, direction));
7187 struct GraphicInfo *g = &graphic_info[graphic];
7190 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7192 stored_player[player_nr].StepFrame = frame_em;
7194 sync_frame = stored_player[player_nr].Frame;
7196 int frame = getAnimationFrame(g->anim_frames,
7199 g->anim_start_frame,
7202 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7203 &g_em->src_x, &g_em->src_y, FALSE);
7206 void InitGraphicInfo_EM(void)
7211 int num_em_gfx_errors = 0;
7213 if (graphic_info_em_object[0][0].bitmap == NULL)
7215 /* EM graphics not yet initialized in em_open_all() */
7220 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7223 /* always start with reliable default values */
7224 for (i = 0; i < TILE_MAX; i++)
7226 object_mapping[i].element_rnd = EL_UNKNOWN;
7227 object_mapping[i].is_backside = FALSE;
7228 object_mapping[i].action = ACTION_DEFAULT;
7229 object_mapping[i].direction = MV_NONE;
7232 /* always start with reliable default values */
7233 for (p = 0; p < MAX_PLAYERS; p++)
7235 for (i = 0; i < SPR_MAX; i++)
7237 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7238 player_mapping[p][i].action = ACTION_DEFAULT;
7239 player_mapping[p][i].direction = MV_NONE;
7243 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7245 int e = em_object_mapping_list[i].element_em;
7247 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7248 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7250 if (em_object_mapping_list[i].action != -1)
7251 object_mapping[e].action = em_object_mapping_list[i].action;
7253 if (em_object_mapping_list[i].direction != -1)
7254 object_mapping[e].direction =
7255 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7258 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7260 int a = em_player_mapping_list[i].action_em;
7261 int p = em_player_mapping_list[i].player_nr;
7263 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7265 if (em_player_mapping_list[i].action != -1)
7266 player_mapping[p][a].action = em_player_mapping_list[i].action;
7268 if (em_player_mapping_list[i].direction != -1)
7269 player_mapping[p][a].direction =
7270 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7273 for (i = 0; i < TILE_MAX; i++)
7275 int element = object_mapping[i].element_rnd;
7276 int action = object_mapping[i].action;
7277 int direction = object_mapping[i].direction;
7278 boolean is_backside = object_mapping[i].is_backside;
7279 boolean action_exploding = ((action == ACTION_EXPLODING ||
7280 action == ACTION_SMASHED_BY_ROCK ||
7281 action == ACTION_SMASHED_BY_SPRING) &&
7282 element != EL_DIAMOND);
7283 boolean action_active = (action == ACTION_ACTIVE);
7284 boolean action_other = (action == ACTION_OTHER);
7286 for (j = 0; j < 8; j++)
7288 int effective_element = get_effective_element_EM(i, j);
7289 int effective_action = (j < 7 ? action :
7290 i == Xdrip_stretch ? action :
7291 i == Xdrip_stretchB ? action :
7292 i == Ydrip_s1 ? action :
7293 i == Ydrip_s1B ? action :
7294 i == Xball_1B ? action :
7295 i == Xball_2 ? action :
7296 i == Xball_2B ? action :
7297 i == Yball_eat ? action :
7298 i == Ykey_1_eat ? action :
7299 i == Ykey_2_eat ? action :
7300 i == Ykey_3_eat ? action :
7301 i == Ykey_4_eat ? action :
7302 i == Ykey_5_eat ? action :
7303 i == Ykey_6_eat ? action :
7304 i == Ykey_7_eat ? action :
7305 i == Ykey_8_eat ? action :
7306 i == Ylenses_eat ? action :
7307 i == Ymagnify_eat ? action :
7308 i == Ygrass_eat ? action :
7309 i == Ydirt_eat ? action :
7310 i == Xsand_stonein_1 ? action :
7311 i == Xsand_stonein_2 ? action :
7312 i == Xsand_stonein_3 ? action :
7313 i == Xsand_stonein_4 ? action :
7314 i == Xsand_stoneout_1 ? action :
7315 i == Xsand_stoneout_2 ? action :
7316 i == Xboom_android ? ACTION_EXPLODING :
7317 action_exploding ? ACTION_EXPLODING :
7318 action_active ? action :
7319 action_other ? action :
7321 int graphic = (el_act_dir2img(effective_element, effective_action,
7323 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7325 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7326 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7327 boolean has_action_graphics = (graphic != base_graphic);
7328 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7329 struct GraphicInfo *g = &graphic_info[graphic];
7330 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7333 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7334 boolean special_animation = (action != ACTION_DEFAULT &&
7335 g->anim_frames == 3 &&
7336 g->anim_delay == 2 &&
7337 g->anim_mode & ANIM_LINEAR);
7338 int sync_frame = (i == Xdrip_stretch ? 7 :
7339 i == Xdrip_stretchB ? 7 :
7340 i == Ydrip_s2 ? j + 8 :
7341 i == Ydrip_s2B ? j + 8 :
7350 i == Xfake_acid_1 ? 0 :
7351 i == Xfake_acid_2 ? 10 :
7352 i == Xfake_acid_3 ? 20 :
7353 i == Xfake_acid_4 ? 30 :
7354 i == Xfake_acid_5 ? 40 :
7355 i == Xfake_acid_6 ? 50 :
7356 i == Xfake_acid_7 ? 60 :
7357 i == Xfake_acid_8 ? 70 :
7359 i == Xball_2B ? j + 8 :
7360 i == Yball_eat ? j + 1 :
7361 i == Ykey_1_eat ? j + 1 :
7362 i == Ykey_2_eat ? j + 1 :
7363 i == Ykey_3_eat ? j + 1 :
7364 i == Ykey_4_eat ? j + 1 :
7365 i == Ykey_5_eat ? j + 1 :
7366 i == Ykey_6_eat ? j + 1 :
7367 i == Ykey_7_eat ? j + 1 :
7368 i == Ykey_8_eat ? j + 1 :
7369 i == Ylenses_eat ? j + 1 :
7370 i == Ymagnify_eat ? j + 1 :
7371 i == Ygrass_eat ? j + 1 :
7372 i == Ydirt_eat ? j + 1 :
7373 i == Xamoeba_1 ? 0 :
7374 i == Xamoeba_2 ? 1 :
7375 i == Xamoeba_3 ? 2 :
7376 i == Xamoeba_4 ? 3 :
7377 i == Xamoeba_5 ? 0 :
7378 i == Xamoeba_6 ? 1 :
7379 i == Xamoeba_7 ? 2 :
7380 i == Xamoeba_8 ? 3 :
7381 i == Xexit_2 ? j + 8 :
7382 i == Xexit_3 ? j + 16 :
7383 i == Xdynamite_1 ? 0 :
7384 i == Xdynamite_2 ? 8 :
7385 i == Xdynamite_3 ? 16 :
7386 i == Xdynamite_4 ? 24 :
7387 i == Xsand_stonein_1 ? j + 1 :
7388 i == Xsand_stonein_2 ? j + 9 :
7389 i == Xsand_stonein_3 ? j + 17 :
7390 i == Xsand_stonein_4 ? j + 25 :
7391 i == Xsand_stoneout_1 && j == 0 ? 0 :
7392 i == Xsand_stoneout_1 && j == 1 ? 0 :
7393 i == Xsand_stoneout_1 && j == 2 ? 1 :
7394 i == Xsand_stoneout_1 && j == 3 ? 2 :
7395 i == Xsand_stoneout_1 && j == 4 ? 2 :
7396 i == Xsand_stoneout_1 && j == 5 ? 3 :
7397 i == Xsand_stoneout_1 && j == 6 ? 4 :
7398 i == Xsand_stoneout_1 && j == 7 ? 4 :
7399 i == Xsand_stoneout_2 && j == 0 ? 5 :
7400 i == Xsand_stoneout_2 && j == 1 ? 6 :
7401 i == Xsand_stoneout_2 && j == 2 ? 7 :
7402 i == Xsand_stoneout_2 && j == 3 ? 8 :
7403 i == Xsand_stoneout_2 && j == 4 ? 9 :
7404 i == Xsand_stoneout_2 && j == 5 ? 11 :
7405 i == Xsand_stoneout_2 && j == 6 ? 13 :
7406 i == Xsand_stoneout_2 && j == 7 ? 15 :
7407 i == Xboom_bug && j == 1 ? 2 :
7408 i == Xboom_bug && j == 2 ? 2 :
7409 i == Xboom_bug && j == 3 ? 4 :
7410 i == Xboom_bug && j == 4 ? 4 :
7411 i == Xboom_bug && j == 5 ? 2 :
7412 i == Xboom_bug && j == 6 ? 2 :
7413 i == Xboom_bug && j == 7 ? 0 :
7414 i == Xboom_bomb && j == 1 ? 2 :
7415 i == Xboom_bomb && j == 2 ? 2 :
7416 i == Xboom_bomb && j == 3 ? 4 :
7417 i == Xboom_bomb && j == 4 ? 4 :
7418 i == Xboom_bomb && j == 5 ? 2 :
7419 i == Xboom_bomb && j == 6 ? 2 :
7420 i == Xboom_bomb && j == 7 ? 0 :
7421 i == Xboom_android && j == 7 ? 6 :
7422 i == Xboom_1 && j == 1 ? 2 :
7423 i == Xboom_1 && j == 2 ? 2 :
7424 i == Xboom_1 && j == 3 ? 4 :
7425 i == Xboom_1 && j == 4 ? 4 :
7426 i == Xboom_1 && j == 5 ? 6 :
7427 i == Xboom_1 && j == 6 ? 6 :
7428 i == Xboom_1 && j == 7 ? 8 :
7429 i == Xboom_2 && j == 0 ? 8 :
7430 i == Xboom_2 && j == 1 ? 8 :
7431 i == Xboom_2 && j == 2 ? 10 :
7432 i == Xboom_2 && j == 3 ? 10 :
7433 i == Xboom_2 && j == 4 ? 10 :
7434 i == Xboom_2 && j == 5 ? 12 :
7435 i == Xboom_2 && j == 6 ? 12 :
7436 i == Xboom_2 && j == 7 ? 12 :
7437 special_animation && j == 4 ? 3 :
7438 effective_action != action ? 0 :
7442 Bitmap *debug_bitmap = g_em->bitmap;
7443 int debug_src_x = g_em->src_x;
7444 int debug_src_y = g_em->src_y;
7447 int frame = getAnimationFrame(g->anim_frames,
7450 g->anim_start_frame,
7453 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7454 g->double_movement && is_backside);
7456 g_em->bitmap = src_bitmap;
7457 g_em->src_x = src_x;
7458 g_em->src_y = src_y;
7459 g_em->src_offset_x = 0;
7460 g_em->src_offset_y = 0;
7461 g_em->dst_offset_x = 0;
7462 g_em->dst_offset_y = 0;
7463 g_em->width = TILEX;
7464 g_em->height = TILEY;
7466 g_em->preserve_background = FALSE;
7468 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7471 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7472 effective_action == ACTION_MOVING ||
7473 effective_action == ACTION_PUSHING ||
7474 effective_action == ACTION_EATING)) ||
7475 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7476 effective_action == ACTION_EMPTYING)))
7479 (effective_action == ACTION_FALLING ||
7480 effective_action == ACTION_FILLING ||
7481 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7482 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7483 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7484 int num_steps = (i == Ydrip_s1 ? 16 :
7485 i == Ydrip_s1B ? 16 :
7486 i == Ydrip_s2 ? 16 :
7487 i == Ydrip_s2B ? 16 :
7488 i == Xsand_stonein_1 ? 32 :
7489 i == Xsand_stonein_2 ? 32 :
7490 i == Xsand_stonein_3 ? 32 :
7491 i == Xsand_stonein_4 ? 32 :
7492 i == Xsand_stoneout_1 ? 16 :
7493 i == Xsand_stoneout_2 ? 16 : 8);
7494 int cx = ABS(dx) * (TILEX / num_steps);
7495 int cy = ABS(dy) * (TILEY / num_steps);
7496 int step_frame = (i == Ydrip_s2 ? j + 8 :
7497 i == Ydrip_s2B ? j + 8 :
7498 i == Xsand_stonein_2 ? j + 8 :
7499 i == Xsand_stonein_3 ? j + 16 :
7500 i == Xsand_stonein_4 ? j + 24 :
7501 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7502 int step = (is_backside ? step_frame : num_steps - step_frame);
7504 if (is_backside) /* tile where movement starts */
7506 if (dx < 0 || dy < 0)
7508 g_em->src_offset_x = cx * step;
7509 g_em->src_offset_y = cy * step;
7513 g_em->dst_offset_x = cx * step;
7514 g_em->dst_offset_y = cy * step;
7517 else /* tile where movement ends */
7519 if (dx < 0 || dy < 0)
7521 g_em->dst_offset_x = cx * step;
7522 g_em->dst_offset_y = cy * step;
7526 g_em->src_offset_x = cx * step;
7527 g_em->src_offset_y = cy * step;
7531 g_em->width = TILEX - cx * step;
7532 g_em->height = TILEY - cy * step;
7535 /* create unique graphic identifier to decide if tile must be redrawn */
7536 /* bit 31 - 16 (16 bit): EM style graphic
7537 bit 15 - 12 ( 4 bit): EM style frame
7538 bit 11 - 6 ( 6 bit): graphic width
7539 bit 5 - 0 ( 6 bit): graphic height */
7540 g_em->unique_identifier =
7541 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7545 /* skip check for EMC elements not contained in original EMC artwork */
7546 if (element == EL_EMC_FAKE_ACID)
7549 if (g_em->bitmap != debug_bitmap ||
7550 g_em->src_x != debug_src_x ||
7551 g_em->src_y != debug_src_y ||
7552 g_em->src_offset_x != 0 ||
7553 g_em->src_offset_y != 0 ||
7554 g_em->dst_offset_x != 0 ||
7555 g_em->dst_offset_y != 0 ||
7556 g_em->width != TILEX ||
7557 g_em->height != TILEY)
7559 static int last_i = -1;
7567 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7568 i, element, element_info[element].token_name,
7569 element_action_info[effective_action].suffix, direction);
7571 if (element != effective_element)
7572 printf(" [%d ('%s')]",
7574 element_info[effective_element].token_name);
7578 if (g_em->bitmap != debug_bitmap)
7579 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7580 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7582 if (g_em->src_x != debug_src_x ||
7583 g_em->src_y != debug_src_y)
7584 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7585 j, (is_backside ? 'B' : 'F'),
7586 g_em->src_x, g_em->src_y,
7587 g_em->src_x / 32, g_em->src_y / 32,
7588 debug_src_x, debug_src_y,
7589 debug_src_x / 32, debug_src_y / 32);
7591 if (g_em->src_offset_x != 0 ||
7592 g_em->src_offset_y != 0 ||
7593 g_em->dst_offset_x != 0 ||
7594 g_em->dst_offset_y != 0)
7595 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7597 g_em->src_offset_x, g_em->src_offset_y,
7598 g_em->dst_offset_x, g_em->dst_offset_y);
7600 if (g_em->width != TILEX ||
7601 g_em->height != TILEY)
7602 printf(" %d (%d): size %d,%d should be %d,%d\n",
7604 g_em->width, g_em->height, TILEX, TILEY);
7606 num_em_gfx_errors++;
7613 for (i = 0; i < TILE_MAX; i++)
7615 for (j = 0; j < 8; j++)
7617 int element = object_mapping[i].element_rnd;
7618 int action = object_mapping[i].action;
7619 int direction = object_mapping[i].direction;
7620 boolean is_backside = object_mapping[i].is_backside;
7621 int graphic_action = el_act_dir2img(element, action, direction);
7622 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7624 if ((action == ACTION_SMASHED_BY_ROCK ||
7625 action == ACTION_SMASHED_BY_SPRING ||
7626 action == ACTION_EATING) &&
7627 graphic_action == graphic_default)
7629 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7630 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7631 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7632 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7635 /* no separate animation for "smashed by rock" -- use rock instead */
7636 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7637 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7639 g_em->bitmap = g_xx->bitmap;
7640 g_em->src_x = g_xx->src_x;
7641 g_em->src_y = g_xx->src_y;
7642 g_em->src_offset_x = g_xx->src_offset_x;
7643 g_em->src_offset_y = g_xx->src_offset_y;
7644 g_em->dst_offset_x = g_xx->dst_offset_x;
7645 g_em->dst_offset_y = g_xx->dst_offset_y;
7646 g_em->width = g_xx->width;
7647 g_em->height = g_xx->height;
7648 g_em->unique_identifier = g_xx->unique_identifier;
7651 g_em->preserve_background = TRUE;
7656 for (p = 0; p < MAX_PLAYERS; p++)
7658 for (i = 0; i < SPR_MAX; i++)
7660 int element = player_mapping[p][i].element_rnd;
7661 int action = player_mapping[p][i].action;
7662 int direction = player_mapping[p][i].direction;
7664 for (j = 0; j < 8; j++)
7666 int effective_element = element;
7667 int effective_action = action;
7668 int graphic = (direction == MV_NONE ?
7669 el_act2img(effective_element, effective_action) :
7670 el_act_dir2img(effective_element, effective_action,
7672 struct GraphicInfo *g = &graphic_info[graphic];
7673 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7679 Bitmap *debug_bitmap = g_em->bitmap;
7680 int debug_src_x = g_em->src_x;
7681 int debug_src_y = g_em->src_y;
7684 int frame = getAnimationFrame(g->anim_frames,
7687 g->anim_start_frame,
7690 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7692 g_em->bitmap = src_bitmap;
7693 g_em->src_x = src_x;
7694 g_em->src_y = src_y;
7695 g_em->src_offset_x = 0;
7696 g_em->src_offset_y = 0;
7697 g_em->dst_offset_x = 0;
7698 g_em->dst_offset_y = 0;
7699 g_em->width = TILEX;
7700 g_em->height = TILEY;
7704 /* skip check for EMC elements not contained in original EMC artwork */
7705 if (element == EL_PLAYER_3 ||
7706 element == EL_PLAYER_4)
7709 if (g_em->bitmap != debug_bitmap ||
7710 g_em->src_x != debug_src_x ||
7711 g_em->src_y != debug_src_y)
7713 static int last_i = -1;
7721 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7722 p, i, element, element_info[element].token_name,
7723 element_action_info[effective_action].suffix, direction);
7725 if (element != effective_element)
7726 printf(" [%d ('%s')]",
7728 element_info[effective_element].token_name);
7732 if (g_em->bitmap != debug_bitmap)
7733 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7734 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7736 if (g_em->src_x != debug_src_x ||
7737 g_em->src_y != debug_src_y)
7738 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7740 g_em->src_x, g_em->src_y,
7741 g_em->src_x / 32, g_em->src_y / 32,
7742 debug_src_x, debug_src_y,
7743 debug_src_x / 32, debug_src_y / 32);
7745 num_em_gfx_errors++;
7755 printf("::: [%d errors found]\n", num_em_gfx_errors);
7761 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7762 boolean any_player_moving,
7763 boolean player_is_dropping)
7765 if (tape.single_step && tape.recording && !tape.pausing)
7766 if (frame == 0 && !player_is_dropping)
7767 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7770 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7771 boolean murphy_is_dropping)
7773 if (tape.single_step && tape.recording && !tape.pausing)
7774 if (murphy_is_waiting)
7775 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7778 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7779 int graphic, int sync_frame, int x, int y)
7781 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7783 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7786 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7788 return (IS_NEXT_FRAME(sync_frame, graphic));
7791 int getGraphicInfo_Delay(int graphic)
7793 return graphic_info[graphic].anim_delay;
7796 void PlayMenuSoundExt(int sound)
7798 if (sound == SND_UNDEFINED)
7801 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7802 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7805 if (IS_LOOP_SOUND(sound))
7806 PlaySoundLoop(sound);
7811 void PlayMenuSound()
7813 PlayMenuSoundExt(menu.sound[game_status]);
7816 void PlayMenuSoundStereo(int sound, int stereo_position)
7818 if (sound == SND_UNDEFINED)
7821 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7822 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7825 if (IS_LOOP_SOUND(sound))
7826 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7828 PlaySoundStereo(sound, stereo_position);
7831 void PlayMenuSoundIfLoopExt(int sound)
7833 if (sound == SND_UNDEFINED)
7836 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7837 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7840 if (IS_LOOP_SOUND(sound))
7841 PlaySoundLoop(sound);
7844 void PlayMenuSoundIfLoop()
7846 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7849 void PlayMenuMusicExt(int music)
7851 if (music == MUS_UNDEFINED)
7854 if (!setup.sound_music)
7860 void PlayMenuMusic()
7862 PlayMenuMusicExt(menu.music[game_status]);
7865 void PlaySoundActivating()
7868 PlaySound(SND_MENU_ITEM_ACTIVATING);
7872 void PlaySoundSelecting()
7875 PlaySound(SND_MENU_ITEM_SELECTING);
7879 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7881 boolean change_fullscreen = (setup.fullscreen !=
7882 video.fullscreen_enabled);
7883 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7884 !strEqual(setup.fullscreen_mode,
7885 video.fullscreen_mode_current));
7886 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7887 setup.window_scaling_percent !=
7888 video.window_scaling_percent);
7890 if (change_window_scaling_percent && video.fullscreen_enabled)
7893 if (!change_window_scaling_percent && !video.fullscreen_available)
7896 #if defined(TARGET_SDL2)
7897 if (change_window_scaling_percent)
7899 SDLSetWindowScaling(setup.window_scaling_percent);
7903 else if (change_fullscreen)
7905 SDLSetWindowFullscreen(setup.fullscreen);
7907 /* set setup value according to successfully changed fullscreen mode */
7908 setup.fullscreen = video.fullscreen_enabled;
7914 if (change_fullscreen ||
7915 change_fullscreen_mode ||
7916 change_window_scaling_percent)
7918 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
7920 /* save backbuffer content which gets lost when toggling fullscreen mode */
7921 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7923 if (change_fullscreen_mode)
7925 /* keep fullscreen, but change fullscreen mode (screen resolution) */
7926 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
7929 if (change_window_scaling_percent)
7931 /* keep window mode, but change window scaling */
7932 video.fullscreen_enabled = TRUE; /* force new window scaling */
7935 /* toggle fullscreen */
7936 ChangeVideoModeIfNeeded(setup.fullscreen);
7938 /* set setup value according to successfully changed fullscreen mode */
7939 setup.fullscreen = video.fullscreen_enabled;
7941 /* restore backbuffer content from temporary backbuffer backup bitmap */
7942 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7944 FreeBitmap(tmp_backbuffer);
7946 /* update visible window/screen */
7947 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7951 void ChangeViewportPropertiesIfNeeded()
7953 int gfx_game_mode = game_status;
7954 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
7956 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
7957 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
7958 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
7959 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
7960 int border_size = vp_playfield->border_size;
7961 int new_sx = vp_playfield->x + border_size;
7962 int new_sy = vp_playfield->y + border_size;
7963 int new_sxsize = vp_playfield->width - 2 * border_size;
7964 int new_sysize = vp_playfield->height - 2 * border_size;
7965 int new_real_sx = vp_playfield->x;
7966 int new_real_sy = vp_playfield->y;
7967 int new_full_sxsize = vp_playfield->width;
7968 int new_full_sysize = vp_playfield->height;
7969 int new_dx = vp_door_1->x;
7970 int new_dy = vp_door_1->y;
7971 int new_dxsize = vp_door_1->width;
7972 int new_dysize = vp_door_1->height;
7973 int new_vx = vp_door_2->x;
7974 int new_vy = vp_door_2->y;
7975 int new_vxsize = vp_door_2->width;
7976 int new_vysize = vp_door_2->height;
7977 int new_ex = vp_door_3->x;
7978 int new_ey = vp_door_3->y;
7979 int new_exsize = vp_door_3->width;
7980 int new_eysize = vp_door_3->height;
7981 int new_tilesize_var =
7982 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
7984 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
7985 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
7986 int new_scr_fieldx = new_sxsize / tilesize;
7987 int new_scr_fieldy = new_sysize / tilesize;
7988 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
7989 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
7990 boolean init_gfx_buffers = FALSE;
7991 boolean init_video_buffer = FALSE;
7992 boolean init_gadgets_and_toons = FALSE;
7993 boolean init_em_graphics = FALSE;
7994 boolean drawing_area_changed = FALSE;
7996 if (viewport.window.width != WIN_XSIZE ||
7997 viewport.window.height != WIN_YSIZE)
7999 WIN_XSIZE = viewport.window.width;
8000 WIN_YSIZE = viewport.window.height;
8002 init_video_buffer = TRUE;
8003 init_gfx_buffers = TRUE;
8005 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8008 if (new_scr_fieldx != SCR_FIELDX ||
8009 new_scr_fieldy != SCR_FIELDY)
8011 /* this always toggles between MAIN and GAME when using small tile size */
8013 SCR_FIELDX = new_scr_fieldx;
8014 SCR_FIELDY = new_scr_fieldy;
8016 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8027 new_sxsize != SXSIZE ||
8028 new_sysize != SYSIZE ||
8029 new_dxsize != DXSIZE ||
8030 new_dysize != DYSIZE ||
8031 new_vxsize != VXSIZE ||
8032 new_vysize != VYSIZE ||
8033 new_exsize != EXSIZE ||
8034 new_eysize != EYSIZE ||
8035 new_real_sx != REAL_SX ||
8036 new_real_sy != REAL_SY ||
8037 new_full_sxsize != FULL_SXSIZE ||
8038 new_full_sysize != FULL_SYSIZE ||
8039 new_tilesize_var != TILESIZE_VAR
8042 if (new_tilesize_var != TILESIZE_VAR)
8044 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8046 // changing tile size invalidates scroll values of engine snapshots
8047 FreeEngineSnapshot();
8049 // changing tile size requires update of graphic mapping for EM engine
8050 init_em_graphics = TRUE;
8055 new_sxsize != SXSIZE ||
8056 new_sysize != SYSIZE ||
8057 new_real_sx != REAL_SX ||
8058 new_real_sy != REAL_SY ||
8059 new_full_sxsize != FULL_SXSIZE ||
8060 new_full_sysize != FULL_SYSIZE)
8062 if (!init_video_buffer)
8063 drawing_area_changed = TRUE;
8074 SXSIZE = new_sxsize;
8075 SYSIZE = new_sysize;
8076 DXSIZE = new_dxsize;
8077 DYSIZE = new_dysize;
8078 VXSIZE = new_vxsize;
8079 VYSIZE = new_vysize;
8080 EXSIZE = new_exsize;
8081 EYSIZE = new_eysize;
8082 REAL_SX = new_real_sx;
8083 REAL_SY = new_real_sy;
8084 FULL_SXSIZE = new_full_sxsize;
8085 FULL_SYSIZE = new_full_sysize;
8086 TILESIZE_VAR = new_tilesize_var;
8088 init_gfx_buffers = TRUE;
8089 init_gadgets_and_toons = TRUE;
8091 // printf("::: viewports: init_gfx_buffers\n");
8092 // printf("::: viewports: init_gadgets_and_toons\n");
8095 if (init_gfx_buffers)
8097 // printf("::: init_gfx_buffers\n");
8099 SCR_FIELDX = new_scr_fieldx_buffers;
8100 SCR_FIELDY = new_scr_fieldy_buffers;
8104 SCR_FIELDX = new_scr_fieldx;
8105 SCR_FIELDY = new_scr_fieldy;
8107 gfx.drawing_area_changed = drawing_area_changed;
8109 SetDrawDeactivationMask(REDRAW_NONE);
8110 SetDrawBackgroundMask(REDRAW_FIELD);
8113 if (init_video_buffer)
8115 // printf("::: init_video_buffer\n");
8117 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8120 if (init_gadgets_and_toons)
8122 // printf("::: init_gadgets_and_toons\n");
8128 if (init_em_graphics)
8130 InitGraphicInfo_EM();