1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static char *print_if_not_empty(int element)
178 static char *s = NULL;
179 char *token_name = element_info[element].token_name;
184 s = checked_malloc(strlen(token_name) + 10 + 1);
186 if (element != EL_EMPTY)
187 sprintf(s, "%d\t['%s']", element, token_name);
189 sprintf(s, "%d", element);
194 void DumpTile(int x, int y)
199 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
205 printf_line("-", 79);
206 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
207 printf_line("-", 79);
209 if (!IN_LEV_FIELD(x, y))
211 printf("(not in level field)\n");
217 printf(" Feld: %d\t['%s']\n", Feld[x][y],
218 element_info[Feld[x][y]].token_name);
219 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
220 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
221 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
222 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
223 printf(" MovPos: %d\n", MovPos[x][y]);
224 printf(" MovDir: %d\n", MovDir[x][y]);
225 printf(" MovDelay: %d\n", MovDelay[x][y]);
226 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
227 printf(" CustomValue: %d\n", CustomValue[x][y]);
228 printf(" GfxElement: %d\n", GfxElement[x][y]);
229 printf(" GfxAction: %d\n", GfxAction[x][y]);
230 printf(" GfxFrame: %d\n", GfxFrame[x][y]);
234 void SetDrawtoField(int mode)
236 if (mode == DRAW_BUFFERED && setup.soft_scrolling)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
247 drawto_field = fieldbuffer;
249 else /* DRAW_BACKBUFFER */
255 BX2 = SCR_FIELDX - 1;
256 BY2 = SCR_FIELDY - 1;
260 drawto_field = backbuffer;
264 static void RedrawPlayfield_RND()
266 if (game.envelope_active)
269 DrawLevel(REDRAW_ALL);
273 void RedrawPlayfield()
275 if (game_status != GAME_MODE_PLAYING)
278 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
279 RedrawPlayfield_EM(TRUE);
280 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
281 RedrawPlayfield_SP(TRUE);
282 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
283 RedrawPlayfield_RND();
285 BlitScreenToBitmap(backbuffer);
287 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
291 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
293 Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
295 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
298 void DrawMaskedBorder_FIELD()
300 if (global.border_status >= GAME_MODE_TITLE &&
301 global.border_status <= GAME_MODE_PLAYING &&
302 border.draw_masked[global.border_status])
303 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
306 void DrawMaskedBorder_DOOR_1()
308 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
309 (global.border_status != GAME_MODE_EDITOR ||
310 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
311 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
314 void DrawMaskedBorder_DOOR_2()
316 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
317 global.border_status != GAME_MODE_EDITOR)
318 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
321 void DrawMaskedBorder_DOOR_3()
323 /* currently not available */
326 void DrawMaskedBorder_ALL()
328 DrawMaskedBorder_FIELD();
329 DrawMaskedBorder_DOOR_1();
330 DrawMaskedBorder_DOOR_2();
331 DrawMaskedBorder_DOOR_3();
334 void DrawMaskedBorder(int redraw_mask)
336 /* never draw masked screen borders on borderless screens */
337 if (effectiveGameStatus() == GAME_MODE_LOADING ||
338 effectiveGameStatus() == GAME_MODE_TITLE)
341 /* never draw masked screen borders when displaying request outside door */
342 if (effectiveGameStatus() == GAME_MODE_PSEUDO_DOOR &&
343 global.use_envelope_request)
346 if (redraw_mask & REDRAW_ALL)
347 DrawMaskedBorder_ALL();
350 if (redraw_mask & REDRAW_FIELD)
351 DrawMaskedBorder_FIELD();
352 if (redraw_mask & REDRAW_DOOR_1)
353 DrawMaskedBorder_DOOR_1();
354 if (redraw_mask & REDRAW_DOOR_2)
355 DrawMaskedBorder_DOOR_2();
356 if (redraw_mask & REDRAW_DOOR_3)
357 DrawMaskedBorder_DOOR_3();
361 static void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
363 DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
364 int fx = FX, fy = FY;
365 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
366 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
368 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
369 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
370 int dx_var = dx * TILESIZE_VAR / TILESIZE;
371 int dy_var = dy * TILESIZE_VAR / TILESIZE;
374 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
375 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
377 if (EVEN(SCR_FIELDX))
379 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
380 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
382 fx += (dx_var > 0 ? TILEX_VAR : 0);
389 if (EVEN(SCR_FIELDY))
391 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
392 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
394 fy += (dy_var > 0 ? TILEY_VAR : 0);
401 if (full_lev_fieldx <= SCR_FIELDX)
403 if (EVEN(SCR_FIELDX))
404 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
406 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
409 if (full_lev_fieldy <= SCR_FIELDY)
411 if (EVEN(SCR_FIELDY))
412 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
414 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
417 if (border.draw_masked[GAME_MODE_PLAYING])
419 if (buffer != backbuffer)
421 /* copy playfield buffer to backbuffer to add masked border */
422 BlitBitmap(buffer, backbuffer, fx, fy, SXSIZE, SYSIZE, SX, SY);
423 DrawMaskedBorder(REDRAW_FIELD);
426 BlitBitmap(backbuffer, target_bitmap,
427 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
432 BlitBitmap(buffer, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
436 void BlitScreenToBitmap(Bitmap *target_bitmap)
438 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
439 BlitScreenToBitmap_EM(target_bitmap);
440 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
441 BlitScreenToBitmap_SP(target_bitmap);
442 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
443 BlitScreenToBitmap_RND(target_bitmap);
449 DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
451 if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
452 redraw_mask |= REDRAW_FIELD;
455 // never redraw single tiles, always redraw the whole field
456 // (redrawing single tiles up to a certain threshold was faster on old,
457 // now legacy graphics, but slows things down on modern graphics now)
458 // UPDATE: this is now globally defined by value of REDRAWTILES_THRESHOLD
459 if (redraw_mask & REDRAW_TILES)
460 redraw_mask |= REDRAW_FIELD;
464 /* !!! TEST ONLY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
465 /* (force full redraw) */
466 if (game_status == GAME_MODE_PLAYING)
467 redraw_mask |= REDRAW_FIELD;
470 if (redraw_mask & REDRAW_FIELD)
471 redraw_mask &= ~REDRAW_TILES;
473 if (redraw_mask == REDRAW_NONE)
478 if (redraw_mask & REDRAW_ALL)
479 printf("[REDRAW_ALL]");
480 if (redraw_mask & REDRAW_FIELD)
481 printf("[REDRAW_FIELD]");
482 if (redraw_mask & REDRAW_TILES)
483 printf("[REDRAW_TILES]");
484 if (redraw_mask & REDRAW_DOOR_1)
485 printf("[REDRAW_DOOR_1]");
486 if (redraw_mask & REDRAW_DOOR_2)
487 printf("[REDRAW_DOOR_2]");
488 if (redraw_mask & REDRAW_FROM_BACKBUFFER)
489 printf("[REDRAW_FROM_BACKBUFFER]");
490 printf(" [%d]\n", FrameCounter);
493 if (redraw_mask & REDRAW_TILES &&
494 game_status == GAME_MODE_PLAYING &&
495 border.draw_masked[GAME_MODE_PLAYING])
496 redraw_mask |= REDRAW_FIELD;
498 if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
500 static boolean last_frame_skipped = FALSE;
501 boolean skip_even_when_not_scrolling = TRUE;
502 boolean just_scrolling = (ScreenMovDir != 0);
503 boolean verbose = FALSE;
505 if (global.fps_slowdown_factor > 1 &&
506 (FrameCounter % global.fps_slowdown_factor) &&
507 (just_scrolling || skip_even_when_not_scrolling))
509 redraw_mask &= ~REDRAW_MAIN;
511 last_frame_skipped = TRUE;
514 printf("FRAME SKIPPED\n");
518 if (last_frame_skipped)
519 redraw_mask |= REDRAW_FIELD;
521 last_frame_skipped = FALSE;
524 printf("frame not skipped\n");
528 /* synchronize X11 graphics at this point; if we would synchronize the
529 display immediately after the buffer switching (after the XFlush),
530 this could mean that we have to wait for the graphics to complete,
531 although we could go on doing calculations for the next frame */
535 /* never draw masked border to backbuffer when using playfield buffer */
536 if (game_status != GAME_MODE_PLAYING ||
537 redraw_mask & REDRAW_FROM_BACKBUFFER ||
538 buffer == backbuffer)
539 DrawMaskedBorder(redraw_mask);
541 DrawMaskedBorder(redraw_mask & REDRAW_DOORS);
543 if (redraw_mask & REDRAW_ALL)
545 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
547 redraw_mask = REDRAW_NONE;
550 if (redraw_mask & REDRAW_FIELD)
552 if (game_status != GAME_MODE_PLAYING ||
553 redraw_mask & REDRAW_FROM_BACKBUFFER)
555 BlitBitmap(backbuffer, window,
556 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
560 BlitScreenToBitmap_RND(window);
563 redraw_mask &= ~REDRAW_MAIN;
566 if (redraw_mask & REDRAW_DOORS)
568 if (redraw_mask & REDRAW_DOOR_1)
569 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
571 if (redraw_mask & REDRAW_DOOR_2)
572 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
574 if (redraw_mask & REDRAW_DOOR_3)
575 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
577 redraw_mask &= ~REDRAW_DOORS;
580 if (redraw_mask & REDRAW_MICROLEVEL)
582 BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
583 SX, SY + 10 * TILEY);
585 redraw_mask &= ~REDRAW_MICROLEVEL;
588 if (redraw_mask & REDRAW_TILES)
594 int dx_var = dx * TILESIZE_VAR / TILESIZE;
595 int dy_var = dy * TILESIZE_VAR / TILESIZE;
597 int fx = FX, fy = FY;
599 int scr_fieldx = SCR_FIELDX + (EVEN(SCR_FIELDX) ? 2 : 0);
600 int scr_fieldy = SCR_FIELDY + (EVEN(SCR_FIELDY) ? 2 : 0);
602 InitGfxClipRegion(TRUE, SX, SY, SXSIZE, SYSIZE);
604 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
605 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
607 if (EVEN(SCR_FIELDX))
609 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
611 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
620 fx += (dx_var > 0 ? TILEX_VAR : 0);
624 if (EVEN(SCR_FIELDY))
626 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
628 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
637 fy += (dy_var > 0 ? TILEY_VAR : 0);
641 for (x = 0; x < scr_fieldx; x++)
642 for (y = 0 ; y < scr_fieldy; y++)
643 if (redraw[redraw_x1 + x][redraw_y1 + y])
644 BlitBitmap(buffer, window,
645 FX + x * TILEX_VAR, FY + y * TILEY_VAR,
646 TILEX_VAR, TILEY_VAR,
647 sx + x * TILEX_VAR, sy + y * TILEY_VAR);
649 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
652 if (redraw_mask & REDRAW_FPS) /* display frames per second */
657 sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
658 if (!global.fps_slowdown)
661 sprintf(text, "%04.1f fps%s", global.frames_per_second, info1);
663 DrawTextExt(window, SX + SXSIZE + SX, 0, text, FONT_TEXT_2, BLIT_OPAQUE);
666 for (x = 0; x < MAX_BUF_XSIZE; x++)
667 for (y = 0; y < MAX_BUF_YSIZE; y++)
670 redraw_mask = REDRAW_NONE;
673 static void FadeCrossSaveBackbuffer()
675 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
678 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
680 static int fade_type_skip = FADE_TYPE_NONE;
681 void (*draw_border_function)(void) = NULL;
682 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
683 int x, y, width, height;
684 int fade_delay, post_delay;
686 if (fade_type == FADE_TYPE_FADE_OUT)
688 if (fade_type_skip != FADE_TYPE_NONE)
690 /* skip all fade operations until specified fade operation */
691 if (fade_type & fade_type_skip)
692 fade_type_skip = FADE_TYPE_NONE;
697 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
699 FadeCrossSaveBackbuffer();
705 redraw_mask |= fade_mask;
707 if (fade_type == FADE_TYPE_SKIP)
709 fade_type_skip = fade_mode;
714 fade_delay = fading.fade_delay;
715 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
717 if (fade_type_skip != FADE_TYPE_NONE)
719 /* skip all fade operations until specified fade operation */
720 if (fade_type & fade_type_skip)
721 fade_type_skip = FADE_TYPE_NONE;
726 if (global.autoplay_leveldir)
731 if (fade_mask == REDRAW_FIELD)
736 height = FULL_SYSIZE;
738 if (border.draw_masked_when_fading)
739 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
741 DrawMaskedBorder_FIELD(); /* draw once */
743 else /* REDRAW_ALL */
751 if (!setup.fade_screens ||
753 fading.fade_mode == FADE_MODE_NONE)
755 if (fade_mode == FADE_MODE_FADE_OUT)
758 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
760 redraw_mask &= ~fade_mask;
765 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
766 draw_border_function);
768 redraw_mask &= ~fade_mask;
771 void FadeIn(int fade_mask)
773 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
774 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
776 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
779 void FadeOut(int fade_mask)
781 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
782 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
784 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
786 global.border_status = game_status;
789 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
791 static struct TitleFadingInfo fading_leave_stored;
794 fading_leave_stored = fading_leave;
796 fading = fading_leave_stored;
799 void FadeSetEnterMenu()
801 fading = menu.enter_menu;
803 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
806 void FadeSetLeaveMenu()
808 fading = menu.leave_menu;
810 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
813 void FadeSetEnterScreen()
815 fading = menu.enter_screen[game_status];
817 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
820 void FadeSetNextScreen()
822 fading = menu.next_screen;
824 // (do not overwrite fade mode set by FadeSetEnterScreen)
825 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
828 void FadeSetLeaveScreen()
830 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
833 void FadeSetFromType(int type)
835 if (type & TYPE_ENTER_SCREEN)
836 FadeSetEnterScreen();
837 else if (type & TYPE_ENTER)
839 else if (type & TYPE_LEAVE)
843 void FadeSetDisabled()
845 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
847 fading = fading_none;
850 void FadeSkipNextFadeIn()
852 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
855 void FadeSkipNextFadeOut()
857 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
860 void SetWindowBackgroundImageIfDefined(int graphic)
862 if (graphic_info[graphic].bitmap)
863 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
866 void SetMainBackgroundImageIfDefined(int graphic)
868 if (graphic_info[graphic].bitmap)
869 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
872 void SetDoorBackgroundImageIfDefined(int graphic)
874 if (graphic_info[graphic].bitmap)
875 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
878 void SetWindowBackgroundImage(int graphic)
880 SetWindowBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
881 graphic_info[graphic].bitmap ?
882 graphic_info[graphic].bitmap :
883 graphic_info[IMG_BACKGROUND].bitmap);
886 void SetMainBackgroundImage(int graphic)
888 SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
889 graphic_info[graphic].bitmap ?
890 graphic_info[graphic].bitmap :
891 graphic_info[IMG_BACKGROUND].bitmap);
894 void SetDoorBackgroundImage(int graphic)
896 SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
897 graphic_info[graphic].bitmap ?
898 graphic_info[graphic].bitmap :
899 graphic_info[IMG_BACKGROUND].bitmap);
902 void SetPanelBackground()
904 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
906 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
907 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
909 SetDoorBackgroundBitmap(bitmap_db_panel);
912 void DrawBackground(int x, int y, int width, int height)
914 /* "drawto" might still point to playfield buffer here (hall of fame) */
915 ClearRectangleOnBackground(backbuffer, x, y, width, height);
917 if (IN_GFX_FIELD_FULL(x, y))
918 redraw_mask |= REDRAW_FIELD;
919 else if (IN_GFX_DOOR_1(x, y))
920 redraw_mask |= REDRAW_DOOR_1;
921 else if (IN_GFX_DOOR_2(x, y))
922 redraw_mask |= REDRAW_DOOR_2;
923 else if (IN_GFX_DOOR_3(x, y))
924 redraw_mask |= REDRAW_DOOR_3;
927 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
929 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
931 if (font->bitmap == NULL)
934 DrawBackground(x, y, width, height);
937 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
939 struct GraphicInfo *g = &graphic_info[graphic];
941 if (g->bitmap == NULL)
944 DrawBackground(x, y, width, height);
949 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
950 /* (when entering hall of fame after playing) */
951 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
953 /* !!! maybe this should be done before clearing the background !!! */
954 if (setup.soft_scrolling && game_status == GAME_MODE_PLAYING)
956 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
957 SetDrawtoField(DRAW_BUFFERED);
960 SetDrawtoField(DRAW_BACKBUFFER);
963 void MarkTileDirty(int x, int y)
965 int xx = redraw_x1 + x;
966 int yy = redraw_y1 + y;
971 redraw[xx][yy] = TRUE;
972 redraw_mask |= REDRAW_TILES;
975 void SetBorderElement()
979 BorderElement = EL_EMPTY;
981 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
983 for (x = 0; x < lev_fieldx; x++)
985 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
986 BorderElement = EL_STEELWALL;
988 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
994 void FloodFillLevel(int from_x, int from_y, int fill_element,
995 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
996 int max_fieldx, int max_fieldy)
1000 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1001 static int safety = 0;
1003 /* check if starting field still has the desired content */
1004 if (field[from_x][from_y] == fill_element)
1009 if (safety > max_fieldx * max_fieldy)
1010 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1012 old_element = field[from_x][from_y];
1013 field[from_x][from_y] = fill_element;
1015 for (i = 0; i < 4; i++)
1017 x = from_x + check[i][0];
1018 y = from_y + check[i][1];
1020 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1021 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1027 void SetRandomAnimationValue(int x, int y)
1029 gfx.anim_random_frame = GfxRandom[x][y];
1032 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
1034 /* animation synchronized with global frame counter, not move position */
1035 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1036 sync_frame = FrameCounter;
1038 return getAnimationFrame(graphic_info[graphic].anim_frames,
1039 graphic_info[graphic].anim_delay,
1040 graphic_info[graphic].anim_mode,
1041 graphic_info[graphic].anim_start_frame,
1045 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1046 Bitmap **bitmap, int *x, int *y,
1047 boolean get_backside)
1049 struct GraphicInfo *g = &graphic_info[graphic];
1050 Bitmap *src_bitmap = g->bitmap;
1051 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1052 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1053 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1055 // if no in-game graphics defined, always use standard graphic size
1056 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1057 tilesize = TILESIZE;
1059 if (tilesize == gfx.standard_tile_size)
1060 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1061 else if (tilesize == game.tile_size)
1062 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1064 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1066 if (g->offset_y == 0) /* frames are ordered horizontally */
1068 int max_width = g->anim_frames_per_line * g->width;
1069 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1071 src_x = pos % max_width;
1072 src_y = src_y % g->height + pos / max_width * g->height;
1074 else if (g->offset_x == 0) /* frames are ordered vertically */
1076 int max_height = g->anim_frames_per_line * g->height;
1077 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1079 src_x = src_x % g->width + pos / max_height * g->width;
1080 src_y = pos % max_height;
1082 else /* frames are ordered diagonally */
1084 src_x = src_x + frame * g->offset_x;
1085 src_y = src_y + frame * g->offset_y;
1088 *bitmap = src_bitmap;
1089 *x = src_x * tilesize / TILESIZE;
1090 *y = src_y * tilesize / TILESIZE;
1093 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1094 int *x, int *y, boolean get_backside)
1096 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1100 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1101 Bitmap **bitmap, int *x, int *y)
1103 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1106 void getFixedGraphicSource(int graphic, int frame,
1107 Bitmap **bitmap, int *x, int *y)
1109 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1112 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1114 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1117 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1118 int *x, int *y, boolean get_backside)
1120 struct GraphicInfo *g = &graphic_info[graphic];
1121 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1122 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1124 if (TILESIZE_VAR != TILESIZE)
1125 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1128 *bitmap = g->bitmap;
1130 if (g->offset_y == 0) /* frames are ordered horizontally */
1132 int max_width = g->anim_frames_per_line * g->width;
1133 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1135 *x = pos % max_width;
1136 *y = src_y % g->height + pos / max_width * g->height;
1138 else if (g->offset_x == 0) /* frames are ordered vertically */
1140 int max_height = g->anim_frames_per_line * g->height;
1141 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1143 *x = src_x % g->width + pos / max_height * g->width;
1144 *y = pos % max_height;
1146 else /* frames are ordered diagonally */
1148 *x = src_x + frame * g->offset_x;
1149 *y = src_y + frame * g->offset_y;
1153 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1155 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1158 void DrawGraphic(int x, int y, int graphic, int frame)
1161 if (!IN_SCR_FIELD(x, y))
1163 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1164 printf("DrawGraphic(): This should never happen!\n");
1169 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1172 MarkTileDirty(x, y);
1175 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1178 if (!IN_SCR_FIELD(x, y))
1180 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1181 printf("DrawGraphic(): This should never happen!\n");
1186 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1188 MarkTileDirty(x, y);
1191 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1197 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1199 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1202 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1208 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1209 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1212 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1215 if (!IN_SCR_FIELD(x, y))
1217 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1218 printf("DrawGraphicThruMask(): This should never happen!\n");
1223 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1226 MarkTileDirty(x, y);
1229 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1232 if (!IN_SCR_FIELD(x, y))
1234 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1235 printf("DrawGraphicThruMask(): This should never happen!\n");
1240 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1242 MarkTileDirty(x, y);
1245 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1251 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1253 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1257 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1258 int graphic, int frame)
1260 struct GraphicInfo *g = &graphic_info[graphic];
1264 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1266 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1270 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1272 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1274 MarkTileDirty(x / tilesize, y / tilesize);
1277 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1283 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1284 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1287 void DrawMiniGraphic(int x, int y, int graphic)
1289 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1290 MarkTileDirty(x / 2, y / 2);
1293 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1298 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1299 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1302 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1303 int graphic, int frame,
1304 int cut_mode, int mask_mode)
1309 int width = TILEX, height = TILEY;
1312 if (dx || dy) /* shifted graphic */
1314 if (x < BX1) /* object enters playfield from the left */
1321 else if (x > BX2) /* object enters playfield from the right */
1327 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1333 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1335 else if (dx) /* general horizontal movement */
1336 MarkTileDirty(x + SIGN(dx), y);
1338 if (y < BY1) /* object enters playfield from the top */
1340 if (cut_mode==CUT_BELOW) /* object completely above top border */
1348 else if (y > BY2) /* object enters playfield from the bottom */
1354 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1360 else if (dy > 0 && cut_mode == CUT_ABOVE)
1362 if (y == BY2) /* object completely above bottom border */
1368 MarkTileDirty(x, y + 1);
1369 } /* object leaves playfield to the bottom */
1370 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1372 else if (dy) /* general vertical movement */
1373 MarkTileDirty(x, y + SIGN(dy));
1377 if (!IN_SCR_FIELD(x, y))
1379 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1380 printf("DrawGraphicShifted(): This should never happen!\n");
1385 width = width * TILESIZE_VAR / TILESIZE;
1386 height = height * TILESIZE_VAR / TILESIZE;
1387 cx = cx * TILESIZE_VAR / TILESIZE;
1388 cy = cy * TILESIZE_VAR / TILESIZE;
1389 dx = dx * TILESIZE_VAR / TILESIZE;
1390 dy = dy * TILESIZE_VAR / TILESIZE;
1392 if (width > 0 && height > 0)
1394 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1399 dst_x = FX + x * TILEX_VAR + dx;
1400 dst_y = FY + y * TILEY_VAR + dy;
1402 if (mask_mode == USE_MASKING)
1403 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1406 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1409 MarkTileDirty(x, y);
1413 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1414 int graphic, int frame,
1415 int cut_mode, int mask_mode)
1420 int width = TILEX_VAR, height = TILEY_VAR;
1423 int x2 = x + SIGN(dx);
1424 int y2 = y + SIGN(dy);
1426 /* movement with two-tile animations must be sync'ed with movement position,
1427 not with current GfxFrame (which can be higher when using slow movement) */
1428 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1429 int anim_frames = graphic_info[graphic].anim_frames;
1431 /* (we also need anim_delay here for movement animations with less frames) */
1432 int anim_delay = graphic_info[graphic].anim_delay;
1433 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1435 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1436 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1438 /* re-calculate animation frame for two-tile movement animation */
1439 frame = getGraphicAnimationFrame(graphic, sync_frame);
1441 /* check if movement start graphic inside screen area and should be drawn */
1442 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1444 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1446 dst_x = FX + x1 * TILEX_VAR;
1447 dst_y = FY + y1 * TILEY_VAR;
1449 if (mask_mode == USE_MASKING)
1450 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1453 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1456 MarkTileDirty(x1, y1);
1459 /* check if movement end graphic inside screen area and should be drawn */
1460 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1462 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1464 dst_x = FX + x2 * TILEX_VAR;
1465 dst_y = FY + y2 * TILEY_VAR;
1467 if (mask_mode == USE_MASKING)
1468 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1471 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1474 MarkTileDirty(x2, y2);
1478 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1479 int graphic, int frame,
1480 int cut_mode, int mask_mode)
1484 DrawGraphic(x, y, graphic, frame);
1489 if (graphic_info[graphic].double_movement) /* EM style movement images */
1490 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1492 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1495 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1496 int frame, int cut_mode)
1498 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1501 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1502 int cut_mode, int mask_mode)
1504 int lx = LEVELX(x), ly = LEVELY(y);
1508 if (IN_LEV_FIELD(lx, ly))
1510 SetRandomAnimationValue(lx, ly);
1512 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1513 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1515 /* do not use double (EM style) movement graphic when not moving */
1516 if (graphic_info[graphic].double_movement && !dx && !dy)
1518 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1519 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1522 else /* border element */
1524 graphic = el2img(element);
1525 frame = getGraphicAnimationFrame(graphic, -1);
1528 if (element == EL_EXPANDABLE_WALL)
1530 boolean left_stopped = FALSE, right_stopped = FALSE;
1532 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1533 left_stopped = TRUE;
1534 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1535 right_stopped = TRUE;
1537 if (left_stopped && right_stopped)
1539 else if (left_stopped)
1541 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1542 frame = graphic_info[graphic].anim_frames - 1;
1544 else if (right_stopped)
1546 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1547 frame = graphic_info[graphic].anim_frames - 1;
1552 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1553 else if (mask_mode == USE_MASKING)
1554 DrawGraphicThruMask(x, y, graphic, frame);
1556 DrawGraphic(x, y, graphic, frame);
1559 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1560 int cut_mode, int mask_mode)
1562 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1563 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1564 cut_mode, mask_mode);
1567 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1570 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1573 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1576 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1579 void DrawLevelElementThruMask(int x, int y, int element)
1581 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1584 void DrawLevelFieldThruMask(int x, int y)
1586 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1589 /* !!! implementation of quicksand is totally broken !!! */
1590 #define IS_CRUMBLED_TILE(x, y, e) \
1591 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1592 !IS_MOVING(x, y) || \
1593 (e) == EL_QUICKSAND_EMPTYING || \
1594 (e) == EL_QUICKSAND_FAST_EMPTYING))
1596 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1601 int width, height, cx, cy;
1602 int sx = SCREENX(x), sy = SCREENY(y);
1603 int crumbled_border_size = graphic_info[graphic].border_size;
1606 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1608 for (i = 1; i < 4; i++)
1610 int dxx = (i & 1 ? dx : 0);
1611 int dyy = (i & 2 ? dy : 0);
1614 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1617 /* check if neighbour field is of same crumble type */
1618 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1619 graphic_info[graphic].class ==
1620 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1622 /* return if check prevents inner corner */
1623 if (same == (dxx == dx && dyy == dy))
1627 /* if we reach this point, we have an inner corner */
1629 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1631 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1632 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1633 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1634 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1636 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1637 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1640 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1645 int width, height, bx, by, cx, cy;
1646 int sx = SCREENX(x), sy = SCREENY(y);
1647 int crumbled_border_size = graphic_info[graphic].border_size;
1648 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1649 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1652 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1654 /* draw simple, sloppy, non-corner-accurate crumbled border */
1656 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1657 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1658 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1659 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1661 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1662 FX + sx * TILEX_VAR + cx,
1663 FY + sy * TILEY_VAR + cy);
1665 /* (remaining middle border part must be at least as big as corner part) */
1666 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1667 crumbled_border_size >= TILESIZE / 3)
1670 /* correct corners of crumbled border, if needed */
1672 for (i = -1; i <= 1; i += 2)
1674 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1675 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1676 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1679 /* check if neighbour field is of same crumble type */
1680 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1681 graphic_info[graphic].class ==
1682 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1684 /* no crumbled corner, but continued crumbled border */
1686 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1687 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1688 int b1 = (i == 1 ? crumbled_border_size_var :
1689 TILESIZE_VAR - 2 * crumbled_border_size_var);
1691 width = crumbled_border_size_var;
1692 height = crumbled_border_size_var;
1694 if (dir == 1 || dir == 2)
1709 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1711 FX + sx * TILEX_VAR + cx,
1712 FY + sy * TILEY_VAR + cy);
1717 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1719 int sx = SCREENX(x), sy = SCREENY(y);
1722 static int xy[4][2] =
1730 if (!IN_LEV_FIELD(x, y))
1733 element = TILE_GFX_ELEMENT(x, y);
1735 /* crumble field itself */
1736 if (IS_CRUMBLED_TILE(x, y, element))
1738 if (!IN_SCR_FIELD(sx, sy))
1741 for (i = 0; i < 4; i++)
1743 int xx = x + xy[i][0];
1744 int yy = y + xy[i][1];
1746 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1749 /* check if neighbour field is of same crumble type */
1750 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1751 graphic_info[graphic].class ==
1752 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1755 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1758 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1759 graphic_info[graphic].anim_frames == 2)
1761 for (i = 0; i < 4; i++)
1763 int dx = (i & 1 ? +1 : -1);
1764 int dy = (i & 2 ? +1 : -1);
1766 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1770 MarkTileDirty(sx, sy);
1772 else /* center field not crumbled -- crumble neighbour fields */
1774 for (i = 0; i < 4; i++)
1776 int xx = x + xy[i][0];
1777 int yy = y + xy[i][1];
1778 int sxx = sx + xy[i][0];
1779 int syy = sy + xy[i][1];
1781 if (!IN_LEV_FIELD(xx, yy) ||
1782 !IN_SCR_FIELD(sxx, syy))
1785 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1788 element = TILE_GFX_ELEMENT(xx, yy);
1790 if (!IS_CRUMBLED_TILE(xx, yy, element))
1793 graphic = el_act2crm(element, ACTION_DEFAULT);
1795 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1797 MarkTileDirty(sxx, syy);
1802 void DrawLevelFieldCrumbled(int x, int y)
1806 if (!IN_LEV_FIELD(x, y))
1809 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1810 GfxElement[x][y] != EL_UNDEFINED &&
1811 GFX_CRUMBLED(GfxElement[x][y]))
1813 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1818 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1820 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1823 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1826 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1827 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1828 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1829 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1830 int sx = SCREENX(x), sy = SCREENY(y);
1832 DrawGraphic(sx, sy, graphic1, frame1);
1833 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1836 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1838 int sx = SCREENX(x), sy = SCREENY(y);
1839 static int xy[4][2] =
1848 for (i = 0; i < 4; i++)
1850 int xx = x + xy[i][0];
1851 int yy = y + xy[i][1];
1852 int sxx = sx + xy[i][0];
1853 int syy = sy + xy[i][1];
1855 if (!IN_LEV_FIELD(xx, yy) ||
1856 !IN_SCR_FIELD(sxx, syy) ||
1857 !GFX_CRUMBLED(Feld[xx][yy]) ||
1861 DrawLevelField(xx, yy);
1865 static int getBorderElement(int x, int y)
1869 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1870 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1871 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1872 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1873 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1874 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1875 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1877 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1878 int steel_position = (x == -1 && y == -1 ? 0 :
1879 x == lev_fieldx && y == -1 ? 1 :
1880 x == -1 && y == lev_fieldy ? 2 :
1881 x == lev_fieldx && y == lev_fieldy ? 3 :
1882 x == -1 || x == lev_fieldx ? 4 :
1883 y == -1 || y == lev_fieldy ? 5 : 6);
1885 return border[steel_position][steel_type];
1888 void DrawScreenElement(int x, int y, int element)
1890 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1891 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1894 void DrawLevelElement(int x, int y, int element)
1896 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1897 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1900 void DrawScreenField(int x, int y)
1902 int lx = LEVELX(x), ly = LEVELY(y);
1903 int element, content;
1905 if (!IN_LEV_FIELD(lx, ly))
1907 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1910 element = getBorderElement(lx, ly);
1912 DrawScreenElement(x, y, element);
1917 element = Feld[lx][ly];
1918 content = Store[lx][ly];
1920 if (IS_MOVING(lx, ly))
1922 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1923 boolean cut_mode = NO_CUTTING;
1925 if (element == EL_QUICKSAND_EMPTYING ||
1926 element == EL_QUICKSAND_FAST_EMPTYING ||
1927 element == EL_MAGIC_WALL_EMPTYING ||
1928 element == EL_BD_MAGIC_WALL_EMPTYING ||
1929 element == EL_DC_MAGIC_WALL_EMPTYING ||
1930 element == EL_AMOEBA_DROPPING)
1931 cut_mode = CUT_ABOVE;
1932 else if (element == EL_QUICKSAND_FILLING ||
1933 element == EL_QUICKSAND_FAST_FILLING ||
1934 element == EL_MAGIC_WALL_FILLING ||
1935 element == EL_BD_MAGIC_WALL_FILLING ||
1936 element == EL_DC_MAGIC_WALL_FILLING)
1937 cut_mode = CUT_BELOW;
1939 if (cut_mode == CUT_ABOVE)
1940 DrawScreenElement(x, y, element);
1942 DrawScreenElement(x, y, EL_EMPTY);
1945 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1946 else if (cut_mode == NO_CUTTING)
1947 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1950 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1952 if (cut_mode == CUT_BELOW &&
1953 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1954 DrawLevelElement(lx, ly + 1, element);
1957 if (content == EL_ACID)
1959 int dir = MovDir[lx][ly];
1960 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1961 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1963 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1966 else if (IS_BLOCKED(lx, ly))
1971 boolean cut_mode = NO_CUTTING;
1972 int element_old, content_old;
1974 Blocked2Moving(lx, ly, &oldx, &oldy);
1977 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1978 MovDir[oldx][oldy] == MV_RIGHT);
1980 element_old = Feld[oldx][oldy];
1981 content_old = Store[oldx][oldy];
1983 if (element_old == EL_QUICKSAND_EMPTYING ||
1984 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1985 element_old == EL_MAGIC_WALL_EMPTYING ||
1986 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1987 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1988 element_old == EL_AMOEBA_DROPPING)
1989 cut_mode = CUT_ABOVE;
1991 DrawScreenElement(x, y, EL_EMPTY);
1994 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1996 else if (cut_mode == NO_CUTTING)
1997 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2000 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2003 else if (IS_DRAWABLE(element))
2004 DrawScreenElement(x, y, element);
2006 DrawScreenElement(x, y, EL_EMPTY);
2009 void DrawLevelField(int x, int y)
2011 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2012 DrawScreenField(SCREENX(x), SCREENY(y));
2013 else if (IS_MOVING(x, y))
2017 Moving2Blocked(x, y, &newx, &newy);
2018 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2019 DrawScreenField(SCREENX(newx), SCREENY(newy));
2021 else if (IS_BLOCKED(x, y))
2025 Blocked2Moving(x, y, &oldx, &oldy);
2026 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2027 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2031 void DrawSizedElement(int x, int y, int element, int tilesize)
2035 graphic = el2edimg(element);
2036 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2039 void DrawMiniElement(int x, int y, int element)
2043 graphic = el2edimg(element);
2044 DrawMiniGraphic(x, y, graphic);
2047 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2050 int x = sx + scroll_x, y = sy + scroll_y;
2052 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2053 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2054 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2055 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2057 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2060 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2062 int x = sx + scroll_x, y = sy + scroll_y;
2064 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2065 DrawMiniElement(sx, sy, EL_EMPTY);
2066 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2067 DrawMiniElement(sx, sy, Feld[x][y]);
2069 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2072 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2073 int x, int y, int xsize, int ysize,
2074 int tile_width, int tile_height)
2078 int dst_x = startx + x * tile_width;
2079 int dst_y = starty + y * tile_height;
2080 int width = graphic_info[graphic].width;
2081 int height = graphic_info[graphic].height;
2082 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2083 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2084 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2085 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2086 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2087 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2088 boolean draw_masked = graphic_info[graphic].draw_masked;
2090 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2092 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2094 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2098 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2099 inner_sx + (x - 1) * tile_width % inner_width);
2100 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2101 inner_sy + (y - 1) * tile_height % inner_height);
2104 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2107 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2111 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2112 int x, int y, int xsize, int ysize, int font_nr)
2114 int font_width = getFontWidth(font_nr);
2115 int font_height = getFontHeight(font_nr);
2117 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2118 font_width, font_height);
2121 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2123 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2124 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2125 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2126 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2127 boolean no_delay = (tape.warp_forward);
2128 unsigned int anim_delay = 0;
2129 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2130 int anim_delay_value = (no_delay ? 0 : frame_delay_value);
2131 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2132 int font_width = getFontWidth(font_nr);
2133 int font_height = getFontHeight(font_nr);
2134 int max_xsize = level.envelope[envelope_nr].xsize;
2135 int max_ysize = level.envelope[envelope_nr].ysize;
2136 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2137 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2138 int xend = max_xsize;
2139 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2140 int xstep = (xstart < xend ? 1 : 0);
2141 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2144 for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2146 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2147 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2148 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2149 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2152 SetDrawtoField(DRAW_BUFFERED);
2154 BlitScreenToBitmap(backbuffer);
2156 SetDrawtoField(DRAW_BACKBUFFER);
2158 for (yy = 0; yy < ysize; yy++)
2159 for (xx = 0; xx < xsize; xx++)
2160 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2162 DrawTextBuffer(sx + font_width, sy + font_height,
2163 level.envelope[envelope_nr].text, font_nr, max_xsize,
2164 xsize - 2, ysize - 2, 0, mask_mode,
2165 level.envelope[envelope_nr].autowrap,
2166 level.envelope[envelope_nr].centered, FALSE);
2168 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2171 WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2175 void ShowEnvelope(int envelope_nr)
2177 int element = EL_ENVELOPE_1 + envelope_nr;
2178 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2179 int sound_opening = element_info[element].sound[ACTION_OPENING];
2180 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2181 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2182 boolean no_delay = (tape.warp_forward);
2183 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2184 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2185 int anim_mode = graphic_info[graphic].anim_mode;
2186 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2187 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2189 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2191 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2193 if (anim_mode == ANIM_DEFAULT)
2194 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2196 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2199 Delay(wait_delay_value);
2201 WaitForEventToContinue();
2203 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2205 if (anim_mode != ANIM_NONE)
2206 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2208 if (anim_mode == ANIM_DEFAULT)
2209 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2211 game.envelope_active = FALSE;
2213 SetDrawtoField(DRAW_BUFFERED);
2215 redraw_mask |= REDRAW_FIELD;
2219 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2221 int border_size = request.border_size;
2222 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2223 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2224 int sx = sx_center - request.width / 2;
2225 int sy = sy_center - request.height / 2;
2227 if (add_border_size)
2237 void DrawEnvelopeRequest(char *text)
2239 char *text_final = text;
2240 char *text_door_style = NULL;
2241 int graphic = IMG_BACKGROUND_REQUEST;
2242 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2243 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2244 int font_nr = FONT_REQUEST;
2245 int font_width = getFontWidth(font_nr);
2246 int font_height = getFontHeight(font_nr);
2247 int border_size = request.border_size;
2248 int line_spacing = request.line_spacing;
2249 int line_height = font_height + line_spacing;
2250 int text_width = request.width - 2 * border_size;
2251 int text_height = request.height - 2 * border_size;
2252 int line_length = text_width / font_width;
2253 int max_lines = text_height / line_height;
2254 int width = request.width;
2255 int height = request.height;
2256 int tile_size = request.step_offset;
2257 int x_steps = width / tile_size;
2258 int y_steps = height / tile_size;
2262 if (request.wrap_single_words)
2264 char *src_text_ptr, *dst_text_ptr;
2266 text_door_style = checked_malloc(2 * strlen(text) + 1);
2268 src_text_ptr = text;
2269 dst_text_ptr = text_door_style;
2271 while (*src_text_ptr)
2273 if (*src_text_ptr == ' ' ||
2274 *src_text_ptr == '?' ||
2275 *src_text_ptr == '!')
2276 *dst_text_ptr++ = '\n';
2278 if (*src_text_ptr != ' ')
2279 *dst_text_ptr++ = *src_text_ptr;
2284 *dst_text_ptr = '\0';
2286 text_final = text_door_style;
2289 setRequestPosition(&sx, &sy, FALSE);
2291 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2293 for (y = 0; y < y_steps; y++)
2294 for (x = 0; x < x_steps; x++)
2295 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2296 x, y, x_steps, y_steps,
2297 tile_size, tile_size);
2299 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2300 line_length, -1, max_lines, line_spacing, mask_mode,
2301 request.autowrap, request.centered, FALSE);
2303 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2304 RedrawGadget(tool_gadget[i]);
2306 // store readily prepared envelope request for later use when animating
2307 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2309 if (text_door_style)
2310 free(text_door_style);
2313 void AnimateEnvelopeRequest(int anim_mode, int action)
2315 int graphic = IMG_BACKGROUND_REQUEST;
2316 boolean draw_masked = graphic_info[graphic].draw_masked;
2317 int delay_value_normal = request.step_delay;
2318 int delay_value_fast = delay_value_normal / 2;
2319 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2320 boolean no_delay = (tape.warp_forward);
2321 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2322 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0);
2323 unsigned int anim_delay = 0;
2325 int width = request.width;
2326 int height = request.height;
2327 int tile_size = request.step_offset;
2328 int max_xsize = width / tile_size;
2329 int max_ysize = height / tile_size;
2330 int max_xsize_inner = max_xsize - 2;
2331 int max_ysize_inner = max_ysize - 2;
2333 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2334 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2335 int xend = max_xsize_inner;
2336 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2337 int xstep = (xstart < xend ? 1 : 0);
2338 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2341 if (setup.quick_doors)
2348 if (action == ACTION_OPENING)
2349 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2350 else if (action == ACTION_CLOSING)
2351 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2354 for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2356 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2357 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2358 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2359 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2360 int src_x = sx_center - width / 2;
2361 int src_y = sy_center - height / 2;
2362 int dst_x = sx_center - xsize * tile_size / 2;
2363 int dst_y = sy_center - ysize * tile_size / 2;
2364 int xsize_size_left = (xsize - 1) * tile_size;
2365 int ysize_size_top = (ysize - 1) * tile_size;
2366 int max_xsize_pos = (max_xsize - 1) * tile_size;
2367 int max_ysize_pos = (max_ysize - 1) * tile_size;
2370 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2372 for (yy = 0; yy < 2; yy++)
2374 for (xx = 0; xx < 2; xx++)
2376 int src_xx = src_x + xx * max_xsize_pos;
2377 int src_yy = src_y + yy * max_ysize_pos;
2378 int dst_xx = dst_x + xx * xsize_size_left;
2379 int dst_yy = dst_y + yy * ysize_size_top;
2380 int xx_size = (xx ? tile_size : xsize_size_left);
2381 int yy_size = (yy ? tile_size : ysize_size_top);
2384 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2385 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2387 BlitBitmap(bitmap_db_cross, backbuffer,
2388 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2392 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2397 WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2402 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2404 int last_game_status = game_status; /* save current game status */
2405 int graphic = IMG_BACKGROUND_REQUEST;
2406 int sound_opening = SND_REQUEST_OPENING;
2407 int sound_closing = SND_REQUEST_CLOSING;
2408 int anim_mode = graphic_info[graphic].anim_mode;
2409 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2410 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2412 if (game_status == GAME_MODE_PLAYING)
2413 BlitScreenToBitmap(backbuffer);
2415 SetDrawtoField(DRAW_BACKBUFFER);
2417 // SetDrawBackgroundMask(REDRAW_NONE);
2419 if (action == ACTION_OPENING)
2421 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2423 if (req_state & REQ_ASK)
2425 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2426 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2428 else if (req_state & REQ_CONFIRM)
2430 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2432 else if (req_state & REQ_PLAYER)
2434 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2435 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2436 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2437 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2440 DrawEnvelopeRequest(text);
2442 if (game_status != GAME_MODE_MAIN)
2446 /* force DOOR font inside door area */
2447 game_status = GAME_MODE_PSEUDO_DOOR;
2449 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2451 if (action == ACTION_OPENING)
2453 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2455 if (anim_mode == ANIM_DEFAULT)
2456 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2458 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2463 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2465 if (anim_mode != ANIM_NONE)
2466 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2468 if (anim_mode == ANIM_DEFAULT)
2469 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2472 game.envelope_active = FALSE;
2474 game_status = last_game_status; /* restore current game status */
2476 if (action == ACTION_CLOSING)
2478 if (game_status != GAME_MODE_MAIN)
2481 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2484 // SetDrawBackgroundMask(last_draw_background_mask);
2486 redraw_mask |= REDRAW_FIELD;
2488 if (game_status == GAME_MODE_MAIN)
2493 if (action == ACTION_CLOSING &&
2494 game_status == GAME_MODE_PLAYING &&
2495 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2496 SetDrawtoField(DRAW_BUFFERED);
2499 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2503 int graphic = el2preimg(element);
2505 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2506 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2509 void DrawLevel(int draw_background_mask)
2513 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2514 SetDrawBackgroundMask(draw_background_mask);
2518 for (x = BX1; x <= BX2; x++)
2519 for (y = BY1; y <= BY2; y++)
2520 DrawScreenField(x, y);
2522 redraw_mask |= REDRAW_FIELD;
2525 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2530 for (x = 0; x < size_x; x++)
2531 for (y = 0; y < size_y; y++)
2532 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2534 redraw_mask |= REDRAW_FIELD;
2537 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2541 for (x = 0; x < size_x; x++)
2542 for (y = 0; y < size_y; y++)
2543 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2545 redraw_mask |= REDRAW_FIELD;
2548 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2550 boolean show_level_border = (BorderElement != EL_EMPTY);
2551 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2552 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2553 int tile_size = preview.tile_size;
2554 int preview_width = preview.xsize * tile_size;
2555 int preview_height = preview.ysize * tile_size;
2556 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2557 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2558 int real_preview_width = real_preview_xsize * tile_size;
2559 int real_preview_height = real_preview_ysize * tile_size;
2560 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2561 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2564 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2567 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2569 dst_x += (preview_width - real_preview_width) / 2;
2570 dst_y += (preview_height - real_preview_height) / 2;
2572 for (x = 0; x < real_preview_xsize; x++)
2574 for (y = 0; y < real_preview_ysize; y++)
2576 int lx = from_x + x + (show_level_border ? -1 : 0);
2577 int ly = from_y + y + (show_level_border ? -1 : 0);
2578 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2579 getBorderElement(lx, ly));
2581 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2582 element, tile_size);
2586 redraw_mask |= REDRAW_MICROLEVEL;
2589 #define MICROLABEL_EMPTY 0
2590 #define MICROLABEL_LEVEL_NAME 1
2591 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2592 #define MICROLABEL_LEVEL_AUTHOR 3
2593 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2594 #define MICROLABEL_IMPORTED_FROM 5
2595 #define MICROLABEL_IMPORTED_BY_HEAD 6
2596 #define MICROLABEL_IMPORTED_BY 7
2598 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2600 int max_text_width = SXSIZE;
2601 int font_width = getFontWidth(font_nr);
2603 if (pos->align == ALIGN_CENTER)
2604 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2605 else if (pos->align == ALIGN_RIGHT)
2606 max_text_width = pos->x;
2608 max_text_width = SXSIZE - pos->x;
2610 return max_text_width / font_width;
2613 static void DrawPreviewLevelLabelExt(int mode)
2615 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2616 char label_text[MAX_OUTPUT_LINESIZE + 1];
2617 int max_len_label_text;
2618 int font_nr = pos->font;
2621 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2624 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2625 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2626 mode == MICROLABEL_IMPORTED_BY_HEAD)
2627 font_nr = pos->font_alt;
2629 max_len_label_text = getMaxTextLength(pos, font_nr);
2631 if (pos->size != -1)
2632 max_len_label_text = pos->size;
2634 for (i = 0; i < max_len_label_text; i++)
2635 label_text[i] = ' ';
2636 label_text[max_len_label_text] = '\0';
2638 if (strlen(label_text) > 0)
2639 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2642 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2643 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2644 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2645 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2646 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2647 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2648 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2649 max_len_label_text);
2650 label_text[max_len_label_text] = '\0';
2652 if (strlen(label_text) > 0)
2653 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2655 redraw_mask |= REDRAW_MICROLEVEL;
2658 static void DrawPreviewLevelExt(boolean restart)
2660 static unsigned int scroll_delay = 0;
2661 static unsigned int label_delay = 0;
2662 static int from_x, from_y, scroll_direction;
2663 static int label_state, label_counter;
2664 unsigned int scroll_delay_value = preview.step_delay;
2665 boolean show_level_border = (BorderElement != EL_EMPTY);
2666 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2667 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2668 int last_game_status = game_status; /* save current game status */
2675 if (preview.anim_mode == ANIM_CENTERED)
2677 if (level_xsize > preview.xsize)
2678 from_x = (level_xsize - preview.xsize) / 2;
2679 if (level_ysize > preview.ysize)
2680 from_y = (level_ysize - preview.ysize) / 2;
2683 from_x += preview.xoffset;
2684 from_y += preview.yoffset;
2686 scroll_direction = MV_RIGHT;
2690 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2691 DrawPreviewLevelLabelExt(label_state);
2693 /* initialize delay counters */
2694 DelayReached(&scroll_delay, 0);
2695 DelayReached(&label_delay, 0);
2697 if (leveldir_current->name)
2699 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2700 char label_text[MAX_OUTPUT_LINESIZE + 1];
2701 int font_nr = pos->font;
2702 int max_len_label_text = getMaxTextLength(pos, font_nr);
2704 if (pos->size != -1)
2705 max_len_label_text = pos->size;
2707 strncpy(label_text, leveldir_current->name, max_len_label_text);
2708 label_text[max_len_label_text] = '\0';
2710 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2711 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2714 game_status = last_game_status; /* restore current game status */
2719 /* scroll preview level, if needed */
2720 if (preview.anim_mode != ANIM_NONE &&
2721 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2722 DelayReached(&scroll_delay, scroll_delay_value))
2724 switch (scroll_direction)
2729 from_x -= preview.step_offset;
2730 from_x = (from_x < 0 ? 0 : from_x);
2733 scroll_direction = MV_UP;
2737 if (from_x < level_xsize - preview.xsize)
2739 from_x += preview.step_offset;
2740 from_x = (from_x > level_xsize - preview.xsize ?
2741 level_xsize - preview.xsize : from_x);
2744 scroll_direction = MV_DOWN;
2750 from_y -= preview.step_offset;
2751 from_y = (from_y < 0 ? 0 : from_y);
2754 scroll_direction = MV_RIGHT;
2758 if (from_y < level_ysize - preview.ysize)
2760 from_y += preview.step_offset;
2761 from_y = (from_y > level_ysize - preview.ysize ?
2762 level_ysize - preview.ysize : from_y);
2765 scroll_direction = MV_LEFT;
2772 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2775 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2776 /* redraw micro level label, if needed */
2777 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2778 !strEqual(level.author, ANONYMOUS_NAME) &&
2779 !strEqual(level.author, leveldir_current->name) &&
2780 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2782 int max_label_counter = 23;
2784 if (leveldir_current->imported_from != NULL &&
2785 strlen(leveldir_current->imported_from) > 0)
2786 max_label_counter += 14;
2787 if (leveldir_current->imported_by != NULL &&
2788 strlen(leveldir_current->imported_by) > 0)
2789 max_label_counter += 14;
2791 label_counter = (label_counter + 1) % max_label_counter;
2792 label_state = (label_counter >= 0 && label_counter <= 7 ?
2793 MICROLABEL_LEVEL_NAME :
2794 label_counter >= 9 && label_counter <= 12 ?
2795 MICROLABEL_LEVEL_AUTHOR_HEAD :
2796 label_counter >= 14 && label_counter <= 21 ?
2797 MICROLABEL_LEVEL_AUTHOR :
2798 label_counter >= 23 && label_counter <= 26 ?
2799 MICROLABEL_IMPORTED_FROM_HEAD :
2800 label_counter >= 28 && label_counter <= 35 ?
2801 MICROLABEL_IMPORTED_FROM :
2802 label_counter >= 37 && label_counter <= 40 ?
2803 MICROLABEL_IMPORTED_BY_HEAD :
2804 label_counter >= 42 && label_counter <= 49 ?
2805 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2807 if (leveldir_current->imported_from == NULL &&
2808 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2809 label_state == MICROLABEL_IMPORTED_FROM))
2810 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2811 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2813 DrawPreviewLevelLabelExt(label_state);
2816 game_status = last_game_status; /* restore current game status */
2819 void DrawPreviewLevelInitial()
2821 DrawPreviewLevelExt(TRUE);
2824 void DrawPreviewLevelAnimation()
2826 DrawPreviewLevelExt(FALSE);
2829 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2830 int graphic, int sync_frame, int mask_mode)
2832 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2834 if (mask_mode == USE_MASKING)
2835 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2837 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2840 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2841 int graphic, int sync_frame,
2844 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2846 if (mask_mode == USE_MASKING)
2847 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2849 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2852 inline void DrawGraphicAnimation(int x, int y, int graphic)
2854 int lx = LEVELX(x), ly = LEVELY(y);
2856 if (!IN_SCR_FIELD(x, y))
2859 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2860 graphic, GfxFrame[lx][ly], NO_MASKING);
2862 MarkTileDirty(x, y);
2865 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2867 int lx = LEVELX(x), ly = LEVELY(y);
2869 if (!IN_SCR_FIELD(x, y))
2872 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2873 graphic, GfxFrame[lx][ly], NO_MASKING);
2874 MarkTileDirty(x, y);
2877 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2879 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2882 void DrawLevelElementAnimation(int x, int y, int element)
2884 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2886 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2889 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2891 int sx = SCREENX(x), sy = SCREENY(y);
2893 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2896 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2899 DrawGraphicAnimation(sx, sy, graphic);
2902 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2903 DrawLevelFieldCrumbled(x, y);
2905 if (GFX_CRUMBLED(Feld[x][y]))
2906 DrawLevelFieldCrumbled(x, y);
2910 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2912 int sx = SCREENX(x), sy = SCREENY(y);
2915 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2918 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2920 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2923 DrawGraphicAnimation(sx, sy, graphic);
2925 if (GFX_CRUMBLED(element))
2926 DrawLevelFieldCrumbled(x, y);
2929 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2931 if (player->use_murphy)
2933 /* this works only because currently only one player can be "murphy" ... */
2934 static int last_horizontal_dir = MV_LEFT;
2935 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2937 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2938 last_horizontal_dir = move_dir;
2940 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2942 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2944 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2950 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2953 static boolean equalGraphics(int graphic1, int graphic2)
2955 struct GraphicInfo *g1 = &graphic_info[graphic1];
2956 struct GraphicInfo *g2 = &graphic_info[graphic2];
2958 return (g1->bitmap == g2->bitmap &&
2959 g1->src_x == g2->src_x &&
2960 g1->src_y == g2->src_y &&
2961 g1->anim_frames == g2->anim_frames &&
2962 g1->anim_delay == g2->anim_delay &&
2963 g1->anim_mode == g2->anim_mode);
2966 void DrawAllPlayers()
2970 for (i = 0; i < MAX_PLAYERS; i++)
2971 if (stored_player[i].active)
2972 DrawPlayer(&stored_player[i]);
2975 void DrawPlayerField(int x, int y)
2977 if (!IS_PLAYER(x, y))
2980 DrawPlayer(PLAYERINFO(x, y));
2983 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
2985 void DrawPlayer(struct PlayerInfo *player)
2987 int jx = player->jx;
2988 int jy = player->jy;
2989 int move_dir = player->MovDir;
2990 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
2991 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
2992 int last_jx = (player->is_moving ? jx - dx : jx);
2993 int last_jy = (player->is_moving ? jy - dy : jy);
2994 int next_jx = jx + dx;
2995 int next_jy = jy + dy;
2996 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
2997 boolean player_is_opaque = FALSE;
2998 int sx = SCREENX(jx), sy = SCREENY(jy);
2999 int sxx = 0, syy = 0;
3000 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3002 int action = ACTION_DEFAULT;
3003 int last_player_graphic = getPlayerGraphic(player, move_dir);
3004 int last_player_frame = player->Frame;
3007 /* GfxElement[][] is set to the element the player is digging or collecting;
3008 remove also for off-screen player if the player is not moving anymore */
3009 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3010 GfxElement[jx][jy] = EL_UNDEFINED;
3012 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3016 if (!IN_LEV_FIELD(jx, jy))
3018 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3019 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3020 printf("DrawPlayerField(): This should never happen!\n");
3025 if (element == EL_EXPLOSION)
3028 action = (player->is_pushing ? ACTION_PUSHING :
3029 player->is_digging ? ACTION_DIGGING :
3030 player->is_collecting ? ACTION_COLLECTING :
3031 player->is_moving ? ACTION_MOVING :
3032 player->is_snapping ? ACTION_SNAPPING :
3033 player->is_dropping ? ACTION_DROPPING :
3034 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3036 if (player->is_waiting)
3037 move_dir = player->dir_waiting;
3039 InitPlayerGfxAnimation(player, action, move_dir);
3041 /* ----------------------------------------------------------------------- */
3042 /* draw things in the field the player is leaving, if needed */
3043 /* ----------------------------------------------------------------------- */
3045 if (player->is_moving)
3047 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3049 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3051 if (last_element == EL_DYNAMITE_ACTIVE ||
3052 last_element == EL_EM_DYNAMITE_ACTIVE ||
3053 last_element == EL_SP_DISK_RED_ACTIVE)
3054 DrawDynamite(last_jx, last_jy);
3056 DrawLevelFieldThruMask(last_jx, last_jy);
3058 else if (last_element == EL_DYNAMITE_ACTIVE ||
3059 last_element == EL_EM_DYNAMITE_ACTIVE ||
3060 last_element == EL_SP_DISK_RED_ACTIVE)
3061 DrawDynamite(last_jx, last_jy);
3063 /* !!! this is not enough to prevent flickering of players which are
3064 moving next to each others without a free tile between them -- this
3065 can only be solved by drawing all players layer by layer (first the
3066 background, then the foreground etc.) !!! => TODO */
3067 else if (!IS_PLAYER(last_jx, last_jy))
3068 DrawLevelField(last_jx, last_jy);
3071 DrawLevelField(last_jx, last_jy);
3074 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3075 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3078 if (!IN_SCR_FIELD(sx, sy))
3081 /* ----------------------------------------------------------------------- */
3082 /* draw things behind the player, if needed */
3083 /* ----------------------------------------------------------------------- */
3086 DrawLevelElement(jx, jy, Back[jx][jy]);
3087 else if (IS_ACTIVE_BOMB(element))
3088 DrawLevelElement(jx, jy, EL_EMPTY);
3091 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3093 int old_element = GfxElement[jx][jy];
3094 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3095 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3097 if (GFX_CRUMBLED(old_element))
3098 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3100 DrawGraphic(sx, sy, old_graphic, frame);
3102 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3103 player_is_opaque = TRUE;
3107 GfxElement[jx][jy] = EL_UNDEFINED;
3109 /* make sure that pushed elements are drawn with correct frame rate */
3110 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3112 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3113 GfxFrame[jx][jy] = player->StepFrame;
3115 DrawLevelField(jx, jy);
3119 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3120 /* ----------------------------------------------------------------------- */
3121 /* draw player himself */
3122 /* ----------------------------------------------------------------------- */
3124 graphic = getPlayerGraphic(player, move_dir);
3126 /* in the case of changed player action or direction, prevent the current
3127 animation frame from being restarted for identical animations */
3128 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3129 player->Frame = last_player_frame;
3131 frame = getGraphicAnimationFrame(graphic, player->Frame);
3135 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3136 sxx = player->GfxPos;
3138 syy = player->GfxPos;
3141 if (!setup.soft_scrolling && ScreenMovPos)
3144 if (player_is_opaque)
3145 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3147 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3149 if (SHIELD_ON(player))
3151 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3152 IMG_SHIELD_NORMAL_ACTIVE);
3153 int frame = getGraphicAnimationFrame(graphic, -1);
3155 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3159 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3162 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3163 sxx = player->GfxPos;
3165 syy = player->GfxPos;
3169 /* ----------------------------------------------------------------------- */
3170 /* draw things the player is pushing, if needed */
3171 /* ----------------------------------------------------------------------- */
3173 if (player->is_pushing && player->is_moving)
3175 int px = SCREENX(jx), py = SCREENY(jy);
3176 int pxx = (TILEX - ABS(sxx)) * dx;
3177 int pyy = (TILEY - ABS(syy)) * dy;
3178 int gfx_frame = GfxFrame[jx][jy];
3184 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3186 element = Feld[next_jx][next_jy];
3187 gfx_frame = GfxFrame[next_jx][next_jy];
3190 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3192 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3193 frame = getGraphicAnimationFrame(graphic, sync_frame);
3195 /* draw background element under pushed element (like the Sokoban field) */
3196 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3198 /* this allows transparent pushing animation over non-black background */
3201 DrawLevelElement(jx, jy, Back[jx][jy]);
3203 DrawLevelElement(jx, jy, EL_EMPTY);
3205 if (Back[next_jx][next_jy])
3206 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3208 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3210 else if (Back[next_jx][next_jy])
3211 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3214 /* do not draw (EM style) pushing animation when pushing is finished */
3215 /* (two-tile animations usually do not contain start and end frame) */
3216 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3217 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3219 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3221 /* masked drawing is needed for EMC style (double) movement graphics */
3222 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3223 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3227 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3228 /* ----------------------------------------------------------------------- */
3229 /* draw player himself */
3230 /* ----------------------------------------------------------------------- */
3232 graphic = getPlayerGraphic(player, move_dir);
3234 /* in the case of changed player action or direction, prevent the current
3235 animation frame from being restarted for identical animations */
3236 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3237 player->Frame = last_player_frame;
3239 frame = getGraphicAnimationFrame(graphic, player->Frame);
3243 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3244 sxx = player->GfxPos;
3246 syy = player->GfxPos;
3249 if (!setup.soft_scrolling && ScreenMovPos)
3252 if (player_is_opaque)
3253 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3255 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3257 if (SHIELD_ON(player))
3259 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3260 IMG_SHIELD_NORMAL_ACTIVE);
3261 int frame = getGraphicAnimationFrame(graphic, -1);
3263 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3267 /* ----------------------------------------------------------------------- */
3268 /* draw things in front of player (active dynamite or dynabombs) */
3269 /* ----------------------------------------------------------------------- */
3271 if (IS_ACTIVE_BOMB(element))
3273 graphic = el2img(element);
3274 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3276 if (game.emulation == EMU_SUPAPLEX)
3277 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3279 DrawGraphicThruMask(sx, sy, graphic, frame);
3282 if (player_is_moving && last_element == EL_EXPLOSION)
3284 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3285 GfxElement[last_jx][last_jy] : EL_EMPTY);
3286 int graphic = el_act2img(element, ACTION_EXPLODING);
3287 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3288 int phase = ExplodePhase[last_jx][last_jy] - 1;
3289 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3292 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3295 /* ----------------------------------------------------------------------- */
3296 /* draw elements the player is just walking/passing through/under */
3297 /* ----------------------------------------------------------------------- */
3299 if (player_is_moving)
3301 /* handle the field the player is leaving ... */
3302 if (IS_ACCESSIBLE_INSIDE(last_element))
3303 DrawLevelField(last_jx, last_jy);
3304 else if (IS_ACCESSIBLE_UNDER(last_element))
3305 DrawLevelFieldThruMask(last_jx, last_jy);
3308 /* do not redraw accessible elements if the player is just pushing them */
3309 if (!player_is_moving || !player->is_pushing)
3311 /* ... and the field the player is entering */
3312 if (IS_ACCESSIBLE_INSIDE(element))
3313 DrawLevelField(jx, jy);
3314 else if (IS_ACCESSIBLE_UNDER(element))
3315 DrawLevelFieldThruMask(jx, jy);
3318 MarkTileDirty(sx, sy);
3321 /* ------------------------------------------------------------------------- */
3323 void WaitForEventToContinue()
3325 boolean still_wait = TRUE;
3327 /* simulate releasing mouse button over last gadget, if still pressed */
3329 HandleGadgets(-1, -1, 0);
3331 button_status = MB_RELEASED;
3345 case EVENT_BUTTONPRESS:
3346 case EVENT_KEYPRESS:
3350 case EVENT_KEYRELEASE:
3351 ClearPlayerAction();
3355 HandleOtherEvents(&event);
3359 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3366 /* don't eat all CPU time */
3371 #define MAX_REQUEST_LINES 13
3372 #define MAX_REQUEST_LINE_FONT1_LEN 7
3373 #define MAX_REQUEST_LINE_FONT2_LEN 10
3375 static int RequestHandleEvents(unsigned int req_state)
3377 int last_game_status = game_status; /* save current game status */
3381 button_status = MB_RELEASED;
3383 request_gadget_id = -1;
3396 case EVENT_BUTTONPRESS:
3397 case EVENT_BUTTONRELEASE:
3398 case EVENT_MOTIONNOTIFY:
3400 if (event.type == EVENT_MOTIONNOTIFY)
3402 if (!PointerInWindow(window))
3403 continue; /* window and pointer are on different screens */
3408 motion_status = TRUE;
3409 mx = ((MotionEvent *) &event)->x;
3410 my = ((MotionEvent *) &event)->y;
3414 motion_status = FALSE;
3415 mx = ((ButtonEvent *) &event)->x;
3416 my = ((ButtonEvent *) &event)->y;
3417 if (event.type == EVENT_BUTTONPRESS)
3418 button_status = ((ButtonEvent *) &event)->button;
3420 button_status = MB_RELEASED;
3423 /* this sets 'request_gadget_id' */
3424 HandleGadgets(mx, my, button_status);
3426 switch (request_gadget_id)
3428 case TOOL_CTRL_ID_YES:
3431 case TOOL_CTRL_ID_NO:
3434 case TOOL_CTRL_ID_CONFIRM:
3435 result = TRUE | FALSE;
3438 case TOOL_CTRL_ID_PLAYER_1:
3441 case TOOL_CTRL_ID_PLAYER_2:
3444 case TOOL_CTRL_ID_PLAYER_3:
3447 case TOOL_CTRL_ID_PLAYER_4:
3458 case EVENT_KEYPRESS:
3459 switch (GetEventKey((KeyEvent *)&event, TRUE))
3462 if (req_state & REQ_CONFIRM)
3467 #if defined(TARGET_SDL2)
3474 #if defined(TARGET_SDL2)
3484 if (req_state & REQ_PLAYER)
3488 case EVENT_KEYRELEASE:
3489 ClearPlayerAction();
3493 HandleOtherEvents(&event);
3497 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3499 int joy = AnyJoystick();
3501 if (joy & JOY_BUTTON_1)
3503 else if (joy & JOY_BUTTON_2)
3507 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3509 HandleGameActions();
3515 if (!PendingEvent()) /* delay only if no pending events */
3519 game_status = GAME_MODE_PSEUDO_DOOR;
3523 game_status = last_game_status; /* restore current game status */
3529 static boolean RequestDoor(char *text, unsigned int req_state)
3531 unsigned int old_door_state;
3532 int last_game_status = game_status; /* save current game status */
3533 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3534 int font_nr = FONT_TEXT_2;
3539 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3541 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3542 font_nr = FONT_TEXT_1;
3545 if (game_status == GAME_MODE_PLAYING)
3546 BlitScreenToBitmap(backbuffer);
3548 /* disable deactivated drawing when quick-loading level tape recording */
3549 if (tape.playing && tape.deactivate_display)
3550 TapeDeactivateDisplayOff(TRUE);
3552 SetMouseCursor(CURSOR_DEFAULT);
3554 #if defined(NETWORK_AVALIABLE)
3555 /* pause network game while waiting for request to answer */
3556 if (options.network &&
3557 game_status == GAME_MODE_PLAYING &&
3558 req_state & REQUEST_WAIT_FOR_INPUT)
3559 SendToServer_PausePlaying();
3562 old_door_state = GetDoorState();
3564 /* simulate releasing mouse button over last gadget, if still pressed */
3566 HandleGadgets(-1, -1, 0);
3570 /* draw released gadget before proceeding */
3573 if (old_door_state & DOOR_OPEN_1)
3575 CloseDoor(DOOR_CLOSE_1);
3577 /* save old door content */
3578 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3579 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3582 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3583 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3585 /* clear door drawing field */
3586 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3588 /* force DOOR font inside door area */
3589 game_status = GAME_MODE_PSEUDO_DOOR;
3591 /* write text for request */
3592 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3594 char text_line[max_request_line_len + 1];
3600 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3602 tc = *(text_ptr + tx);
3603 // if (!tc || tc == ' ')
3604 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3608 if ((tc == '?' || tc == '!') && tl == 0)
3618 strncpy(text_line, text_ptr, tl);
3621 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3622 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3623 text_line, font_nr);
3625 text_ptr += tl + (tc == ' ' ? 1 : 0);
3626 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3629 game_status = last_game_status; /* restore current game status */
3631 if (req_state & REQ_ASK)
3633 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3634 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3636 else if (req_state & REQ_CONFIRM)
3638 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3640 else if (req_state & REQ_PLAYER)
3642 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3643 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3644 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3645 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3648 /* copy request gadgets to door backbuffer */
3649 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3651 OpenDoor(DOOR_OPEN_1);
3653 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3655 if (game_status == GAME_MODE_PLAYING)
3657 SetPanelBackground();
3658 SetDrawBackgroundMask(REDRAW_DOOR_1);
3662 SetDrawBackgroundMask(REDRAW_FIELD);
3668 if (game_status != GAME_MODE_MAIN)
3671 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3673 // ---------- handle request buttons ----------
3674 result = RequestHandleEvents(req_state);
3676 if (game_status != GAME_MODE_MAIN)
3681 if (!(req_state & REQ_STAY_OPEN))
3683 CloseDoor(DOOR_CLOSE_1);
3685 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3686 (req_state & REQ_REOPEN))
3687 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3692 if (game_status == GAME_MODE_PLAYING)
3694 SetPanelBackground();
3695 SetDrawBackgroundMask(REDRAW_DOOR_1);
3699 SetDrawBackgroundMask(REDRAW_FIELD);
3702 #if defined(NETWORK_AVALIABLE)
3703 /* continue network game after request */
3704 if (options.network &&
3705 game_status == GAME_MODE_PLAYING &&
3706 req_state & REQUEST_WAIT_FOR_INPUT)
3707 SendToServer_ContinuePlaying();
3710 /* restore deactivated drawing when quick-loading level tape recording */
3711 if (tape.playing && tape.deactivate_display)
3712 TapeDeactivateDisplayOn();
3717 static boolean RequestEnvelope(char *text, unsigned int req_state)
3721 if (game_status == GAME_MODE_PLAYING)
3722 BlitScreenToBitmap(backbuffer);
3724 /* disable deactivated drawing when quick-loading level tape recording */
3725 if (tape.playing && tape.deactivate_display)
3726 TapeDeactivateDisplayOff(TRUE);
3728 SetMouseCursor(CURSOR_DEFAULT);
3730 #if defined(NETWORK_AVALIABLE)
3731 /* pause network game while waiting for request to answer */
3732 if (options.network &&
3733 game_status == GAME_MODE_PLAYING &&
3734 req_state & REQUEST_WAIT_FOR_INPUT)
3735 SendToServer_PausePlaying();
3738 /* simulate releasing mouse button over last gadget, if still pressed */
3740 HandleGadgets(-1, -1, 0);
3744 // (replace with setting corresponding request background)
3745 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3746 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3748 /* clear door drawing field */
3749 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3751 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3753 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3755 if (game_status == GAME_MODE_PLAYING)
3757 SetPanelBackground();
3758 SetDrawBackgroundMask(REDRAW_DOOR_1);
3762 SetDrawBackgroundMask(REDRAW_FIELD);
3768 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3770 // ---------- handle request buttons ----------
3771 result = RequestHandleEvents(req_state);
3773 if (game_status != GAME_MODE_MAIN)
3778 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3782 if (game_status == GAME_MODE_PLAYING)
3784 SetPanelBackground();
3785 SetDrawBackgroundMask(REDRAW_DOOR_1);
3789 SetDrawBackgroundMask(REDRAW_FIELD);
3792 #if defined(NETWORK_AVALIABLE)
3793 /* continue network game after request */
3794 if (options.network &&
3795 game_status == GAME_MODE_PLAYING &&
3796 req_state & REQUEST_WAIT_FOR_INPUT)
3797 SendToServer_ContinuePlaying();
3800 /* restore deactivated drawing when quick-loading level tape recording */
3801 if (tape.playing && tape.deactivate_display)
3802 TapeDeactivateDisplayOn();
3807 boolean Request(char *text, unsigned int req_state)
3809 if (global.use_envelope_request)
3810 return RequestEnvelope(text, req_state);
3812 return RequestDoor(text, req_state);
3815 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3817 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3818 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3821 if (dpo1->sort_priority != dpo2->sort_priority)
3822 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3824 compare_result = dpo1->nr - dpo2->nr;
3826 return compare_result;
3829 void InitGraphicCompatibilityInfo_Doors()
3835 struct DoorInfo *door;
3839 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3840 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3842 { -1, -1, -1, NULL }
3844 struct Rect door_rect_list[] =
3846 { DX, DY, DXSIZE, DYSIZE },
3847 { VX, VY, VXSIZE, VYSIZE }
3851 for (i = 0; doors[i].door_token != -1; i++)
3853 int door_token = doors[i].door_token;
3854 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3855 int part_1 = doors[i].part_1;
3856 int part_8 = doors[i].part_8;
3857 int part_2 = part_1 + 1;
3858 int part_3 = part_1 + 2;
3859 struct DoorInfo *door = doors[i].door;
3860 struct Rect *door_rect = &door_rect_list[door_index];
3861 boolean door_gfx_redefined = FALSE;
3863 /* check if any door part graphic definitions have been redefined */
3865 for (j = 0; door_part_controls[j].door_token != -1; j++)
3867 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3868 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3870 if (dpc->door_token == door_token && fi->redefined)
3871 door_gfx_redefined = TRUE;
3874 /* check for old-style door graphic/animation modifications */
3876 if (!door_gfx_redefined)
3878 if (door->anim_mode & ANIM_STATIC_PANEL)
3880 door->panel.step_xoffset = 0;
3881 door->panel.step_yoffset = 0;
3884 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3886 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3887 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3888 int num_door_steps, num_panel_steps;
3890 /* remove door part graphics other than the two default wings */
3892 for (j = 0; door_part_controls[j].door_token != -1; j++)
3894 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3895 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3897 if (dpc->graphic >= part_3 &&
3898 dpc->graphic <= part_8)
3902 /* set graphics and screen positions of the default wings */
3904 g_part_1->width = door_rect->width;
3905 g_part_1->height = door_rect->height;
3906 g_part_2->width = door_rect->width;
3907 g_part_2->height = door_rect->height;
3908 g_part_2->src_x = door_rect->width;
3909 g_part_2->src_y = g_part_1->src_y;
3911 door->part_2.x = door->part_1.x;
3912 door->part_2.y = door->part_1.y;
3914 if (door->width != -1)
3916 g_part_1->width = door->width;
3917 g_part_2->width = door->width;
3919 // special treatment for graphics and screen position of right wing
3920 g_part_2->src_x += door_rect->width - door->width;
3921 door->part_2.x += door_rect->width - door->width;
3924 if (door->height != -1)
3926 g_part_1->height = door->height;
3927 g_part_2->height = door->height;
3929 // special treatment for graphics and screen position of bottom wing
3930 g_part_2->src_y += door_rect->height - door->height;
3931 door->part_2.y += door_rect->height - door->height;
3934 /* set animation delays for the default wings and panels */
3936 door->part_1.step_delay = door->step_delay;
3937 door->part_2.step_delay = door->step_delay;
3938 door->panel.step_delay = door->step_delay;
3940 /* set animation draw order for the default wings */
3942 door->part_1.sort_priority = 2; /* draw left wing over ... */
3943 door->part_2.sort_priority = 1; /* ... right wing */
3945 /* set animation draw offset for the default wings */
3947 if (door->anim_mode & ANIM_HORIZONTAL)
3949 door->part_1.step_xoffset = door->step_offset;
3950 door->part_1.step_yoffset = 0;
3951 door->part_2.step_xoffset = door->step_offset * -1;
3952 door->part_2.step_yoffset = 0;
3954 num_door_steps = g_part_1->width / door->step_offset;
3956 else // ANIM_VERTICAL
3958 door->part_1.step_xoffset = 0;
3959 door->part_1.step_yoffset = door->step_offset;
3960 door->part_2.step_xoffset = 0;
3961 door->part_2.step_yoffset = door->step_offset * -1;
3963 num_door_steps = g_part_1->height / door->step_offset;
3966 /* set animation draw offset for the default panels */
3968 if (door->step_offset > 1)
3970 num_panel_steps = 2 * door_rect->height / door->step_offset;
3971 door->panel.start_step = num_panel_steps - num_door_steps;
3972 door->panel.start_step_closing = door->panel.start_step;
3976 num_panel_steps = door_rect->height / door->step_offset;
3977 door->panel.start_step = num_panel_steps - num_door_steps / 2;
3978 door->panel.start_step_closing = door->panel.start_step;
3979 door->panel.step_delay *= 2;
3990 for (i = 0; door_part_controls[i].door_token != -1; i++)
3992 struct DoorPartControlInfo *dpc = &door_part_controls[i];
3993 struct DoorPartOrderInfo *dpo = &door_part_order[i];
3995 /* initialize "start_step_opening" and "start_step_closing", if needed */
3996 if (dpc->pos->start_step_opening == 0 &&
3997 dpc->pos->start_step_closing == 0)
3999 // dpc->pos->start_step_opening = dpc->pos->start_step;
4000 dpc->pos->start_step_closing = dpc->pos->start_step;
4003 /* fill structure for door part draw order (sorted below) */
4005 dpo->sort_priority = dpc->pos->sort_priority;
4008 /* sort door part controls according to sort_priority and graphic number */
4009 qsort(door_part_order, MAX_DOOR_PARTS,
4010 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4013 unsigned int OpenDoor(unsigned int door_state)
4015 if (door_state & DOOR_COPY_BACK)
4017 if (door_state & DOOR_OPEN_1)
4018 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4019 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4021 if (door_state & DOOR_OPEN_2)
4022 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4023 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4025 door_state &= ~DOOR_COPY_BACK;
4028 return MoveDoor(door_state);
4031 unsigned int CloseDoor(unsigned int door_state)
4033 unsigned int old_door_state = GetDoorState();
4035 if (!(door_state & DOOR_NO_COPY_BACK))
4037 if (old_door_state & DOOR_OPEN_1)
4038 BlitBitmap(backbuffer, bitmap_db_door_1,
4039 DX, DY, DXSIZE, DYSIZE, 0, 0);
4041 if (old_door_state & DOOR_OPEN_2)
4042 BlitBitmap(backbuffer, bitmap_db_door_2,
4043 VX, VY, VXSIZE, VYSIZE, 0, 0);
4045 door_state &= ~DOOR_NO_COPY_BACK;
4048 return MoveDoor(door_state);
4051 unsigned int GetDoorState()
4053 return MoveDoor(DOOR_GET_STATE);
4056 unsigned int SetDoorState(unsigned int door_state)
4058 return MoveDoor(door_state | DOOR_SET_STATE);
4061 int euclid(int a, int b)
4063 return (b ? euclid(b, a % b) : a);
4066 unsigned int MoveDoor(unsigned int door_state)
4068 struct Rect door_rect_list[] =
4070 { DX, DY, DXSIZE, DYSIZE },
4071 { VX, VY, VXSIZE, VYSIZE }
4073 static int door1 = DOOR_OPEN_1;
4074 static int door2 = DOOR_CLOSE_2;
4075 unsigned int door_delay = 0;
4076 unsigned int door_delay_value;
4079 if (door_state == DOOR_GET_STATE)
4080 return (door1 | door2);
4082 if (door_state & DOOR_SET_STATE)
4084 if (door_state & DOOR_ACTION_1)
4085 door1 = door_state & DOOR_ACTION_1;
4086 if (door_state & DOOR_ACTION_2)
4087 door2 = door_state & DOOR_ACTION_2;
4089 return (door1 | door2);
4092 if (!(door_state & DOOR_FORCE_REDRAW))
4094 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4095 door_state &= ~DOOR_OPEN_1;
4096 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4097 door_state &= ~DOOR_CLOSE_1;
4098 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4099 door_state &= ~DOOR_OPEN_2;
4100 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4101 door_state &= ~DOOR_CLOSE_2;
4104 if (global.autoplay_leveldir)
4106 door_state |= DOOR_NO_DELAY;
4107 door_state &= ~DOOR_CLOSE_ALL;
4110 if (game_status == GAME_MODE_EDITOR)
4111 door_state |= DOOR_NO_DELAY;
4113 if (door_state & DOOR_ACTION)
4115 boolean door_panel_drawn[NUM_DOORS];
4116 boolean panel_has_doors[NUM_DOORS];
4117 boolean door_part_skip[MAX_DOOR_PARTS];
4118 boolean door_part_done[MAX_DOOR_PARTS];
4119 boolean door_part_done_all;
4120 int num_steps[MAX_DOOR_PARTS];
4121 int max_move_delay = 0; // delay for complete animations of all doors
4122 int max_step_delay = 0; // delay (ms) between two animation frames
4123 int num_move_steps = 0; // number of animation steps for all doors
4124 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4125 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4126 int current_move_delay = 0;
4130 for (i = 0; i < NUM_DOORS; i++)
4131 panel_has_doors[i] = FALSE;
4133 for (i = 0; i < MAX_DOOR_PARTS; i++)
4135 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4136 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4137 int door_token = dpc->door_token;
4139 door_part_done[i] = FALSE;
4140 door_part_skip[i] = (!(door_state & door_token) ||
4144 for (i = 0; i < MAX_DOOR_PARTS; i++)
4146 int nr = door_part_order[i].nr;
4147 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4148 struct DoorPartPosInfo *pos = dpc->pos;
4149 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4150 int door_token = dpc->door_token;
4151 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4152 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4153 int step_xoffset = ABS(pos->step_xoffset);
4154 int step_yoffset = ABS(pos->step_yoffset);
4155 int step_delay = pos->step_delay;
4156 int current_door_state = door_state & door_token;
4157 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4158 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4159 boolean part_opening = (is_panel ? door_closing : door_opening);
4160 int start_step = (part_opening ? pos->start_step_opening :
4161 pos->start_step_closing);
4162 float move_xsize = (step_xoffset ? g->width : 0);
4163 float move_ysize = (step_yoffset ? g->height : 0);
4164 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4165 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4166 int move_steps = (move_xsteps && move_ysteps ?
4167 MIN(move_xsteps, move_ysteps) :
4168 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4169 int move_delay = move_steps * step_delay;
4171 if (door_part_skip[nr])
4174 max_move_delay = MAX(max_move_delay, move_delay);
4175 max_step_delay = (max_step_delay == 0 ? step_delay :
4176 euclid(max_step_delay, step_delay));
4177 num_steps[nr] = move_steps;
4181 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4183 panel_has_doors[door_index] = TRUE;
4187 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4189 num_move_steps = max_move_delay / max_step_delay;
4190 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4192 door_delay_value = max_step_delay;
4194 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4196 start = num_move_steps - 1;
4200 /* opening door sound has priority over simultaneously closing door */
4201 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4202 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4203 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4204 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4207 for (k = start; k < num_move_steps; k++)
4209 door_part_done_all = TRUE;
4211 for (i = 0; i < NUM_DOORS; i++)
4212 door_panel_drawn[i] = FALSE;
4214 for (i = 0; i < MAX_DOOR_PARTS; i++)
4216 int nr = door_part_order[i].nr;
4217 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4218 struct DoorPartPosInfo *pos = dpc->pos;
4219 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4220 int door_token = dpc->door_token;
4221 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4222 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4223 boolean is_panel_and_door_has_closed = FALSE;
4224 struct Rect *door_rect = &door_rect_list[door_index];
4225 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4227 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4228 int current_door_state = door_state & door_token;
4229 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4230 boolean door_closing = !door_opening;
4231 boolean part_opening = (is_panel ? door_closing : door_opening);
4232 boolean part_closing = !part_opening;
4233 int start_step = (part_opening ? pos->start_step_opening :
4234 pos->start_step_closing);
4235 int step_delay = pos->step_delay;
4236 int step_factor = step_delay / max_step_delay;
4237 int k1 = (step_factor ? k / step_factor + 1 : k);
4238 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4239 int kk = MAX(0, k2);
4242 int src_x, src_y, src_xx, src_yy;
4243 int dst_x, dst_y, dst_xx, dst_yy;
4246 if (door_part_skip[nr])
4249 if (!(door_state & door_token))
4257 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4258 int kk_door = MAX(0, k2_door);
4259 int sync_frame = kk_door * door_delay_value;
4260 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4262 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4267 if (!door_panel_drawn[door_index])
4269 ClearRectangle(drawto, door_rect->x, door_rect->y,
4270 door_rect->width, door_rect->height);
4272 door_panel_drawn[door_index] = TRUE;
4275 // draw opening or closing door parts
4277 if (pos->step_xoffset < 0) // door part on right side
4280 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4283 if (dst_xx + width > door_rect->width)
4284 width = door_rect->width - dst_xx;
4286 else // door part on left side
4289 dst_xx = pos->x - kk * pos->step_xoffset;
4293 src_xx = ABS(dst_xx);
4297 width = g->width - src_xx;
4299 // printf("::: k == %d [%d] \n", k, start_step);
4302 if (pos->step_yoffset < 0) // door part on bottom side
4305 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4308 if (dst_yy + height > door_rect->height)
4309 height = door_rect->height - dst_yy;
4311 else // door part on top side
4314 dst_yy = pos->y - kk * pos->step_yoffset;
4318 src_yy = ABS(dst_yy);
4322 height = g->height - src_yy;
4325 src_x = g_src_x + src_xx;
4326 src_y = g_src_y + src_yy;
4328 dst_x = door_rect->x + dst_xx;
4329 dst_y = door_rect->y + dst_yy;
4331 is_panel_and_door_has_closed =
4334 panel_has_doors[door_index] &&
4335 k >= num_move_steps_doors_only - 1);
4337 if (width >= 0 && width <= g->width &&
4338 height >= 0 && height <= g->height &&
4339 !is_panel_and_door_has_closed)
4341 if (is_panel || !pos->draw_masked)
4342 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4345 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4349 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4351 if ((part_opening && (width < 0 || height < 0)) ||
4352 (part_closing && (width >= g->width && height >= g->height)))
4353 door_part_done[nr] = TRUE;
4355 // continue door part animations, but not panel after door has closed
4356 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4357 door_part_done_all = FALSE;
4360 if (!(door_state & DOOR_NO_DELAY))
4364 if (game_status == GAME_MODE_MAIN)
4367 WaitUntilDelayReached(&door_delay, door_delay_value);
4369 current_move_delay += max_step_delay;
4372 if (door_part_done_all)
4377 if (door_state & DOOR_ACTION_1)
4378 door1 = door_state & DOOR_ACTION_1;
4379 if (door_state & DOOR_ACTION_2)
4380 door2 = door_state & DOOR_ACTION_2;
4382 return (door1 | door2);
4385 void DrawSpecialEditorDoor()
4387 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4388 int top_border_width = gfx1->width;
4389 int top_border_height = gfx1->height;
4390 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4391 int ex = EX - outer_border;
4392 int ey = EY - outer_border;
4393 int vy = VY - outer_border;
4394 int exsize = EXSIZE + 2 * outer_border;
4396 /* draw bigger level editor toolbox window */
4397 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4398 top_border_width, top_border_height, ex, ey - top_border_height);
4399 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4400 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4402 redraw_mask |= REDRAW_ALL;
4405 void UndrawSpecialEditorDoor()
4407 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4408 int top_border_width = gfx1->width;
4409 int top_border_height = gfx1->height;
4410 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4411 int ex = EX - outer_border;
4412 int ey = EY - outer_border;
4413 int ey_top = ey - top_border_height;
4414 int exsize = EXSIZE + 2 * outer_border;
4415 int eysize = EYSIZE + 2 * outer_border;
4417 /* draw normal tape recorder window */
4418 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4420 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4421 ex, ey_top, top_border_width, top_border_height,
4423 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4424 ex, ey, exsize, eysize, ex, ey);
4428 // if screen background is set to "[NONE]", clear editor toolbox window
4429 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4430 ClearRectangle(drawto, ex, ey, exsize, eysize);
4433 redraw_mask |= REDRAW_ALL;
4437 /* ---------- new tool button stuff ---------------------------------------- */
4442 struct TextPosInfo *pos;
4445 } toolbutton_info[NUM_TOOL_BUTTONS] =
4448 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4449 TOOL_CTRL_ID_YES, "yes"
4452 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4453 TOOL_CTRL_ID_NO, "no"
4456 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4457 TOOL_CTRL_ID_CONFIRM, "confirm"
4460 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4461 TOOL_CTRL_ID_PLAYER_1, "player 1"
4464 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4465 TOOL_CTRL_ID_PLAYER_2, "player 2"
4468 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4469 TOOL_CTRL_ID_PLAYER_3, "player 3"
4472 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4473 TOOL_CTRL_ID_PLAYER_4, "player 4"
4477 void CreateToolButtons()
4481 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4483 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4484 struct TextPosInfo *pos = toolbutton_info[i].pos;
4485 struct GadgetInfo *gi;
4486 Bitmap *deco_bitmap = None;
4487 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4488 unsigned int event_mask = GD_EVENT_RELEASED;
4491 int gd_x = gfx->src_x;
4492 int gd_y = gfx->src_y;
4493 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4494 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4497 if (global.use_envelope_request)
4498 setRequestPosition(&dx, &dy, TRUE);
4500 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4502 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4504 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4505 pos->size, &deco_bitmap, &deco_x, &deco_y);
4506 deco_xpos = (gfx->width - pos->size) / 2;
4507 deco_ypos = (gfx->height - pos->size) / 2;
4510 gi = CreateGadget(GDI_CUSTOM_ID, id,
4511 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4512 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4513 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4514 GDI_WIDTH, gfx->width,
4515 GDI_HEIGHT, gfx->height,
4516 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4517 GDI_STATE, GD_BUTTON_UNPRESSED,
4518 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4519 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4520 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4521 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4522 GDI_DECORATION_SIZE, pos->size, pos->size,
4523 GDI_DECORATION_SHIFTING, 1, 1,
4524 GDI_DIRECT_DRAW, FALSE,
4525 GDI_EVENT_MASK, event_mask,
4526 GDI_CALLBACK_ACTION, HandleToolButtons,
4530 Error(ERR_EXIT, "cannot create gadget");
4532 tool_gadget[id] = gi;
4536 void FreeToolButtons()
4540 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4541 FreeGadget(tool_gadget[i]);
4544 static void UnmapToolButtons()
4548 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4549 UnmapGadget(tool_gadget[i]);
4552 static void HandleToolButtons(struct GadgetInfo *gi)
4554 request_gadget_id = gi->custom_id;
4557 static struct Mapping_EM_to_RND_object
4560 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4561 boolean is_backside; /* backside of moving element */
4567 em_object_mapping_list[] =
4570 Xblank, TRUE, FALSE,
4574 Yacid_splash_eB, FALSE, FALSE,
4575 EL_ACID_SPLASH_RIGHT, -1, -1
4578 Yacid_splash_wB, FALSE, FALSE,
4579 EL_ACID_SPLASH_LEFT, -1, -1
4582 #ifdef EM_ENGINE_BAD_ROLL
4584 Xstone_force_e, FALSE, FALSE,
4585 EL_ROCK, -1, MV_BIT_RIGHT
4588 Xstone_force_w, FALSE, FALSE,
4589 EL_ROCK, -1, MV_BIT_LEFT
4592 Xnut_force_e, FALSE, FALSE,
4593 EL_NUT, -1, MV_BIT_RIGHT
4596 Xnut_force_w, FALSE, FALSE,
4597 EL_NUT, -1, MV_BIT_LEFT
4600 Xspring_force_e, FALSE, FALSE,
4601 EL_SPRING, -1, MV_BIT_RIGHT
4604 Xspring_force_w, FALSE, FALSE,
4605 EL_SPRING, -1, MV_BIT_LEFT
4608 Xemerald_force_e, FALSE, FALSE,
4609 EL_EMERALD, -1, MV_BIT_RIGHT
4612 Xemerald_force_w, FALSE, FALSE,
4613 EL_EMERALD, -1, MV_BIT_LEFT
4616 Xdiamond_force_e, FALSE, FALSE,
4617 EL_DIAMOND, -1, MV_BIT_RIGHT
4620 Xdiamond_force_w, FALSE, FALSE,
4621 EL_DIAMOND, -1, MV_BIT_LEFT
4624 Xbomb_force_e, FALSE, FALSE,
4625 EL_BOMB, -1, MV_BIT_RIGHT
4628 Xbomb_force_w, FALSE, FALSE,
4629 EL_BOMB, -1, MV_BIT_LEFT
4631 #endif /* EM_ENGINE_BAD_ROLL */
4634 Xstone, TRUE, FALSE,
4638 Xstone_pause, FALSE, FALSE,
4642 Xstone_fall, FALSE, FALSE,
4646 Ystone_s, FALSE, FALSE,
4647 EL_ROCK, ACTION_FALLING, -1
4650 Ystone_sB, FALSE, TRUE,
4651 EL_ROCK, ACTION_FALLING, -1
4654 Ystone_e, FALSE, FALSE,
4655 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4658 Ystone_eB, FALSE, TRUE,
4659 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4662 Ystone_w, FALSE, FALSE,
4663 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4666 Ystone_wB, FALSE, TRUE,
4667 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4674 Xnut_pause, FALSE, FALSE,
4678 Xnut_fall, FALSE, FALSE,
4682 Ynut_s, FALSE, FALSE,
4683 EL_NUT, ACTION_FALLING, -1
4686 Ynut_sB, FALSE, TRUE,
4687 EL_NUT, ACTION_FALLING, -1
4690 Ynut_e, FALSE, FALSE,
4691 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4694 Ynut_eB, FALSE, TRUE,
4695 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4698 Ynut_w, FALSE, FALSE,
4699 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4702 Ynut_wB, FALSE, TRUE,
4703 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4706 Xbug_n, TRUE, FALSE,
4710 Xbug_e, TRUE, FALSE,
4711 EL_BUG_RIGHT, -1, -1
4714 Xbug_s, TRUE, FALSE,
4718 Xbug_w, TRUE, FALSE,
4722 Xbug_gon, FALSE, FALSE,
4726 Xbug_goe, FALSE, FALSE,
4727 EL_BUG_RIGHT, -1, -1
4730 Xbug_gos, FALSE, FALSE,
4734 Xbug_gow, FALSE, FALSE,
4738 Ybug_n, FALSE, FALSE,
4739 EL_BUG, ACTION_MOVING, MV_BIT_UP
4742 Ybug_nB, FALSE, TRUE,
4743 EL_BUG, ACTION_MOVING, MV_BIT_UP
4746 Ybug_e, FALSE, FALSE,
4747 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4750 Ybug_eB, FALSE, TRUE,
4751 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4754 Ybug_s, FALSE, FALSE,
4755 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4758 Ybug_sB, FALSE, TRUE,
4759 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4762 Ybug_w, FALSE, FALSE,
4763 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4766 Ybug_wB, FALSE, TRUE,
4767 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4770 Ybug_w_n, FALSE, FALSE,
4771 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4774 Ybug_n_e, FALSE, FALSE,
4775 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4778 Ybug_e_s, FALSE, FALSE,
4779 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4782 Ybug_s_w, FALSE, FALSE,
4783 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4786 Ybug_e_n, FALSE, FALSE,
4787 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4790 Ybug_s_e, FALSE, FALSE,
4791 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4794 Ybug_w_s, FALSE, FALSE,
4795 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4798 Ybug_n_w, FALSE, FALSE,
4799 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4802 Ybug_stone, FALSE, FALSE,
4803 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4806 Ybug_spring, FALSE, FALSE,
4807 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4810 Xtank_n, TRUE, FALSE,
4811 EL_SPACESHIP_UP, -1, -1
4814 Xtank_e, TRUE, FALSE,
4815 EL_SPACESHIP_RIGHT, -1, -1
4818 Xtank_s, TRUE, FALSE,
4819 EL_SPACESHIP_DOWN, -1, -1
4822 Xtank_w, TRUE, FALSE,
4823 EL_SPACESHIP_LEFT, -1, -1
4826 Xtank_gon, FALSE, FALSE,
4827 EL_SPACESHIP_UP, -1, -1
4830 Xtank_goe, FALSE, FALSE,
4831 EL_SPACESHIP_RIGHT, -1, -1
4834 Xtank_gos, FALSE, FALSE,
4835 EL_SPACESHIP_DOWN, -1, -1
4838 Xtank_gow, FALSE, FALSE,
4839 EL_SPACESHIP_LEFT, -1, -1
4842 Ytank_n, FALSE, FALSE,
4843 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4846 Ytank_nB, FALSE, TRUE,
4847 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4850 Ytank_e, FALSE, FALSE,
4851 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4854 Ytank_eB, FALSE, TRUE,
4855 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4858 Ytank_s, FALSE, FALSE,
4859 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4862 Ytank_sB, FALSE, TRUE,
4863 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4866 Ytank_w, FALSE, FALSE,
4867 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4870 Ytank_wB, FALSE, TRUE,
4871 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4874 Ytank_w_n, FALSE, FALSE,
4875 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4878 Ytank_n_e, FALSE, FALSE,
4879 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4882 Ytank_e_s, FALSE, FALSE,
4883 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4886 Ytank_s_w, FALSE, FALSE,
4887 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4890 Ytank_e_n, FALSE, FALSE,
4891 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4894 Ytank_s_e, FALSE, FALSE,
4895 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4898 Ytank_w_s, FALSE, FALSE,
4899 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4902 Ytank_n_w, FALSE, FALSE,
4903 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4906 Ytank_stone, FALSE, FALSE,
4907 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4910 Ytank_spring, FALSE, FALSE,
4911 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4914 Xandroid, TRUE, FALSE,
4915 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4918 Xandroid_1_n, FALSE, FALSE,
4919 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4922 Xandroid_2_n, FALSE, FALSE,
4923 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4926 Xandroid_1_e, FALSE, FALSE,
4927 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4930 Xandroid_2_e, FALSE, FALSE,
4931 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4934 Xandroid_1_w, FALSE, FALSE,
4935 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4938 Xandroid_2_w, FALSE, FALSE,
4939 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4942 Xandroid_1_s, FALSE, FALSE,
4943 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4946 Xandroid_2_s, FALSE, FALSE,
4947 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4950 Yandroid_n, FALSE, FALSE,
4951 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4954 Yandroid_nB, FALSE, TRUE,
4955 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4958 Yandroid_ne, FALSE, FALSE,
4959 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4962 Yandroid_neB, FALSE, TRUE,
4963 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4966 Yandroid_e, FALSE, FALSE,
4967 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4970 Yandroid_eB, FALSE, TRUE,
4971 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4974 Yandroid_se, FALSE, FALSE,
4975 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4978 Yandroid_seB, FALSE, TRUE,
4979 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4982 Yandroid_s, FALSE, FALSE,
4983 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4986 Yandroid_sB, FALSE, TRUE,
4987 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4990 Yandroid_sw, FALSE, FALSE,
4991 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
4994 Yandroid_swB, FALSE, TRUE,
4995 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
4998 Yandroid_w, FALSE, FALSE,
4999 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5002 Yandroid_wB, FALSE, TRUE,
5003 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5006 Yandroid_nw, FALSE, FALSE,
5007 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5010 Yandroid_nwB, FALSE, TRUE,
5011 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5014 Xspring, TRUE, FALSE,
5018 Xspring_pause, FALSE, FALSE,
5022 Xspring_e, FALSE, FALSE,
5026 Xspring_w, FALSE, FALSE,
5030 Xspring_fall, FALSE, FALSE,
5034 Yspring_s, FALSE, FALSE,
5035 EL_SPRING, ACTION_FALLING, -1
5038 Yspring_sB, FALSE, TRUE,
5039 EL_SPRING, ACTION_FALLING, -1
5042 Yspring_e, FALSE, FALSE,
5043 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5046 Yspring_eB, FALSE, TRUE,
5047 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5050 Yspring_w, FALSE, FALSE,
5051 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5054 Yspring_wB, FALSE, TRUE,
5055 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5058 Yspring_kill_e, FALSE, FALSE,
5059 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5062 Yspring_kill_eB, FALSE, TRUE,
5063 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5066 Yspring_kill_w, FALSE, FALSE,
5067 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5070 Yspring_kill_wB, FALSE, TRUE,
5071 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5074 Xeater_n, TRUE, FALSE,
5075 EL_YAMYAM_UP, -1, -1
5078 Xeater_e, TRUE, FALSE,
5079 EL_YAMYAM_RIGHT, -1, -1
5082 Xeater_w, TRUE, FALSE,
5083 EL_YAMYAM_LEFT, -1, -1
5086 Xeater_s, TRUE, FALSE,
5087 EL_YAMYAM_DOWN, -1, -1
5090 Yeater_n, FALSE, FALSE,
5091 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5094 Yeater_nB, FALSE, TRUE,
5095 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5098 Yeater_e, FALSE, FALSE,
5099 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5102 Yeater_eB, FALSE, TRUE,
5103 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5106 Yeater_s, FALSE, FALSE,
5107 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5110 Yeater_sB, FALSE, TRUE,
5111 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5114 Yeater_w, FALSE, FALSE,
5115 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5118 Yeater_wB, FALSE, TRUE,
5119 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5122 Yeater_stone, FALSE, FALSE,
5123 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5126 Yeater_spring, FALSE, FALSE,
5127 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5130 Xalien, TRUE, FALSE,
5134 Xalien_pause, FALSE, FALSE,
5138 Yalien_n, FALSE, FALSE,
5139 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5142 Yalien_nB, FALSE, TRUE,
5143 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5146 Yalien_e, FALSE, FALSE,
5147 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5150 Yalien_eB, FALSE, TRUE,
5151 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5154 Yalien_s, FALSE, FALSE,
5155 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5158 Yalien_sB, FALSE, TRUE,
5159 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5162 Yalien_w, FALSE, FALSE,
5163 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5166 Yalien_wB, FALSE, TRUE,
5167 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5170 Yalien_stone, FALSE, FALSE,
5171 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5174 Yalien_spring, FALSE, FALSE,
5175 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5178 Xemerald, TRUE, FALSE,
5182 Xemerald_pause, FALSE, FALSE,
5186 Xemerald_fall, FALSE, FALSE,
5190 Xemerald_shine, FALSE, FALSE,
5191 EL_EMERALD, ACTION_TWINKLING, -1
5194 Yemerald_s, FALSE, FALSE,
5195 EL_EMERALD, ACTION_FALLING, -1
5198 Yemerald_sB, FALSE, TRUE,
5199 EL_EMERALD, ACTION_FALLING, -1
5202 Yemerald_e, FALSE, FALSE,
5203 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5206 Yemerald_eB, FALSE, TRUE,
5207 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5210 Yemerald_w, FALSE, FALSE,
5211 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5214 Yemerald_wB, FALSE, TRUE,
5215 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5218 Yemerald_eat, FALSE, FALSE,
5219 EL_EMERALD, ACTION_COLLECTING, -1
5222 Yemerald_stone, FALSE, FALSE,
5223 EL_NUT, ACTION_BREAKING, -1
5226 Xdiamond, TRUE, FALSE,
5230 Xdiamond_pause, FALSE, FALSE,
5234 Xdiamond_fall, FALSE, FALSE,
5238 Xdiamond_shine, FALSE, FALSE,
5239 EL_DIAMOND, ACTION_TWINKLING, -1
5242 Ydiamond_s, FALSE, FALSE,
5243 EL_DIAMOND, ACTION_FALLING, -1
5246 Ydiamond_sB, FALSE, TRUE,
5247 EL_DIAMOND, ACTION_FALLING, -1
5250 Ydiamond_e, FALSE, FALSE,
5251 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5254 Ydiamond_eB, FALSE, TRUE,
5255 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5258 Ydiamond_w, FALSE, FALSE,
5259 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5262 Ydiamond_wB, FALSE, TRUE,
5263 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5266 Ydiamond_eat, FALSE, FALSE,
5267 EL_DIAMOND, ACTION_COLLECTING, -1
5270 Ydiamond_stone, FALSE, FALSE,
5271 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5274 Xdrip_fall, TRUE, FALSE,
5275 EL_AMOEBA_DROP, -1, -1
5278 Xdrip_stretch, FALSE, FALSE,
5279 EL_AMOEBA_DROP, ACTION_FALLING, -1
5282 Xdrip_stretchB, FALSE, TRUE,
5283 EL_AMOEBA_DROP, ACTION_FALLING, -1
5286 Xdrip_eat, FALSE, FALSE,
5287 EL_AMOEBA_DROP, ACTION_GROWING, -1
5290 Ydrip_s1, FALSE, FALSE,
5291 EL_AMOEBA_DROP, ACTION_FALLING, -1
5294 Ydrip_s1B, FALSE, TRUE,
5295 EL_AMOEBA_DROP, ACTION_FALLING, -1
5298 Ydrip_s2, FALSE, FALSE,
5299 EL_AMOEBA_DROP, ACTION_FALLING, -1
5302 Ydrip_s2B, FALSE, TRUE,
5303 EL_AMOEBA_DROP, ACTION_FALLING, -1
5310 Xbomb_pause, FALSE, FALSE,
5314 Xbomb_fall, FALSE, FALSE,
5318 Ybomb_s, FALSE, FALSE,
5319 EL_BOMB, ACTION_FALLING, -1
5322 Ybomb_sB, FALSE, TRUE,
5323 EL_BOMB, ACTION_FALLING, -1
5326 Ybomb_e, FALSE, FALSE,
5327 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5330 Ybomb_eB, FALSE, TRUE,
5331 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5334 Ybomb_w, FALSE, FALSE,
5335 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5338 Ybomb_wB, FALSE, TRUE,
5339 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5342 Ybomb_eat, FALSE, FALSE,
5343 EL_BOMB, ACTION_ACTIVATING, -1
5346 Xballoon, TRUE, FALSE,
5350 Yballoon_n, FALSE, FALSE,
5351 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5354 Yballoon_nB, FALSE, TRUE,
5355 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5358 Yballoon_e, FALSE, FALSE,
5359 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5362 Yballoon_eB, FALSE, TRUE,
5363 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5366 Yballoon_s, FALSE, FALSE,
5367 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5370 Yballoon_sB, FALSE, TRUE,
5371 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5374 Yballoon_w, FALSE, FALSE,
5375 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5378 Yballoon_wB, FALSE, TRUE,
5379 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5382 Xgrass, TRUE, FALSE,
5383 EL_EMC_GRASS, -1, -1
5386 Ygrass_nB, FALSE, FALSE,
5387 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5390 Ygrass_eB, FALSE, FALSE,
5391 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5394 Ygrass_sB, FALSE, FALSE,
5395 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5398 Ygrass_wB, FALSE, FALSE,
5399 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5406 Ydirt_nB, FALSE, FALSE,
5407 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5410 Ydirt_eB, FALSE, FALSE,
5411 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5414 Ydirt_sB, FALSE, FALSE,
5415 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5418 Ydirt_wB, FALSE, FALSE,
5419 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5422 Xacid_ne, TRUE, FALSE,
5423 EL_ACID_POOL_TOPRIGHT, -1, -1
5426 Xacid_se, TRUE, FALSE,
5427 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5430 Xacid_s, TRUE, FALSE,
5431 EL_ACID_POOL_BOTTOM, -1, -1
5434 Xacid_sw, TRUE, FALSE,
5435 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5438 Xacid_nw, TRUE, FALSE,
5439 EL_ACID_POOL_TOPLEFT, -1, -1
5442 Xacid_1, TRUE, FALSE,
5446 Xacid_2, FALSE, FALSE,
5450 Xacid_3, FALSE, FALSE,
5454 Xacid_4, FALSE, FALSE,
5458 Xacid_5, FALSE, FALSE,
5462 Xacid_6, FALSE, FALSE,
5466 Xacid_7, FALSE, FALSE,
5470 Xacid_8, FALSE, FALSE,
5474 Xball_1, TRUE, FALSE,
5475 EL_EMC_MAGIC_BALL, -1, -1
5478 Xball_1B, FALSE, FALSE,
5479 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5482 Xball_2, FALSE, FALSE,
5483 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5486 Xball_2B, FALSE, FALSE,
5487 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5490 Yball_eat, FALSE, FALSE,
5491 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5494 Ykey_1_eat, FALSE, FALSE,
5495 EL_EM_KEY_1, ACTION_COLLECTING, -1
5498 Ykey_2_eat, FALSE, FALSE,
5499 EL_EM_KEY_2, ACTION_COLLECTING, -1
5502 Ykey_3_eat, FALSE, FALSE,
5503 EL_EM_KEY_3, ACTION_COLLECTING, -1
5506 Ykey_4_eat, FALSE, FALSE,
5507 EL_EM_KEY_4, ACTION_COLLECTING, -1
5510 Ykey_5_eat, FALSE, FALSE,
5511 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5514 Ykey_6_eat, FALSE, FALSE,
5515 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5518 Ykey_7_eat, FALSE, FALSE,
5519 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5522 Ykey_8_eat, FALSE, FALSE,
5523 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5526 Ylenses_eat, FALSE, FALSE,
5527 EL_EMC_LENSES, ACTION_COLLECTING, -1
5530 Ymagnify_eat, FALSE, FALSE,
5531 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5534 Ygrass_eat, FALSE, FALSE,
5535 EL_EMC_GRASS, ACTION_SNAPPING, -1
5538 Ydirt_eat, FALSE, FALSE,
5539 EL_SAND, ACTION_SNAPPING, -1
5542 Xgrow_ns, TRUE, FALSE,
5543 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5546 Ygrow_ns_eat, FALSE, FALSE,
5547 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5550 Xgrow_ew, TRUE, FALSE,
5551 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5554 Ygrow_ew_eat, FALSE, FALSE,
5555 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5558 Xwonderwall, TRUE, FALSE,
5559 EL_MAGIC_WALL, -1, -1
5562 XwonderwallB, FALSE, FALSE,
5563 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5566 Xamoeba_1, TRUE, FALSE,
5567 EL_AMOEBA_DRY, ACTION_OTHER, -1
5570 Xamoeba_2, FALSE, FALSE,
5571 EL_AMOEBA_DRY, ACTION_OTHER, -1
5574 Xamoeba_3, FALSE, FALSE,
5575 EL_AMOEBA_DRY, ACTION_OTHER, -1
5578 Xamoeba_4, FALSE, FALSE,
5579 EL_AMOEBA_DRY, ACTION_OTHER, -1
5582 Xamoeba_5, TRUE, FALSE,
5583 EL_AMOEBA_WET, ACTION_OTHER, -1
5586 Xamoeba_6, FALSE, FALSE,
5587 EL_AMOEBA_WET, ACTION_OTHER, -1
5590 Xamoeba_7, FALSE, FALSE,
5591 EL_AMOEBA_WET, ACTION_OTHER, -1
5594 Xamoeba_8, FALSE, FALSE,
5595 EL_AMOEBA_WET, ACTION_OTHER, -1
5598 Xdoor_1, TRUE, FALSE,
5599 EL_EM_GATE_1, -1, -1
5602 Xdoor_2, TRUE, FALSE,
5603 EL_EM_GATE_2, -1, -1
5606 Xdoor_3, TRUE, FALSE,
5607 EL_EM_GATE_3, -1, -1
5610 Xdoor_4, TRUE, FALSE,
5611 EL_EM_GATE_4, -1, -1
5614 Xdoor_5, TRUE, FALSE,
5615 EL_EMC_GATE_5, -1, -1
5618 Xdoor_6, TRUE, FALSE,
5619 EL_EMC_GATE_6, -1, -1
5622 Xdoor_7, TRUE, FALSE,
5623 EL_EMC_GATE_7, -1, -1
5626 Xdoor_8, TRUE, FALSE,
5627 EL_EMC_GATE_8, -1, -1
5630 Xkey_1, TRUE, FALSE,
5634 Xkey_2, TRUE, FALSE,
5638 Xkey_3, TRUE, FALSE,
5642 Xkey_4, TRUE, FALSE,
5646 Xkey_5, TRUE, FALSE,
5647 EL_EMC_KEY_5, -1, -1
5650 Xkey_6, TRUE, FALSE,
5651 EL_EMC_KEY_6, -1, -1
5654 Xkey_7, TRUE, FALSE,
5655 EL_EMC_KEY_7, -1, -1
5658 Xkey_8, TRUE, FALSE,
5659 EL_EMC_KEY_8, -1, -1
5662 Xwind_n, TRUE, FALSE,
5663 EL_BALLOON_SWITCH_UP, -1, -1
5666 Xwind_e, TRUE, FALSE,
5667 EL_BALLOON_SWITCH_RIGHT, -1, -1
5670 Xwind_s, TRUE, FALSE,
5671 EL_BALLOON_SWITCH_DOWN, -1, -1
5674 Xwind_w, TRUE, FALSE,
5675 EL_BALLOON_SWITCH_LEFT, -1, -1
5678 Xwind_nesw, TRUE, FALSE,
5679 EL_BALLOON_SWITCH_ANY, -1, -1
5682 Xwind_stop, TRUE, FALSE,
5683 EL_BALLOON_SWITCH_NONE, -1, -1
5687 EL_EM_EXIT_CLOSED, -1, -1
5690 Xexit_1, TRUE, FALSE,
5691 EL_EM_EXIT_OPEN, -1, -1
5694 Xexit_2, FALSE, FALSE,
5695 EL_EM_EXIT_OPEN, -1, -1
5698 Xexit_3, FALSE, FALSE,
5699 EL_EM_EXIT_OPEN, -1, -1
5702 Xdynamite, TRUE, FALSE,
5703 EL_EM_DYNAMITE, -1, -1
5706 Ydynamite_eat, FALSE, FALSE,
5707 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5710 Xdynamite_1, TRUE, FALSE,
5711 EL_EM_DYNAMITE_ACTIVE, -1, -1
5714 Xdynamite_2, FALSE, FALSE,
5715 EL_EM_DYNAMITE_ACTIVE, -1, -1
5718 Xdynamite_3, FALSE, FALSE,
5719 EL_EM_DYNAMITE_ACTIVE, -1, -1
5722 Xdynamite_4, FALSE, FALSE,
5723 EL_EM_DYNAMITE_ACTIVE, -1, -1
5726 Xbumper, TRUE, FALSE,
5727 EL_EMC_SPRING_BUMPER, -1, -1
5730 XbumperB, FALSE, FALSE,
5731 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5734 Xwheel, TRUE, FALSE,
5735 EL_ROBOT_WHEEL, -1, -1
5738 XwheelB, FALSE, FALSE,
5739 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5742 Xswitch, TRUE, FALSE,
5743 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5746 XswitchB, FALSE, FALSE,
5747 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5751 EL_QUICKSAND_EMPTY, -1, -1
5754 Xsand_stone, TRUE, FALSE,
5755 EL_QUICKSAND_FULL, -1, -1
5758 Xsand_stonein_1, FALSE, TRUE,
5759 EL_ROCK, ACTION_FILLING, -1
5762 Xsand_stonein_2, FALSE, TRUE,
5763 EL_ROCK, ACTION_FILLING, -1
5766 Xsand_stonein_3, FALSE, TRUE,
5767 EL_ROCK, ACTION_FILLING, -1
5770 Xsand_stonein_4, FALSE, TRUE,
5771 EL_ROCK, ACTION_FILLING, -1
5774 Xsand_stonesand_1, FALSE, FALSE,
5775 EL_QUICKSAND_EMPTYING, -1, -1
5778 Xsand_stonesand_2, FALSE, FALSE,
5779 EL_QUICKSAND_EMPTYING, -1, -1
5782 Xsand_stonesand_3, FALSE, FALSE,
5783 EL_QUICKSAND_EMPTYING, -1, -1
5786 Xsand_stonesand_4, FALSE, FALSE,
5787 EL_QUICKSAND_EMPTYING, -1, -1
5790 Xsand_stonesand_quickout_1, FALSE, FALSE,
5791 EL_QUICKSAND_EMPTYING, -1, -1
5794 Xsand_stonesand_quickout_2, FALSE, FALSE,
5795 EL_QUICKSAND_EMPTYING, -1, -1
5798 Xsand_stoneout_1, FALSE, FALSE,
5799 EL_ROCK, ACTION_EMPTYING, -1
5802 Xsand_stoneout_2, FALSE, FALSE,
5803 EL_ROCK, ACTION_EMPTYING, -1
5806 Xsand_sandstone_1, FALSE, FALSE,
5807 EL_QUICKSAND_FILLING, -1, -1
5810 Xsand_sandstone_2, FALSE, FALSE,
5811 EL_QUICKSAND_FILLING, -1, -1
5814 Xsand_sandstone_3, FALSE, FALSE,
5815 EL_QUICKSAND_FILLING, -1, -1
5818 Xsand_sandstone_4, FALSE, FALSE,
5819 EL_QUICKSAND_FILLING, -1, -1
5822 Xplant, TRUE, FALSE,
5823 EL_EMC_PLANT, -1, -1
5826 Yplant, FALSE, FALSE,
5827 EL_EMC_PLANT, -1, -1
5830 Xlenses, TRUE, FALSE,
5831 EL_EMC_LENSES, -1, -1
5834 Xmagnify, TRUE, FALSE,
5835 EL_EMC_MAGNIFIER, -1, -1
5838 Xdripper, TRUE, FALSE,
5839 EL_EMC_DRIPPER, -1, -1
5842 XdripperB, FALSE, FALSE,
5843 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5846 Xfake_blank, TRUE, FALSE,
5847 EL_INVISIBLE_WALL, -1, -1
5850 Xfake_blankB, FALSE, FALSE,
5851 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5854 Xfake_grass, TRUE, FALSE,
5855 EL_EMC_FAKE_GRASS, -1, -1
5858 Xfake_grassB, FALSE, FALSE,
5859 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5862 Xfake_door_1, TRUE, FALSE,
5863 EL_EM_GATE_1_GRAY, -1, -1
5866 Xfake_door_2, TRUE, FALSE,
5867 EL_EM_GATE_2_GRAY, -1, -1
5870 Xfake_door_3, TRUE, FALSE,
5871 EL_EM_GATE_3_GRAY, -1, -1
5874 Xfake_door_4, TRUE, FALSE,
5875 EL_EM_GATE_4_GRAY, -1, -1
5878 Xfake_door_5, TRUE, FALSE,
5879 EL_EMC_GATE_5_GRAY, -1, -1
5882 Xfake_door_6, TRUE, FALSE,
5883 EL_EMC_GATE_6_GRAY, -1, -1
5886 Xfake_door_7, TRUE, FALSE,
5887 EL_EMC_GATE_7_GRAY, -1, -1
5890 Xfake_door_8, TRUE, FALSE,
5891 EL_EMC_GATE_8_GRAY, -1, -1
5894 Xfake_acid_1, TRUE, FALSE,
5895 EL_EMC_FAKE_ACID, -1, -1
5898 Xfake_acid_2, FALSE, FALSE,
5899 EL_EMC_FAKE_ACID, -1, -1
5902 Xfake_acid_3, FALSE, FALSE,
5903 EL_EMC_FAKE_ACID, -1, -1
5906 Xfake_acid_4, FALSE, FALSE,
5907 EL_EMC_FAKE_ACID, -1, -1
5910 Xfake_acid_5, FALSE, FALSE,
5911 EL_EMC_FAKE_ACID, -1, -1
5914 Xfake_acid_6, FALSE, FALSE,
5915 EL_EMC_FAKE_ACID, -1, -1
5918 Xfake_acid_7, FALSE, FALSE,
5919 EL_EMC_FAKE_ACID, -1, -1
5922 Xfake_acid_8, FALSE, FALSE,
5923 EL_EMC_FAKE_ACID, -1, -1
5926 Xsteel_1, TRUE, FALSE,
5927 EL_STEELWALL, -1, -1
5930 Xsteel_2, TRUE, FALSE,
5931 EL_EMC_STEELWALL_2, -1, -1
5934 Xsteel_3, TRUE, FALSE,
5935 EL_EMC_STEELWALL_3, -1, -1
5938 Xsteel_4, TRUE, FALSE,
5939 EL_EMC_STEELWALL_4, -1, -1
5942 Xwall_1, TRUE, FALSE,
5946 Xwall_2, TRUE, FALSE,
5947 EL_EMC_WALL_14, -1, -1
5950 Xwall_3, TRUE, FALSE,
5951 EL_EMC_WALL_15, -1, -1
5954 Xwall_4, TRUE, FALSE,
5955 EL_EMC_WALL_16, -1, -1
5958 Xround_wall_1, TRUE, FALSE,
5959 EL_WALL_SLIPPERY, -1, -1
5962 Xround_wall_2, TRUE, FALSE,
5963 EL_EMC_WALL_SLIPPERY_2, -1, -1
5966 Xround_wall_3, TRUE, FALSE,
5967 EL_EMC_WALL_SLIPPERY_3, -1, -1
5970 Xround_wall_4, TRUE, FALSE,
5971 EL_EMC_WALL_SLIPPERY_4, -1, -1
5974 Xdecor_1, TRUE, FALSE,
5975 EL_EMC_WALL_8, -1, -1
5978 Xdecor_2, TRUE, FALSE,
5979 EL_EMC_WALL_6, -1, -1
5982 Xdecor_3, TRUE, FALSE,
5983 EL_EMC_WALL_4, -1, -1
5986 Xdecor_4, TRUE, FALSE,
5987 EL_EMC_WALL_7, -1, -1
5990 Xdecor_5, TRUE, FALSE,
5991 EL_EMC_WALL_5, -1, -1
5994 Xdecor_6, TRUE, FALSE,
5995 EL_EMC_WALL_9, -1, -1
5998 Xdecor_7, TRUE, FALSE,
5999 EL_EMC_WALL_10, -1, -1
6002 Xdecor_8, TRUE, FALSE,
6003 EL_EMC_WALL_1, -1, -1
6006 Xdecor_9, TRUE, FALSE,
6007 EL_EMC_WALL_2, -1, -1
6010 Xdecor_10, TRUE, FALSE,
6011 EL_EMC_WALL_3, -1, -1
6014 Xdecor_11, TRUE, FALSE,
6015 EL_EMC_WALL_11, -1, -1
6018 Xdecor_12, TRUE, FALSE,
6019 EL_EMC_WALL_12, -1, -1
6022 Xalpha_0, TRUE, FALSE,
6023 EL_CHAR('0'), -1, -1
6026 Xalpha_1, TRUE, FALSE,
6027 EL_CHAR('1'), -1, -1
6030 Xalpha_2, TRUE, FALSE,
6031 EL_CHAR('2'), -1, -1
6034 Xalpha_3, TRUE, FALSE,
6035 EL_CHAR('3'), -1, -1
6038 Xalpha_4, TRUE, FALSE,
6039 EL_CHAR('4'), -1, -1
6042 Xalpha_5, TRUE, FALSE,
6043 EL_CHAR('5'), -1, -1
6046 Xalpha_6, TRUE, FALSE,
6047 EL_CHAR('6'), -1, -1
6050 Xalpha_7, TRUE, FALSE,
6051 EL_CHAR('7'), -1, -1
6054 Xalpha_8, TRUE, FALSE,
6055 EL_CHAR('8'), -1, -1
6058 Xalpha_9, TRUE, FALSE,
6059 EL_CHAR('9'), -1, -1
6062 Xalpha_excla, TRUE, FALSE,
6063 EL_CHAR('!'), -1, -1
6066 Xalpha_quote, TRUE, FALSE,
6067 EL_CHAR('"'), -1, -1
6070 Xalpha_comma, TRUE, FALSE,
6071 EL_CHAR(','), -1, -1
6074 Xalpha_minus, TRUE, FALSE,
6075 EL_CHAR('-'), -1, -1
6078 Xalpha_perio, TRUE, FALSE,
6079 EL_CHAR('.'), -1, -1
6082 Xalpha_colon, TRUE, FALSE,
6083 EL_CHAR(':'), -1, -1
6086 Xalpha_quest, TRUE, FALSE,
6087 EL_CHAR('?'), -1, -1
6090 Xalpha_a, TRUE, FALSE,
6091 EL_CHAR('A'), -1, -1
6094 Xalpha_b, TRUE, FALSE,
6095 EL_CHAR('B'), -1, -1
6098 Xalpha_c, TRUE, FALSE,
6099 EL_CHAR('C'), -1, -1
6102 Xalpha_d, TRUE, FALSE,
6103 EL_CHAR('D'), -1, -1
6106 Xalpha_e, TRUE, FALSE,
6107 EL_CHAR('E'), -1, -1
6110 Xalpha_f, TRUE, FALSE,
6111 EL_CHAR('F'), -1, -1
6114 Xalpha_g, TRUE, FALSE,
6115 EL_CHAR('G'), -1, -1
6118 Xalpha_h, TRUE, FALSE,
6119 EL_CHAR('H'), -1, -1
6122 Xalpha_i, TRUE, FALSE,
6123 EL_CHAR('I'), -1, -1
6126 Xalpha_j, TRUE, FALSE,
6127 EL_CHAR('J'), -1, -1
6130 Xalpha_k, TRUE, FALSE,
6131 EL_CHAR('K'), -1, -1
6134 Xalpha_l, TRUE, FALSE,
6135 EL_CHAR('L'), -1, -1
6138 Xalpha_m, TRUE, FALSE,
6139 EL_CHAR('M'), -1, -1
6142 Xalpha_n, TRUE, FALSE,
6143 EL_CHAR('N'), -1, -1
6146 Xalpha_o, TRUE, FALSE,
6147 EL_CHAR('O'), -1, -1
6150 Xalpha_p, TRUE, FALSE,
6151 EL_CHAR('P'), -1, -1
6154 Xalpha_q, TRUE, FALSE,
6155 EL_CHAR('Q'), -1, -1
6158 Xalpha_r, TRUE, FALSE,
6159 EL_CHAR('R'), -1, -1
6162 Xalpha_s, TRUE, FALSE,
6163 EL_CHAR('S'), -1, -1
6166 Xalpha_t, TRUE, FALSE,
6167 EL_CHAR('T'), -1, -1
6170 Xalpha_u, TRUE, FALSE,
6171 EL_CHAR('U'), -1, -1
6174 Xalpha_v, TRUE, FALSE,
6175 EL_CHAR('V'), -1, -1
6178 Xalpha_w, TRUE, FALSE,
6179 EL_CHAR('W'), -1, -1
6182 Xalpha_x, TRUE, FALSE,
6183 EL_CHAR('X'), -1, -1
6186 Xalpha_y, TRUE, FALSE,
6187 EL_CHAR('Y'), -1, -1
6190 Xalpha_z, TRUE, FALSE,
6191 EL_CHAR('Z'), -1, -1
6194 Xalpha_arrow_e, TRUE, FALSE,
6195 EL_CHAR('>'), -1, -1
6198 Xalpha_arrow_w, TRUE, FALSE,
6199 EL_CHAR('<'), -1, -1
6202 Xalpha_copyr, TRUE, FALSE,
6203 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6207 Xboom_bug, FALSE, FALSE,
6208 EL_BUG, ACTION_EXPLODING, -1
6211 Xboom_bomb, FALSE, FALSE,
6212 EL_BOMB, ACTION_EXPLODING, -1
6215 Xboom_android, FALSE, FALSE,
6216 EL_EMC_ANDROID, ACTION_OTHER, -1
6219 Xboom_1, FALSE, FALSE,
6220 EL_DEFAULT, ACTION_EXPLODING, -1
6223 Xboom_2, FALSE, FALSE,
6224 EL_DEFAULT, ACTION_EXPLODING, -1
6227 Znormal, FALSE, FALSE,
6231 Zdynamite, FALSE, FALSE,
6235 Zplayer, FALSE, FALSE,
6239 ZBORDER, FALSE, FALSE,
6249 static struct Mapping_EM_to_RND_player
6258 em_player_mapping_list[] =
6262 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6266 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6270 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6274 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6278 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6282 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6286 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6290 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6294 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6298 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6302 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6306 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6310 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6314 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6318 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6322 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6326 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6330 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6334 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6338 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6342 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6346 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6350 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6354 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6358 EL_PLAYER_1, ACTION_DEFAULT, -1,
6362 EL_PLAYER_2, ACTION_DEFAULT, -1,
6366 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6370 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6374 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6378 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6382 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6386 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6390 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6394 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6398 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6402 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6406 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6410 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6414 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6418 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6422 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6426 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6430 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6434 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6438 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6442 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6446 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6450 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6454 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6458 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6462 EL_PLAYER_3, ACTION_DEFAULT, -1,
6466 EL_PLAYER_4, ACTION_DEFAULT, -1,
6475 int map_element_RND_to_EM(int element_rnd)
6477 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6478 static boolean mapping_initialized = FALSE;
6480 if (!mapping_initialized)
6484 /* return "Xalpha_quest" for all undefined elements in mapping array */
6485 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6486 mapping_RND_to_EM[i] = Xalpha_quest;
6488 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6489 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6490 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6491 em_object_mapping_list[i].element_em;
6493 mapping_initialized = TRUE;
6496 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6497 return mapping_RND_to_EM[element_rnd];
6499 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6504 int map_element_EM_to_RND(int element_em)
6506 static unsigned short mapping_EM_to_RND[TILE_MAX];
6507 static boolean mapping_initialized = FALSE;
6509 if (!mapping_initialized)
6513 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6514 for (i = 0; i < TILE_MAX; i++)
6515 mapping_EM_to_RND[i] = EL_UNKNOWN;
6517 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6518 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6519 em_object_mapping_list[i].element_rnd;
6521 mapping_initialized = TRUE;
6524 if (element_em >= 0 && element_em < TILE_MAX)
6525 return mapping_EM_to_RND[element_em];
6527 Error(ERR_WARN, "invalid EM level element %d", element_em);
6532 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6534 struct LevelInfo_EM *level_em = level->native_em_level;
6535 struct LEVEL *lev = level_em->lev;
6538 for (i = 0; i < TILE_MAX; i++)
6539 lev->android_array[i] = Xblank;
6541 for (i = 0; i < level->num_android_clone_elements; i++)
6543 int element_rnd = level->android_clone_element[i];
6544 int element_em = map_element_RND_to_EM(element_rnd);
6546 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6547 if (em_object_mapping_list[j].element_rnd == element_rnd)
6548 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6552 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6554 struct LevelInfo_EM *level_em = level->native_em_level;
6555 struct LEVEL *lev = level_em->lev;
6558 level->num_android_clone_elements = 0;
6560 for (i = 0; i < TILE_MAX; i++)
6562 int element_em = lev->android_array[i];
6564 boolean element_found = FALSE;
6566 if (element_em == Xblank)
6569 element_rnd = map_element_EM_to_RND(element_em);
6571 for (j = 0; j < level->num_android_clone_elements; j++)
6572 if (level->android_clone_element[j] == element_rnd)
6573 element_found = TRUE;
6577 level->android_clone_element[level->num_android_clone_elements++] =
6580 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6585 if (level->num_android_clone_elements == 0)
6587 level->num_android_clone_elements = 1;
6588 level->android_clone_element[0] = EL_EMPTY;
6592 int map_direction_RND_to_EM(int direction)
6594 return (direction == MV_UP ? 0 :
6595 direction == MV_RIGHT ? 1 :
6596 direction == MV_DOWN ? 2 :
6597 direction == MV_LEFT ? 3 :
6601 int map_direction_EM_to_RND(int direction)
6603 return (direction == 0 ? MV_UP :
6604 direction == 1 ? MV_RIGHT :
6605 direction == 2 ? MV_DOWN :
6606 direction == 3 ? MV_LEFT :
6610 int map_element_RND_to_SP(int element_rnd)
6612 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6614 if (element_rnd >= EL_SP_START &&
6615 element_rnd <= EL_SP_END)
6616 element_sp = element_rnd - EL_SP_START;
6617 else if (element_rnd == EL_EMPTY_SPACE)
6619 else if (element_rnd == EL_INVISIBLE_WALL)
6625 int map_element_SP_to_RND(int element_sp)
6627 int element_rnd = EL_UNKNOWN;
6629 if (element_sp >= 0x00 &&
6631 element_rnd = EL_SP_START + element_sp;
6632 else if (element_sp == 0x28)
6633 element_rnd = EL_INVISIBLE_WALL;
6638 int map_action_SP_to_RND(int action_sp)
6642 case actActive: return ACTION_ACTIVE;
6643 case actImpact: return ACTION_IMPACT;
6644 case actExploding: return ACTION_EXPLODING;
6645 case actDigging: return ACTION_DIGGING;
6646 case actSnapping: return ACTION_SNAPPING;
6647 case actCollecting: return ACTION_COLLECTING;
6648 case actPassing: return ACTION_PASSING;
6649 case actPushing: return ACTION_PUSHING;
6650 case actDropping: return ACTION_DROPPING;
6652 default: return ACTION_DEFAULT;
6656 int get_next_element(int element)
6660 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6661 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6662 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6663 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6664 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6665 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6666 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6667 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6668 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6669 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6670 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6672 default: return element;
6676 int el_act_dir2img(int element, int action, int direction)
6678 element = GFX_ELEMENT(element);
6679 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6681 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6682 return element_info[element].direction_graphic[action][direction];
6685 static int el_act_dir2crm(int element, int action, int direction)
6687 element = GFX_ELEMENT(element);
6688 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6690 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6691 return element_info[element].direction_crumbled[action][direction];
6694 int el_act2img(int element, int action)
6696 element = GFX_ELEMENT(element);
6698 return element_info[element].graphic[action];
6701 int el_act2crm(int element, int action)
6703 element = GFX_ELEMENT(element);
6705 return element_info[element].crumbled[action];
6708 int el_dir2img(int element, int direction)
6710 element = GFX_ELEMENT(element);
6712 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6715 int el2baseimg(int element)
6717 return element_info[element].graphic[ACTION_DEFAULT];
6720 int el2img(int element)
6722 element = GFX_ELEMENT(element);
6724 return element_info[element].graphic[ACTION_DEFAULT];
6727 int el2edimg(int element)
6729 element = GFX_ELEMENT(element);
6731 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6734 int el2preimg(int element)
6736 element = GFX_ELEMENT(element);
6738 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6741 int el2panelimg(int element)
6743 element = GFX_ELEMENT(element);
6745 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6748 int font2baseimg(int font_nr)
6750 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6753 int getBeltNrFromBeltElement(int element)
6755 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6756 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6757 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6760 int getBeltNrFromBeltActiveElement(int element)
6762 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6763 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6764 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6767 int getBeltNrFromBeltSwitchElement(int element)
6769 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6770 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6771 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6774 int getBeltDirNrFromBeltElement(int element)
6776 static int belt_base_element[4] =
6778 EL_CONVEYOR_BELT_1_LEFT,
6779 EL_CONVEYOR_BELT_2_LEFT,
6780 EL_CONVEYOR_BELT_3_LEFT,
6781 EL_CONVEYOR_BELT_4_LEFT
6784 int belt_nr = getBeltNrFromBeltElement(element);
6785 int belt_dir_nr = element - belt_base_element[belt_nr];
6787 return (belt_dir_nr % 3);
6790 int getBeltDirNrFromBeltSwitchElement(int element)
6792 static int belt_base_element[4] =
6794 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6795 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6796 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6797 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6800 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6801 int belt_dir_nr = element - belt_base_element[belt_nr];
6803 return (belt_dir_nr % 3);
6806 int getBeltDirFromBeltElement(int element)
6808 static int belt_move_dir[3] =
6815 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6817 return belt_move_dir[belt_dir_nr];
6820 int getBeltDirFromBeltSwitchElement(int element)
6822 static int belt_move_dir[3] =
6829 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6831 return belt_move_dir[belt_dir_nr];
6834 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6836 static int belt_base_element[4] =
6838 EL_CONVEYOR_BELT_1_LEFT,
6839 EL_CONVEYOR_BELT_2_LEFT,
6840 EL_CONVEYOR_BELT_3_LEFT,
6841 EL_CONVEYOR_BELT_4_LEFT
6844 return belt_base_element[belt_nr] + belt_dir_nr;
6847 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6849 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6851 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6854 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6856 static int belt_base_element[4] =
6858 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6859 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6860 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6861 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6864 return belt_base_element[belt_nr] + belt_dir_nr;
6867 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6869 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6871 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6874 boolean getTeamMode_EM()
6876 return game.team_mode;
6879 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6881 int game_frame_delay_value;
6883 game_frame_delay_value =
6884 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6885 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6888 if (tape.playing && tape.warp_forward && !tape.pausing)
6889 game_frame_delay_value = 0;
6891 return game_frame_delay_value;
6894 unsigned int InitRND(int seed)
6896 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6897 return InitEngineRandom_EM(seed);
6898 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6899 return InitEngineRandom_SP(seed);
6901 return InitEngineRandom_RND(seed);
6904 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6905 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6907 inline static int get_effective_element_EM(int tile, int frame_em)
6909 int element = object_mapping[tile].element_rnd;
6910 int action = object_mapping[tile].action;
6911 boolean is_backside = object_mapping[tile].is_backside;
6912 boolean action_removing = (action == ACTION_DIGGING ||
6913 action == ACTION_SNAPPING ||
6914 action == ACTION_COLLECTING);
6920 case Yacid_splash_eB:
6921 case Yacid_splash_wB:
6922 return (frame_em > 5 ? EL_EMPTY : element);
6928 else /* frame_em == 7 */
6932 case Yacid_splash_eB:
6933 case Yacid_splash_wB:
6936 case Yemerald_stone:
6939 case Ydiamond_stone:
6943 case Xdrip_stretchB:
6962 case Xsand_stonein_1:
6963 case Xsand_stonein_2:
6964 case Xsand_stonein_3:
6965 case Xsand_stonein_4:
6969 return (is_backside || action_removing ? EL_EMPTY : element);
6974 inline static boolean check_linear_animation_EM(int tile)
6978 case Xsand_stonesand_1:
6979 case Xsand_stonesand_quickout_1:
6980 case Xsand_sandstone_1:
6981 case Xsand_stonein_1:
6982 case Xsand_stoneout_1:
7001 case Yacid_splash_eB:
7002 case Yacid_splash_wB:
7003 case Yemerald_stone:
7010 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7011 boolean has_crumbled_graphics,
7012 int crumbled, int sync_frame)
7014 /* if element can be crumbled, but certain action graphics are just empty
7015 space (like instantly snapping sand to empty space in 1 frame), do not
7016 treat these empty space graphics as crumbled graphics in EMC engine */
7017 if (crumbled == IMG_EMPTY_SPACE)
7018 has_crumbled_graphics = FALSE;
7020 if (has_crumbled_graphics)
7022 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7023 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7024 g_crumbled->anim_delay,
7025 g_crumbled->anim_mode,
7026 g_crumbled->anim_start_frame,
7029 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7030 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7032 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7034 g_em->has_crumbled_graphics = TRUE;
7038 g_em->crumbled_bitmap = NULL;
7039 g_em->crumbled_src_x = 0;
7040 g_em->crumbled_src_y = 0;
7041 g_em->crumbled_border_size = 0;
7043 g_em->has_crumbled_graphics = FALSE;
7047 void ResetGfxAnimation_EM(int x, int y, int tile)
7052 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7053 int tile, int frame_em, int x, int y)
7055 int action = object_mapping[tile].action;
7056 int direction = object_mapping[tile].direction;
7057 int effective_element = get_effective_element_EM(tile, frame_em);
7058 int graphic = (direction == MV_NONE ?
7059 el_act2img(effective_element, action) :
7060 el_act_dir2img(effective_element, action, direction));
7061 struct GraphicInfo *g = &graphic_info[graphic];
7063 boolean action_removing = (action == ACTION_DIGGING ||
7064 action == ACTION_SNAPPING ||
7065 action == ACTION_COLLECTING);
7066 boolean action_moving = (action == ACTION_FALLING ||
7067 action == ACTION_MOVING ||
7068 action == ACTION_PUSHING ||
7069 action == ACTION_EATING ||
7070 action == ACTION_FILLING ||
7071 action == ACTION_EMPTYING);
7072 boolean action_falling = (action == ACTION_FALLING ||
7073 action == ACTION_FILLING ||
7074 action == ACTION_EMPTYING);
7076 /* special case: graphic uses "2nd movement tile" and has defined
7077 7 frames for movement animation (or less) => use default graphic
7078 for last (8th) frame which ends the movement animation */
7079 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7081 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7082 graphic = (direction == MV_NONE ?
7083 el_act2img(effective_element, action) :
7084 el_act_dir2img(effective_element, action, direction));
7086 g = &graphic_info[graphic];
7089 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7093 else if (action_moving)
7095 boolean is_backside = object_mapping[tile].is_backside;
7099 int direction = object_mapping[tile].direction;
7100 int move_dir = (action_falling ? MV_DOWN : direction);
7105 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7106 if (g->double_movement && frame_em == 0)
7110 if (move_dir == MV_LEFT)
7111 GfxFrame[x - 1][y] = GfxFrame[x][y];
7112 else if (move_dir == MV_RIGHT)
7113 GfxFrame[x + 1][y] = GfxFrame[x][y];
7114 else if (move_dir == MV_UP)
7115 GfxFrame[x][y - 1] = GfxFrame[x][y];
7116 else if (move_dir == MV_DOWN)
7117 GfxFrame[x][y + 1] = GfxFrame[x][y];
7124 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7125 if (tile == Xsand_stonesand_quickout_1 ||
7126 tile == Xsand_stonesand_quickout_2)
7130 if (graphic_info[graphic].anim_global_sync)
7131 sync_frame = FrameCounter;
7132 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7133 sync_frame = GfxFrame[x][y];
7135 sync_frame = 0; /* playfield border (pseudo steel) */
7137 SetRandomAnimationValue(x, y);
7139 int frame = getAnimationFrame(g->anim_frames,
7142 g->anim_start_frame,
7145 g_em->unique_identifier =
7146 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7149 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7150 int tile, int frame_em, int x, int y)
7152 int action = object_mapping[tile].action;
7153 int direction = object_mapping[tile].direction;
7154 boolean is_backside = object_mapping[tile].is_backside;
7155 int effective_element = get_effective_element_EM(tile, frame_em);
7156 int effective_action = action;
7157 int graphic = (direction == MV_NONE ?
7158 el_act2img(effective_element, effective_action) :
7159 el_act_dir2img(effective_element, effective_action,
7161 int crumbled = (direction == MV_NONE ?
7162 el_act2crm(effective_element, effective_action) :
7163 el_act_dir2crm(effective_element, effective_action,
7165 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7166 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7167 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7168 struct GraphicInfo *g = &graphic_info[graphic];
7171 /* special case: graphic uses "2nd movement tile" and has defined
7172 7 frames for movement animation (or less) => use default graphic
7173 for last (8th) frame which ends the movement animation */
7174 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7176 effective_action = ACTION_DEFAULT;
7177 graphic = (direction == MV_NONE ?
7178 el_act2img(effective_element, effective_action) :
7179 el_act_dir2img(effective_element, effective_action,
7181 crumbled = (direction == MV_NONE ?
7182 el_act2crm(effective_element, effective_action) :
7183 el_act_dir2crm(effective_element, effective_action,
7186 g = &graphic_info[graphic];
7189 if (graphic_info[graphic].anim_global_sync)
7190 sync_frame = FrameCounter;
7191 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7192 sync_frame = GfxFrame[x][y];
7194 sync_frame = 0; /* playfield border (pseudo steel) */
7196 SetRandomAnimationValue(x, y);
7198 int frame = getAnimationFrame(g->anim_frames,
7201 g->anim_start_frame,
7204 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7205 g->double_movement && is_backside);
7207 /* (updating the "crumbled" graphic definitions is probably not really needed,
7208 as animations for crumbled graphics can't be longer than one EMC cycle) */
7209 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7213 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7214 int player_nr, int anim, int frame_em)
7216 int element = player_mapping[player_nr][anim].element_rnd;
7217 int action = player_mapping[player_nr][anim].action;
7218 int direction = player_mapping[player_nr][anim].direction;
7219 int graphic = (direction == MV_NONE ?
7220 el_act2img(element, action) :
7221 el_act_dir2img(element, action, direction));
7222 struct GraphicInfo *g = &graphic_info[graphic];
7225 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7227 stored_player[player_nr].StepFrame = frame_em;
7229 sync_frame = stored_player[player_nr].Frame;
7231 int frame = getAnimationFrame(g->anim_frames,
7234 g->anim_start_frame,
7237 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7238 &g_em->src_x, &g_em->src_y, FALSE);
7241 void InitGraphicInfo_EM(void)
7246 int num_em_gfx_errors = 0;
7248 if (graphic_info_em_object[0][0].bitmap == NULL)
7250 /* EM graphics not yet initialized in em_open_all() */
7255 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7258 /* always start with reliable default values */
7259 for (i = 0; i < TILE_MAX; i++)
7261 object_mapping[i].element_rnd = EL_UNKNOWN;
7262 object_mapping[i].is_backside = FALSE;
7263 object_mapping[i].action = ACTION_DEFAULT;
7264 object_mapping[i].direction = MV_NONE;
7267 /* always start with reliable default values */
7268 for (p = 0; p < MAX_PLAYERS; p++)
7270 for (i = 0; i < SPR_MAX; i++)
7272 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7273 player_mapping[p][i].action = ACTION_DEFAULT;
7274 player_mapping[p][i].direction = MV_NONE;
7278 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7280 int e = em_object_mapping_list[i].element_em;
7282 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7283 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7285 if (em_object_mapping_list[i].action != -1)
7286 object_mapping[e].action = em_object_mapping_list[i].action;
7288 if (em_object_mapping_list[i].direction != -1)
7289 object_mapping[e].direction =
7290 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7293 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7295 int a = em_player_mapping_list[i].action_em;
7296 int p = em_player_mapping_list[i].player_nr;
7298 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7300 if (em_player_mapping_list[i].action != -1)
7301 player_mapping[p][a].action = em_player_mapping_list[i].action;
7303 if (em_player_mapping_list[i].direction != -1)
7304 player_mapping[p][a].direction =
7305 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7308 for (i = 0; i < TILE_MAX; i++)
7310 int element = object_mapping[i].element_rnd;
7311 int action = object_mapping[i].action;
7312 int direction = object_mapping[i].direction;
7313 boolean is_backside = object_mapping[i].is_backside;
7314 boolean action_exploding = ((action == ACTION_EXPLODING ||
7315 action == ACTION_SMASHED_BY_ROCK ||
7316 action == ACTION_SMASHED_BY_SPRING) &&
7317 element != EL_DIAMOND);
7318 boolean action_active = (action == ACTION_ACTIVE);
7319 boolean action_other = (action == ACTION_OTHER);
7321 for (j = 0; j < 8; j++)
7323 int effective_element = get_effective_element_EM(i, j);
7324 int effective_action = (j < 7 ? action :
7325 i == Xdrip_stretch ? action :
7326 i == Xdrip_stretchB ? action :
7327 i == Ydrip_s1 ? action :
7328 i == Ydrip_s1B ? action :
7329 i == Xball_1B ? action :
7330 i == Xball_2 ? action :
7331 i == Xball_2B ? action :
7332 i == Yball_eat ? action :
7333 i == Ykey_1_eat ? action :
7334 i == Ykey_2_eat ? action :
7335 i == Ykey_3_eat ? action :
7336 i == Ykey_4_eat ? action :
7337 i == Ykey_5_eat ? action :
7338 i == Ykey_6_eat ? action :
7339 i == Ykey_7_eat ? action :
7340 i == Ykey_8_eat ? action :
7341 i == Ylenses_eat ? action :
7342 i == Ymagnify_eat ? action :
7343 i == Ygrass_eat ? action :
7344 i == Ydirt_eat ? action :
7345 i == Xsand_stonein_1 ? action :
7346 i == Xsand_stonein_2 ? action :
7347 i == Xsand_stonein_3 ? action :
7348 i == Xsand_stonein_4 ? action :
7349 i == Xsand_stoneout_1 ? action :
7350 i == Xsand_stoneout_2 ? action :
7351 i == Xboom_android ? ACTION_EXPLODING :
7352 action_exploding ? ACTION_EXPLODING :
7353 action_active ? action :
7354 action_other ? action :
7356 int graphic = (el_act_dir2img(effective_element, effective_action,
7358 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7360 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7361 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7362 boolean has_action_graphics = (graphic != base_graphic);
7363 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7364 struct GraphicInfo *g = &graphic_info[graphic];
7365 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7368 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7369 boolean special_animation = (action != ACTION_DEFAULT &&
7370 g->anim_frames == 3 &&
7371 g->anim_delay == 2 &&
7372 g->anim_mode & ANIM_LINEAR);
7373 int sync_frame = (i == Xdrip_stretch ? 7 :
7374 i == Xdrip_stretchB ? 7 :
7375 i == Ydrip_s2 ? j + 8 :
7376 i == Ydrip_s2B ? j + 8 :
7385 i == Xfake_acid_1 ? 0 :
7386 i == Xfake_acid_2 ? 10 :
7387 i == Xfake_acid_3 ? 20 :
7388 i == Xfake_acid_4 ? 30 :
7389 i == Xfake_acid_5 ? 40 :
7390 i == Xfake_acid_6 ? 50 :
7391 i == Xfake_acid_7 ? 60 :
7392 i == Xfake_acid_8 ? 70 :
7394 i == Xball_2B ? j + 8 :
7395 i == Yball_eat ? j + 1 :
7396 i == Ykey_1_eat ? j + 1 :
7397 i == Ykey_2_eat ? j + 1 :
7398 i == Ykey_3_eat ? j + 1 :
7399 i == Ykey_4_eat ? j + 1 :
7400 i == Ykey_5_eat ? j + 1 :
7401 i == Ykey_6_eat ? j + 1 :
7402 i == Ykey_7_eat ? j + 1 :
7403 i == Ykey_8_eat ? j + 1 :
7404 i == Ylenses_eat ? j + 1 :
7405 i == Ymagnify_eat ? j + 1 :
7406 i == Ygrass_eat ? j + 1 :
7407 i == Ydirt_eat ? j + 1 :
7408 i == Xamoeba_1 ? 0 :
7409 i == Xamoeba_2 ? 1 :
7410 i == Xamoeba_3 ? 2 :
7411 i == Xamoeba_4 ? 3 :
7412 i == Xamoeba_5 ? 0 :
7413 i == Xamoeba_6 ? 1 :
7414 i == Xamoeba_7 ? 2 :
7415 i == Xamoeba_8 ? 3 :
7416 i == Xexit_2 ? j + 8 :
7417 i == Xexit_3 ? j + 16 :
7418 i == Xdynamite_1 ? 0 :
7419 i == Xdynamite_2 ? 8 :
7420 i == Xdynamite_3 ? 16 :
7421 i == Xdynamite_4 ? 24 :
7422 i == Xsand_stonein_1 ? j + 1 :
7423 i == Xsand_stonein_2 ? j + 9 :
7424 i == Xsand_stonein_3 ? j + 17 :
7425 i == Xsand_stonein_4 ? j + 25 :
7426 i == Xsand_stoneout_1 && j == 0 ? 0 :
7427 i == Xsand_stoneout_1 && j == 1 ? 0 :
7428 i == Xsand_stoneout_1 && j == 2 ? 1 :
7429 i == Xsand_stoneout_1 && j == 3 ? 2 :
7430 i == Xsand_stoneout_1 && j == 4 ? 2 :
7431 i == Xsand_stoneout_1 && j == 5 ? 3 :
7432 i == Xsand_stoneout_1 && j == 6 ? 4 :
7433 i == Xsand_stoneout_1 && j == 7 ? 4 :
7434 i == Xsand_stoneout_2 && j == 0 ? 5 :
7435 i == Xsand_stoneout_2 && j == 1 ? 6 :
7436 i == Xsand_stoneout_2 && j == 2 ? 7 :
7437 i == Xsand_stoneout_2 && j == 3 ? 8 :
7438 i == Xsand_stoneout_2 && j == 4 ? 9 :
7439 i == Xsand_stoneout_2 && j == 5 ? 11 :
7440 i == Xsand_stoneout_2 && j == 6 ? 13 :
7441 i == Xsand_stoneout_2 && j == 7 ? 15 :
7442 i == Xboom_bug && j == 1 ? 2 :
7443 i == Xboom_bug && j == 2 ? 2 :
7444 i == Xboom_bug && j == 3 ? 4 :
7445 i == Xboom_bug && j == 4 ? 4 :
7446 i == Xboom_bug && j == 5 ? 2 :
7447 i == Xboom_bug && j == 6 ? 2 :
7448 i == Xboom_bug && j == 7 ? 0 :
7449 i == Xboom_bomb && j == 1 ? 2 :
7450 i == Xboom_bomb && j == 2 ? 2 :
7451 i == Xboom_bomb && j == 3 ? 4 :
7452 i == Xboom_bomb && j == 4 ? 4 :
7453 i == Xboom_bomb && j == 5 ? 2 :
7454 i == Xboom_bomb && j == 6 ? 2 :
7455 i == Xboom_bomb && j == 7 ? 0 :
7456 i == Xboom_android && j == 7 ? 6 :
7457 i == Xboom_1 && j == 1 ? 2 :
7458 i == Xboom_1 && j == 2 ? 2 :
7459 i == Xboom_1 && j == 3 ? 4 :
7460 i == Xboom_1 && j == 4 ? 4 :
7461 i == Xboom_1 && j == 5 ? 6 :
7462 i == Xboom_1 && j == 6 ? 6 :
7463 i == Xboom_1 && j == 7 ? 8 :
7464 i == Xboom_2 && j == 0 ? 8 :
7465 i == Xboom_2 && j == 1 ? 8 :
7466 i == Xboom_2 && j == 2 ? 10 :
7467 i == Xboom_2 && j == 3 ? 10 :
7468 i == Xboom_2 && j == 4 ? 10 :
7469 i == Xboom_2 && j == 5 ? 12 :
7470 i == Xboom_2 && j == 6 ? 12 :
7471 i == Xboom_2 && j == 7 ? 12 :
7472 special_animation && j == 4 ? 3 :
7473 effective_action != action ? 0 :
7477 Bitmap *debug_bitmap = g_em->bitmap;
7478 int debug_src_x = g_em->src_x;
7479 int debug_src_y = g_em->src_y;
7482 int frame = getAnimationFrame(g->anim_frames,
7485 g->anim_start_frame,
7488 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7489 g->double_movement && is_backside);
7491 g_em->bitmap = src_bitmap;
7492 g_em->src_x = src_x;
7493 g_em->src_y = src_y;
7494 g_em->src_offset_x = 0;
7495 g_em->src_offset_y = 0;
7496 g_em->dst_offset_x = 0;
7497 g_em->dst_offset_y = 0;
7498 g_em->width = TILEX;
7499 g_em->height = TILEY;
7501 g_em->preserve_background = FALSE;
7503 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7506 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7507 effective_action == ACTION_MOVING ||
7508 effective_action == ACTION_PUSHING ||
7509 effective_action == ACTION_EATING)) ||
7510 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7511 effective_action == ACTION_EMPTYING)))
7514 (effective_action == ACTION_FALLING ||
7515 effective_action == ACTION_FILLING ||
7516 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7517 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7518 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7519 int num_steps = (i == Ydrip_s1 ? 16 :
7520 i == Ydrip_s1B ? 16 :
7521 i == Ydrip_s2 ? 16 :
7522 i == Ydrip_s2B ? 16 :
7523 i == Xsand_stonein_1 ? 32 :
7524 i == Xsand_stonein_2 ? 32 :
7525 i == Xsand_stonein_3 ? 32 :
7526 i == Xsand_stonein_4 ? 32 :
7527 i == Xsand_stoneout_1 ? 16 :
7528 i == Xsand_stoneout_2 ? 16 : 8);
7529 int cx = ABS(dx) * (TILEX / num_steps);
7530 int cy = ABS(dy) * (TILEY / num_steps);
7531 int step_frame = (i == Ydrip_s2 ? j + 8 :
7532 i == Ydrip_s2B ? j + 8 :
7533 i == Xsand_stonein_2 ? j + 8 :
7534 i == Xsand_stonein_3 ? j + 16 :
7535 i == Xsand_stonein_4 ? j + 24 :
7536 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7537 int step = (is_backside ? step_frame : num_steps - step_frame);
7539 if (is_backside) /* tile where movement starts */
7541 if (dx < 0 || dy < 0)
7543 g_em->src_offset_x = cx * step;
7544 g_em->src_offset_y = cy * step;
7548 g_em->dst_offset_x = cx * step;
7549 g_em->dst_offset_y = cy * step;
7552 else /* tile where movement ends */
7554 if (dx < 0 || dy < 0)
7556 g_em->dst_offset_x = cx * step;
7557 g_em->dst_offset_y = cy * step;
7561 g_em->src_offset_x = cx * step;
7562 g_em->src_offset_y = cy * step;
7566 g_em->width = TILEX - cx * step;
7567 g_em->height = TILEY - cy * step;
7570 /* create unique graphic identifier to decide if tile must be redrawn */
7571 /* bit 31 - 16 (16 bit): EM style graphic
7572 bit 15 - 12 ( 4 bit): EM style frame
7573 bit 11 - 6 ( 6 bit): graphic width
7574 bit 5 - 0 ( 6 bit): graphic height */
7575 g_em->unique_identifier =
7576 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7580 /* skip check for EMC elements not contained in original EMC artwork */
7581 if (element == EL_EMC_FAKE_ACID)
7584 if (g_em->bitmap != debug_bitmap ||
7585 g_em->src_x != debug_src_x ||
7586 g_em->src_y != debug_src_y ||
7587 g_em->src_offset_x != 0 ||
7588 g_em->src_offset_y != 0 ||
7589 g_em->dst_offset_x != 0 ||
7590 g_em->dst_offset_y != 0 ||
7591 g_em->width != TILEX ||
7592 g_em->height != TILEY)
7594 static int last_i = -1;
7602 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7603 i, element, element_info[element].token_name,
7604 element_action_info[effective_action].suffix, direction);
7606 if (element != effective_element)
7607 printf(" [%d ('%s')]",
7609 element_info[effective_element].token_name);
7613 if (g_em->bitmap != debug_bitmap)
7614 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7615 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7617 if (g_em->src_x != debug_src_x ||
7618 g_em->src_y != debug_src_y)
7619 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7620 j, (is_backside ? 'B' : 'F'),
7621 g_em->src_x, g_em->src_y,
7622 g_em->src_x / 32, g_em->src_y / 32,
7623 debug_src_x, debug_src_y,
7624 debug_src_x / 32, debug_src_y / 32);
7626 if (g_em->src_offset_x != 0 ||
7627 g_em->src_offset_y != 0 ||
7628 g_em->dst_offset_x != 0 ||
7629 g_em->dst_offset_y != 0)
7630 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7632 g_em->src_offset_x, g_em->src_offset_y,
7633 g_em->dst_offset_x, g_em->dst_offset_y);
7635 if (g_em->width != TILEX ||
7636 g_em->height != TILEY)
7637 printf(" %d (%d): size %d,%d should be %d,%d\n",
7639 g_em->width, g_em->height, TILEX, TILEY);
7641 num_em_gfx_errors++;
7648 for (i = 0; i < TILE_MAX; i++)
7650 for (j = 0; j < 8; j++)
7652 int element = object_mapping[i].element_rnd;
7653 int action = object_mapping[i].action;
7654 int direction = object_mapping[i].direction;
7655 boolean is_backside = object_mapping[i].is_backside;
7656 int graphic_action = el_act_dir2img(element, action, direction);
7657 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7659 if ((action == ACTION_SMASHED_BY_ROCK ||
7660 action == ACTION_SMASHED_BY_SPRING ||
7661 action == ACTION_EATING) &&
7662 graphic_action == graphic_default)
7664 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7665 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7666 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7667 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7670 /* no separate animation for "smashed by rock" -- use rock instead */
7671 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7672 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7674 g_em->bitmap = g_xx->bitmap;
7675 g_em->src_x = g_xx->src_x;
7676 g_em->src_y = g_xx->src_y;
7677 g_em->src_offset_x = g_xx->src_offset_x;
7678 g_em->src_offset_y = g_xx->src_offset_y;
7679 g_em->dst_offset_x = g_xx->dst_offset_x;
7680 g_em->dst_offset_y = g_xx->dst_offset_y;
7681 g_em->width = g_xx->width;
7682 g_em->height = g_xx->height;
7683 g_em->unique_identifier = g_xx->unique_identifier;
7686 g_em->preserve_background = TRUE;
7691 for (p = 0; p < MAX_PLAYERS; p++)
7693 for (i = 0; i < SPR_MAX; i++)
7695 int element = player_mapping[p][i].element_rnd;
7696 int action = player_mapping[p][i].action;
7697 int direction = player_mapping[p][i].direction;
7699 for (j = 0; j < 8; j++)
7701 int effective_element = element;
7702 int effective_action = action;
7703 int graphic = (direction == MV_NONE ?
7704 el_act2img(effective_element, effective_action) :
7705 el_act_dir2img(effective_element, effective_action,
7707 struct GraphicInfo *g = &graphic_info[graphic];
7708 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7714 Bitmap *debug_bitmap = g_em->bitmap;
7715 int debug_src_x = g_em->src_x;
7716 int debug_src_y = g_em->src_y;
7719 int frame = getAnimationFrame(g->anim_frames,
7722 g->anim_start_frame,
7725 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7727 g_em->bitmap = src_bitmap;
7728 g_em->src_x = src_x;
7729 g_em->src_y = src_y;
7730 g_em->src_offset_x = 0;
7731 g_em->src_offset_y = 0;
7732 g_em->dst_offset_x = 0;
7733 g_em->dst_offset_y = 0;
7734 g_em->width = TILEX;
7735 g_em->height = TILEY;
7739 /* skip check for EMC elements not contained in original EMC artwork */
7740 if (element == EL_PLAYER_3 ||
7741 element == EL_PLAYER_4)
7744 if (g_em->bitmap != debug_bitmap ||
7745 g_em->src_x != debug_src_x ||
7746 g_em->src_y != debug_src_y)
7748 static int last_i = -1;
7756 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7757 p, i, element, element_info[element].token_name,
7758 element_action_info[effective_action].suffix, direction);
7760 if (element != effective_element)
7761 printf(" [%d ('%s')]",
7763 element_info[effective_element].token_name);
7767 if (g_em->bitmap != debug_bitmap)
7768 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7769 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7771 if (g_em->src_x != debug_src_x ||
7772 g_em->src_y != debug_src_y)
7773 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7775 g_em->src_x, g_em->src_y,
7776 g_em->src_x / 32, g_em->src_y / 32,
7777 debug_src_x, debug_src_y,
7778 debug_src_x / 32, debug_src_y / 32);
7780 num_em_gfx_errors++;
7790 printf("::: [%d errors found]\n", num_em_gfx_errors);
7796 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7797 boolean any_player_moving,
7798 boolean player_is_dropping)
7800 if (tape.single_step && tape.recording && !tape.pausing)
7801 if (frame == 0 && !player_is_dropping)
7802 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7805 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7806 boolean murphy_is_dropping)
7808 if (tape.single_step && tape.recording && !tape.pausing)
7809 if (murphy_is_waiting)
7810 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7813 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7814 int graphic, int sync_frame, int x, int y)
7816 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7818 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7821 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7823 return (IS_NEXT_FRAME(sync_frame, graphic));
7826 int getGraphicInfo_Delay(int graphic)
7828 return graphic_info[graphic].anim_delay;
7831 void PlayMenuSoundExt(int sound)
7833 if (sound == SND_UNDEFINED)
7836 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7837 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7840 if (IS_LOOP_SOUND(sound))
7841 PlaySoundLoop(sound);
7846 void PlayMenuSound()
7848 PlayMenuSoundExt(menu.sound[game_status]);
7851 void PlayMenuSoundStereo(int sound, int stereo_position)
7853 if (sound == SND_UNDEFINED)
7856 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7857 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7860 if (IS_LOOP_SOUND(sound))
7861 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7863 PlaySoundStereo(sound, stereo_position);
7866 void PlayMenuSoundIfLoopExt(int sound)
7868 if (sound == SND_UNDEFINED)
7871 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7872 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7875 if (IS_LOOP_SOUND(sound))
7876 PlaySoundLoop(sound);
7879 void PlayMenuSoundIfLoop()
7881 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7884 void PlayMenuMusicExt(int music)
7886 if (music == MUS_UNDEFINED)
7889 if (!setup.sound_music)
7895 void PlayMenuMusic()
7897 PlayMenuMusicExt(menu.music[game_status]);
7900 void PlaySoundActivating()
7903 PlaySound(SND_MENU_ITEM_ACTIVATING);
7907 void PlaySoundSelecting()
7910 PlaySound(SND_MENU_ITEM_SELECTING);
7914 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7916 boolean change_fullscreen = (setup.fullscreen !=
7917 video.fullscreen_enabled);
7918 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7919 !strEqual(setup.fullscreen_mode,
7920 video.fullscreen_mode_current));
7921 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7922 setup.window_scaling_percent !=
7923 video.window_scaling_percent);
7925 if (change_window_scaling_percent && video.fullscreen_enabled)
7928 if (!change_window_scaling_percent && !video.fullscreen_available)
7931 #if defined(TARGET_SDL2)
7932 if (change_window_scaling_percent)
7934 SDLSetWindowScaling(setup.window_scaling_percent);
7938 else if (change_fullscreen)
7940 SDLSetWindowFullscreen(setup.fullscreen);
7942 /* set setup value according to successfully changed fullscreen mode */
7943 setup.fullscreen = video.fullscreen_enabled;
7949 if (change_fullscreen ||
7950 change_fullscreen_mode ||
7951 change_window_scaling_percent)
7953 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
7955 /* save backbuffer content which gets lost when toggling fullscreen mode */
7956 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7958 if (change_fullscreen_mode)
7960 /* keep fullscreen, but change fullscreen mode (screen resolution) */
7961 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
7964 if (change_window_scaling_percent)
7966 /* keep window mode, but change window scaling */
7967 video.fullscreen_enabled = TRUE; /* force new window scaling */
7970 /* toggle fullscreen */
7971 ChangeVideoModeIfNeeded(setup.fullscreen);
7973 /* set setup value according to successfully changed fullscreen mode */
7974 setup.fullscreen = video.fullscreen_enabled;
7976 /* restore backbuffer content from temporary backbuffer backup bitmap */
7977 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7979 FreeBitmap(tmp_backbuffer);
7981 /* update visible window/screen */
7982 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7986 void ChangeViewportPropertiesIfNeeded()
7988 int gfx_game_mode = game_status;
7989 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
7991 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
7992 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
7993 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
7994 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
7995 int border_size = vp_playfield->border_size;
7996 int new_sx = vp_playfield->x + border_size;
7997 int new_sy = vp_playfield->y + border_size;
7998 int new_sxsize = vp_playfield->width - 2 * border_size;
7999 int new_sysize = vp_playfield->height - 2 * border_size;
8000 int new_real_sx = vp_playfield->x;
8001 int new_real_sy = vp_playfield->y;
8002 int new_full_sxsize = vp_playfield->width;
8003 int new_full_sysize = vp_playfield->height;
8004 int new_dx = vp_door_1->x;
8005 int new_dy = vp_door_1->y;
8006 int new_dxsize = vp_door_1->width;
8007 int new_dysize = vp_door_1->height;
8008 int new_vx = vp_door_2->x;
8009 int new_vy = vp_door_2->y;
8010 int new_vxsize = vp_door_2->width;
8011 int new_vysize = vp_door_2->height;
8012 int new_ex = vp_door_3->x;
8013 int new_ey = vp_door_3->y;
8014 int new_exsize = vp_door_3->width;
8015 int new_eysize = vp_door_3->height;
8016 int new_tilesize_var =
8017 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8019 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8020 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8021 int new_scr_fieldx = new_sxsize / tilesize;
8022 int new_scr_fieldy = new_sysize / tilesize;
8023 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8024 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8025 boolean init_gfx_buffers = FALSE;
8026 boolean init_video_buffer = FALSE;
8027 boolean init_gadgets_and_toons = FALSE;
8028 boolean init_em_graphics = FALSE;
8029 boolean drawing_area_changed = FALSE;
8031 if (viewport.window.width != WIN_XSIZE ||
8032 viewport.window.height != WIN_YSIZE)
8034 WIN_XSIZE = viewport.window.width;
8035 WIN_YSIZE = viewport.window.height;
8037 init_video_buffer = TRUE;
8038 init_gfx_buffers = TRUE;
8040 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8043 if (new_scr_fieldx != SCR_FIELDX ||
8044 new_scr_fieldy != SCR_FIELDY)
8046 /* this always toggles between MAIN and GAME when using small tile size */
8048 SCR_FIELDX = new_scr_fieldx;
8049 SCR_FIELDY = new_scr_fieldy;
8051 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8062 new_sxsize != SXSIZE ||
8063 new_sysize != SYSIZE ||
8064 new_dxsize != DXSIZE ||
8065 new_dysize != DYSIZE ||
8066 new_vxsize != VXSIZE ||
8067 new_vysize != VYSIZE ||
8068 new_exsize != EXSIZE ||
8069 new_eysize != EYSIZE ||
8070 new_real_sx != REAL_SX ||
8071 new_real_sy != REAL_SY ||
8072 new_full_sxsize != FULL_SXSIZE ||
8073 new_full_sysize != FULL_SYSIZE ||
8074 new_tilesize_var != TILESIZE_VAR
8077 if (new_tilesize_var != TILESIZE_VAR)
8079 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8081 // changing tile size invalidates scroll values of engine snapshots
8082 FreeEngineSnapshot();
8084 // changing tile size requires update of graphic mapping for EM engine
8085 init_em_graphics = TRUE;
8090 new_sxsize != SXSIZE ||
8091 new_sysize != SYSIZE ||
8092 new_real_sx != REAL_SX ||
8093 new_real_sy != REAL_SY ||
8094 new_full_sxsize != FULL_SXSIZE ||
8095 new_full_sysize != FULL_SYSIZE)
8097 if (!init_video_buffer)
8098 drawing_area_changed = TRUE;
8109 SXSIZE = new_sxsize;
8110 SYSIZE = new_sysize;
8111 DXSIZE = new_dxsize;
8112 DYSIZE = new_dysize;
8113 VXSIZE = new_vxsize;
8114 VYSIZE = new_vysize;
8115 EXSIZE = new_exsize;
8116 EYSIZE = new_eysize;
8117 REAL_SX = new_real_sx;
8118 REAL_SY = new_real_sy;
8119 FULL_SXSIZE = new_full_sxsize;
8120 FULL_SYSIZE = new_full_sysize;
8121 TILESIZE_VAR = new_tilesize_var;
8123 init_gfx_buffers = TRUE;
8124 init_gadgets_and_toons = TRUE;
8126 // printf("::: viewports: init_gfx_buffers\n");
8127 // printf("::: viewports: init_gadgets_and_toons\n");
8130 if (init_gfx_buffers)
8132 // printf("::: init_gfx_buffers\n");
8134 SCR_FIELDX = new_scr_fieldx_buffers;
8135 SCR_FIELDY = new_scr_fieldy_buffers;
8139 SCR_FIELDX = new_scr_fieldx;
8140 SCR_FIELDY = new_scr_fieldy;
8142 gfx.drawing_area_changed = drawing_area_changed;
8144 SetDrawDeactivationMask(REDRAW_NONE);
8145 SetDrawBackgroundMask(REDRAW_FIELD);
8148 if (init_video_buffer)
8150 // printf("::: init_video_buffer\n");
8152 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8155 if (init_gadgets_and_toons)
8157 // printf("::: init_gadgets_and_toons\n");
8163 if (init_em_graphics)
8165 InitGraphicInfo_EM();