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 (tilesize == gfx.standard_tile_size)
1056 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1057 else if (tilesize == game.tile_size)
1058 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1060 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1062 if (g->offset_y == 0) /* frames are ordered horizontally */
1064 int max_width = g->anim_frames_per_line * g->width;
1065 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1067 src_x = pos % max_width;
1068 src_y = src_y % g->height + pos / max_width * g->height;
1070 else if (g->offset_x == 0) /* frames are ordered vertically */
1072 int max_height = g->anim_frames_per_line * g->height;
1073 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1075 src_x = src_x % g->width + pos / max_height * g->width;
1076 src_y = pos % max_height;
1078 else /* frames are ordered diagonally */
1080 src_x = src_x + frame * g->offset_x;
1081 src_y = src_y + frame * g->offset_y;
1084 *bitmap = src_bitmap;
1085 *x = src_x * tilesize / TILESIZE;
1086 *y = src_y * tilesize / TILESIZE;
1089 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1090 int *x, int *y, boolean get_backside)
1092 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1096 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1097 Bitmap **bitmap, int *x, int *y)
1099 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1102 void getFixedGraphicSource(int graphic, int frame,
1103 Bitmap **bitmap, int *x, int *y)
1105 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1108 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1110 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1113 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1114 int *x, int *y, boolean get_backside)
1116 struct GraphicInfo *g = &graphic_info[graphic];
1117 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1118 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1120 if (TILESIZE_VAR != TILESIZE)
1121 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1124 *bitmap = g->bitmap;
1126 if (g->offset_y == 0) /* frames are ordered horizontally */
1128 int max_width = g->anim_frames_per_line * g->width;
1129 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1131 *x = pos % max_width;
1132 *y = src_y % g->height + pos / max_width * g->height;
1134 else if (g->offset_x == 0) /* frames are ordered vertically */
1136 int max_height = g->anim_frames_per_line * g->height;
1137 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1139 *x = src_x % g->width + pos / max_height * g->width;
1140 *y = pos % max_height;
1142 else /* frames are ordered diagonally */
1144 *x = src_x + frame * g->offset_x;
1145 *y = src_y + frame * g->offset_y;
1149 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1151 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1154 void DrawGraphic(int x, int y, int graphic, int frame)
1157 if (!IN_SCR_FIELD(x, y))
1159 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1160 printf("DrawGraphic(): This should never happen!\n");
1165 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1168 MarkTileDirty(x, y);
1171 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1174 if (!IN_SCR_FIELD(x, y))
1176 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1177 printf("DrawGraphic(): This should never happen!\n");
1182 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1184 MarkTileDirty(x, y);
1187 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1193 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1195 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1198 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1204 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1205 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1208 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1211 if (!IN_SCR_FIELD(x, y))
1213 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1214 printf("DrawGraphicThruMask(): This should never happen!\n");
1219 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1222 MarkTileDirty(x, y);
1225 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1228 if (!IN_SCR_FIELD(x, y))
1230 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1231 printf("DrawGraphicThruMask(): This should never happen!\n");
1236 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1238 MarkTileDirty(x, y);
1241 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1247 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1249 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1253 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1254 int graphic, int frame)
1259 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1261 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dst_x, dst_y);
1264 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1266 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1268 MarkTileDirty(x / tilesize, y / tilesize);
1271 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1277 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1278 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1281 void DrawMiniGraphic(int x, int y, int graphic)
1283 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1284 MarkTileDirty(x / 2, y / 2);
1287 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1292 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1293 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1296 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1297 int graphic, int frame,
1298 int cut_mode, int mask_mode)
1303 int width = TILEX, height = TILEY;
1306 if (dx || dy) /* shifted graphic */
1308 if (x < BX1) /* object enters playfield from the left */
1315 else if (x > BX2) /* object enters playfield from the right */
1321 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1327 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1329 else if (dx) /* general horizontal movement */
1330 MarkTileDirty(x + SIGN(dx), y);
1332 if (y < BY1) /* object enters playfield from the top */
1334 if (cut_mode==CUT_BELOW) /* object completely above top border */
1342 else if (y > BY2) /* object enters playfield from the bottom */
1348 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1354 else if (dy > 0 && cut_mode == CUT_ABOVE)
1356 if (y == BY2) /* object completely above bottom border */
1362 MarkTileDirty(x, y + 1);
1363 } /* object leaves playfield to the bottom */
1364 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1366 else if (dy) /* general vertical movement */
1367 MarkTileDirty(x, y + SIGN(dy));
1371 if (!IN_SCR_FIELD(x, y))
1373 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1374 printf("DrawGraphicShifted(): This should never happen!\n");
1379 width = width * TILESIZE_VAR / TILESIZE;
1380 height = height * TILESIZE_VAR / TILESIZE;
1381 cx = cx * TILESIZE_VAR / TILESIZE;
1382 cy = cy * TILESIZE_VAR / TILESIZE;
1383 dx = dx * TILESIZE_VAR / TILESIZE;
1384 dy = dy * TILESIZE_VAR / TILESIZE;
1386 if (width > 0 && height > 0)
1388 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1393 dst_x = FX + x * TILEX_VAR + dx;
1394 dst_y = FY + y * TILEY_VAR + dy;
1396 if (mask_mode == USE_MASKING)
1397 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1400 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1403 MarkTileDirty(x, y);
1407 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1408 int graphic, int frame,
1409 int cut_mode, int mask_mode)
1414 int width = TILEX_VAR, height = TILEY_VAR;
1417 int x2 = x + SIGN(dx);
1418 int y2 = y + SIGN(dy);
1420 /* movement with two-tile animations must be sync'ed with movement position,
1421 not with current GfxFrame (which can be higher when using slow movement) */
1422 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1423 int anim_frames = graphic_info[graphic].anim_frames;
1425 /* (we also need anim_delay here for movement animations with less frames) */
1426 int anim_delay = graphic_info[graphic].anim_delay;
1427 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1429 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1430 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1432 /* re-calculate animation frame for two-tile movement animation */
1433 frame = getGraphicAnimationFrame(graphic, sync_frame);
1435 /* check if movement start graphic inside screen area and should be drawn */
1436 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1438 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1440 dst_x = FX + x1 * TILEX_VAR;
1441 dst_y = FY + y1 * TILEY_VAR;
1443 if (mask_mode == USE_MASKING)
1444 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1447 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1450 MarkTileDirty(x1, y1);
1453 /* check if movement end graphic inside screen area and should be drawn */
1454 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1456 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1458 dst_x = FX + x2 * TILEX_VAR;
1459 dst_y = FY + y2 * TILEY_VAR;
1461 if (mask_mode == USE_MASKING)
1462 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1465 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1468 MarkTileDirty(x2, y2);
1472 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1473 int graphic, int frame,
1474 int cut_mode, int mask_mode)
1478 DrawGraphic(x, y, graphic, frame);
1483 if (graphic_info[graphic].double_movement) /* EM style movement images */
1484 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1486 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1489 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1490 int frame, int cut_mode)
1492 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1495 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1496 int cut_mode, int mask_mode)
1498 int lx = LEVELX(x), ly = LEVELY(y);
1502 if (IN_LEV_FIELD(lx, ly))
1504 SetRandomAnimationValue(lx, ly);
1506 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1507 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1509 /* do not use double (EM style) movement graphic when not moving */
1510 if (graphic_info[graphic].double_movement && !dx && !dy)
1512 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1513 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1516 else /* border element */
1518 graphic = el2img(element);
1519 frame = getGraphicAnimationFrame(graphic, -1);
1522 if (element == EL_EXPANDABLE_WALL)
1524 boolean left_stopped = FALSE, right_stopped = FALSE;
1526 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1527 left_stopped = TRUE;
1528 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1529 right_stopped = TRUE;
1531 if (left_stopped && right_stopped)
1533 else if (left_stopped)
1535 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1536 frame = graphic_info[graphic].anim_frames - 1;
1538 else if (right_stopped)
1540 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1541 frame = graphic_info[graphic].anim_frames - 1;
1546 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1547 else if (mask_mode == USE_MASKING)
1548 DrawGraphicThruMask(x, y, graphic, frame);
1550 DrawGraphic(x, y, graphic, frame);
1553 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1554 int cut_mode, int mask_mode)
1556 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1557 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1558 cut_mode, mask_mode);
1561 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1564 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1567 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1570 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1573 void DrawLevelElementThruMask(int x, int y, int element)
1575 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1578 void DrawLevelFieldThruMask(int x, int y)
1580 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1583 /* !!! implementation of quicksand is totally broken !!! */
1584 #define IS_CRUMBLED_TILE(x, y, e) \
1585 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1586 !IS_MOVING(x, y) || \
1587 (e) == EL_QUICKSAND_EMPTYING || \
1588 (e) == EL_QUICKSAND_FAST_EMPTYING))
1590 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1595 int width, height, cx, cy;
1596 int sx = SCREENX(x), sy = SCREENY(y);
1597 int crumbled_border_size = graphic_info[graphic].border_size;
1600 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1602 for (i = 1; i < 4; i++)
1604 int dxx = (i & 1 ? dx : 0);
1605 int dyy = (i & 2 ? dy : 0);
1608 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1611 /* check if neighbour field is of same crumble type */
1612 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1613 graphic_info[graphic].class ==
1614 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1616 /* return if check prevents inner corner */
1617 if (same == (dxx == dx && dyy == dy))
1621 /* if we reach this point, we have an inner corner */
1623 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1625 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1626 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1627 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1628 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1630 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1631 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1634 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1639 int width, height, bx, by, cx, cy;
1640 int sx = SCREENX(x), sy = SCREENY(y);
1641 int crumbled_border_size = graphic_info[graphic].border_size;
1642 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1643 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1646 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1648 /* draw simple, sloppy, non-corner-accurate crumbled border */
1650 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1651 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1652 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1653 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1655 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1656 FX + sx * TILEX_VAR + cx,
1657 FY + sy * TILEY_VAR + cy);
1659 /* (remaining middle border part must be at least as big as corner part) */
1660 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1661 crumbled_border_size >= TILESIZE / 3)
1664 /* correct corners of crumbled border, if needed */
1666 for (i = -1; i <= 1; i += 2)
1668 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1669 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1670 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1673 /* check if neighbour field is of same crumble type */
1674 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1675 graphic_info[graphic].class ==
1676 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1678 /* no crumbled corner, but continued crumbled border */
1680 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1681 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1682 int b1 = (i == 1 ? crumbled_border_size_var :
1683 TILESIZE_VAR - 2 * crumbled_border_size_var);
1685 width = crumbled_border_size_var;
1686 height = crumbled_border_size_var;
1688 if (dir == 1 || dir == 2)
1703 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1705 FX + sx * TILEX_VAR + cx,
1706 FY + sy * TILEY_VAR + cy);
1711 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1713 int sx = SCREENX(x), sy = SCREENY(y);
1716 static int xy[4][2] =
1724 if (!IN_LEV_FIELD(x, y))
1727 element = TILE_GFX_ELEMENT(x, y);
1729 /* crumble field itself */
1730 if (IS_CRUMBLED_TILE(x, y, element))
1732 if (!IN_SCR_FIELD(sx, sy))
1735 for (i = 0; i < 4; i++)
1737 int xx = x + xy[i][0];
1738 int yy = y + xy[i][1];
1740 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1743 /* check if neighbour field is of same crumble type */
1744 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1745 graphic_info[graphic].class ==
1746 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1749 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1752 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1753 graphic_info[graphic].anim_frames == 2)
1755 for (i = 0; i < 4; i++)
1757 int dx = (i & 1 ? +1 : -1);
1758 int dy = (i & 2 ? +1 : -1);
1760 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1764 MarkTileDirty(sx, sy);
1766 else /* center field not crumbled -- crumble neighbour fields */
1768 for (i = 0; i < 4; i++)
1770 int xx = x + xy[i][0];
1771 int yy = y + xy[i][1];
1772 int sxx = sx + xy[i][0];
1773 int syy = sy + xy[i][1];
1775 if (!IN_LEV_FIELD(xx, yy) ||
1776 !IN_SCR_FIELD(sxx, syy))
1779 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1782 element = TILE_GFX_ELEMENT(xx, yy);
1784 if (!IS_CRUMBLED_TILE(xx, yy, element))
1787 graphic = el_act2crm(element, ACTION_DEFAULT);
1789 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1791 MarkTileDirty(sxx, syy);
1796 void DrawLevelFieldCrumbled(int x, int y)
1800 if (!IN_LEV_FIELD(x, y))
1803 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1804 GfxElement[x][y] != EL_UNDEFINED &&
1805 GFX_CRUMBLED(GfxElement[x][y]))
1807 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1812 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1814 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1817 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1820 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1821 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1822 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1823 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1824 int sx = SCREENX(x), sy = SCREENY(y);
1826 DrawGraphic(sx, sy, graphic1, frame1);
1827 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1830 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1832 int sx = SCREENX(x), sy = SCREENY(y);
1833 static int xy[4][2] =
1842 for (i = 0; i < 4; i++)
1844 int xx = x + xy[i][0];
1845 int yy = y + xy[i][1];
1846 int sxx = sx + xy[i][0];
1847 int syy = sy + xy[i][1];
1849 if (!IN_LEV_FIELD(xx, yy) ||
1850 !IN_SCR_FIELD(sxx, syy) ||
1851 !GFX_CRUMBLED(Feld[xx][yy]) ||
1855 DrawLevelField(xx, yy);
1859 static int getBorderElement(int x, int y)
1863 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1864 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1865 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1866 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1867 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1868 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1869 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1871 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1872 int steel_position = (x == -1 && y == -1 ? 0 :
1873 x == lev_fieldx && y == -1 ? 1 :
1874 x == -1 && y == lev_fieldy ? 2 :
1875 x == lev_fieldx && y == lev_fieldy ? 3 :
1876 x == -1 || x == lev_fieldx ? 4 :
1877 y == -1 || y == lev_fieldy ? 5 : 6);
1879 return border[steel_position][steel_type];
1882 void DrawScreenElement(int x, int y, int element)
1884 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1885 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1888 void DrawLevelElement(int x, int y, int element)
1890 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1891 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1894 void DrawScreenField(int x, int y)
1896 int lx = LEVELX(x), ly = LEVELY(y);
1897 int element, content;
1899 if (!IN_LEV_FIELD(lx, ly))
1901 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1904 element = getBorderElement(lx, ly);
1906 DrawScreenElement(x, y, element);
1911 element = Feld[lx][ly];
1912 content = Store[lx][ly];
1914 if (IS_MOVING(lx, ly))
1916 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1917 boolean cut_mode = NO_CUTTING;
1919 if (element == EL_QUICKSAND_EMPTYING ||
1920 element == EL_QUICKSAND_FAST_EMPTYING ||
1921 element == EL_MAGIC_WALL_EMPTYING ||
1922 element == EL_BD_MAGIC_WALL_EMPTYING ||
1923 element == EL_DC_MAGIC_WALL_EMPTYING ||
1924 element == EL_AMOEBA_DROPPING)
1925 cut_mode = CUT_ABOVE;
1926 else if (element == EL_QUICKSAND_FILLING ||
1927 element == EL_QUICKSAND_FAST_FILLING ||
1928 element == EL_MAGIC_WALL_FILLING ||
1929 element == EL_BD_MAGIC_WALL_FILLING ||
1930 element == EL_DC_MAGIC_WALL_FILLING)
1931 cut_mode = CUT_BELOW;
1933 if (cut_mode == CUT_ABOVE)
1934 DrawScreenElement(x, y, element);
1936 DrawScreenElement(x, y, EL_EMPTY);
1939 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1940 else if (cut_mode == NO_CUTTING)
1941 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1944 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1946 if (cut_mode == CUT_BELOW &&
1947 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1948 DrawLevelElement(lx, ly + 1, element);
1951 if (content == EL_ACID)
1953 int dir = MovDir[lx][ly];
1954 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1955 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1957 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1960 else if (IS_BLOCKED(lx, ly))
1965 boolean cut_mode = NO_CUTTING;
1966 int element_old, content_old;
1968 Blocked2Moving(lx, ly, &oldx, &oldy);
1971 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1972 MovDir[oldx][oldy] == MV_RIGHT);
1974 element_old = Feld[oldx][oldy];
1975 content_old = Store[oldx][oldy];
1977 if (element_old == EL_QUICKSAND_EMPTYING ||
1978 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1979 element_old == EL_MAGIC_WALL_EMPTYING ||
1980 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1981 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1982 element_old == EL_AMOEBA_DROPPING)
1983 cut_mode = CUT_ABOVE;
1985 DrawScreenElement(x, y, EL_EMPTY);
1988 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1990 else if (cut_mode == NO_CUTTING)
1991 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1994 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1997 else if (IS_DRAWABLE(element))
1998 DrawScreenElement(x, y, element);
2000 DrawScreenElement(x, y, EL_EMPTY);
2003 void DrawLevelField(int x, int y)
2005 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2006 DrawScreenField(SCREENX(x), SCREENY(y));
2007 else if (IS_MOVING(x, y))
2011 Moving2Blocked(x, y, &newx, &newy);
2012 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2013 DrawScreenField(SCREENX(newx), SCREENY(newy));
2015 else if (IS_BLOCKED(x, y))
2019 Blocked2Moving(x, y, &oldx, &oldy);
2020 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2021 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2025 void DrawMiniElement(int x, int y, int element)
2029 graphic = el2edimg(element);
2030 DrawMiniGraphic(x, y, graphic);
2033 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2035 int x = sx + scroll_x, y = sy + scroll_y;
2037 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2038 DrawMiniElement(sx, sy, EL_EMPTY);
2039 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2040 DrawMiniElement(sx, sy, Feld[x][y]);
2042 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2045 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2046 int x, int y, int xsize, int ysize,
2047 int tile_width, int tile_height)
2051 int dst_x = startx + x * tile_width;
2052 int dst_y = starty + y * tile_height;
2053 int width = graphic_info[graphic].width;
2054 int height = graphic_info[graphic].height;
2055 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2056 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2057 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2058 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2059 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2060 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2061 boolean draw_masked = graphic_info[graphic].draw_masked;
2063 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2065 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2067 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2071 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2072 inner_sx + (x - 1) * tile_width % inner_width);
2073 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2074 inner_sy + (y - 1) * tile_height % inner_height);
2077 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2080 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2084 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2085 int x, int y, int xsize, int ysize, int font_nr)
2087 int font_width = getFontWidth(font_nr);
2088 int font_height = getFontHeight(font_nr);
2090 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2091 font_width, font_height);
2094 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2096 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2097 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2098 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2099 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2100 boolean no_delay = (tape.warp_forward);
2101 unsigned int anim_delay = 0;
2102 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2103 int anim_delay_value = (no_delay ? 0 : frame_delay_value);
2104 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2105 int font_width = getFontWidth(font_nr);
2106 int font_height = getFontHeight(font_nr);
2107 int max_xsize = level.envelope[envelope_nr].xsize;
2108 int max_ysize = level.envelope[envelope_nr].ysize;
2109 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2110 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2111 int xend = max_xsize;
2112 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2113 int xstep = (xstart < xend ? 1 : 0);
2114 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2117 for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2119 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2120 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2121 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2122 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2125 SetDrawtoField(DRAW_BUFFERED);
2127 BlitScreenToBitmap(backbuffer);
2129 SetDrawtoField(DRAW_BACKBUFFER);
2131 for (yy = 0; yy < ysize; yy++)
2132 for (xx = 0; xx < xsize; xx++)
2133 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2135 DrawTextBuffer(sx + font_width, sy + font_height,
2136 level.envelope[envelope_nr].text, font_nr, max_xsize,
2137 xsize - 2, ysize - 2, 0, mask_mode,
2138 level.envelope[envelope_nr].autowrap,
2139 level.envelope[envelope_nr].centered, FALSE);
2141 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2144 WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2148 void ShowEnvelope(int envelope_nr)
2150 int element = EL_ENVELOPE_1 + envelope_nr;
2151 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2152 int sound_opening = element_info[element].sound[ACTION_OPENING];
2153 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2154 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2155 boolean no_delay = (tape.warp_forward);
2156 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2157 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2158 int anim_mode = graphic_info[graphic].anim_mode;
2159 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2160 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2162 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2164 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2166 if (anim_mode == ANIM_DEFAULT)
2167 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2169 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2172 Delay(wait_delay_value);
2174 WaitForEventToContinue();
2176 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2178 if (anim_mode != ANIM_NONE)
2179 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2181 if (anim_mode == ANIM_DEFAULT)
2182 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2184 game.envelope_active = FALSE;
2186 SetDrawtoField(DRAW_BUFFERED);
2188 redraw_mask |= REDRAW_FIELD;
2192 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2194 int border_size = request.border_size;
2195 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2196 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2197 int sx = sx_center - request.width / 2;
2198 int sy = sy_center - request.height / 2;
2200 if (add_border_size)
2210 void DrawEnvelopeRequest(char *text)
2212 char *text_final = text;
2213 char *text_door_style = NULL;
2214 int graphic = IMG_BACKGROUND_REQUEST;
2215 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2216 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2217 int font_nr = FONT_REQUEST;
2218 int font_width = getFontWidth(font_nr);
2219 int font_height = getFontHeight(font_nr);
2220 int border_size = request.border_size;
2221 int line_spacing = request.line_spacing;
2222 int line_height = font_height + line_spacing;
2223 int text_width = request.width - 2 * border_size;
2224 int text_height = request.height - 2 * border_size;
2225 int line_length = text_width / font_width;
2226 int max_lines = text_height / line_height;
2227 int width = request.width;
2228 int height = request.height;
2229 int tile_size = request.step_offset;
2230 int x_steps = width / tile_size;
2231 int y_steps = height / tile_size;
2235 if (request.wrap_single_words)
2237 char *src_text_ptr, *dst_text_ptr;
2239 text_door_style = checked_malloc(2 * strlen(text) + 1);
2241 src_text_ptr = text;
2242 dst_text_ptr = text_door_style;
2244 while (*src_text_ptr)
2246 if (*src_text_ptr == ' ' ||
2247 *src_text_ptr == '?' ||
2248 *src_text_ptr == '!')
2249 *dst_text_ptr++ = '\n';
2251 if (*src_text_ptr != ' ')
2252 *dst_text_ptr++ = *src_text_ptr;
2257 *dst_text_ptr = '\0';
2259 text_final = text_door_style;
2262 setRequestPosition(&sx, &sy, FALSE);
2264 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2266 for (y = 0; y < y_steps; y++)
2267 for (x = 0; x < x_steps; x++)
2268 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2269 x, y, x_steps, y_steps,
2270 tile_size, tile_size);
2272 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2273 line_length, -1, max_lines, line_spacing, mask_mode,
2274 request.autowrap, request.centered, FALSE);
2276 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2277 RedrawGadget(tool_gadget[i]);
2279 // store readily prepared envelope request for later use when animating
2280 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2282 if (text_door_style)
2283 free(text_door_style);
2286 void AnimateEnvelopeRequest(int anim_mode, int action)
2288 int graphic = IMG_BACKGROUND_REQUEST;
2289 boolean draw_masked = graphic_info[graphic].draw_masked;
2290 int delay_value_normal = request.step_delay;
2291 int delay_value_fast = delay_value_normal / 2;
2292 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2293 boolean no_delay = (tape.warp_forward);
2294 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2295 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0);
2296 unsigned int anim_delay = 0;
2298 int width = request.width;
2299 int height = request.height;
2300 int tile_size = request.step_offset;
2301 int max_xsize = width / tile_size;
2302 int max_ysize = height / tile_size;
2303 int max_xsize_inner = max_xsize - 2;
2304 int max_ysize_inner = max_ysize - 2;
2306 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2307 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2308 int xend = max_xsize_inner;
2309 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2310 int xstep = (xstart < xend ? 1 : 0);
2311 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2314 if (setup.quick_doors)
2321 if (action == ACTION_OPENING)
2322 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2323 else if (action == ACTION_CLOSING)
2324 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2327 for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2329 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2330 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2331 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2332 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2333 int src_x = sx_center - width / 2;
2334 int src_y = sy_center - height / 2;
2335 int dst_x = sx_center - xsize * tile_size / 2;
2336 int dst_y = sy_center - ysize * tile_size / 2;
2337 int xsize_size_left = (xsize - 1) * tile_size;
2338 int ysize_size_top = (ysize - 1) * tile_size;
2339 int max_xsize_pos = (max_xsize - 1) * tile_size;
2340 int max_ysize_pos = (max_ysize - 1) * tile_size;
2343 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2345 for (yy = 0; yy < 2; yy++)
2347 for (xx = 0; xx < 2; xx++)
2349 int src_xx = src_x + xx * max_xsize_pos;
2350 int src_yy = src_y + yy * max_ysize_pos;
2351 int dst_xx = dst_x + xx * xsize_size_left;
2352 int dst_yy = dst_y + yy * ysize_size_top;
2353 int xx_size = (xx ? tile_size : xsize_size_left);
2354 int yy_size = (yy ? tile_size : ysize_size_top);
2357 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2358 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2360 BlitBitmap(bitmap_db_cross, backbuffer,
2361 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2365 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2370 WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2375 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2377 int last_game_status = game_status; /* save current game status */
2378 int graphic = IMG_BACKGROUND_REQUEST;
2379 int sound_opening = SND_REQUEST_OPENING;
2380 int sound_closing = SND_REQUEST_CLOSING;
2381 int anim_mode = graphic_info[graphic].anim_mode;
2382 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2383 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2385 if (game_status == GAME_MODE_PLAYING)
2386 BlitScreenToBitmap(backbuffer);
2388 SetDrawtoField(DRAW_BACKBUFFER);
2390 // SetDrawBackgroundMask(REDRAW_NONE);
2392 if (action == ACTION_OPENING)
2394 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2396 if (req_state & REQ_ASK)
2398 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2399 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2401 else if (req_state & REQ_CONFIRM)
2403 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2405 else if (req_state & REQ_PLAYER)
2407 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2408 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2409 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2410 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2413 DrawEnvelopeRequest(text);
2415 if (game_status != GAME_MODE_MAIN)
2419 /* force DOOR font inside door area */
2420 game_status = GAME_MODE_PSEUDO_DOOR;
2422 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2424 if (action == ACTION_OPENING)
2426 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2428 if (anim_mode == ANIM_DEFAULT)
2429 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2431 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2436 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2438 if (anim_mode != ANIM_NONE)
2439 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2441 if (anim_mode == ANIM_DEFAULT)
2442 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2445 game.envelope_active = FALSE;
2447 game_status = last_game_status; /* restore current game status */
2449 if (action == ACTION_CLOSING)
2451 if (game_status != GAME_MODE_MAIN)
2454 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2457 // SetDrawBackgroundMask(last_draw_background_mask);
2459 redraw_mask |= REDRAW_FIELD;
2461 if (game_status == GAME_MODE_MAIN)
2466 if (action == ACTION_CLOSING &&
2467 game_status == GAME_MODE_PLAYING &&
2468 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2469 SetDrawtoField(DRAW_BUFFERED);
2472 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2476 int graphic = el2preimg(element);
2478 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2479 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2482 void DrawLevel(int draw_background_mask)
2486 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2487 SetDrawBackgroundMask(draw_background_mask);
2491 for (x = BX1; x <= BX2; x++)
2492 for (y = BY1; y <= BY2; y++)
2493 DrawScreenField(x, y);
2495 redraw_mask |= REDRAW_FIELD;
2498 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2502 for (x = 0; x < size_x; x++)
2503 for (y = 0; y < size_y; y++)
2504 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2506 redraw_mask |= REDRAW_FIELD;
2509 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2511 boolean show_level_border = (BorderElement != EL_EMPTY);
2512 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2513 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2514 int tile_size = preview.tile_size;
2515 int preview_width = preview.xsize * tile_size;
2516 int preview_height = preview.ysize * tile_size;
2517 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2518 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2519 int real_preview_width = real_preview_xsize * tile_size;
2520 int real_preview_height = real_preview_ysize * tile_size;
2521 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2522 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2525 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2528 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2530 dst_x += (preview_width - real_preview_width) / 2;
2531 dst_y += (preview_height - real_preview_height) / 2;
2533 for (x = 0; x < real_preview_xsize; x++)
2535 for (y = 0; y < real_preview_ysize; y++)
2537 int lx = from_x + x + (show_level_border ? -1 : 0);
2538 int ly = from_y + y + (show_level_border ? -1 : 0);
2539 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2540 getBorderElement(lx, ly));
2542 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2543 element, tile_size);
2547 redraw_mask |= REDRAW_MICROLEVEL;
2550 #define MICROLABEL_EMPTY 0
2551 #define MICROLABEL_LEVEL_NAME 1
2552 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2553 #define MICROLABEL_LEVEL_AUTHOR 3
2554 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2555 #define MICROLABEL_IMPORTED_FROM 5
2556 #define MICROLABEL_IMPORTED_BY_HEAD 6
2557 #define MICROLABEL_IMPORTED_BY 7
2559 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2561 int max_text_width = SXSIZE;
2562 int font_width = getFontWidth(font_nr);
2564 if (pos->align == ALIGN_CENTER)
2565 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2566 else if (pos->align == ALIGN_RIGHT)
2567 max_text_width = pos->x;
2569 max_text_width = SXSIZE - pos->x;
2571 return max_text_width / font_width;
2574 static void DrawPreviewLevelLabelExt(int mode)
2576 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2577 char label_text[MAX_OUTPUT_LINESIZE + 1];
2578 int max_len_label_text;
2579 int font_nr = pos->font;
2582 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2585 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2586 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2587 mode == MICROLABEL_IMPORTED_BY_HEAD)
2588 font_nr = pos->font_alt;
2590 max_len_label_text = getMaxTextLength(pos, font_nr);
2592 if (pos->size != -1)
2593 max_len_label_text = pos->size;
2595 for (i = 0; i < max_len_label_text; i++)
2596 label_text[i] = ' ';
2597 label_text[max_len_label_text] = '\0';
2599 if (strlen(label_text) > 0)
2600 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2603 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2604 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2605 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2606 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2607 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2608 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2609 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2610 max_len_label_text);
2611 label_text[max_len_label_text] = '\0';
2613 if (strlen(label_text) > 0)
2614 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2616 redraw_mask |= REDRAW_MICROLEVEL;
2619 static void DrawPreviewLevelExt(boolean restart)
2621 static unsigned int scroll_delay = 0;
2622 static unsigned int label_delay = 0;
2623 static int from_x, from_y, scroll_direction;
2624 static int label_state, label_counter;
2625 unsigned int scroll_delay_value = preview.step_delay;
2626 boolean show_level_border = (BorderElement != EL_EMPTY);
2627 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2628 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2629 int last_game_status = game_status; /* save current game status */
2636 if (preview.anim_mode == ANIM_CENTERED)
2638 if (level_xsize > preview.xsize)
2639 from_x = (level_xsize - preview.xsize) / 2;
2640 if (level_ysize > preview.ysize)
2641 from_y = (level_ysize - preview.ysize) / 2;
2644 from_x += preview.xoffset;
2645 from_y += preview.yoffset;
2647 scroll_direction = MV_RIGHT;
2651 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2652 DrawPreviewLevelLabelExt(label_state);
2654 /* initialize delay counters */
2655 DelayReached(&scroll_delay, 0);
2656 DelayReached(&label_delay, 0);
2658 if (leveldir_current->name)
2660 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2661 char label_text[MAX_OUTPUT_LINESIZE + 1];
2662 int font_nr = pos->font;
2663 int max_len_label_text = getMaxTextLength(pos, font_nr);
2665 if (pos->size != -1)
2666 max_len_label_text = pos->size;
2668 strncpy(label_text, leveldir_current->name, max_len_label_text);
2669 label_text[max_len_label_text] = '\0';
2671 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2672 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2675 game_status = last_game_status; /* restore current game status */
2680 /* scroll preview level, if needed */
2681 if (preview.anim_mode != ANIM_NONE &&
2682 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2683 DelayReached(&scroll_delay, scroll_delay_value))
2685 switch (scroll_direction)
2690 from_x -= preview.step_offset;
2691 from_x = (from_x < 0 ? 0 : from_x);
2694 scroll_direction = MV_UP;
2698 if (from_x < level_xsize - preview.xsize)
2700 from_x += preview.step_offset;
2701 from_x = (from_x > level_xsize - preview.xsize ?
2702 level_xsize - preview.xsize : from_x);
2705 scroll_direction = MV_DOWN;
2711 from_y -= preview.step_offset;
2712 from_y = (from_y < 0 ? 0 : from_y);
2715 scroll_direction = MV_RIGHT;
2719 if (from_y < level_ysize - preview.ysize)
2721 from_y += preview.step_offset;
2722 from_y = (from_y > level_ysize - preview.ysize ?
2723 level_ysize - preview.ysize : from_y);
2726 scroll_direction = MV_LEFT;
2733 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2736 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2737 /* redraw micro level label, if needed */
2738 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2739 !strEqual(level.author, ANONYMOUS_NAME) &&
2740 !strEqual(level.author, leveldir_current->name) &&
2741 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2743 int max_label_counter = 23;
2745 if (leveldir_current->imported_from != NULL &&
2746 strlen(leveldir_current->imported_from) > 0)
2747 max_label_counter += 14;
2748 if (leveldir_current->imported_by != NULL &&
2749 strlen(leveldir_current->imported_by) > 0)
2750 max_label_counter += 14;
2752 label_counter = (label_counter + 1) % max_label_counter;
2753 label_state = (label_counter >= 0 && label_counter <= 7 ?
2754 MICROLABEL_LEVEL_NAME :
2755 label_counter >= 9 && label_counter <= 12 ?
2756 MICROLABEL_LEVEL_AUTHOR_HEAD :
2757 label_counter >= 14 && label_counter <= 21 ?
2758 MICROLABEL_LEVEL_AUTHOR :
2759 label_counter >= 23 && label_counter <= 26 ?
2760 MICROLABEL_IMPORTED_FROM_HEAD :
2761 label_counter >= 28 && label_counter <= 35 ?
2762 MICROLABEL_IMPORTED_FROM :
2763 label_counter >= 37 && label_counter <= 40 ?
2764 MICROLABEL_IMPORTED_BY_HEAD :
2765 label_counter >= 42 && label_counter <= 49 ?
2766 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2768 if (leveldir_current->imported_from == NULL &&
2769 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2770 label_state == MICROLABEL_IMPORTED_FROM))
2771 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2772 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2774 DrawPreviewLevelLabelExt(label_state);
2777 game_status = last_game_status; /* restore current game status */
2780 void DrawPreviewLevelInitial()
2782 DrawPreviewLevelExt(TRUE);
2785 void DrawPreviewLevelAnimation()
2787 DrawPreviewLevelExt(FALSE);
2790 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2791 int graphic, int sync_frame, int mask_mode)
2793 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2795 if (mask_mode == USE_MASKING)
2796 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2798 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2801 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2802 int graphic, int sync_frame,
2805 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2807 if (mask_mode == USE_MASKING)
2808 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2810 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2813 inline void DrawGraphicAnimation(int x, int y, int graphic)
2815 int lx = LEVELX(x), ly = LEVELY(y);
2817 if (!IN_SCR_FIELD(x, y))
2820 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2821 graphic, GfxFrame[lx][ly], NO_MASKING);
2823 MarkTileDirty(x, y);
2826 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2828 int lx = LEVELX(x), ly = LEVELY(y);
2830 if (!IN_SCR_FIELD(x, y))
2833 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2834 graphic, GfxFrame[lx][ly], NO_MASKING);
2835 MarkTileDirty(x, y);
2838 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2840 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2843 void DrawLevelElementAnimation(int x, int y, int element)
2845 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2847 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2850 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2852 int sx = SCREENX(x), sy = SCREENY(y);
2854 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2857 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2860 DrawGraphicAnimation(sx, sy, graphic);
2863 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2864 DrawLevelFieldCrumbled(x, y);
2866 if (GFX_CRUMBLED(Feld[x][y]))
2867 DrawLevelFieldCrumbled(x, y);
2871 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2873 int sx = SCREENX(x), sy = SCREENY(y);
2876 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2879 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2881 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2884 DrawGraphicAnimation(sx, sy, graphic);
2886 if (GFX_CRUMBLED(element))
2887 DrawLevelFieldCrumbled(x, y);
2890 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2892 if (player->use_murphy)
2894 /* this works only because currently only one player can be "murphy" ... */
2895 static int last_horizontal_dir = MV_LEFT;
2896 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2898 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2899 last_horizontal_dir = move_dir;
2901 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2903 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2905 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2911 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2914 static boolean equalGraphics(int graphic1, int graphic2)
2916 struct GraphicInfo *g1 = &graphic_info[graphic1];
2917 struct GraphicInfo *g2 = &graphic_info[graphic2];
2919 return (g1->bitmap == g2->bitmap &&
2920 g1->src_x == g2->src_x &&
2921 g1->src_y == g2->src_y &&
2922 g1->anim_frames == g2->anim_frames &&
2923 g1->anim_delay == g2->anim_delay &&
2924 g1->anim_mode == g2->anim_mode);
2927 void DrawAllPlayers()
2931 for (i = 0; i < MAX_PLAYERS; i++)
2932 if (stored_player[i].active)
2933 DrawPlayer(&stored_player[i]);
2936 void DrawPlayerField(int x, int y)
2938 if (!IS_PLAYER(x, y))
2941 DrawPlayer(PLAYERINFO(x, y));
2944 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
2946 void DrawPlayer(struct PlayerInfo *player)
2948 int jx = player->jx;
2949 int jy = player->jy;
2950 int move_dir = player->MovDir;
2951 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
2952 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
2953 int last_jx = (player->is_moving ? jx - dx : jx);
2954 int last_jy = (player->is_moving ? jy - dy : jy);
2955 int next_jx = jx + dx;
2956 int next_jy = jy + dy;
2957 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
2958 boolean player_is_opaque = FALSE;
2959 int sx = SCREENX(jx), sy = SCREENY(jy);
2960 int sxx = 0, syy = 0;
2961 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
2963 int action = ACTION_DEFAULT;
2964 int last_player_graphic = getPlayerGraphic(player, move_dir);
2965 int last_player_frame = player->Frame;
2968 /* GfxElement[][] is set to the element the player is digging or collecting;
2969 remove also for off-screen player if the player is not moving anymore */
2970 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
2971 GfxElement[jx][jy] = EL_UNDEFINED;
2973 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
2977 if (!IN_LEV_FIELD(jx, jy))
2979 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
2980 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
2981 printf("DrawPlayerField(): This should never happen!\n");
2986 if (element == EL_EXPLOSION)
2989 action = (player->is_pushing ? ACTION_PUSHING :
2990 player->is_digging ? ACTION_DIGGING :
2991 player->is_collecting ? ACTION_COLLECTING :
2992 player->is_moving ? ACTION_MOVING :
2993 player->is_snapping ? ACTION_SNAPPING :
2994 player->is_dropping ? ACTION_DROPPING :
2995 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
2997 if (player->is_waiting)
2998 move_dir = player->dir_waiting;
3000 InitPlayerGfxAnimation(player, action, move_dir);
3002 /* ----------------------------------------------------------------------- */
3003 /* draw things in the field the player is leaving, if needed */
3004 /* ----------------------------------------------------------------------- */
3006 if (player->is_moving)
3008 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3010 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3012 if (last_element == EL_DYNAMITE_ACTIVE ||
3013 last_element == EL_EM_DYNAMITE_ACTIVE ||
3014 last_element == EL_SP_DISK_RED_ACTIVE)
3015 DrawDynamite(last_jx, last_jy);
3017 DrawLevelFieldThruMask(last_jx, last_jy);
3019 else if (last_element == EL_DYNAMITE_ACTIVE ||
3020 last_element == EL_EM_DYNAMITE_ACTIVE ||
3021 last_element == EL_SP_DISK_RED_ACTIVE)
3022 DrawDynamite(last_jx, last_jy);
3024 /* !!! this is not enough to prevent flickering of players which are
3025 moving next to each others without a free tile between them -- this
3026 can only be solved by drawing all players layer by layer (first the
3027 background, then the foreground etc.) !!! => TODO */
3028 else if (!IS_PLAYER(last_jx, last_jy))
3029 DrawLevelField(last_jx, last_jy);
3032 DrawLevelField(last_jx, last_jy);
3035 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3036 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3039 if (!IN_SCR_FIELD(sx, sy))
3042 /* ----------------------------------------------------------------------- */
3043 /* draw things behind the player, if needed */
3044 /* ----------------------------------------------------------------------- */
3047 DrawLevelElement(jx, jy, Back[jx][jy]);
3048 else if (IS_ACTIVE_BOMB(element))
3049 DrawLevelElement(jx, jy, EL_EMPTY);
3052 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3054 int old_element = GfxElement[jx][jy];
3055 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3056 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3058 if (GFX_CRUMBLED(old_element))
3059 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3061 DrawGraphic(sx, sy, old_graphic, frame);
3063 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3064 player_is_opaque = TRUE;
3068 GfxElement[jx][jy] = EL_UNDEFINED;
3070 /* make sure that pushed elements are drawn with correct frame rate */
3071 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3073 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3074 GfxFrame[jx][jy] = player->StepFrame;
3076 DrawLevelField(jx, jy);
3080 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3081 /* ----------------------------------------------------------------------- */
3082 /* draw player himself */
3083 /* ----------------------------------------------------------------------- */
3085 graphic = getPlayerGraphic(player, move_dir);
3087 /* in the case of changed player action or direction, prevent the current
3088 animation frame from being restarted for identical animations */
3089 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3090 player->Frame = last_player_frame;
3092 frame = getGraphicAnimationFrame(graphic, player->Frame);
3096 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3097 sxx = player->GfxPos;
3099 syy = player->GfxPos;
3102 if (!setup.soft_scrolling && ScreenMovPos)
3105 if (player_is_opaque)
3106 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3108 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3110 if (SHIELD_ON(player))
3112 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3113 IMG_SHIELD_NORMAL_ACTIVE);
3114 int frame = getGraphicAnimationFrame(graphic, -1);
3116 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3120 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3123 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3124 sxx = player->GfxPos;
3126 syy = player->GfxPos;
3130 /* ----------------------------------------------------------------------- */
3131 /* draw things the player is pushing, if needed */
3132 /* ----------------------------------------------------------------------- */
3134 if (player->is_pushing && player->is_moving)
3136 int px = SCREENX(jx), py = SCREENY(jy);
3137 int pxx = (TILEX - ABS(sxx)) * dx;
3138 int pyy = (TILEY - ABS(syy)) * dy;
3139 int gfx_frame = GfxFrame[jx][jy];
3145 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3147 element = Feld[next_jx][next_jy];
3148 gfx_frame = GfxFrame[next_jx][next_jy];
3151 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3153 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3154 frame = getGraphicAnimationFrame(graphic, sync_frame);
3156 /* draw background element under pushed element (like the Sokoban field) */
3157 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3159 /* this allows transparent pushing animation over non-black background */
3162 DrawLevelElement(jx, jy, Back[jx][jy]);
3164 DrawLevelElement(jx, jy, EL_EMPTY);
3166 if (Back[next_jx][next_jy])
3167 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3169 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3171 else if (Back[next_jx][next_jy])
3172 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3175 /* do not draw (EM style) pushing animation when pushing is finished */
3176 /* (two-tile animations usually do not contain start and end frame) */
3177 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3178 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3180 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3182 /* masked drawing is needed for EMC style (double) movement graphics */
3183 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3184 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3188 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3189 /* ----------------------------------------------------------------------- */
3190 /* draw player himself */
3191 /* ----------------------------------------------------------------------- */
3193 graphic = getPlayerGraphic(player, move_dir);
3195 /* in the case of changed player action or direction, prevent the current
3196 animation frame from being restarted for identical animations */
3197 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3198 player->Frame = last_player_frame;
3200 frame = getGraphicAnimationFrame(graphic, player->Frame);
3204 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3205 sxx = player->GfxPos;
3207 syy = player->GfxPos;
3210 if (!setup.soft_scrolling && ScreenMovPos)
3213 if (player_is_opaque)
3214 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3216 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3218 if (SHIELD_ON(player))
3220 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3221 IMG_SHIELD_NORMAL_ACTIVE);
3222 int frame = getGraphicAnimationFrame(graphic, -1);
3224 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3228 /* ----------------------------------------------------------------------- */
3229 /* draw things in front of player (active dynamite or dynabombs) */
3230 /* ----------------------------------------------------------------------- */
3232 if (IS_ACTIVE_BOMB(element))
3234 graphic = el2img(element);
3235 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3237 if (game.emulation == EMU_SUPAPLEX)
3238 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3240 DrawGraphicThruMask(sx, sy, graphic, frame);
3243 if (player_is_moving && last_element == EL_EXPLOSION)
3245 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3246 GfxElement[last_jx][last_jy] : EL_EMPTY);
3247 int graphic = el_act2img(element, ACTION_EXPLODING);
3248 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3249 int phase = ExplodePhase[last_jx][last_jy] - 1;
3250 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3253 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3256 /* ----------------------------------------------------------------------- */
3257 /* draw elements the player is just walking/passing through/under */
3258 /* ----------------------------------------------------------------------- */
3260 if (player_is_moving)
3262 /* handle the field the player is leaving ... */
3263 if (IS_ACCESSIBLE_INSIDE(last_element))
3264 DrawLevelField(last_jx, last_jy);
3265 else if (IS_ACCESSIBLE_UNDER(last_element))
3266 DrawLevelFieldThruMask(last_jx, last_jy);
3269 /* do not redraw accessible elements if the player is just pushing them */
3270 if (!player_is_moving || !player->is_pushing)
3272 /* ... and the field the player is entering */
3273 if (IS_ACCESSIBLE_INSIDE(element))
3274 DrawLevelField(jx, jy);
3275 else if (IS_ACCESSIBLE_UNDER(element))
3276 DrawLevelFieldThruMask(jx, jy);
3279 MarkTileDirty(sx, sy);
3282 /* ------------------------------------------------------------------------- */
3284 void WaitForEventToContinue()
3286 boolean still_wait = TRUE;
3288 /* simulate releasing mouse button over last gadget, if still pressed */
3290 HandleGadgets(-1, -1, 0);
3292 button_status = MB_RELEASED;
3306 case EVENT_BUTTONPRESS:
3307 case EVENT_KEYPRESS:
3311 case EVENT_KEYRELEASE:
3312 ClearPlayerAction();
3316 HandleOtherEvents(&event);
3320 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3327 /* don't eat all CPU time */
3332 #define MAX_REQUEST_LINES 13
3333 #define MAX_REQUEST_LINE_FONT1_LEN 7
3334 #define MAX_REQUEST_LINE_FONT2_LEN 10
3336 static int RequestHandleEvents(unsigned int req_state)
3338 int last_game_status = game_status; /* save current game status */
3342 button_status = MB_RELEASED;
3344 request_gadget_id = -1;
3357 case EVENT_BUTTONPRESS:
3358 case EVENT_BUTTONRELEASE:
3359 case EVENT_MOTIONNOTIFY:
3361 if (event.type == EVENT_MOTIONNOTIFY)
3363 if (!PointerInWindow(window))
3364 continue; /* window and pointer are on different screens */
3369 motion_status = TRUE;
3370 mx = ((MotionEvent *) &event)->x;
3371 my = ((MotionEvent *) &event)->y;
3375 motion_status = FALSE;
3376 mx = ((ButtonEvent *) &event)->x;
3377 my = ((ButtonEvent *) &event)->y;
3378 if (event.type == EVENT_BUTTONPRESS)
3379 button_status = ((ButtonEvent *) &event)->button;
3381 button_status = MB_RELEASED;
3384 /* this sets 'request_gadget_id' */
3385 HandleGadgets(mx, my, button_status);
3387 switch (request_gadget_id)
3389 case TOOL_CTRL_ID_YES:
3392 case TOOL_CTRL_ID_NO:
3395 case TOOL_CTRL_ID_CONFIRM:
3396 result = TRUE | FALSE;
3399 case TOOL_CTRL_ID_PLAYER_1:
3402 case TOOL_CTRL_ID_PLAYER_2:
3405 case TOOL_CTRL_ID_PLAYER_3:
3408 case TOOL_CTRL_ID_PLAYER_4:
3419 case EVENT_KEYPRESS:
3420 switch (GetEventKey((KeyEvent *)&event, TRUE))
3423 if (req_state & REQ_CONFIRM)
3428 #if defined(TARGET_SDL2)
3435 #if defined(TARGET_SDL2)
3445 if (req_state & REQ_PLAYER)
3449 case EVENT_KEYRELEASE:
3450 ClearPlayerAction();
3454 HandleOtherEvents(&event);
3458 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3460 int joy = AnyJoystick();
3462 if (joy & JOY_BUTTON_1)
3464 else if (joy & JOY_BUTTON_2)
3468 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3470 HandleGameActions();
3476 if (!PendingEvent()) /* delay only if no pending events */
3480 game_status = GAME_MODE_PSEUDO_DOOR;
3484 game_status = last_game_status; /* restore current game status */
3490 static boolean RequestDoor(char *text, unsigned int req_state)
3492 unsigned int old_door_state;
3493 int last_game_status = game_status; /* save current game status */
3494 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3495 int font_nr = FONT_TEXT_2;
3500 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3502 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3503 font_nr = FONT_TEXT_1;
3506 if (game_status == GAME_MODE_PLAYING)
3507 BlitScreenToBitmap(backbuffer);
3509 /* disable deactivated drawing when quick-loading level tape recording */
3510 if (tape.playing && tape.deactivate_display)
3511 TapeDeactivateDisplayOff(TRUE);
3513 SetMouseCursor(CURSOR_DEFAULT);
3515 #if defined(NETWORK_AVALIABLE)
3516 /* pause network game while waiting for request to answer */
3517 if (options.network &&
3518 game_status == GAME_MODE_PLAYING &&
3519 req_state & REQUEST_WAIT_FOR_INPUT)
3520 SendToServer_PausePlaying();
3523 old_door_state = GetDoorState();
3525 /* simulate releasing mouse button over last gadget, if still pressed */
3527 HandleGadgets(-1, -1, 0);
3531 /* draw released gadget before proceeding */
3534 if (old_door_state & DOOR_OPEN_1)
3536 CloseDoor(DOOR_CLOSE_1);
3538 /* save old door content */
3539 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3540 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3543 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3544 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3546 /* clear door drawing field */
3547 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3549 /* force DOOR font inside door area */
3550 game_status = GAME_MODE_PSEUDO_DOOR;
3552 /* write text for request */
3553 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3555 char text_line[max_request_line_len + 1];
3561 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3563 tc = *(text_ptr + tx);
3564 // if (!tc || tc == ' ')
3565 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3569 if ((tc == '?' || tc == '!') && tl == 0)
3579 strncpy(text_line, text_ptr, tl);
3582 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3583 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3584 text_line, font_nr);
3586 text_ptr += tl + (tc == ' ' ? 1 : 0);
3587 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3590 game_status = last_game_status; /* restore current game status */
3592 if (req_state & REQ_ASK)
3594 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3595 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3597 else if (req_state & REQ_CONFIRM)
3599 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3601 else if (req_state & REQ_PLAYER)
3603 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3604 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3605 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3606 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3609 /* copy request gadgets to door backbuffer */
3610 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3612 OpenDoor(DOOR_OPEN_1);
3614 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3616 if (game_status == GAME_MODE_PLAYING)
3618 SetPanelBackground();
3619 SetDrawBackgroundMask(REDRAW_DOOR_1);
3623 SetDrawBackgroundMask(REDRAW_FIELD);
3629 if (game_status != GAME_MODE_MAIN)
3632 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3634 // ---------- handle request buttons ----------
3635 result = RequestHandleEvents(req_state);
3637 if (game_status != GAME_MODE_MAIN)
3642 if (!(req_state & REQ_STAY_OPEN))
3644 CloseDoor(DOOR_CLOSE_1);
3646 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3647 (req_state & REQ_REOPEN))
3648 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3653 if (game_status == GAME_MODE_PLAYING)
3655 SetPanelBackground();
3656 SetDrawBackgroundMask(REDRAW_DOOR_1);
3660 SetDrawBackgroundMask(REDRAW_FIELD);
3663 #if defined(NETWORK_AVALIABLE)
3664 /* continue network game after request */
3665 if (options.network &&
3666 game_status == GAME_MODE_PLAYING &&
3667 req_state & REQUEST_WAIT_FOR_INPUT)
3668 SendToServer_ContinuePlaying();
3671 /* restore deactivated drawing when quick-loading level tape recording */
3672 if (tape.playing && tape.deactivate_display)
3673 TapeDeactivateDisplayOn();
3678 static boolean RequestEnvelope(char *text, unsigned int req_state)
3682 if (game_status == GAME_MODE_PLAYING)
3683 BlitScreenToBitmap(backbuffer);
3685 /* disable deactivated drawing when quick-loading level tape recording */
3686 if (tape.playing && tape.deactivate_display)
3687 TapeDeactivateDisplayOff(TRUE);
3689 SetMouseCursor(CURSOR_DEFAULT);
3691 #if defined(NETWORK_AVALIABLE)
3692 /* pause network game while waiting for request to answer */
3693 if (options.network &&
3694 game_status == GAME_MODE_PLAYING &&
3695 req_state & REQUEST_WAIT_FOR_INPUT)
3696 SendToServer_PausePlaying();
3699 /* simulate releasing mouse button over last gadget, if still pressed */
3701 HandleGadgets(-1, -1, 0);
3705 // (replace with setting corresponding request background)
3706 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3707 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3709 /* clear door drawing field */
3710 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3712 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3714 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3716 if (game_status == GAME_MODE_PLAYING)
3718 SetPanelBackground();
3719 SetDrawBackgroundMask(REDRAW_DOOR_1);
3723 SetDrawBackgroundMask(REDRAW_FIELD);
3729 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3731 // ---------- handle request buttons ----------
3732 result = RequestHandleEvents(req_state);
3734 if (game_status != GAME_MODE_MAIN)
3739 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3743 if (game_status == GAME_MODE_PLAYING)
3745 SetPanelBackground();
3746 SetDrawBackgroundMask(REDRAW_DOOR_1);
3750 SetDrawBackgroundMask(REDRAW_FIELD);
3753 #if defined(NETWORK_AVALIABLE)
3754 /* continue network game after request */
3755 if (options.network &&
3756 game_status == GAME_MODE_PLAYING &&
3757 req_state & REQUEST_WAIT_FOR_INPUT)
3758 SendToServer_ContinuePlaying();
3761 /* restore deactivated drawing when quick-loading level tape recording */
3762 if (tape.playing && tape.deactivate_display)
3763 TapeDeactivateDisplayOn();
3768 boolean Request(char *text, unsigned int req_state)
3770 if (global.use_envelope_request)
3771 return RequestEnvelope(text, req_state);
3773 return RequestDoor(text, req_state);
3776 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3778 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3779 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3782 if (dpo1->sort_priority != dpo2->sort_priority)
3783 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3785 compare_result = dpo1->nr - dpo2->nr;
3787 return compare_result;
3790 void InitGraphicCompatibilityInfo_Doors()
3796 struct DoorInfo *door;
3800 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3801 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3803 { -1, -1, -1, NULL }
3805 struct Rect door_rect_list[] =
3807 { DX, DY, DXSIZE, DYSIZE },
3808 { VX, VY, VXSIZE, VYSIZE }
3812 for (i = 0; doors[i].door_token != -1; i++)
3814 int door_token = doors[i].door_token;
3815 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3816 int part_1 = doors[i].part_1;
3817 int part_8 = doors[i].part_8;
3818 int part_2 = part_1 + 1;
3819 int part_3 = part_1 + 2;
3820 struct DoorInfo *door = doors[i].door;
3821 struct Rect *door_rect = &door_rect_list[door_index];
3822 boolean door_gfx_redefined = FALSE;
3824 /* check if any door part graphic definitions have been redefined */
3826 for (j = 0; door_part_controls[j].door_token != -1; j++)
3828 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3829 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3831 if (dpc->door_token == door_token && fi->redefined)
3832 door_gfx_redefined = TRUE;
3835 /* check for old-style door graphic/animation modifications */
3837 if (!door_gfx_redefined)
3839 if (door->anim_mode & ANIM_STATIC_PANEL)
3841 door->panel.step_xoffset = 0;
3842 door->panel.step_yoffset = 0;
3845 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3847 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3848 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3849 int num_door_steps, num_panel_steps;
3851 /* remove door part graphics other than the two default wings */
3853 for (j = 0; door_part_controls[j].door_token != -1; j++)
3855 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3856 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3858 if (dpc->graphic >= part_3 &&
3859 dpc->graphic <= part_8)
3863 /* set graphics and screen positions of the default wings */
3865 g_part_1->width = door_rect->width;
3866 g_part_1->height = door_rect->height;
3867 g_part_2->width = door_rect->width;
3868 g_part_2->height = door_rect->height;
3869 g_part_2->src_x = door_rect->width;
3870 g_part_2->src_y = g_part_1->src_y;
3872 door->part_2.x = door->part_1.x;
3873 door->part_2.y = door->part_1.y;
3875 if (door->width != -1)
3877 g_part_1->width = door->width;
3878 g_part_2->width = door->width;
3880 // special treatment for graphics and screen position of right wing
3881 g_part_2->src_x += door_rect->width - door->width;
3882 door->part_2.x += door_rect->width - door->width;
3885 if (door->height != -1)
3887 g_part_1->height = door->height;
3888 g_part_2->height = door->height;
3890 // special treatment for graphics and screen position of bottom wing
3891 g_part_2->src_y += door_rect->height - door->height;
3892 door->part_2.y += door_rect->height - door->height;
3895 /* set animation delays for the default wings and panels */
3897 door->part_1.step_delay = door->step_delay;
3898 door->part_2.step_delay = door->step_delay;
3899 door->panel.step_delay = door->step_delay;
3901 /* set animation draw order for the default wings */
3903 door->part_1.sort_priority = 2; /* draw left wing over ... */
3904 door->part_2.sort_priority = 1; /* ... right wing */
3906 /* set animation draw offset for the default wings */
3908 if (door->anim_mode & ANIM_HORIZONTAL)
3910 door->part_1.step_xoffset = door->step_offset;
3911 door->part_1.step_yoffset = 0;
3912 door->part_2.step_xoffset = door->step_offset * -1;
3913 door->part_2.step_yoffset = 0;
3915 num_door_steps = g_part_1->width / door->step_offset;
3917 else // ANIM_VERTICAL
3919 door->part_1.step_xoffset = 0;
3920 door->part_1.step_yoffset = door->step_offset;
3921 door->part_2.step_xoffset = 0;
3922 door->part_2.step_yoffset = door->step_offset * -1;
3924 num_door_steps = g_part_1->height / door->step_offset;
3927 /* set animation draw offset for the default panels */
3929 if (door->step_offset > 1)
3931 num_panel_steps = 2 * door_rect->height / door->step_offset;
3932 door->panel.start_step = num_panel_steps - num_door_steps;
3936 num_panel_steps = door_rect->height / door->step_offset;
3937 door->panel.start_step = num_panel_steps - num_door_steps / 2;
3938 door->panel.step_delay *= 2;
3949 for (i = 0; door_part_controls[i].door_token != -1; i++)
3951 struct DoorPartControlInfo *dpc = &door_part_controls[i];
3952 struct DoorPartOrderInfo *dpo = &door_part_order[i];
3954 /* initialize "start_step_opening" and "start_step_closing", if needed */
3955 if (dpc->pos->start_step_opening == 0 &&
3956 dpc->pos->start_step_closing == 0)
3958 // dpc->pos->start_step_opening = dpc->pos->start_step;
3959 dpc->pos->start_step_closing = dpc->pos->start_step;
3962 /* fill structure for door part draw order (sorted below) */
3964 dpo->sort_priority = dpc->pos->sort_priority;
3967 /* sort door part controls according to sort_priority and graphic number */
3968 qsort(door_part_order, MAX_DOOR_PARTS,
3969 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
3972 unsigned int OpenDoor(unsigned int door_state)
3974 if (door_state & DOOR_COPY_BACK)
3976 if (door_state & DOOR_OPEN_1)
3977 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3978 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
3980 if (door_state & DOOR_OPEN_2)
3981 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
3982 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
3984 door_state &= ~DOOR_COPY_BACK;
3987 return MoveDoor(door_state);
3990 unsigned int CloseDoor(unsigned int door_state)
3992 unsigned int old_door_state = GetDoorState();
3994 if (!(door_state & DOOR_NO_COPY_BACK))
3996 if (old_door_state & DOOR_OPEN_1)
3997 BlitBitmap(backbuffer, bitmap_db_door_1,
3998 DX, DY, DXSIZE, DYSIZE, 0, 0);
4000 if (old_door_state & DOOR_OPEN_2)
4001 BlitBitmap(backbuffer, bitmap_db_door_2,
4002 VX, VY, VXSIZE, VYSIZE, 0, 0);
4004 door_state &= ~DOOR_NO_COPY_BACK;
4007 return MoveDoor(door_state);
4010 unsigned int GetDoorState()
4012 return MoveDoor(DOOR_GET_STATE);
4015 unsigned int SetDoorState(unsigned int door_state)
4017 return MoveDoor(door_state | DOOR_SET_STATE);
4020 int euclid(int a, int b)
4022 return (b ? euclid(b, a % b) : a);
4025 unsigned int MoveDoor(unsigned int door_state)
4027 struct Rect door_rect_list[] =
4029 { DX, DY, DXSIZE, DYSIZE },
4030 { VX, VY, VXSIZE, VYSIZE }
4032 static int door1 = DOOR_OPEN_1;
4033 static int door2 = DOOR_CLOSE_2;
4034 unsigned int door_delay = 0;
4035 unsigned int door_delay_value;
4038 if (door_1.width < 0 || door_1.width > DXSIZE)
4039 door_1.width = DXSIZE;
4040 if (door_1.height < 0 || door_1.height > DYSIZE)
4041 door_1.height = DYSIZE;
4042 if (door_2.width < 0 || door_2.width > VXSIZE)
4043 door_2.width = VXSIZE;
4044 if (door_2.height < 0 || door_2.height > VYSIZE)
4045 door_2.height = VYSIZE;
4047 if (door_state == DOOR_GET_STATE)
4048 return (door1 | door2);
4050 if (door_state & DOOR_SET_STATE)
4052 if (door_state & DOOR_ACTION_1)
4053 door1 = door_state & DOOR_ACTION_1;
4054 if (door_state & DOOR_ACTION_2)
4055 door2 = door_state & DOOR_ACTION_2;
4057 return (door1 | door2);
4060 if (!(door_state & DOOR_FORCE_REDRAW))
4062 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4063 door_state &= ~DOOR_OPEN_1;
4064 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4065 door_state &= ~DOOR_CLOSE_1;
4066 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4067 door_state &= ~DOOR_OPEN_2;
4068 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4069 door_state &= ~DOOR_CLOSE_2;
4072 if (global.autoplay_leveldir)
4074 door_state |= DOOR_NO_DELAY;
4075 door_state &= ~DOOR_CLOSE_ALL;
4078 if (game_status == GAME_MODE_EDITOR)
4079 door_state |= DOOR_NO_DELAY;
4081 if (door_state & DOOR_ACTION)
4083 boolean door_panel_drawn[NUM_DOORS];
4084 boolean panel_has_doors[NUM_DOORS];
4085 boolean door_part_skip[MAX_DOOR_PARTS];
4086 boolean door_part_done[MAX_DOOR_PARTS];
4087 boolean door_part_done_all;
4088 int num_steps[MAX_DOOR_PARTS];
4089 int max_move_delay = 0; // delay for complete animations of all doors
4090 int max_step_delay = 0; // delay (ms) between two animation frames
4091 int num_move_steps = 0; // number of animation steps for all doors
4092 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4093 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4094 int current_move_delay = 0;
4098 for (i = 0; i < NUM_DOORS; i++)
4099 panel_has_doors[i] = FALSE;
4101 for (i = 0; i < MAX_DOOR_PARTS; i++)
4103 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4104 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4105 int door_token = dpc->door_token;
4107 door_part_done[i] = FALSE;
4108 door_part_skip[i] = (!(door_state & door_token) ||
4112 for (i = 0; i < MAX_DOOR_PARTS; i++)
4114 int nr = door_part_order[i].nr;
4115 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4116 struct DoorPartPosInfo *pos = dpc->pos;
4117 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4118 int door_token = dpc->door_token;
4119 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4120 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4121 int step_xoffset = ABS(pos->step_xoffset);
4122 int step_yoffset = ABS(pos->step_yoffset);
4123 int step_delay = pos->step_delay;
4124 int current_door_state = door_state & door_token;
4125 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4126 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4127 boolean part_opening = (is_panel ? door_closing : door_opening);
4128 int start_step = (part_opening ? pos->start_step_opening :
4129 pos->start_step_closing);
4130 float move_xsize = (step_xoffset ? g->width : 0);
4131 float move_ysize = (step_yoffset ? g->height : 0);
4132 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4133 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4134 int move_steps = (move_xsteps && move_ysteps ?
4135 MIN(move_xsteps, move_ysteps) :
4136 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4137 int move_delay = move_steps * step_delay;
4139 if (door_part_skip[nr])
4142 max_move_delay = MAX(max_move_delay, move_delay);
4143 max_step_delay = (max_step_delay == 0 ? step_delay :
4144 euclid(max_step_delay, step_delay));
4145 num_steps[nr] = move_steps;
4149 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4151 panel_has_doors[door_index] = TRUE;
4155 num_move_steps = max_move_delay / max_step_delay;
4156 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4158 door_delay_value = max_step_delay;
4160 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4162 start = num_move_steps - 1;
4166 /* opening door sound has priority over simultaneously closing door */
4167 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4168 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4169 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4170 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4173 for (k = start; k < num_move_steps; k++)
4175 door_part_done_all = TRUE;
4177 for (i = 0; i < NUM_DOORS; i++)
4178 door_panel_drawn[i] = FALSE;
4180 for (i = 0; i < MAX_DOOR_PARTS; i++)
4182 int nr = door_part_order[i].nr;
4183 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4184 struct DoorPartPosInfo *pos = dpc->pos;
4185 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4186 int door_token = dpc->door_token;
4187 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4188 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4189 boolean is_panel_and_door_has_closed = FALSE;
4190 struct Rect *door_rect = &door_rect_list[door_index];
4191 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4193 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4194 int current_door_state = door_state & door_token;
4195 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4196 boolean door_closing = !door_opening;
4197 boolean part_opening = (is_panel ? door_closing : door_opening);
4198 boolean part_closing = !part_opening;
4199 int start_step = (part_opening ? pos->start_step_opening :
4200 pos->start_step_closing);
4201 int step_delay = pos->step_delay;
4202 int step_factor = step_delay / max_step_delay;
4203 int k1 = (step_factor ? k / step_factor + 1 : k);
4204 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4205 int kk = MAX(0, k2);
4208 int src_x, src_y, src_xx, src_yy;
4209 int dst_x, dst_y, dst_xx, dst_yy;
4212 if (door_part_skip[nr])
4215 if (!(door_state & door_token))
4223 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4224 int kk_door = MAX(0, k2_door);
4225 int sync_frame = kk_door * door_delay_value;
4226 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4228 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4233 if (!door_panel_drawn[door_index])
4235 ClearRectangle(drawto, door_rect->x, door_rect->y,
4236 door_rect->width, door_rect->height);
4238 door_panel_drawn[door_index] = TRUE;
4241 // draw opening or closing door parts
4243 if (pos->step_xoffset < 0) // door part on right side
4246 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4249 if (dst_xx + width > door_rect->width)
4250 width = door_rect->width - dst_xx;
4252 else // door part on left side
4255 dst_xx = pos->x - kk * pos->step_xoffset;
4259 src_xx = ABS(dst_xx);
4263 width = g->width - src_xx;
4265 // printf("::: k == %d [%d] \n", k, start_step);
4268 if (pos->step_yoffset < 0) // door part on bottom side
4271 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4274 if (dst_yy + height > door_rect->height)
4275 height = door_rect->height - dst_yy;
4277 else // door part on top side
4280 dst_yy = pos->y - kk * pos->step_yoffset;
4284 src_yy = ABS(dst_yy);
4288 height = g->height - src_yy;
4291 src_x = g_src_x + src_xx;
4292 src_y = g_src_y + src_yy;
4294 dst_x = door_rect->x + dst_xx;
4295 dst_y = door_rect->y + dst_yy;
4297 is_panel_and_door_has_closed =
4300 panel_has_doors[door_index] &&
4301 k >= num_move_steps_doors_only - 1);
4303 if (width >= 0 && width <= g->width &&
4304 height >= 0 && height <= g->height &&
4305 !is_panel_and_door_has_closed)
4307 if (is_panel || !pos->draw_masked)
4308 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4311 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4315 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4317 if ((part_opening && (width < 0 || height < 0)) ||
4318 (part_closing && (width >= g->width && height >= g->height)))
4319 door_part_done[nr] = TRUE;
4321 // continue door part animations, but not panel after door has closed
4322 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4323 door_part_done_all = FALSE;
4326 if (!(door_state & DOOR_NO_DELAY))
4330 if (game_status == GAME_MODE_MAIN)
4333 WaitUntilDelayReached(&door_delay, door_delay_value);
4335 current_move_delay += max_step_delay;
4338 if (door_part_done_all)
4343 if (door_state & DOOR_ACTION_1)
4344 door1 = door_state & DOOR_ACTION_1;
4345 if (door_state & DOOR_ACTION_2)
4346 door2 = door_state & DOOR_ACTION_2;
4348 return (door1 | door2);
4351 void DrawSpecialEditorDoor()
4353 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4354 int top_border_width = gfx1->width;
4355 int top_border_height = gfx1->height;
4356 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4357 int ex = EX - outer_border;
4358 int ey = EY - outer_border;
4359 int vy = VY - outer_border;
4360 int exsize = EXSIZE + 2 * outer_border;
4362 CloseDoor(DOOR_CLOSE_2);
4364 /* draw bigger level editor toolbox window */
4365 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4366 top_border_width, top_border_height, ex, ey - top_border_height);
4367 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4368 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4370 redraw_mask |= REDRAW_ALL;
4373 void UndrawSpecialEditorDoor()
4375 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4376 int top_border_width = gfx1->width;
4377 int top_border_height = gfx1->height;
4378 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4379 int ex = EX - outer_border;
4380 int ey = EY - outer_border;
4381 int ey_top = ey - top_border_height;
4382 int exsize = EXSIZE + 2 * outer_border;
4383 int eysize = EYSIZE + 2 * outer_border;
4385 /* draw normal tape recorder window */
4386 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4388 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4389 ex, ey_top, top_border_width, top_border_height,
4391 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4392 ex, ey, exsize, eysize, ex, ey);
4396 // if screen background is set to "[NONE]", clear editor toolbox window
4397 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4398 ClearRectangle(drawto, ex, ey, exsize, eysize);
4401 redraw_mask |= REDRAW_ALL;
4405 /* ---------- new tool button stuff ---------------------------------------- */
4410 struct TextPosInfo *pos;
4413 } toolbutton_info[NUM_TOOL_BUTTONS] =
4416 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4417 TOOL_CTRL_ID_YES, "yes"
4420 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4421 TOOL_CTRL_ID_NO, "no"
4424 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4425 TOOL_CTRL_ID_CONFIRM, "confirm"
4428 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4429 TOOL_CTRL_ID_PLAYER_1, "player 1"
4432 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4433 TOOL_CTRL_ID_PLAYER_2, "player 2"
4436 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4437 TOOL_CTRL_ID_PLAYER_3, "player 3"
4440 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4441 TOOL_CTRL_ID_PLAYER_4, "player 4"
4445 void CreateToolButtons()
4449 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4451 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4452 struct TextPosInfo *pos = toolbutton_info[i].pos;
4453 struct GadgetInfo *gi;
4454 Bitmap *deco_bitmap = None;
4455 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4456 unsigned int event_mask = GD_EVENT_RELEASED;
4459 int gd_x = gfx->src_x;
4460 int gd_y = gfx->src_y;
4461 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4462 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4465 if (global.use_envelope_request)
4466 setRequestPosition(&dx, &dy, TRUE);
4468 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4470 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4472 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4473 pos->size, &deco_bitmap, &deco_x, &deco_y);
4474 deco_xpos = (gfx->width - pos->size) / 2;
4475 deco_ypos = (gfx->height - pos->size) / 2;
4478 gi = CreateGadget(GDI_CUSTOM_ID, id,
4479 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4480 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4481 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4482 GDI_WIDTH, gfx->width,
4483 GDI_HEIGHT, gfx->height,
4484 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4485 GDI_STATE, GD_BUTTON_UNPRESSED,
4486 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4487 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4488 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4489 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4490 GDI_DECORATION_SIZE, pos->size, pos->size,
4491 GDI_DECORATION_SHIFTING, 1, 1,
4492 GDI_DIRECT_DRAW, FALSE,
4493 GDI_EVENT_MASK, event_mask,
4494 GDI_CALLBACK_ACTION, HandleToolButtons,
4498 Error(ERR_EXIT, "cannot create gadget");
4500 tool_gadget[id] = gi;
4504 void FreeToolButtons()
4508 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4509 FreeGadget(tool_gadget[i]);
4512 static void UnmapToolButtons()
4516 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4517 UnmapGadget(tool_gadget[i]);
4520 static void HandleToolButtons(struct GadgetInfo *gi)
4522 request_gadget_id = gi->custom_id;
4525 static struct Mapping_EM_to_RND_object
4528 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4529 boolean is_backside; /* backside of moving element */
4535 em_object_mapping_list[] =
4538 Xblank, TRUE, FALSE,
4542 Yacid_splash_eB, FALSE, FALSE,
4543 EL_ACID_SPLASH_RIGHT, -1, -1
4546 Yacid_splash_wB, FALSE, FALSE,
4547 EL_ACID_SPLASH_LEFT, -1, -1
4550 #ifdef EM_ENGINE_BAD_ROLL
4552 Xstone_force_e, FALSE, FALSE,
4553 EL_ROCK, -1, MV_BIT_RIGHT
4556 Xstone_force_w, FALSE, FALSE,
4557 EL_ROCK, -1, MV_BIT_LEFT
4560 Xnut_force_e, FALSE, FALSE,
4561 EL_NUT, -1, MV_BIT_RIGHT
4564 Xnut_force_w, FALSE, FALSE,
4565 EL_NUT, -1, MV_BIT_LEFT
4568 Xspring_force_e, FALSE, FALSE,
4569 EL_SPRING, -1, MV_BIT_RIGHT
4572 Xspring_force_w, FALSE, FALSE,
4573 EL_SPRING, -1, MV_BIT_LEFT
4576 Xemerald_force_e, FALSE, FALSE,
4577 EL_EMERALD, -1, MV_BIT_RIGHT
4580 Xemerald_force_w, FALSE, FALSE,
4581 EL_EMERALD, -1, MV_BIT_LEFT
4584 Xdiamond_force_e, FALSE, FALSE,
4585 EL_DIAMOND, -1, MV_BIT_RIGHT
4588 Xdiamond_force_w, FALSE, FALSE,
4589 EL_DIAMOND, -1, MV_BIT_LEFT
4592 Xbomb_force_e, FALSE, FALSE,
4593 EL_BOMB, -1, MV_BIT_RIGHT
4596 Xbomb_force_w, FALSE, FALSE,
4597 EL_BOMB, -1, MV_BIT_LEFT
4599 #endif /* EM_ENGINE_BAD_ROLL */
4602 Xstone, TRUE, FALSE,
4606 Xstone_pause, FALSE, FALSE,
4610 Xstone_fall, FALSE, FALSE,
4614 Ystone_s, FALSE, FALSE,
4615 EL_ROCK, ACTION_FALLING, -1
4618 Ystone_sB, FALSE, TRUE,
4619 EL_ROCK, ACTION_FALLING, -1
4622 Ystone_e, FALSE, FALSE,
4623 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4626 Ystone_eB, FALSE, TRUE,
4627 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4630 Ystone_w, FALSE, FALSE,
4631 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4634 Ystone_wB, FALSE, TRUE,
4635 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4642 Xnut_pause, FALSE, FALSE,
4646 Xnut_fall, FALSE, FALSE,
4650 Ynut_s, FALSE, FALSE,
4651 EL_NUT, ACTION_FALLING, -1
4654 Ynut_sB, FALSE, TRUE,
4655 EL_NUT, ACTION_FALLING, -1
4658 Ynut_e, FALSE, FALSE,
4659 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4662 Ynut_eB, FALSE, TRUE,
4663 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4666 Ynut_w, FALSE, FALSE,
4667 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4670 Ynut_wB, FALSE, TRUE,
4671 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4674 Xbug_n, TRUE, FALSE,
4678 Xbug_e, TRUE, FALSE,
4679 EL_BUG_RIGHT, -1, -1
4682 Xbug_s, TRUE, FALSE,
4686 Xbug_w, TRUE, FALSE,
4690 Xbug_gon, FALSE, FALSE,
4694 Xbug_goe, FALSE, FALSE,
4695 EL_BUG_RIGHT, -1, -1
4698 Xbug_gos, FALSE, FALSE,
4702 Xbug_gow, FALSE, FALSE,
4706 Ybug_n, FALSE, FALSE,
4707 EL_BUG, ACTION_MOVING, MV_BIT_UP
4710 Ybug_nB, FALSE, TRUE,
4711 EL_BUG, ACTION_MOVING, MV_BIT_UP
4714 Ybug_e, FALSE, FALSE,
4715 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4718 Ybug_eB, FALSE, TRUE,
4719 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4722 Ybug_s, FALSE, FALSE,
4723 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4726 Ybug_sB, FALSE, TRUE,
4727 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4730 Ybug_w, FALSE, FALSE,
4731 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4734 Ybug_wB, FALSE, TRUE,
4735 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4738 Ybug_w_n, FALSE, FALSE,
4739 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4742 Ybug_n_e, FALSE, FALSE,
4743 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4746 Ybug_e_s, FALSE, FALSE,
4747 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4750 Ybug_s_w, FALSE, FALSE,
4751 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4754 Ybug_e_n, FALSE, FALSE,
4755 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4758 Ybug_s_e, FALSE, FALSE,
4759 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4762 Ybug_w_s, FALSE, FALSE,
4763 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4766 Ybug_n_w, FALSE, FALSE,
4767 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4770 Ybug_stone, FALSE, FALSE,
4771 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4774 Ybug_spring, FALSE, FALSE,
4775 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4778 Xtank_n, TRUE, FALSE,
4779 EL_SPACESHIP_UP, -1, -1
4782 Xtank_e, TRUE, FALSE,
4783 EL_SPACESHIP_RIGHT, -1, -1
4786 Xtank_s, TRUE, FALSE,
4787 EL_SPACESHIP_DOWN, -1, -1
4790 Xtank_w, TRUE, FALSE,
4791 EL_SPACESHIP_LEFT, -1, -1
4794 Xtank_gon, FALSE, FALSE,
4795 EL_SPACESHIP_UP, -1, -1
4798 Xtank_goe, FALSE, FALSE,
4799 EL_SPACESHIP_RIGHT, -1, -1
4802 Xtank_gos, FALSE, FALSE,
4803 EL_SPACESHIP_DOWN, -1, -1
4806 Xtank_gow, FALSE, FALSE,
4807 EL_SPACESHIP_LEFT, -1, -1
4810 Ytank_n, FALSE, FALSE,
4811 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4814 Ytank_nB, FALSE, TRUE,
4815 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4818 Ytank_e, FALSE, FALSE,
4819 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4822 Ytank_eB, FALSE, TRUE,
4823 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4826 Ytank_s, FALSE, FALSE,
4827 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4830 Ytank_sB, FALSE, TRUE,
4831 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4834 Ytank_w, FALSE, FALSE,
4835 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4838 Ytank_wB, FALSE, TRUE,
4839 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4842 Ytank_w_n, FALSE, FALSE,
4843 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4846 Ytank_n_e, FALSE, FALSE,
4847 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4850 Ytank_e_s, FALSE, FALSE,
4851 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4854 Ytank_s_w, FALSE, FALSE,
4855 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4858 Ytank_e_n, FALSE, FALSE,
4859 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4862 Ytank_s_e, FALSE, FALSE,
4863 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4866 Ytank_w_s, FALSE, FALSE,
4867 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4870 Ytank_n_w, FALSE, FALSE,
4871 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4874 Ytank_stone, FALSE, FALSE,
4875 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4878 Ytank_spring, FALSE, FALSE,
4879 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4882 Xandroid, TRUE, FALSE,
4883 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4886 Xandroid_1_n, FALSE, FALSE,
4887 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4890 Xandroid_2_n, FALSE, FALSE,
4891 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4894 Xandroid_1_e, FALSE, FALSE,
4895 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4898 Xandroid_2_e, FALSE, FALSE,
4899 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4902 Xandroid_1_w, FALSE, FALSE,
4903 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4906 Xandroid_2_w, FALSE, FALSE,
4907 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4910 Xandroid_1_s, FALSE, FALSE,
4911 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4914 Xandroid_2_s, FALSE, FALSE,
4915 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4918 Yandroid_n, FALSE, FALSE,
4919 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4922 Yandroid_nB, FALSE, TRUE,
4923 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4926 Yandroid_ne, FALSE, FALSE,
4927 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4930 Yandroid_neB, FALSE, TRUE,
4931 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4934 Yandroid_e, FALSE, FALSE,
4935 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4938 Yandroid_eB, FALSE, TRUE,
4939 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4942 Yandroid_se, FALSE, FALSE,
4943 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4946 Yandroid_seB, FALSE, TRUE,
4947 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4950 Yandroid_s, FALSE, FALSE,
4951 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4954 Yandroid_sB, FALSE, TRUE,
4955 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4958 Yandroid_sw, FALSE, FALSE,
4959 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
4962 Yandroid_swB, FALSE, TRUE,
4963 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
4966 Yandroid_w, FALSE, FALSE,
4967 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
4970 Yandroid_wB, FALSE, TRUE,
4971 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
4974 Yandroid_nw, FALSE, FALSE,
4975 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
4978 Yandroid_nwB, FALSE, TRUE,
4979 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
4982 Xspring, TRUE, FALSE,
4986 Xspring_pause, FALSE, FALSE,
4990 Xspring_e, FALSE, FALSE,
4994 Xspring_w, FALSE, FALSE,
4998 Xspring_fall, FALSE, FALSE,
5002 Yspring_s, FALSE, FALSE,
5003 EL_SPRING, ACTION_FALLING, -1
5006 Yspring_sB, FALSE, TRUE,
5007 EL_SPRING, ACTION_FALLING, -1
5010 Yspring_e, FALSE, FALSE,
5011 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5014 Yspring_eB, FALSE, TRUE,
5015 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5018 Yspring_w, FALSE, FALSE,
5019 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5022 Yspring_wB, FALSE, TRUE,
5023 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5026 Yspring_kill_e, FALSE, FALSE,
5027 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5030 Yspring_kill_eB, FALSE, TRUE,
5031 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5034 Yspring_kill_w, FALSE, FALSE,
5035 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5038 Yspring_kill_wB, FALSE, TRUE,
5039 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5042 Xeater_n, TRUE, FALSE,
5043 EL_YAMYAM_UP, -1, -1
5046 Xeater_e, TRUE, FALSE,
5047 EL_YAMYAM_RIGHT, -1, -1
5050 Xeater_w, TRUE, FALSE,
5051 EL_YAMYAM_LEFT, -1, -1
5054 Xeater_s, TRUE, FALSE,
5055 EL_YAMYAM_DOWN, -1, -1
5058 Yeater_n, FALSE, FALSE,
5059 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5062 Yeater_nB, FALSE, TRUE,
5063 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5066 Yeater_e, FALSE, FALSE,
5067 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5070 Yeater_eB, FALSE, TRUE,
5071 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5074 Yeater_s, FALSE, FALSE,
5075 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5078 Yeater_sB, FALSE, TRUE,
5079 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5082 Yeater_w, FALSE, FALSE,
5083 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5086 Yeater_wB, FALSE, TRUE,
5087 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5090 Yeater_stone, FALSE, FALSE,
5091 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5094 Yeater_spring, FALSE, FALSE,
5095 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5098 Xalien, TRUE, FALSE,
5102 Xalien_pause, FALSE, FALSE,
5106 Yalien_n, FALSE, FALSE,
5107 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5110 Yalien_nB, FALSE, TRUE,
5111 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5114 Yalien_e, FALSE, FALSE,
5115 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5118 Yalien_eB, FALSE, TRUE,
5119 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5122 Yalien_s, FALSE, FALSE,
5123 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5126 Yalien_sB, FALSE, TRUE,
5127 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5130 Yalien_w, FALSE, FALSE,
5131 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5134 Yalien_wB, FALSE, TRUE,
5135 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5138 Yalien_stone, FALSE, FALSE,
5139 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5142 Yalien_spring, FALSE, FALSE,
5143 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5146 Xemerald, TRUE, FALSE,
5150 Xemerald_pause, FALSE, FALSE,
5154 Xemerald_fall, FALSE, FALSE,
5158 Xemerald_shine, FALSE, FALSE,
5159 EL_EMERALD, ACTION_TWINKLING, -1
5162 Yemerald_s, FALSE, FALSE,
5163 EL_EMERALD, ACTION_FALLING, -1
5166 Yemerald_sB, FALSE, TRUE,
5167 EL_EMERALD, ACTION_FALLING, -1
5170 Yemerald_e, FALSE, FALSE,
5171 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5174 Yemerald_eB, FALSE, TRUE,
5175 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5178 Yemerald_w, FALSE, FALSE,
5179 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5182 Yemerald_wB, FALSE, TRUE,
5183 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5186 Yemerald_eat, FALSE, FALSE,
5187 EL_EMERALD, ACTION_COLLECTING, -1
5190 Yemerald_stone, FALSE, FALSE,
5191 EL_NUT, ACTION_BREAKING, -1
5194 Xdiamond, TRUE, FALSE,
5198 Xdiamond_pause, FALSE, FALSE,
5202 Xdiamond_fall, FALSE, FALSE,
5206 Xdiamond_shine, FALSE, FALSE,
5207 EL_DIAMOND, ACTION_TWINKLING, -1
5210 Ydiamond_s, FALSE, FALSE,
5211 EL_DIAMOND, ACTION_FALLING, -1
5214 Ydiamond_sB, FALSE, TRUE,
5215 EL_DIAMOND, ACTION_FALLING, -1
5218 Ydiamond_e, FALSE, FALSE,
5219 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5222 Ydiamond_eB, FALSE, TRUE,
5223 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5226 Ydiamond_w, FALSE, FALSE,
5227 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5230 Ydiamond_wB, FALSE, TRUE,
5231 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5234 Ydiamond_eat, FALSE, FALSE,
5235 EL_DIAMOND, ACTION_COLLECTING, -1
5238 Ydiamond_stone, FALSE, FALSE,
5239 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5242 Xdrip_fall, TRUE, FALSE,
5243 EL_AMOEBA_DROP, -1, -1
5246 Xdrip_stretch, FALSE, FALSE,
5247 EL_AMOEBA_DROP, ACTION_FALLING, -1
5250 Xdrip_stretchB, FALSE, TRUE,
5251 EL_AMOEBA_DROP, ACTION_FALLING, -1
5254 Xdrip_eat, FALSE, FALSE,
5255 EL_AMOEBA_DROP, ACTION_GROWING, -1
5258 Ydrip_s1, FALSE, FALSE,
5259 EL_AMOEBA_DROP, ACTION_FALLING, -1
5262 Ydrip_s1B, FALSE, TRUE,
5263 EL_AMOEBA_DROP, ACTION_FALLING, -1
5266 Ydrip_s2, FALSE, FALSE,
5267 EL_AMOEBA_DROP, ACTION_FALLING, -1
5270 Ydrip_s2B, FALSE, TRUE,
5271 EL_AMOEBA_DROP, ACTION_FALLING, -1
5278 Xbomb_pause, FALSE, FALSE,
5282 Xbomb_fall, FALSE, FALSE,
5286 Ybomb_s, FALSE, FALSE,
5287 EL_BOMB, ACTION_FALLING, -1
5290 Ybomb_sB, FALSE, TRUE,
5291 EL_BOMB, ACTION_FALLING, -1
5294 Ybomb_e, FALSE, FALSE,
5295 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5298 Ybomb_eB, FALSE, TRUE,
5299 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5302 Ybomb_w, FALSE, FALSE,
5303 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5306 Ybomb_wB, FALSE, TRUE,
5307 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5310 Ybomb_eat, FALSE, FALSE,
5311 EL_BOMB, ACTION_ACTIVATING, -1
5314 Xballoon, TRUE, FALSE,
5318 Yballoon_n, FALSE, FALSE,
5319 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5322 Yballoon_nB, FALSE, TRUE,
5323 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5326 Yballoon_e, FALSE, FALSE,
5327 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5330 Yballoon_eB, FALSE, TRUE,
5331 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5334 Yballoon_s, FALSE, FALSE,
5335 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5338 Yballoon_sB, FALSE, TRUE,
5339 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5342 Yballoon_w, FALSE, FALSE,
5343 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5346 Yballoon_wB, FALSE, TRUE,
5347 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5350 Xgrass, TRUE, FALSE,
5351 EL_EMC_GRASS, -1, -1
5354 Ygrass_nB, FALSE, FALSE,
5355 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5358 Ygrass_eB, FALSE, FALSE,
5359 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5362 Ygrass_sB, FALSE, FALSE,
5363 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5366 Ygrass_wB, FALSE, FALSE,
5367 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5374 Ydirt_nB, FALSE, FALSE,
5375 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5378 Ydirt_eB, FALSE, FALSE,
5379 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5382 Ydirt_sB, FALSE, FALSE,
5383 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5386 Ydirt_wB, FALSE, FALSE,
5387 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5390 Xacid_ne, TRUE, FALSE,
5391 EL_ACID_POOL_TOPRIGHT, -1, -1
5394 Xacid_se, TRUE, FALSE,
5395 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5398 Xacid_s, TRUE, FALSE,
5399 EL_ACID_POOL_BOTTOM, -1, -1
5402 Xacid_sw, TRUE, FALSE,
5403 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5406 Xacid_nw, TRUE, FALSE,
5407 EL_ACID_POOL_TOPLEFT, -1, -1
5410 Xacid_1, TRUE, FALSE,
5414 Xacid_2, FALSE, FALSE,
5418 Xacid_3, FALSE, FALSE,
5422 Xacid_4, FALSE, FALSE,
5426 Xacid_5, FALSE, FALSE,
5430 Xacid_6, FALSE, FALSE,
5434 Xacid_7, FALSE, FALSE,
5438 Xacid_8, FALSE, FALSE,
5442 Xball_1, TRUE, FALSE,
5443 EL_EMC_MAGIC_BALL, -1, -1
5446 Xball_1B, FALSE, FALSE,
5447 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5450 Xball_2, FALSE, FALSE,
5451 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5454 Xball_2B, FALSE, FALSE,
5455 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5458 Yball_eat, FALSE, FALSE,
5459 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5462 Ykey_1_eat, FALSE, FALSE,
5463 EL_EM_KEY_1, ACTION_COLLECTING, -1
5466 Ykey_2_eat, FALSE, FALSE,
5467 EL_EM_KEY_2, ACTION_COLLECTING, -1
5470 Ykey_3_eat, FALSE, FALSE,
5471 EL_EM_KEY_3, ACTION_COLLECTING, -1
5474 Ykey_4_eat, FALSE, FALSE,
5475 EL_EM_KEY_4, ACTION_COLLECTING, -1
5478 Ykey_5_eat, FALSE, FALSE,
5479 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5482 Ykey_6_eat, FALSE, FALSE,
5483 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5486 Ykey_7_eat, FALSE, FALSE,
5487 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5490 Ykey_8_eat, FALSE, FALSE,
5491 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5494 Ylenses_eat, FALSE, FALSE,
5495 EL_EMC_LENSES, ACTION_COLLECTING, -1
5498 Ymagnify_eat, FALSE, FALSE,
5499 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5502 Ygrass_eat, FALSE, FALSE,
5503 EL_EMC_GRASS, ACTION_SNAPPING, -1
5506 Ydirt_eat, FALSE, FALSE,
5507 EL_SAND, ACTION_SNAPPING, -1
5510 Xgrow_ns, TRUE, FALSE,
5511 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5514 Ygrow_ns_eat, FALSE, FALSE,
5515 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5518 Xgrow_ew, TRUE, FALSE,
5519 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5522 Ygrow_ew_eat, FALSE, FALSE,
5523 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5526 Xwonderwall, TRUE, FALSE,
5527 EL_MAGIC_WALL, -1, -1
5530 XwonderwallB, FALSE, FALSE,
5531 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5534 Xamoeba_1, TRUE, FALSE,
5535 EL_AMOEBA_DRY, ACTION_OTHER, -1
5538 Xamoeba_2, FALSE, FALSE,
5539 EL_AMOEBA_DRY, ACTION_OTHER, -1
5542 Xamoeba_3, FALSE, FALSE,
5543 EL_AMOEBA_DRY, ACTION_OTHER, -1
5546 Xamoeba_4, FALSE, FALSE,
5547 EL_AMOEBA_DRY, ACTION_OTHER, -1
5550 Xamoeba_5, TRUE, FALSE,
5551 EL_AMOEBA_WET, ACTION_OTHER, -1
5554 Xamoeba_6, FALSE, FALSE,
5555 EL_AMOEBA_WET, ACTION_OTHER, -1
5558 Xamoeba_7, FALSE, FALSE,
5559 EL_AMOEBA_WET, ACTION_OTHER, -1
5562 Xamoeba_8, FALSE, FALSE,
5563 EL_AMOEBA_WET, ACTION_OTHER, -1
5566 Xdoor_1, TRUE, FALSE,
5567 EL_EM_GATE_1, -1, -1
5570 Xdoor_2, TRUE, FALSE,
5571 EL_EM_GATE_2, -1, -1
5574 Xdoor_3, TRUE, FALSE,
5575 EL_EM_GATE_3, -1, -1
5578 Xdoor_4, TRUE, FALSE,
5579 EL_EM_GATE_4, -1, -1
5582 Xdoor_5, TRUE, FALSE,
5583 EL_EMC_GATE_5, -1, -1
5586 Xdoor_6, TRUE, FALSE,
5587 EL_EMC_GATE_6, -1, -1
5590 Xdoor_7, TRUE, FALSE,
5591 EL_EMC_GATE_7, -1, -1
5594 Xdoor_8, TRUE, FALSE,
5595 EL_EMC_GATE_8, -1, -1
5598 Xkey_1, TRUE, FALSE,
5602 Xkey_2, TRUE, FALSE,
5606 Xkey_3, TRUE, FALSE,
5610 Xkey_4, TRUE, FALSE,
5614 Xkey_5, TRUE, FALSE,
5615 EL_EMC_KEY_5, -1, -1
5618 Xkey_6, TRUE, FALSE,
5619 EL_EMC_KEY_6, -1, -1
5622 Xkey_7, TRUE, FALSE,
5623 EL_EMC_KEY_7, -1, -1
5626 Xkey_8, TRUE, FALSE,
5627 EL_EMC_KEY_8, -1, -1
5630 Xwind_n, TRUE, FALSE,
5631 EL_BALLOON_SWITCH_UP, -1, -1
5634 Xwind_e, TRUE, FALSE,
5635 EL_BALLOON_SWITCH_RIGHT, -1, -1
5638 Xwind_s, TRUE, FALSE,
5639 EL_BALLOON_SWITCH_DOWN, -1, -1
5642 Xwind_w, TRUE, FALSE,
5643 EL_BALLOON_SWITCH_LEFT, -1, -1
5646 Xwind_nesw, TRUE, FALSE,
5647 EL_BALLOON_SWITCH_ANY, -1, -1
5650 Xwind_stop, TRUE, FALSE,
5651 EL_BALLOON_SWITCH_NONE, -1, -1
5655 EL_EM_EXIT_CLOSED, -1, -1
5658 Xexit_1, TRUE, FALSE,
5659 EL_EM_EXIT_OPEN, -1, -1
5662 Xexit_2, FALSE, FALSE,
5663 EL_EM_EXIT_OPEN, -1, -1
5666 Xexit_3, FALSE, FALSE,
5667 EL_EM_EXIT_OPEN, -1, -1
5670 Xdynamite, TRUE, FALSE,
5671 EL_EM_DYNAMITE, -1, -1
5674 Ydynamite_eat, FALSE, FALSE,
5675 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5678 Xdynamite_1, TRUE, FALSE,
5679 EL_EM_DYNAMITE_ACTIVE, -1, -1
5682 Xdynamite_2, FALSE, FALSE,
5683 EL_EM_DYNAMITE_ACTIVE, -1, -1
5686 Xdynamite_3, FALSE, FALSE,
5687 EL_EM_DYNAMITE_ACTIVE, -1, -1
5690 Xdynamite_4, FALSE, FALSE,
5691 EL_EM_DYNAMITE_ACTIVE, -1, -1
5694 Xbumper, TRUE, FALSE,
5695 EL_EMC_SPRING_BUMPER, -1, -1
5698 XbumperB, FALSE, FALSE,
5699 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5702 Xwheel, TRUE, FALSE,
5703 EL_ROBOT_WHEEL, -1, -1
5706 XwheelB, FALSE, FALSE,
5707 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5710 Xswitch, TRUE, FALSE,
5711 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5714 XswitchB, FALSE, FALSE,
5715 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5719 EL_QUICKSAND_EMPTY, -1, -1
5722 Xsand_stone, TRUE, FALSE,
5723 EL_QUICKSAND_FULL, -1, -1
5726 Xsand_stonein_1, FALSE, TRUE,
5727 EL_ROCK, ACTION_FILLING, -1
5730 Xsand_stonein_2, FALSE, TRUE,
5731 EL_ROCK, ACTION_FILLING, -1
5734 Xsand_stonein_3, FALSE, TRUE,
5735 EL_ROCK, ACTION_FILLING, -1
5738 Xsand_stonein_4, FALSE, TRUE,
5739 EL_ROCK, ACTION_FILLING, -1
5742 Xsand_stonesand_1, FALSE, FALSE,
5743 EL_QUICKSAND_EMPTYING, -1, -1
5746 Xsand_stonesand_2, FALSE, FALSE,
5747 EL_QUICKSAND_EMPTYING, -1, -1
5750 Xsand_stonesand_3, FALSE, FALSE,
5751 EL_QUICKSAND_EMPTYING, -1, -1
5754 Xsand_stonesand_4, FALSE, FALSE,
5755 EL_QUICKSAND_EMPTYING, -1, -1
5758 Xsand_stonesand_quickout_1, FALSE, FALSE,
5759 EL_QUICKSAND_EMPTYING, -1, -1
5762 Xsand_stonesand_quickout_2, FALSE, FALSE,
5763 EL_QUICKSAND_EMPTYING, -1, -1
5766 Xsand_stoneout_1, FALSE, FALSE,
5767 EL_ROCK, ACTION_EMPTYING, -1
5770 Xsand_stoneout_2, FALSE, FALSE,
5771 EL_ROCK, ACTION_EMPTYING, -1
5774 Xsand_sandstone_1, FALSE, FALSE,
5775 EL_QUICKSAND_FILLING, -1, -1
5778 Xsand_sandstone_2, FALSE, FALSE,
5779 EL_QUICKSAND_FILLING, -1, -1
5782 Xsand_sandstone_3, FALSE, FALSE,
5783 EL_QUICKSAND_FILLING, -1, -1
5786 Xsand_sandstone_4, FALSE, FALSE,
5787 EL_QUICKSAND_FILLING, -1, -1
5790 Xplant, TRUE, FALSE,
5791 EL_EMC_PLANT, -1, -1
5794 Yplant, FALSE, FALSE,
5795 EL_EMC_PLANT, -1, -1
5798 Xlenses, TRUE, FALSE,
5799 EL_EMC_LENSES, -1, -1
5802 Xmagnify, TRUE, FALSE,
5803 EL_EMC_MAGNIFIER, -1, -1
5806 Xdripper, TRUE, FALSE,
5807 EL_EMC_DRIPPER, -1, -1
5810 XdripperB, FALSE, FALSE,
5811 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5814 Xfake_blank, TRUE, FALSE,
5815 EL_INVISIBLE_WALL, -1, -1
5818 Xfake_blankB, FALSE, FALSE,
5819 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5822 Xfake_grass, TRUE, FALSE,
5823 EL_EMC_FAKE_GRASS, -1, -1
5826 Xfake_grassB, FALSE, FALSE,
5827 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5830 Xfake_door_1, TRUE, FALSE,
5831 EL_EM_GATE_1_GRAY, -1, -1
5834 Xfake_door_2, TRUE, FALSE,
5835 EL_EM_GATE_2_GRAY, -1, -1
5838 Xfake_door_3, TRUE, FALSE,
5839 EL_EM_GATE_3_GRAY, -1, -1
5842 Xfake_door_4, TRUE, FALSE,
5843 EL_EM_GATE_4_GRAY, -1, -1
5846 Xfake_door_5, TRUE, FALSE,
5847 EL_EMC_GATE_5_GRAY, -1, -1
5850 Xfake_door_6, TRUE, FALSE,
5851 EL_EMC_GATE_6_GRAY, -1, -1
5854 Xfake_door_7, TRUE, FALSE,
5855 EL_EMC_GATE_7_GRAY, -1, -1
5858 Xfake_door_8, TRUE, FALSE,
5859 EL_EMC_GATE_8_GRAY, -1, -1
5862 Xfake_acid_1, TRUE, FALSE,
5863 EL_EMC_FAKE_ACID, -1, -1
5866 Xfake_acid_2, FALSE, FALSE,
5867 EL_EMC_FAKE_ACID, -1, -1
5870 Xfake_acid_3, FALSE, FALSE,
5871 EL_EMC_FAKE_ACID, -1, -1
5874 Xfake_acid_4, FALSE, FALSE,
5875 EL_EMC_FAKE_ACID, -1, -1
5878 Xfake_acid_5, FALSE, FALSE,
5879 EL_EMC_FAKE_ACID, -1, -1
5882 Xfake_acid_6, FALSE, FALSE,
5883 EL_EMC_FAKE_ACID, -1, -1
5886 Xfake_acid_7, FALSE, FALSE,
5887 EL_EMC_FAKE_ACID, -1, -1
5890 Xfake_acid_8, FALSE, FALSE,
5891 EL_EMC_FAKE_ACID, -1, -1
5894 Xsteel_1, TRUE, FALSE,
5895 EL_STEELWALL, -1, -1
5898 Xsteel_2, TRUE, FALSE,
5899 EL_EMC_STEELWALL_2, -1, -1
5902 Xsteel_3, TRUE, FALSE,
5903 EL_EMC_STEELWALL_3, -1, -1
5906 Xsteel_4, TRUE, FALSE,
5907 EL_EMC_STEELWALL_4, -1, -1
5910 Xwall_1, TRUE, FALSE,
5914 Xwall_2, TRUE, FALSE,
5915 EL_EMC_WALL_14, -1, -1
5918 Xwall_3, TRUE, FALSE,
5919 EL_EMC_WALL_15, -1, -1
5922 Xwall_4, TRUE, FALSE,
5923 EL_EMC_WALL_16, -1, -1
5926 Xround_wall_1, TRUE, FALSE,
5927 EL_WALL_SLIPPERY, -1, -1
5930 Xround_wall_2, TRUE, FALSE,
5931 EL_EMC_WALL_SLIPPERY_2, -1, -1
5934 Xround_wall_3, TRUE, FALSE,
5935 EL_EMC_WALL_SLIPPERY_3, -1, -1
5938 Xround_wall_4, TRUE, FALSE,
5939 EL_EMC_WALL_SLIPPERY_4, -1, -1
5942 Xdecor_1, TRUE, FALSE,
5943 EL_EMC_WALL_8, -1, -1
5946 Xdecor_2, TRUE, FALSE,
5947 EL_EMC_WALL_6, -1, -1
5950 Xdecor_3, TRUE, FALSE,
5951 EL_EMC_WALL_4, -1, -1
5954 Xdecor_4, TRUE, FALSE,
5955 EL_EMC_WALL_7, -1, -1
5958 Xdecor_5, TRUE, FALSE,
5959 EL_EMC_WALL_5, -1, -1
5962 Xdecor_6, TRUE, FALSE,
5963 EL_EMC_WALL_9, -1, -1
5966 Xdecor_7, TRUE, FALSE,
5967 EL_EMC_WALL_10, -1, -1
5970 Xdecor_8, TRUE, FALSE,
5971 EL_EMC_WALL_1, -1, -1
5974 Xdecor_9, TRUE, FALSE,
5975 EL_EMC_WALL_2, -1, -1
5978 Xdecor_10, TRUE, FALSE,
5979 EL_EMC_WALL_3, -1, -1
5982 Xdecor_11, TRUE, FALSE,
5983 EL_EMC_WALL_11, -1, -1
5986 Xdecor_12, TRUE, FALSE,
5987 EL_EMC_WALL_12, -1, -1
5990 Xalpha_0, TRUE, FALSE,
5991 EL_CHAR('0'), -1, -1
5994 Xalpha_1, TRUE, FALSE,
5995 EL_CHAR('1'), -1, -1
5998 Xalpha_2, TRUE, FALSE,
5999 EL_CHAR('2'), -1, -1
6002 Xalpha_3, TRUE, FALSE,
6003 EL_CHAR('3'), -1, -1
6006 Xalpha_4, TRUE, FALSE,
6007 EL_CHAR('4'), -1, -1
6010 Xalpha_5, TRUE, FALSE,
6011 EL_CHAR('5'), -1, -1
6014 Xalpha_6, TRUE, FALSE,
6015 EL_CHAR('6'), -1, -1
6018 Xalpha_7, TRUE, FALSE,
6019 EL_CHAR('7'), -1, -1
6022 Xalpha_8, TRUE, FALSE,
6023 EL_CHAR('8'), -1, -1
6026 Xalpha_9, TRUE, FALSE,
6027 EL_CHAR('9'), -1, -1
6030 Xalpha_excla, TRUE, FALSE,
6031 EL_CHAR('!'), -1, -1
6034 Xalpha_quote, TRUE, FALSE,
6035 EL_CHAR('"'), -1, -1
6038 Xalpha_comma, TRUE, FALSE,
6039 EL_CHAR(','), -1, -1
6042 Xalpha_minus, TRUE, FALSE,
6043 EL_CHAR('-'), -1, -1
6046 Xalpha_perio, TRUE, FALSE,
6047 EL_CHAR('.'), -1, -1
6050 Xalpha_colon, TRUE, FALSE,
6051 EL_CHAR(':'), -1, -1
6054 Xalpha_quest, TRUE, FALSE,
6055 EL_CHAR('?'), -1, -1
6058 Xalpha_a, TRUE, FALSE,
6059 EL_CHAR('A'), -1, -1
6062 Xalpha_b, TRUE, FALSE,
6063 EL_CHAR('B'), -1, -1
6066 Xalpha_c, TRUE, FALSE,
6067 EL_CHAR('C'), -1, -1
6070 Xalpha_d, TRUE, FALSE,
6071 EL_CHAR('D'), -1, -1
6074 Xalpha_e, TRUE, FALSE,
6075 EL_CHAR('E'), -1, -1
6078 Xalpha_f, TRUE, FALSE,
6079 EL_CHAR('F'), -1, -1
6082 Xalpha_g, TRUE, FALSE,
6083 EL_CHAR('G'), -1, -1
6086 Xalpha_h, TRUE, FALSE,
6087 EL_CHAR('H'), -1, -1
6090 Xalpha_i, TRUE, FALSE,
6091 EL_CHAR('I'), -1, -1
6094 Xalpha_j, TRUE, FALSE,
6095 EL_CHAR('J'), -1, -1
6098 Xalpha_k, TRUE, FALSE,
6099 EL_CHAR('K'), -1, -1
6102 Xalpha_l, TRUE, FALSE,
6103 EL_CHAR('L'), -1, -1
6106 Xalpha_m, TRUE, FALSE,
6107 EL_CHAR('M'), -1, -1
6110 Xalpha_n, TRUE, FALSE,
6111 EL_CHAR('N'), -1, -1
6114 Xalpha_o, TRUE, FALSE,
6115 EL_CHAR('O'), -1, -1
6118 Xalpha_p, TRUE, FALSE,
6119 EL_CHAR('P'), -1, -1
6122 Xalpha_q, TRUE, FALSE,
6123 EL_CHAR('Q'), -1, -1
6126 Xalpha_r, TRUE, FALSE,
6127 EL_CHAR('R'), -1, -1
6130 Xalpha_s, TRUE, FALSE,
6131 EL_CHAR('S'), -1, -1
6134 Xalpha_t, TRUE, FALSE,
6135 EL_CHAR('T'), -1, -1
6138 Xalpha_u, TRUE, FALSE,
6139 EL_CHAR('U'), -1, -1
6142 Xalpha_v, TRUE, FALSE,
6143 EL_CHAR('V'), -1, -1
6146 Xalpha_w, TRUE, FALSE,
6147 EL_CHAR('W'), -1, -1
6150 Xalpha_x, TRUE, FALSE,
6151 EL_CHAR('X'), -1, -1
6154 Xalpha_y, TRUE, FALSE,
6155 EL_CHAR('Y'), -1, -1
6158 Xalpha_z, TRUE, FALSE,
6159 EL_CHAR('Z'), -1, -1
6162 Xalpha_arrow_e, TRUE, FALSE,
6163 EL_CHAR('>'), -1, -1
6166 Xalpha_arrow_w, TRUE, FALSE,
6167 EL_CHAR('<'), -1, -1
6170 Xalpha_copyr, TRUE, FALSE,
6171 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6175 Xboom_bug, FALSE, FALSE,
6176 EL_BUG, ACTION_EXPLODING, -1
6179 Xboom_bomb, FALSE, FALSE,
6180 EL_BOMB, ACTION_EXPLODING, -1
6183 Xboom_android, FALSE, FALSE,
6184 EL_EMC_ANDROID, ACTION_OTHER, -1
6187 Xboom_1, FALSE, FALSE,
6188 EL_DEFAULT, ACTION_EXPLODING, -1
6191 Xboom_2, FALSE, FALSE,
6192 EL_DEFAULT, ACTION_EXPLODING, -1
6195 Znormal, FALSE, FALSE,
6199 Zdynamite, FALSE, FALSE,
6203 Zplayer, FALSE, FALSE,
6207 ZBORDER, FALSE, FALSE,
6217 static struct Mapping_EM_to_RND_player
6226 em_player_mapping_list[] =
6230 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6234 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6238 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6242 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6246 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6250 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6254 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6258 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6262 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6266 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6270 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6274 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6278 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6282 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6286 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6290 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6294 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6298 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6302 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6306 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6310 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6314 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6318 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6322 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6326 EL_PLAYER_1, ACTION_DEFAULT, -1,
6330 EL_PLAYER_2, ACTION_DEFAULT, -1,
6334 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6338 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6342 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6346 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6350 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6354 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6358 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6362 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6366 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6370 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6374 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6378 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6382 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6386 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6390 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6394 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6398 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6402 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6406 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6410 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6414 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6418 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6422 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6426 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6430 EL_PLAYER_3, ACTION_DEFAULT, -1,
6434 EL_PLAYER_4, ACTION_DEFAULT, -1,
6443 int map_element_RND_to_EM(int element_rnd)
6445 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6446 static boolean mapping_initialized = FALSE;
6448 if (!mapping_initialized)
6452 /* return "Xalpha_quest" for all undefined elements in mapping array */
6453 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6454 mapping_RND_to_EM[i] = Xalpha_quest;
6456 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6457 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6458 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6459 em_object_mapping_list[i].element_em;
6461 mapping_initialized = TRUE;
6464 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6465 return mapping_RND_to_EM[element_rnd];
6467 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6472 int map_element_EM_to_RND(int element_em)
6474 static unsigned short mapping_EM_to_RND[TILE_MAX];
6475 static boolean mapping_initialized = FALSE;
6477 if (!mapping_initialized)
6481 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6482 for (i = 0; i < TILE_MAX; i++)
6483 mapping_EM_to_RND[i] = EL_UNKNOWN;
6485 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6486 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6487 em_object_mapping_list[i].element_rnd;
6489 mapping_initialized = TRUE;
6492 if (element_em >= 0 && element_em < TILE_MAX)
6493 return mapping_EM_to_RND[element_em];
6495 Error(ERR_WARN, "invalid EM level element %d", element_em);
6500 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6502 struct LevelInfo_EM *level_em = level->native_em_level;
6503 struct LEVEL *lev = level_em->lev;
6506 for (i = 0; i < TILE_MAX; i++)
6507 lev->android_array[i] = Xblank;
6509 for (i = 0; i < level->num_android_clone_elements; i++)
6511 int element_rnd = level->android_clone_element[i];
6512 int element_em = map_element_RND_to_EM(element_rnd);
6514 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6515 if (em_object_mapping_list[j].element_rnd == element_rnd)
6516 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6520 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6522 struct LevelInfo_EM *level_em = level->native_em_level;
6523 struct LEVEL *lev = level_em->lev;
6526 level->num_android_clone_elements = 0;
6528 for (i = 0; i < TILE_MAX; i++)
6530 int element_em = lev->android_array[i];
6532 boolean element_found = FALSE;
6534 if (element_em == Xblank)
6537 element_rnd = map_element_EM_to_RND(element_em);
6539 for (j = 0; j < level->num_android_clone_elements; j++)
6540 if (level->android_clone_element[j] == element_rnd)
6541 element_found = TRUE;
6545 level->android_clone_element[level->num_android_clone_elements++] =
6548 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6553 if (level->num_android_clone_elements == 0)
6555 level->num_android_clone_elements = 1;
6556 level->android_clone_element[0] = EL_EMPTY;
6560 int map_direction_RND_to_EM(int direction)
6562 return (direction == MV_UP ? 0 :
6563 direction == MV_RIGHT ? 1 :
6564 direction == MV_DOWN ? 2 :
6565 direction == MV_LEFT ? 3 :
6569 int map_direction_EM_to_RND(int direction)
6571 return (direction == 0 ? MV_UP :
6572 direction == 1 ? MV_RIGHT :
6573 direction == 2 ? MV_DOWN :
6574 direction == 3 ? MV_LEFT :
6578 int map_element_RND_to_SP(int element_rnd)
6580 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6582 if (element_rnd >= EL_SP_START &&
6583 element_rnd <= EL_SP_END)
6584 element_sp = element_rnd - EL_SP_START;
6585 else if (element_rnd == EL_EMPTY_SPACE)
6587 else if (element_rnd == EL_INVISIBLE_WALL)
6593 int map_element_SP_to_RND(int element_sp)
6595 int element_rnd = EL_UNKNOWN;
6597 if (element_sp >= 0x00 &&
6599 element_rnd = EL_SP_START + element_sp;
6600 else if (element_sp == 0x28)
6601 element_rnd = EL_INVISIBLE_WALL;
6606 int map_action_SP_to_RND(int action_sp)
6610 case actActive: return ACTION_ACTIVE;
6611 case actImpact: return ACTION_IMPACT;
6612 case actExploding: return ACTION_EXPLODING;
6613 case actDigging: return ACTION_DIGGING;
6614 case actSnapping: return ACTION_SNAPPING;
6615 case actCollecting: return ACTION_COLLECTING;
6616 case actPassing: return ACTION_PASSING;
6617 case actPushing: return ACTION_PUSHING;
6618 case actDropping: return ACTION_DROPPING;
6620 default: return ACTION_DEFAULT;
6624 int get_next_element(int element)
6628 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6629 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6630 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6631 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6632 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6633 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6634 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6635 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6636 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6637 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6638 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6640 default: return element;
6644 int el_act_dir2img(int element, int action, int direction)
6646 element = GFX_ELEMENT(element);
6647 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6649 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6650 return element_info[element].direction_graphic[action][direction];
6653 static int el_act_dir2crm(int element, int action, int direction)
6655 element = GFX_ELEMENT(element);
6656 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6658 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6659 return element_info[element].direction_crumbled[action][direction];
6662 int el_act2img(int element, int action)
6664 element = GFX_ELEMENT(element);
6666 return element_info[element].graphic[action];
6669 int el_act2crm(int element, int action)
6671 element = GFX_ELEMENT(element);
6673 return element_info[element].crumbled[action];
6676 int el_dir2img(int element, int direction)
6678 element = GFX_ELEMENT(element);
6680 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6683 int el2baseimg(int element)
6685 return element_info[element].graphic[ACTION_DEFAULT];
6688 int el2img(int element)
6690 element = GFX_ELEMENT(element);
6692 return element_info[element].graphic[ACTION_DEFAULT];
6695 int el2edimg(int element)
6697 element = GFX_ELEMENT(element);
6699 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6702 int el2preimg(int element)
6704 element = GFX_ELEMENT(element);
6706 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6709 int el2panelimg(int element)
6711 element = GFX_ELEMENT(element);
6713 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6716 int font2baseimg(int font_nr)
6718 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6721 int getBeltNrFromBeltElement(int element)
6723 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6724 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6725 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6728 int getBeltNrFromBeltActiveElement(int element)
6730 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6731 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6732 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6735 int getBeltNrFromBeltSwitchElement(int element)
6737 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6738 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6739 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6742 int getBeltDirNrFromBeltElement(int element)
6744 static int belt_base_element[4] =
6746 EL_CONVEYOR_BELT_1_LEFT,
6747 EL_CONVEYOR_BELT_2_LEFT,
6748 EL_CONVEYOR_BELT_3_LEFT,
6749 EL_CONVEYOR_BELT_4_LEFT
6752 int belt_nr = getBeltNrFromBeltElement(element);
6753 int belt_dir_nr = element - belt_base_element[belt_nr];
6755 return (belt_dir_nr % 3);
6758 int getBeltDirNrFromBeltSwitchElement(int element)
6760 static int belt_base_element[4] =
6762 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6763 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6764 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6765 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6768 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6769 int belt_dir_nr = element - belt_base_element[belt_nr];
6771 return (belt_dir_nr % 3);
6774 int getBeltDirFromBeltElement(int element)
6776 static int belt_move_dir[3] =
6783 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6785 return belt_move_dir[belt_dir_nr];
6788 int getBeltDirFromBeltSwitchElement(int element)
6790 static int belt_move_dir[3] =
6797 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6799 return belt_move_dir[belt_dir_nr];
6802 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6804 static int belt_base_element[4] =
6806 EL_CONVEYOR_BELT_1_LEFT,
6807 EL_CONVEYOR_BELT_2_LEFT,
6808 EL_CONVEYOR_BELT_3_LEFT,
6809 EL_CONVEYOR_BELT_4_LEFT
6812 return belt_base_element[belt_nr] + belt_dir_nr;
6815 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6817 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6819 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6822 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6824 static int belt_base_element[4] =
6826 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6827 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6828 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6829 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6832 return belt_base_element[belt_nr] + belt_dir_nr;
6835 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6837 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6839 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6842 boolean getTeamMode_EM()
6844 return game.team_mode;
6847 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6849 int game_frame_delay_value;
6851 game_frame_delay_value =
6852 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6853 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6856 if (tape.playing && tape.warp_forward && !tape.pausing)
6857 game_frame_delay_value = 0;
6859 return game_frame_delay_value;
6862 unsigned int InitRND(int seed)
6864 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6865 return InitEngineRandom_EM(seed);
6866 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6867 return InitEngineRandom_SP(seed);
6869 return InitEngineRandom_RND(seed);
6872 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6873 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6875 inline static int get_effective_element_EM(int tile, int frame_em)
6877 int element = object_mapping[tile].element_rnd;
6878 int action = object_mapping[tile].action;
6879 boolean is_backside = object_mapping[tile].is_backside;
6880 boolean action_removing = (action == ACTION_DIGGING ||
6881 action == ACTION_SNAPPING ||
6882 action == ACTION_COLLECTING);
6888 case Yacid_splash_eB:
6889 case Yacid_splash_wB:
6890 return (frame_em > 5 ? EL_EMPTY : element);
6896 else /* frame_em == 7 */
6900 case Yacid_splash_eB:
6901 case Yacid_splash_wB:
6904 case Yemerald_stone:
6907 case Ydiamond_stone:
6911 case Xdrip_stretchB:
6930 case Xsand_stonein_1:
6931 case Xsand_stonein_2:
6932 case Xsand_stonein_3:
6933 case Xsand_stonein_4:
6937 return (is_backside || action_removing ? EL_EMPTY : element);
6942 inline static boolean check_linear_animation_EM(int tile)
6946 case Xsand_stonesand_1:
6947 case Xsand_stonesand_quickout_1:
6948 case Xsand_sandstone_1:
6949 case Xsand_stonein_1:
6950 case Xsand_stoneout_1:
6969 case Yacid_splash_eB:
6970 case Yacid_splash_wB:
6971 case Yemerald_stone:
6978 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
6979 boolean has_crumbled_graphics,
6980 int crumbled, int sync_frame)
6982 /* if element can be crumbled, but certain action graphics are just empty
6983 space (like instantly snapping sand to empty space in 1 frame), do not
6984 treat these empty space graphics as crumbled graphics in EMC engine */
6985 if (crumbled == IMG_EMPTY_SPACE)
6986 has_crumbled_graphics = FALSE;
6988 if (has_crumbled_graphics)
6990 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
6991 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
6992 g_crumbled->anim_delay,
6993 g_crumbled->anim_mode,
6994 g_crumbled->anim_start_frame,
6997 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
6998 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7000 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7002 g_em->has_crumbled_graphics = TRUE;
7006 g_em->crumbled_bitmap = NULL;
7007 g_em->crumbled_src_x = 0;
7008 g_em->crumbled_src_y = 0;
7009 g_em->crumbled_border_size = 0;
7011 g_em->has_crumbled_graphics = FALSE;
7015 void ResetGfxAnimation_EM(int x, int y, int tile)
7020 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7021 int tile, int frame_em, int x, int y)
7023 int action = object_mapping[tile].action;
7024 int direction = object_mapping[tile].direction;
7025 int effective_element = get_effective_element_EM(tile, frame_em);
7026 int graphic = (direction == MV_NONE ?
7027 el_act2img(effective_element, action) :
7028 el_act_dir2img(effective_element, action, direction));
7029 struct GraphicInfo *g = &graphic_info[graphic];
7031 boolean action_removing = (action == ACTION_DIGGING ||
7032 action == ACTION_SNAPPING ||
7033 action == ACTION_COLLECTING);
7034 boolean action_moving = (action == ACTION_FALLING ||
7035 action == ACTION_MOVING ||
7036 action == ACTION_PUSHING ||
7037 action == ACTION_EATING ||
7038 action == ACTION_FILLING ||
7039 action == ACTION_EMPTYING);
7040 boolean action_falling = (action == ACTION_FALLING ||
7041 action == ACTION_FILLING ||
7042 action == ACTION_EMPTYING);
7044 /* special case: graphic uses "2nd movement tile" and has defined
7045 7 frames for movement animation (or less) => use default graphic
7046 for last (8th) frame which ends the movement animation */
7047 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7049 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7050 graphic = (direction == MV_NONE ?
7051 el_act2img(effective_element, action) :
7052 el_act_dir2img(effective_element, action, direction));
7054 g = &graphic_info[graphic];
7057 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7061 else if (action_moving)
7063 boolean is_backside = object_mapping[tile].is_backside;
7067 int direction = object_mapping[tile].direction;
7068 int move_dir = (action_falling ? MV_DOWN : direction);
7073 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7074 if (g->double_movement && frame_em == 0)
7078 if (move_dir == MV_LEFT)
7079 GfxFrame[x - 1][y] = GfxFrame[x][y];
7080 else if (move_dir == MV_RIGHT)
7081 GfxFrame[x + 1][y] = GfxFrame[x][y];
7082 else if (move_dir == MV_UP)
7083 GfxFrame[x][y - 1] = GfxFrame[x][y];
7084 else if (move_dir == MV_DOWN)
7085 GfxFrame[x][y + 1] = GfxFrame[x][y];
7092 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7093 if (tile == Xsand_stonesand_quickout_1 ||
7094 tile == Xsand_stonesand_quickout_2)
7098 if (graphic_info[graphic].anim_global_sync)
7099 sync_frame = FrameCounter;
7100 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7101 sync_frame = GfxFrame[x][y];
7103 sync_frame = 0; /* playfield border (pseudo steel) */
7105 SetRandomAnimationValue(x, y);
7107 int frame = getAnimationFrame(g->anim_frames,
7110 g->anim_start_frame,
7113 g_em->unique_identifier =
7114 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7117 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7118 int tile, int frame_em, int x, int y)
7120 int action = object_mapping[tile].action;
7121 int direction = object_mapping[tile].direction;
7122 boolean is_backside = object_mapping[tile].is_backside;
7123 int effective_element = get_effective_element_EM(tile, frame_em);
7124 int effective_action = action;
7125 int graphic = (direction == MV_NONE ?
7126 el_act2img(effective_element, effective_action) :
7127 el_act_dir2img(effective_element, effective_action,
7129 int crumbled = (direction == MV_NONE ?
7130 el_act2crm(effective_element, effective_action) :
7131 el_act_dir2crm(effective_element, effective_action,
7133 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7134 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7135 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7136 struct GraphicInfo *g = &graphic_info[graphic];
7139 /* special case: graphic uses "2nd movement tile" and has defined
7140 7 frames for movement animation (or less) => use default graphic
7141 for last (8th) frame which ends the movement animation */
7142 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7144 effective_action = ACTION_DEFAULT;
7145 graphic = (direction == MV_NONE ?
7146 el_act2img(effective_element, effective_action) :
7147 el_act_dir2img(effective_element, effective_action,
7149 crumbled = (direction == MV_NONE ?
7150 el_act2crm(effective_element, effective_action) :
7151 el_act_dir2crm(effective_element, effective_action,
7154 g = &graphic_info[graphic];
7157 if (graphic_info[graphic].anim_global_sync)
7158 sync_frame = FrameCounter;
7159 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7160 sync_frame = GfxFrame[x][y];
7162 sync_frame = 0; /* playfield border (pseudo steel) */
7164 SetRandomAnimationValue(x, y);
7166 int frame = getAnimationFrame(g->anim_frames,
7169 g->anim_start_frame,
7172 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7173 g->double_movement && is_backside);
7175 /* (updating the "crumbled" graphic definitions is probably not really needed,
7176 as animations for crumbled graphics can't be longer than one EMC cycle) */
7177 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7181 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7182 int player_nr, int anim, int frame_em)
7184 int element = player_mapping[player_nr][anim].element_rnd;
7185 int action = player_mapping[player_nr][anim].action;
7186 int direction = player_mapping[player_nr][anim].direction;
7187 int graphic = (direction == MV_NONE ?
7188 el_act2img(element, action) :
7189 el_act_dir2img(element, action, direction));
7190 struct GraphicInfo *g = &graphic_info[graphic];
7193 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7195 stored_player[player_nr].StepFrame = frame_em;
7197 sync_frame = stored_player[player_nr].Frame;
7199 int frame = getAnimationFrame(g->anim_frames,
7202 g->anim_start_frame,
7205 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7206 &g_em->src_x, &g_em->src_y, FALSE);
7209 void InitGraphicInfo_EM(void)
7214 int num_em_gfx_errors = 0;
7216 if (graphic_info_em_object[0][0].bitmap == NULL)
7218 /* EM graphics not yet initialized in em_open_all() */
7223 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7226 /* always start with reliable default values */
7227 for (i = 0; i < TILE_MAX; i++)
7229 object_mapping[i].element_rnd = EL_UNKNOWN;
7230 object_mapping[i].is_backside = FALSE;
7231 object_mapping[i].action = ACTION_DEFAULT;
7232 object_mapping[i].direction = MV_NONE;
7235 /* always start with reliable default values */
7236 for (p = 0; p < MAX_PLAYERS; p++)
7238 for (i = 0; i < SPR_MAX; i++)
7240 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7241 player_mapping[p][i].action = ACTION_DEFAULT;
7242 player_mapping[p][i].direction = MV_NONE;
7246 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7248 int e = em_object_mapping_list[i].element_em;
7250 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7251 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7253 if (em_object_mapping_list[i].action != -1)
7254 object_mapping[e].action = em_object_mapping_list[i].action;
7256 if (em_object_mapping_list[i].direction != -1)
7257 object_mapping[e].direction =
7258 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7261 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7263 int a = em_player_mapping_list[i].action_em;
7264 int p = em_player_mapping_list[i].player_nr;
7266 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7268 if (em_player_mapping_list[i].action != -1)
7269 player_mapping[p][a].action = em_player_mapping_list[i].action;
7271 if (em_player_mapping_list[i].direction != -1)
7272 player_mapping[p][a].direction =
7273 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7276 for (i = 0; i < TILE_MAX; i++)
7278 int element = object_mapping[i].element_rnd;
7279 int action = object_mapping[i].action;
7280 int direction = object_mapping[i].direction;
7281 boolean is_backside = object_mapping[i].is_backside;
7282 boolean action_exploding = ((action == ACTION_EXPLODING ||
7283 action == ACTION_SMASHED_BY_ROCK ||
7284 action == ACTION_SMASHED_BY_SPRING) &&
7285 element != EL_DIAMOND);
7286 boolean action_active = (action == ACTION_ACTIVE);
7287 boolean action_other = (action == ACTION_OTHER);
7289 for (j = 0; j < 8; j++)
7291 int effective_element = get_effective_element_EM(i, j);
7292 int effective_action = (j < 7 ? action :
7293 i == Xdrip_stretch ? action :
7294 i == Xdrip_stretchB ? action :
7295 i == Ydrip_s1 ? action :
7296 i == Ydrip_s1B ? action :
7297 i == Xball_1B ? action :
7298 i == Xball_2 ? action :
7299 i == Xball_2B ? action :
7300 i == Yball_eat ? action :
7301 i == Ykey_1_eat ? action :
7302 i == Ykey_2_eat ? action :
7303 i == Ykey_3_eat ? action :
7304 i == Ykey_4_eat ? action :
7305 i == Ykey_5_eat ? action :
7306 i == Ykey_6_eat ? action :
7307 i == Ykey_7_eat ? action :
7308 i == Ykey_8_eat ? action :
7309 i == Ylenses_eat ? action :
7310 i == Ymagnify_eat ? action :
7311 i == Ygrass_eat ? action :
7312 i == Ydirt_eat ? action :
7313 i == Xsand_stonein_1 ? action :
7314 i == Xsand_stonein_2 ? action :
7315 i == Xsand_stonein_3 ? action :
7316 i == Xsand_stonein_4 ? action :
7317 i == Xsand_stoneout_1 ? action :
7318 i == Xsand_stoneout_2 ? action :
7319 i == Xboom_android ? ACTION_EXPLODING :
7320 action_exploding ? ACTION_EXPLODING :
7321 action_active ? action :
7322 action_other ? action :
7324 int graphic = (el_act_dir2img(effective_element, effective_action,
7326 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7328 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7329 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7330 boolean has_action_graphics = (graphic != base_graphic);
7331 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7332 struct GraphicInfo *g = &graphic_info[graphic];
7333 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7336 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7337 boolean special_animation = (action != ACTION_DEFAULT &&
7338 g->anim_frames == 3 &&
7339 g->anim_delay == 2 &&
7340 g->anim_mode & ANIM_LINEAR);
7341 int sync_frame = (i == Xdrip_stretch ? 7 :
7342 i == Xdrip_stretchB ? 7 :
7343 i == Ydrip_s2 ? j + 8 :
7344 i == Ydrip_s2B ? j + 8 :
7353 i == Xfake_acid_1 ? 0 :
7354 i == Xfake_acid_2 ? 10 :
7355 i == Xfake_acid_3 ? 20 :
7356 i == Xfake_acid_4 ? 30 :
7357 i == Xfake_acid_5 ? 40 :
7358 i == Xfake_acid_6 ? 50 :
7359 i == Xfake_acid_7 ? 60 :
7360 i == Xfake_acid_8 ? 70 :
7362 i == Xball_2B ? j + 8 :
7363 i == Yball_eat ? j + 1 :
7364 i == Ykey_1_eat ? j + 1 :
7365 i == Ykey_2_eat ? j + 1 :
7366 i == Ykey_3_eat ? j + 1 :
7367 i == Ykey_4_eat ? j + 1 :
7368 i == Ykey_5_eat ? j + 1 :
7369 i == Ykey_6_eat ? j + 1 :
7370 i == Ykey_7_eat ? j + 1 :
7371 i == Ykey_8_eat ? j + 1 :
7372 i == Ylenses_eat ? j + 1 :
7373 i == Ymagnify_eat ? j + 1 :
7374 i == Ygrass_eat ? j + 1 :
7375 i == Ydirt_eat ? j + 1 :
7376 i == Xamoeba_1 ? 0 :
7377 i == Xamoeba_2 ? 1 :
7378 i == Xamoeba_3 ? 2 :
7379 i == Xamoeba_4 ? 3 :
7380 i == Xamoeba_5 ? 0 :
7381 i == Xamoeba_6 ? 1 :
7382 i == Xamoeba_7 ? 2 :
7383 i == Xamoeba_8 ? 3 :
7384 i == Xexit_2 ? j + 8 :
7385 i == Xexit_3 ? j + 16 :
7386 i == Xdynamite_1 ? 0 :
7387 i == Xdynamite_2 ? 8 :
7388 i == Xdynamite_3 ? 16 :
7389 i == Xdynamite_4 ? 24 :
7390 i == Xsand_stonein_1 ? j + 1 :
7391 i == Xsand_stonein_2 ? j + 9 :
7392 i == Xsand_stonein_3 ? j + 17 :
7393 i == Xsand_stonein_4 ? j + 25 :
7394 i == Xsand_stoneout_1 && j == 0 ? 0 :
7395 i == Xsand_stoneout_1 && j == 1 ? 0 :
7396 i == Xsand_stoneout_1 && j == 2 ? 1 :
7397 i == Xsand_stoneout_1 && j == 3 ? 2 :
7398 i == Xsand_stoneout_1 && j == 4 ? 2 :
7399 i == Xsand_stoneout_1 && j == 5 ? 3 :
7400 i == Xsand_stoneout_1 && j == 6 ? 4 :
7401 i == Xsand_stoneout_1 && j == 7 ? 4 :
7402 i == Xsand_stoneout_2 && j == 0 ? 5 :
7403 i == Xsand_stoneout_2 && j == 1 ? 6 :
7404 i == Xsand_stoneout_2 && j == 2 ? 7 :
7405 i == Xsand_stoneout_2 && j == 3 ? 8 :
7406 i == Xsand_stoneout_2 && j == 4 ? 9 :
7407 i == Xsand_stoneout_2 && j == 5 ? 11 :
7408 i == Xsand_stoneout_2 && j == 6 ? 13 :
7409 i == Xsand_stoneout_2 && j == 7 ? 15 :
7410 i == Xboom_bug && j == 1 ? 2 :
7411 i == Xboom_bug && j == 2 ? 2 :
7412 i == Xboom_bug && j == 3 ? 4 :
7413 i == Xboom_bug && j == 4 ? 4 :
7414 i == Xboom_bug && j == 5 ? 2 :
7415 i == Xboom_bug && j == 6 ? 2 :
7416 i == Xboom_bug && j == 7 ? 0 :
7417 i == Xboom_bomb && j == 1 ? 2 :
7418 i == Xboom_bomb && j == 2 ? 2 :
7419 i == Xboom_bomb && j == 3 ? 4 :
7420 i == Xboom_bomb && j == 4 ? 4 :
7421 i == Xboom_bomb && j == 5 ? 2 :
7422 i == Xboom_bomb && j == 6 ? 2 :
7423 i == Xboom_bomb && j == 7 ? 0 :
7424 i == Xboom_android && j == 7 ? 6 :
7425 i == Xboom_1 && j == 1 ? 2 :
7426 i == Xboom_1 && j == 2 ? 2 :
7427 i == Xboom_1 && j == 3 ? 4 :
7428 i == Xboom_1 && j == 4 ? 4 :
7429 i == Xboom_1 && j == 5 ? 6 :
7430 i == Xboom_1 && j == 6 ? 6 :
7431 i == Xboom_1 && j == 7 ? 8 :
7432 i == Xboom_2 && j == 0 ? 8 :
7433 i == Xboom_2 && j == 1 ? 8 :
7434 i == Xboom_2 && j == 2 ? 10 :
7435 i == Xboom_2 && j == 3 ? 10 :
7436 i == Xboom_2 && j == 4 ? 10 :
7437 i == Xboom_2 && j == 5 ? 12 :
7438 i == Xboom_2 && j == 6 ? 12 :
7439 i == Xboom_2 && j == 7 ? 12 :
7440 special_animation && j == 4 ? 3 :
7441 effective_action != action ? 0 :
7445 Bitmap *debug_bitmap = g_em->bitmap;
7446 int debug_src_x = g_em->src_x;
7447 int debug_src_y = g_em->src_y;
7450 int frame = getAnimationFrame(g->anim_frames,
7453 g->anim_start_frame,
7456 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7457 g->double_movement && is_backside);
7459 g_em->bitmap = src_bitmap;
7460 g_em->src_x = src_x;
7461 g_em->src_y = src_y;
7462 g_em->src_offset_x = 0;
7463 g_em->src_offset_y = 0;
7464 g_em->dst_offset_x = 0;
7465 g_em->dst_offset_y = 0;
7466 g_em->width = TILEX;
7467 g_em->height = TILEY;
7469 g_em->preserve_background = FALSE;
7471 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7474 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7475 effective_action == ACTION_MOVING ||
7476 effective_action == ACTION_PUSHING ||
7477 effective_action == ACTION_EATING)) ||
7478 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7479 effective_action == ACTION_EMPTYING)))
7482 (effective_action == ACTION_FALLING ||
7483 effective_action == ACTION_FILLING ||
7484 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7485 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7486 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7487 int num_steps = (i == Ydrip_s1 ? 16 :
7488 i == Ydrip_s1B ? 16 :
7489 i == Ydrip_s2 ? 16 :
7490 i == Ydrip_s2B ? 16 :
7491 i == Xsand_stonein_1 ? 32 :
7492 i == Xsand_stonein_2 ? 32 :
7493 i == Xsand_stonein_3 ? 32 :
7494 i == Xsand_stonein_4 ? 32 :
7495 i == Xsand_stoneout_1 ? 16 :
7496 i == Xsand_stoneout_2 ? 16 : 8);
7497 int cx = ABS(dx) * (TILEX / num_steps);
7498 int cy = ABS(dy) * (TILEY / num_steps);
7499 int step_frame = (i == Ydrip_s2 ? j + 8 :
7500 i == Ydrip_s2B ? j + 8 :
7501 i == Xsand_stonein_2 ? j + 8 :
7502 i == Xsand_stonein_3 ? j + 16 :
7503 i == Xsand_stonein_4 ? j + 24 :
7504 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7505 int step = (is_backside ? step_frame : num_steps - step_frame);
7507 if (is_backside) /* tile where movement starts */
7509 if (dx < 0 || dy < 0)
7511 g_em->src_offset_x = cx * step;
7512 g_em->src_offset_y = cy * step;
7516 g_em->dst_offset_x = cx * step;
7517 g_em->dst_offset_y = cy * step;
7520 else /* tile where movement ends */
7522 if (dx < 0 || dy < 0)
7524 g_em->dst_offset_x = cx * step;
7525 g_em->dst_offset_y = cy * step;
7529 g_em->src_offset_x = cx * step;
7530 g_em->src_offset_y = cy * step;
7534 g_em->width = TILEX - cx * step;
7535 g_em->height = TILEY - cy * step;
7538 /* create unique graphic identifier to decide if tile must be redrawn */
7539 /* bit 31 - 16 (16 bit): EM style graphic
7540 bit 15 - 12 ( 4 bit): EM style frame
7541 bit 11 - 6 ( 6 bit): graphic width
7542 bit 5 - 0 ( 6 bit): graphic height */
7543 g_em->unique_identifier =
7544 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7548 /* skip check for EMC elements not contained in original EMC artwork */
7549 if (element == EL_EMC_FAKE_ACID)
7552 if (g_em->bitmap != debug_bitmap ||
7553 g_em->src_x != debug_src_x ||
7554 g_em->src_y != debug_src_y ||
7555 g_em->src_offset_x != 0 ||
7556 g_em->src_offset_y != 0 ||
7557 g_em->dst_offset_x != 0 ||
7558 g_em->dst_offset_y != 0 ||
7559 g_em->width != TILEX ||
7560 g_em->height != TILEY)
7562 static int last_i = -1;
7570 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7571 i, element, element_info[element].token_name,
7572 element_action_info[effective_action].suffix, direction);
7574 if (element != effective_element)
7575 printf(" [%d ('%s')]",
7577 element_info[effective_element].token_name);
7581 if (g_em->bitmap != debug_bitmap)
7582 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7583 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7585 if (g_em->src_x != debug_src_x ||
7586 g_em->src_y != debug_src_y)
7587 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7588 j, (is_backside ? 'B' : 'F'),
7589 g_em->src_x, g_em->src_y,
7590 g_em->src_x / 32, g_em->src_y / 32,
7591 debug_src_x, debug_src_y,
7592 debug_src_x / 32, debug_src_y / 32);
7594 if (g_em->src_offset_x != 0 ||
7595 g_em->src_offset_y != 0 ||
7596 g_em->dst_offset_x != 0 ||
7597 g_em->dst_offset_y != 0)
7598 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7600 g_em->src_offset_x, g_em->src_offset_y,
7601 g_em->dst_offset_x, g_em->dst_offset_y);
7603 if (g_em->width != TILEX ||
7604 g_em->height != TILEY)
7605 printf(" %d (%d): size %d,%d should be %d,%d\n",
7607 g_em->width, g_em->height, TILEX, TILEY);
7609 num_em_gfx_errors++;
7616 for (i = 0; i < TILE_MAX; i++)
7618 for (j = 0; j < 8; j++)
7620 int element = object_mapping[i].element_rnd;
7621 int action = object_mapping[i].action;
7622 int direction = object_mapping[i].direction;
7623 boolean is_backside = object_mapping[i].is_backside;
7624 int graphic_action = el_act_dir2img(element, action, direction);
7625 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7627 if ((action == ACTION_SMASHED_BY_ROCK ||
7628 action == ACTION_SMASHED_BY_SPRING ||
7629 action == ACTION_EATING) &&
7630 graphic_action == graphic_default)
7632 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7633 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7634 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7635 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7638 /* no separate animation for "smashed by rock" -- use rock instead */
7639 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7640 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7642 g_em->bitmap = g_xx->bitmap;
7643 g_em->src_x = g_xx->src_x;
7644 g_em->src_y = g_xx->src_y;
7645 g_em->src_offset_x = g_xx->src_offset_x;
7646 g_em->src_offset_y = g_xx->src_offset_y;
7647 g_em->dst_offset_x = g_xx->dst_offset_x;
7648 g_em->dst_offset_y = g_xx->dst_offset_y;
7649 g_em->width = g_xx->width;
7650 g_em->height = g_xx->height;
7651 g_em->unique_identifier = g_xx->unique_identifier;
7654 g_em->preserve_background = TRUE;
7659 for (p = 0; p < MAX_PLAYERS; p++)
7661 for (i = 0; i < SPR_MAX; i++)
7663 int element = player_mapping[p][i].element_rnd;
7664 int action = player_mapping[p][i].action;
7665 int direction = player_mapping[p][i].direction;
7667 for (j = 0; j < 8; j++)
7669 int effective_element = element;
7670 int effective_action = action;
7671 int graphic = (direction == MV_NONE ?
7672 el_act2img(effective_element, effective_action) :
7673 el_act_dir2img(effective_element, effective_action,
7675 struct GraphicInfo *g = &graphic_info[graphic];
7676 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7682 Bitmap *debug_bitmap = g_em->bitmap;
7683 int debug_src_x = g_em->src_x;
7684 int debug_src_y = g_em->src_y;
7687 int frame = getAnimationFrame(g->anim_frames,
7690 g->anim_start_frame,
7693 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7695 g_em->bitmap = src_bitmap;
7696 g_em->src_x = src_x;
7697 g_em->src_y = src_y;
7698 g_em->src_offset_x = 0;
7699 g_em->src_offset_y = 0;
7700 g_em->dst_offset_x = 0;
7701 g_em->dst_offset_y = 0;
7702 g_em->width = TILEX;
7703 g_em->height = TILEY;
7707 /* skip check for EMC elements not contained in original EMC artwork */
7708 if (element == EL_PLAYER_3 ||
7709 element == EL_PLAYER_4)
7712 if (g_em->bitmap != debug_bitmap ||
7713 g_em->src_x != debug_src_x ||
7714 g_em->src_y != debug_src_y)
7716 static int last_i = -1;
7724 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7725 p, i, element, element_info[element].token_name,
7726 element_action_info[effective_action].suffix, direction);
7728 if (element != effective_element)
7729 printf(" [%d ('%s')]",
7731 element_info[effective_element].token_name);
7735 if (g_em->bitmap != debug_bitmap)
7736 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7737 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7739 if (g_em->src_x != debug_src_x ||
7740 g_em->src_y != debug_src_y)
7741 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7743 g_em->src_x, g_em->src_y,
7744 g_em->src_x / 32, g_em->src_y / 32,
7745 debug_src_x, debug_src_y,
7746 debug_src_x / 32, debug_src_y / 32);
7748 num_em_gfx_errors++;
7758 printf("::: [%d errors found]\n", num_em_gfx_errors);
7764 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7765 boolean any_player_moving,
7766 boolean player_is_dropping)
7768 if (tape.single_step && tape.recording && !tape.pausing)
7769 if (frame == 0 && !player_is_dropping)
7770 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7773 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7774 boolean murphy_is_dropping)
7776 if (tape.single_step && tape.recording && !tape.pausing)
7777 if (murphy_is_waiting)
7778 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7781 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7782 int graphic, int sync_frame, int x, int y)
7784 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7786 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7789 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7791 return (IS_NEXT_FRAME(sync_frame, graphic));
7794 int getGraphicInfo_Delay(int graphic)
7796 return graphic_info[graphic].anim_delay;
7799 void PlayMenuSoundExt(int sound)
7801 if (sound == SND_UNDEFINED)
7804 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7805 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7808 if (IS_LOOP_SOUND(sound))
7809 PlaySoundLoop(sound);
7814 void PlayMenuSound()
7816 PlayMenuSoundExt(menu.sound[game_status]);
7819 void PlayMenuSoundStereo(int sound, int stereo_position)
7821 if (sound == SND_UNDEFINED)
7824 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7825 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7828 if (IS_LOOP_SOUND(sound))
7829 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7831 PlaySoundStereo(sound, stereo_position);
7834 void PlayMenuSoundIfLoopExt(int sound)
7836 if (sound == SND_UNDEFINED)
7839 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7840 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7843 if (IS_LOOP_SOUND(sound))
7844 PlaySoundLoop(sound);
7847 void PlayMenuSoundIfLoop()
7849 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7852 void PlayMenuMusicExt(int music)
7854 if (music == MUS_UNDEFINED)
7857 if (!setup.sound_music)
7863 void PlayMenuMusic()
7865 PlayMenuMusicExt(menu.music[game_status]);
7868 void PlaySoundActivating()
7871 PlaySound(SND_MENU_ITEM_ACTIVATING);
7875 void PlaySoundSelecting()
7878 PlaySound(SND_MENU_ITEM_SELECTING);
7882 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7884 boolean change_fullscreen = (setup.fullscreen !=
7885 video.fullscreen_enabled);
7886 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7887 !strEqual(setup.fullscreen_mode,
7888 video.fullscreen_mode_current));
7889 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7890 setup.window_scaling_percent !=
7891 video.window_scaling_percent);
7893 if (change_window_scaling_percent && video.fullscreen_enabled)
7896 if (!change_window_scaling_percent && !video.fullscreen_available)
7899 #if defined(TARGET_SDL2)
7900 if (change_window_scaling_percent)
7902 SDLSetWindowScaling(setup.window_scaling_percent);
7906 else if (change_fullscreen)
7908 SDLSetWindowFullscreen(setup.fullscreen);
7910 /* set setup value according to successfully changed fullscreen mode */
7911 setup.fullscreen = video.fullscreen_enabled;
7917 if (change_fullscreen ||
7918 change_fullscreen_mode ||
7919 change_window_scaling_percent)
7921 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
7923 /* save backbuffer content which gets lost when toggling fullscreen mode */
7924 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7926 if (change_fullscreen_mode)
7928 /* keep fullscreen, but change fullscreen mode (screen resolution) */
7929 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
7932 if (change_window_scaling_percent)
7934 /* keep window mode, but change window scaling */
7935 video.fullscreen_enabled = TRUE; /* force new window scaling */
7938 /* toggle fullscreen */
7939 ChangeVideoModeIfNeeded(setup.fullscreen);
7941 /* set setup value according to successfully changed fullscreen mode */
7942 setup.fullscreen = video.fullscreen_enabled;
7944 /* restore backbuffer content from temporary backbuffer backup bitmap */
7945 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7947 FreeBitmap(tmp_backbuffer);
7949 /* update visible window/screen */
7950 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7954 void ChangeViewportPropertiesIfNeeded()
7956 int gfx_game_mode = game_status;
7957 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
7959 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
7960 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
7961 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
7962 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
7963 int border_size = vp_playfield->border_size;
7964 int new_sx = vp_playfield->x + border_size;
7965 int new_sy = vp_playfield->y + border_size;
7966 int new_sxsize = vp_playfield->width - 2 * border_size;
7967 int new_sysize = vp_playfield->height - 2 * border_size;
7968 int new_real_sx = vp_playfield->x;
7969 int new_real_sy = vp_playfield->y;
7970 int new_full_sxsize = vp_playfield->width;
7971 int new_full_sysize = vp_playfield->height;
7972 int new_dx = vp_door_1->x;
7973 int new_dy = vp_door_1->y;
7974 int new_dxsize = vp_door_1->width;
7975 int new_dysize = vp_door_1->height;
7976 int new_vx = vp_door_2->x;
7977 int new_vy = vp_door_2->y;
7978 int new_vxsize = vp_door_2->width;
7979 int new_vysize = vp_door_2->height;
7980 int new_ex = vp_door_3->x;
7981 int new_ey = vp_door_3->y;
7982 int new_exsize = vp_door_3->width;
7983 int new_eysize = vp_door_3->height;
7984 int new_tilesize_var =
7985 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
7987 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
7988 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
7989 int new_scr_fieldx = new_sxsize / tilesize;
7990 int new_scr_fieldy = new_sysize / tilesize;
7991 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
7992 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
7993 boolean init_gfx_buffers = FALSE;
7994 boolean init_video_buffer = FALSE;
7995 boolean init_gadgets_and_toons = FALSE;
7996 boolean init_em_graphics = FALSE;
7998 if (viewport.window.width != WIN_XSIZE ||
7999 viewport.window.height != WIN_YSIZE)
8001 WIN_XSIZE = viewport.window.width;
8002 WIN_YSIZE = viewport.window.height;
8004 init_video_buffer = TRUE;
8005 init_gfx_buffers = TRUE;
8007 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8010 if (new_scr_fieldx != SCR_FIELDX ||
8011 new_scr_fieldy != SCR_FIELDY)
8013 /* this always toggles between MAIN and GAME when using small tile size */
8015 SCR_FIELDX = new_scr_fieldx;
8016 SCR_FIELDY = new_scr_fieldy;
8018 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8029 new_sxsize != SXSIZE ||
8030 new_sysize != SYSIZE ||
8031 new_dxsize != DXSIZE ||
8032 new_dysize != DYSIZE ||
8033 new_vxsize != VXSIZE ||
8034 new_vysize != VYSIZE ||
8035 new_exsize != EXSIZE ||
8036 new_eysize != EYSIZE ||
8037 new_real_sx != REAL_SX ||
8038 new_real_sy != REAL_SY ||
8039 new_full_sxsize != FULL_SXSIZE ||
8040 new_full_sysize != FULL_SYSIZE ||
8041 new_tilesize_var != TILESIZE_VAR
8044 if (new_tilesize_var != TILESIZE_VAR)
8046 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8048 // changing tile size invalidates scroll values of engine snapshots
8049 FreeEngineSnapshot();
8051 // changing tile size requires update of graphic mapping for EM engine
8052 init_em_graphics = TRUE;
8063 SXSIZE = new_sxsize;
8064 SYSIZE = new_sysize;
8065 DXSIZE = new_dxsize;
8066 DYSIZE = new_dysize;
8067 VXSIZE = new_vxsize;
8068 VYSIZE = new_vysize;
8069 EXSIZE = new_exsize;
8070 EYSIZE = new_eysize;
8071 REAL_SX = new_real_sx;
8072 REAL_SY = new_real_sy;
8073 FULL_SXSIZE = new_full_sxsize;
8074 FULL_SYSIZE = new_full_sysize;
8075 TILESIZE_VAR = new_tilesize_var;
8077 init_gfx_buffers = TRUE;
8078 init_gadgets_and_toons = TRUE;
8080 // printf("::: viewports: init_gfx_buffers\n");
8081 // printf("::: viewports: init_gadgets_and_toons\n");
8084 if (init_gfx_buffers)
8086 // printf("::: init_gfx_buffers\n");
8088 SCR_FIELDX = new_scr_fieldx_buffers;
8089 SCR_FIELDY = new_scr_fieldy_buffers;
8093 SCR_FIELDX = new_scr_fieldx;
8094 SCR_FIELDY = new_scr_fieldy;
8097 if (init_video_buffer)
8099 // printf("::: init_video_buffer\n");
8101 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8103 SetDrawDeactivationMask(REDRAW_NONE);
8104 SetDrawBackgroundMask(REDRAW_FIELD);
8107 if (init_gadgets_and_toons)
8109 // printf("::: init_gadgets_and_toons\n");
8115 if (init_em_graphics)
8117 InitGraphicInfo_EM();