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) / 2;
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);
2143 int end = MAX(xend - xstart, yend - ystart);
2146 for (i = start; i <= end; i++)
2148 int last_frame = end; // last frame of this "for" loop
2149 int x = xstart + i * xstep;
2150 int y = ystart + i * ystep;
2151 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2152 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2153 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2154 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2157 SetDrawtoField(DRAW_BUFFERED);
2159 BlitScreenToBitmap(backbuffer);
2161 SetDrawtoField(DRAW_BACKBUFFER);
2163 for (yy = 0; yy < ysize; yy++)
2164 for (xx = 0; xx < xsize; xx++)
2165 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2167 DrawTextBuffer(sx + font_width, sy + font_height,
2168 level.envelope[envelope_nr].text, font_nr, max_xsize,
2169 xsize - 2, ysize - 2, 0, mask_mode,
2170 level.envelope[envelope_nr].autowrap,
2171 level.envelope[envelope_nr].centered, FALSE);
2173 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2176 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2180 void ShowEnvelope(int envelope_nr)
2182 int element = EL_ENVELOPE_1 + envelope_nr;
2183 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2184 int sound_opening = element_info[element].sound[ACTION_OPENING];
2185 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2186 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2187 boolean no_delay = (tape.warp_forward);
2188 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2189 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2190 int anim_mode = graphic_info[graphic].anim_mode;
2191 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2192 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2194 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2196 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2198 if (anim_mode == ANIM_DEFAULT)
2199 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2201 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2204 Delay(wait_delay_value);
2206 WaitForEventToContinue();
2208 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2210 if (anim_mode != ANIM_NONE)
2211 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2213 if (anim_mode == ANIM_DEFAULT)
2214 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2216 game.envelope_active = FALSE;
2218 SetDrawtoField(DRAW_BUFFERED);
2220 redraw_mask |= REDRAW_FIELD;
2224 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2226 int border_size = request.border_size;
2227 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2228 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2229 int sx = sx_center - request.width / 2;
2230 int sy = sy_center - request.height / 2;
2232 if (add_border_size)
2242 void DrawEnvelopeRequest(char *text)
2244 char *text_final = text;
2245 char *text_door_style = NULL;
2246 int graphic = IMG_BACKGROUND_REQUEST;
2247 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2248 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2249 int font_nr = FONT_REQUEST;
2250 int font_width = getFontWidth(font_nr);
2251 int font_height = getFontHeight(font_nr);
2252 int border_size = request.border_size;
2253 int line_spacing = request.line_spacing;
2254 int line_height = font_height + line_spacing;
2255 int text_width = request.width - 2 * border_size;
2256 int text_height = request.height - 2 * border_size;
2257 int line_length = text_width / font_width;
2258 int max_lines = text_height / line_height;
2259 int width = request.width;
2260 int height = request.height;
2261 int tile_size = request.step_offset;
2262 int x_steps = width / tile_size;
2263 int y_steps = height / tile_size;
2267 if (request.wrap_single_words)
2269 char *src_text_ptr, *dst_text_ptr;
2271 text_door_style = checked_malloc(2 * strlen(text) + 1);
2273 src_text_ptr = text;
2274 dst_text_ptr = text_door_style;
2276 while (*src_text_ptr)
2278 if (*src_text_ptr == ' ' ||
2279 *src_text_ptr == '?' ||
2280 *src_text_ptr == '!')
2281 *dst_text_ptr++ = '\n';
2283 if (*src_text_ptr != ' ')
2284 *dst_text_ptr++ = *src_text_ptr;
2289 *dst_text_ptr = '\0';
2291 text_final = text_door_style;
2294 setRequestPosition(&sx, &sy, FALSE);
2296 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2298 for (y = 0; y < y_steps; y++)
2299 for (x = 0; x < x_steps; x++)
2300 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2301 x, y, x_steps, y_steps,
2302 tile_size, tile_size);
2304 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2305 line_length, -1, max_lines, line_spacing, mask_mode,
2306 request.autowrap, request.centered, FALSE);
2308 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2309 RedrawGadget(tool_gadget[i]);
2311 // store readily prepared envelope request for later use when animating
2312 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2314 if (text_door_style)
2315 free(text_door_style);
2318 void AnimateEnvelopeRequest(int anim_mode, int action)
2320 int graphic = IMG_BACKGROUND_REQUEST;
2321 boolean draw_masked = graphic_info[graphic].draw_masked;
2322 int delay_value_normal = request.step_delay;
2323 int delay_value_fast = delay_value_normal / 2;
2324 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2325 boolean no_delay = (tape.warp_forward);
2326 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2327 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2328 unsigned int anim_delay = 0;
2330 int width = request.width;
2331 int height = request.height;
2332 int tile_size = request.step_offset;
2333 int max_xsize = width / tile_size;
2334 int max_ysize = height / tile_size;
2335 int max_xsize_inner = max_xsize - 2;
2336 int max_ysize_inner = max_ysize - 2;
2338 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2339 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2340 int xend = max_xsize_inner;
2341 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2342 int xstep = (xstart < xend ? 1 : 0);
2343 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2345 int end = MAX(xend - xstart, yend - ystart);
2348 if (setup.quick_doors)
2356 if (action == ACTION_OPENING)
2357 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2358 else if (action == ACTION_CLOSING)
2359 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2362 for (i = start; i <= end; i++)
2364 int last_frame = end; // last frame of this "for" loop
2365 int x = xstart + i * xstep;
2366 int y = ystart + i * ystep;
2367 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2368 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2369 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2370 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2371 int src_x = sx_center - width / 2;
2372 int src_y = sy_center - height / 2;
2373 int dst_x = sx_center - xsize * tile_size / 2;
2374 int dst_y = sy_center - ysize * tile_size / 2;
2375 int xsize_size_left = (xsize - 1) * tile_size;
2376 int ysize_size_top = (ysize - 1) * tile_size;
2377 int max_xsize_pos = (max_xsize - 1) * tile_size;
2378 int max_ysize_pos = (max_ysize - 1) * tile_size;
2381 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2383 for (yy = 0; yy < 2; yy++)
2385 for (xx = 0; xx < 2; xx++)
2387 int src_xx = src_x + xx * max_xsize_pos;
2388 int src_yy = src_y + yy * max_ysize_pos;
2389 int dst_xx = dst_x + xx * xsize_size_left;
2390 int dst_yy = dst_y + yy * ysize_size_top;
2391 int xx_size = (xx ? tile_size : xsize_size_left);
2392 int yy_size = (yy ? tile_size : ysize_size_top);
2395 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2396 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2398 BlitBitmap(bitmap_db_cross, backbuffer,
2399 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2403 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2408 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2413 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2415 int last_game_status = game_status; /* save current game status */
2416 int graphic = IMG_BACKGROUND_REQUEST;
2417 int sound_opening = SND_REQUEST_OPENING;
2418 int sound_closing = SND_REQUEST_CLOSING;
2419 int anim_mode = graphic_info[graphic].anim_mode;
2420 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2421 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2423 if (game_status == GAME_MODE_PLAYING)
2424 BlitScreenToBitmap(backbuffer);
2426 SetDrawtoField(DRAW_BACKBUFFER);
2428 // SetDrawBackgroundMask(REDRAW_NONE);
2430 if (action == ACTION_OPENING)
2432 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2434 if (req_state & REQ_ASK)
2436 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2437 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2439 else if (req_state & REQ_CONFIRM)
2441 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2443 else if (req_state & REQ_PLAYER)
2445 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2446 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2447 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2448 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2451 DrawEnvelopeRequest(text);
2453 if (game_status != GAME_MODE_MAIN)
2457 /* force DOOR font inside door area */
2458 game_status = GAME_MODE_PSEUDO_DOOR;
2460 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2462 if (action == ACTION_OPENING)
2464 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2466 if (anim_mode == ANIM_DEFAULT)
2467 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2469 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2474 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2476 if (anim_mode != ANIM_NONE)
2477 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2479 if (anim_mode == ANIM_DEFAULT)
2480 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2483 game.envelope_active = FALSE;
2485 game_status = last_game_status; /* restore current game status */
2487 if (action == ACTION_CLOSING)
2489 if (game_status != GAME_MODE_MAIN)
2492 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2495 // SetDrawBackgroundMask(last_draw_background_mask);
2497 redraw_mask |= REDRAW_FIELD;
2499 if (game_status == GAME_MODE_MAIN)
2504 if (action == ACTION_CLOSING &&
2505 game_status == GAME_MODE_PLAYING &&
2506 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2507 SetDrawtoField(DRAW_BUFFERED);
2510 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2514 int graphic = el2preimg(element);
2516 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2517 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2520 void DrawLevel(int draw_background_mask)
2524 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2525 SetDrawBackgroundMask(draw_background_mask);
2529 for (x = BX1; x <= BX2; x++)
2530 for (y = BY1; y <= BY2; y++)
2531 DrawScreenField(x, y);
2533 redraw_mask |= REDRAW_FIELD;
2536 void DrawSizedLevel(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 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2545 redraw_mask |= REDRAW_FIELD;
2548 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2552 for (x = 0; x < size_x; x++)
2553 for (y = 0; y < size_y; y++)
2554 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2556 redraw_mask |= REDRAW_FIELD;
2559 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2561 boolean show_level_border = (BorderElement != EL_EMPTY);
2562 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2563 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2564 int tile_size = preview.tile_size;
2565 int preview_width = preview.xsize * tile_size;
2566 int preview_height = preview.ysize * tile_size;
2567 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2568 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2569 int real_preview_width = real_preview_xsize * tile_size;
2570 int real_preview_height = real_preview_ysize * tile_size;
2571 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2572 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2575 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2578 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2580 dst_x += (preview_width - real_preview_width) / 2;
2581 dst_y += (preview_height - real_preview_height) / 2;
2583 for (x = 0; x < real_preview_xsize; x++)
2585 for (y = 0; y < real_preview_ysize; y++)
2587 int lx = from_x + x + (show_level_border ? -1 : 0);
2588 int ly = from_y + y + (show_level_border ? -1 : 0);
2589 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2590 getBorderElement(lx, ly));
2592 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2593 element, tile_size);
2597 redraw_mask |= REDRAW_MICROLEVEL;
2600 #define MICROLABEL_EMPTY 0
2601 #define MICROLABEL_LEVEL_NAME 1
2602 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2603 #define MICROLABEL_LEVEL_AUTHOR 3
2604 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2605 #define MICROLABEL_IMPORTED_FROM 5
2606 #define MICROLABEL_IMPORTED_BY_HEAD 6
2607 #define MICROLABEL_IMPORTED_BY 7
2609 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2611 int max_text_width = SXSIZE;
2612 int font_width = getFontWidth(font_nr);
2614 if (pos->align == ALIGN_CENTER)
2615 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2616 else if (pos->align == ALIGN_RIGHT)
2617 max_text_width = pos->x;
2619 max_text_width = SXSIZE - pos->x;
2621 return max_text_width / font_width;
2624 static void DrawPreviewLevelLabelExt(int mode)
2626 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2627 char label_text[MAX_OUTPUT_LINESIZE + 1];
2628 int max_len_label_text;
2629 int font_nr = pos->font;
2632 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2635 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2636 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2637 mode == MICROLABEL_IMPORTED_BY_HEAD)
2638 font_nr = pos->font_alt;
2640 max_len_label_text = getMaxTextLength(pos, font_nr);
2642 if (pos->size != -1)
2643 max_len_label_text = pos->size;
2645 for (i = 0; i < max_len_label_text; i++)
2646 label_text[i] = ' ';
2647 label_text[max_len_label_text] = '\0';
2649 if (strlen(label_text) > 0)
2650 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2653 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2654 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2655 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2656 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2657 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2658 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2659 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2660 max_len_label_text);
2661 label_text[max_len_label_text] = '\0';
2663 if (strlen(label_text) > 0)
2664 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2666 redraw_mask |= REDRAW_MICROLEVEL;
2669 static void DrawPreviewLevelExt(boolean restart)
2671 static unsigned int scroll_delay = 0;
2672 static unsigned int label_delay = 0;
2673 static int from_x, from_y, scroll_direction;
2674 static int label_state, label_counter;
2675 unsigned int scroll_delay_value = preview.step_delay;
2676 boolean show_level_border = (BorderElement != EL_EMPTY);
2677 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2678 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2679 int last_game_status = game_status; /* save current game status */
2686 if (preview.anim_mode == ANIM_CENTERED)
2688 if (level_xsize > preview.xsize)
2689 from_x = (level_xsize - preview.xsize) / 2;
2690 if (level_ysize > preview.ysize)
2691 from_y = (level_ysize - preview.ysize) / 2;
2694 from_x += preview.xoffset;
2695 from_y += preview.yoffset;
2697 scroll_direction = MV_RIGHT;
2701 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2702 DrawPreviewLevelLabelExt(label_state);
2704 /* initialize delay counters */
2705 DelayReached(&scroll_delay, 0);
2706 DelayReached(&label_delay, 0);
2708 if (leveldir_current->name)
2710 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2711 char label_text[MAX_OUTPUT_LINESIZE + 1];
2712 int font_nr = pos->font;
2713 int max_len_label_text = getMaxTextLength(pos, font_nr);
2715 if (pos->size != -1)
2716 max_len_label_text = pos->size;
2718 strncpy(label_text, leveldir_current->name, max_len_label_text);
2719 label_text[max_len_label_text] = '\0';
2721 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2722 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2725 game_status = last_game_status; /* restore current game status */
2730 /* scroll preview level, if needed */
2731 if (preview.anim_mode != ANIM_NONE &&
2732 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2733 DelayReached(&scroll_delay, scroll_delay_value))
2735 switch (scroll_direction)
2740 from_x -= preview.step_offset;
2741 from_x = (from_x < 0 ? 0 : from_x);
2744 scroll_direction = MV_UP;
2748 if (from_x < level_xsize - preview.xsize)
2750 from_x += preview.step_offset;
2751 from_x = (from_x > level_xsize - preview.xsize ?
2752 level_xsize - preview.xsize : from_x);
2755 scroll_direction = MV_DOWN;
2761 from_y -= preview.step_offset;
2762 from_y = (from_y < 0 ? 0 : from_y);
2765 scroll_direction = MV_RIGHT;
2769 if (from_y < level_ysize - preview.ysize)
2771 from_y += preview.step_offset;
2772 from_y = (from_y > level_ysize - preview.ysize ?
2773 level_ysize - preview.ysize : from_y);
2776 scroll_direction = MV_LEFT;
2783 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2786 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2787 /* redraw micro level label, if needed */
2788 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2789 !strEqual(level.author, ANONYMOUS_NAME) &&
2790 !strEqual(level.author, leveldir_current->name) &&
2791 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2793 int max_label_counter = 23;
2795 if (leveldir_current->imported_from != NULL &&
2796 strlen(leveldir_current->imported_from) > 0)
2797 max_label_counter += 14;
2798 if (leveldir_current->imported_by != NULL &&
2799 strlen(leveldir_current->imported_by) > 0)
2800 max_label_counter += 14;
2802 label_counter = (label_counter + 1) % max_label_counter;
2803 label_state = (label_counter >= 0 && label_counter <= 7 ?
2804 MICROLABEL_LEVEL_NAME :
2805 label_counter >= 9 && label_counter <= 12 ?
2806 MICROLABEL_LEVEL_AUTHOR_HEAD :
2807 label_counter >= 14 && label_counter <= 21 ?
2808 MICROLABEL_LEVEL_AUTHOR :
2809 label_counter >= 23 && label_counter <= 26 ?
2810 MICROLABEL_IMPORTED_FROM_HEAD :
2811 label_counter >= 28 && label_counter <= 35 ?
2812 MICROLABEL_IMPORTED_FROM :
2813 label_counter >= 37 && label_counter <= 40 ?
2814 MICROLABEL_IMPORTED_BY_HEAD :
2815 label_counter >= 42 && label_counter <= 49 ?
2816 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2818 if (leveldir_current->imported_from == NULL &&
2819 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2820 label_state == MICROLABEL_IMPORTED_FROM))
2821 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2822 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2824 DrawPreviewLevelLabelExt(label_state);
2827 game_status = last_game_status; /* restore current game status */
2830 void DrawPreviewLevelInitial()
2832 DrawPreviewLevelExt(TRUE);
2835 void DrawPreviewLevelAnimation()
2837 DrawPreviewLevelExt(FALSE);
2840 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2841 int graphic, int sync_frame, int mask_mode)
2843 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2845 if (mask_mode == USE_MASKING)
2846 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2848 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2851 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2852 int graphic, int sync_frame,
2855 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2857 if (mask_mode == USE_MASKING)
2858 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2860 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2863 inline void DrawGraphicAnimation(int x, int y, int graphic)
2865 int lx = LEVELX(x), ly = LEVELY(y);
2867 if (!IN_SCR_FIELD(x, y))
2870 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2871 graphic, GfxFrame[lx][ly], NO_MASKING);
2873 MarkTileDirty(x, y);
2876 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2878 int lx = LEVELX(x), ly = LEVELY(y);
2880 if (!IN_SCR_FIELD(x, y))
2883 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2884 graphic, GfxFrame[lx][ly], NO_MASKING);
2885 MarkTileDirty(x, y);
2888 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2890 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2893 void DrawLevelElementAnimation(int x, int y, int element)
2895 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2897 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2900 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2902 int sx = SCREENX(x), sy = SCREENY(y);
2904 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2907 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2910 DrawGraphicAnimation(sx, sy, graphic);
2913 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2914 DrawLevelFieldCrumbled(x, y);
2916 if (GFX_CRUMBLED(Feld[x][y]))
2917 DrawLevelFieldCrumbled(x, y);
2921 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2923 int sx = SCREENX(x), sy = SCREENY(y);
2926 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2929 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2931 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2934 DrawGraphicAnimation(sx, sy, graphic);
2936 if (GFX_CRUMBLED(element))
2937 DrawLevelFieldCrumbled(x, y);
2940 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2942 if (player->use_murphy)
2944 /* this works only because currently only one player can be "murphy" ... */
2945 static int last_horizontal_dir = MV_LEFT;
2946 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2948 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2949 last_horizontal_dir = move_dir;
2951 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2953 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2955 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2961 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2964 static boolean equalGraphics(int graphic1, int graphic2)
2966 struct GraphicInfo *g1 = &graphic_info[graphic1];
2967 struct GraphicInfo *g2 = &graphic_info[graphic2];
2969 return (g1->bitmap == g2->bitmap &&
2970 g1->src_x == g2->src_x &&
2971 g1->src_y == g2->src_y &&
2972 g1->anim_frames == g2->anim_frames &&
2973 g1->anim_delay == g2->anim_delay &&
2974 g1->anim_mode == g2->anim_mode);
2977 void DrawAllPlayers()
2981 for (i = 0; i < MAX_PLAYERS; i++)
2982 if (stored_player[i].active)
2983 DrawPlayer(&stored_player[i]);
2986 void DrawPlayerField(int x, int y)
2988 if (!IS_PLAYER(x, y))
2991 DrawPlayer(PLAYERINFO(x, y));
2994 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
2996 void DrawPlayer(struct PlayerInfo *player)
2998 int jx = player->jx;
2999 int jy = player->jy;
3000 int move_dir = player->MovDir;
3001 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3002 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3003 int last_jx = (player->is_moving ? jx - dx : jx);
3004 int last_jy = (player->is_moving ? jy - dy : jy);
3005 int next_jx = jx + dx;
3006 int next_jy = jy + dy;
3007 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3008 boolean player_is_opaque = FALSE;
3009 int sx = SCREENX(jx), sy = SCREENY(jy);
3010 int sxx = 0, syy = 0;
3011 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3013 int action = ACTION_DEFAULT;
3014 int last_player_graphic = getPlayerGraphic(player, move_dir);
3015 int last_player_frame = player->Frame;
3018 /* GfxElement[][] is set to the element the player is digging or collecting;
3019 remove also for off-screen player if the player is not moving anymore */
3020 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3021 GfxElement[jx][jy] = EL_UNDEFINED;
3023 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3027 if (!IN_LEV_FIELD(jx, jy))
3029 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3030 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3031 printf("DrawPlayerField(): This should never happen!\n");
3036 if (element == EL_EXPLOSION)
3039 action = (player->is_pushing ? ACTION_PUSHING :
3040 player->is_digging ? ACTION_DIGGING :
3041 player->is_collecting ? ACTION_COLLECTING :
3042 player->is_moving ? ACTION_MOVING :
3043 player->is_snapping ? ACTION_SNAPPING :
3044 player->is_dropping ? ACTION_DROPPING :
3045 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3047 if (player->is_waiting)
3048 move_dir = player->dir_waiting;
3050 InitPlayerGfxAnimation(player, action, move_dir);
3052 /* ----------------------------------------------------------------------- */
3053 /* draw things in the field the player is leaving, if needed */
3054 /* ----------------------------------------------------------------------- */
3056 if (player->is_moving)
3058 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3060 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3062 if (last_element == EL_DYNAMITE_ACTIVE ||
3063 last_element == EL_EM_DYNAMITE_ACTIVE ||
3064 last_element == EL_SP_DISK_RED_ACTIVE)
3065 DrawDynamite(last_jx, last_jy);
3067 DrawLevelFieldThruMask(last_jx, last_jy);
3069 else if (last_element == EL_DYNAMITE_ACTIVE ||
3070 last_element == EL_EM_DYNAMITE_ACTIVE ||
3071 last_element == EL_SP_DISK_RED_ACTIVE)
3072 DrawDynamite(last_jx, last_jy);
3074 /* !!! this is not enough to prevent flickering of players which are
3075 moving next to each others without a free tile between them -- this
3076 can only be solved by drawing all players layer by layer (first the
3077 background, then the foreground etc.) !!! => TODO */
3078 else if (!IS_PLAYER(last_jx, last_jy))
3079 DrawLevelField(last_jx, last_jy);
3082 DrawLevelField(last_jx, last_jy);
3085 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3086 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3089 if (!IN_SCR_FIELD(sx, sy))
3092 /* ----------------------------------------------------------------------- */
3093 /* draw things behind the player, if needed */
3094 /* ----------------------------------------------------------------------- */
3097 DrawLevelElement(jx, jy, Back[jx][jy]);
3098 else if (IS_ACTIVE_BOMB(element))
3099 DrawLevelElement(jx, jy, EL_EMPTY);
3102 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3104 int old_element = GfxElement[jx][jy];
3105 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3106 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3108 if (GFX_CRUMBLED(old_element))
3109 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3111 DrawGraphic(sx, sy, old_graphic, frame);
3113 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3114 player_is_opaque = TRUE;
3118 GfxElement[jx][jy] = EL_UNDEFINED;
3120 /* make sure that pushed elements are drawn with correct frame rate */
3121 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3123 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3124 GfxFrame[jx][jy] = player->StepFrame;
3126 DrawLevelField(jx, jy);
3130 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3131 /* ----------------------------------------------------------------------- */
3132 /* draw player himself */
3133 /* ----------------------------------------------------------------------- */
3135 graphic = getPlayerGraphic(player, move_dir);
3137 /* in the case of changed player action or direction, prevent the current
3138 animation frame from being restarted for identical animations */
3139 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3140 player->Frame = last_player_frame;
3142 frame = getGraphicAnimationFrame(graphic, player->Frame);
3146 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3147 sxx = player->GfxPos;
3149 syy = player->GfxPos;
3152 if (!setup.soft_scrolling && ScreenMovPos)
3155 if (player_is_opaque)
3156 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3158 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3160 if (SHIELD_ON(player))
3162 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3163 IMG_SHIELD_NORMAL_ACTIVE);
3164 int frame = getGraphicAnimationFrame(graphic, -1);
3166 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3170 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3173 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3174 sxx = player->GfxPos;
3176 syy = player->GfxPos;
3180 /* ----------------------------------------------------------------------- */
3181 /* draw things the player is pushing, if needed */
3182 /* ----------------------------------------------------------------------- */
3184 if (player->is_pushing && player->is_moving)
3186 int px = SCREENX(jx), py = SCREENY(jy);
3187 int pxx = (TILEX - ABS(sxx)) * dx;
3188 int pyy = (TILEY - ABS(syy)) * dy;
3189 int gfx_frame = GfxFrame[jx][jy];
3195 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3197 element = Feld[next_jx][next_jy];
3198 gfx_frame = GfxFrame[next_jx][next_jy];
3201 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3203 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3204 frame = getGraphicAnimationFrame(graphic, sync_frame);
3206 /* draw background element under pushed element (like the Sokoban field) */
3207 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3209 /* this allows transparent pushing animation over non-black background */
3212 DrawLevelElement(jx, jy, Back[jx][jy]);
3214 DrawLevelElement(jx, jy, EL_EMPTY);
3216 if (Back[next_jx][next_jy])
3217 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3219 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3221 else if (Back[next_jx][next_jy])
3222 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3225 /* do not draw (EM style) pushing animation when pushing is finished */
3226 /* (two-tile animations usually do not contain start and end frame) */
3227 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3228 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3230 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3232 /* masked drawing is needed for EMC style (double) movement graphics */
3233 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3234 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3238 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3239 /* ----------------------------------------------------------------------- */
3240 /* draw player himself */
3241 /* ----------------------------------------------------------------------- */
3243 graphic = getPlayerGraphic(player, move_dir);
3245 /* in the case of changed player action or direction, prevent the current
3246 animation frame from being restarted for identical animations */
3247 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3248 player->Frame = last_player_frame;
3250 frame = getGraphicAnimationFrame(graphic, player->Frame);
3254 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3255 sxx = player->GfxPos;
3257 syy = player->GfxPos;
3260 if (!setup.soft_scrolling && ScreenMovPos)
3263 if (player_is_opaque)
3264 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3266 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3268 if (SHIELD_ON(player))
3270 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3271 IMG_SHIELD_NORMAL_ACTIVE);
3272 int frame = getGraphicAnimationFrame(graphic, -1);
3274 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3278 /* ----------------------------------------------------------------------- */
3279 /* draw things in front of player (active dynamite or dynabombs) */
3280 /* ----------------------------------------------------------------------- */
3282 if (IS_ACTIVE_BOMB(element))
3284 graphic = el2img(element);
3285 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3287 if (game.emulation == EMU_SUPAPLEX)
3288 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3290 DrawGraphicThruMask(sx, sy, graphic, frame);
3293 if (player_is_moving && last_element == EL_EXPLOSION)
3295 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3296 GfxElement[last_jx][last_jy] : EL_EMPTY);
3297 int graphic = el_act2img(element, ACTION_EXPLODING);
3298 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3299 int phase = ExplodePhase[last_jx][last_jy] - 1;
3300 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3303 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3306 /* ----------------------------------------------------------------------- */
3307 /* draw elements the player is just walking/passing through/under */
3308 /* ----------------------------------------------------------------------- */
3310 if (player_is_moving)
3312 /* handle the field the player is leaving ... */
3313 if (IS_ACCESSIBLE_INSIDE(last_element))
3314 DrawLevelField(last_jx, last_jy);
3315 else if (IS_ACCESSIBLE_UNDER(last_element))
3316 DrawLevelFieldThruMask(last_jx, last_jy);
3319 /* do not redraw accessible elements if the player is just pushing them */
3320 if (!player_is_moving || !player->is_pushing)
3322 /* ... and the field the player is entering */
3323 if (IS_ACCESSIBLE_INSIDE(element))
3324 DrawLevelField(jx, jy);
3325 else if (IS_ACCESSIBLE_UNDER(element))
3326 DrawLevelFieldThruMask(jx, jy);
3329 MarkTileDirty(sx, sy);
3332 /* ------------------------------------------------------------------------- */
3334 void WaitForEventToContinue()
3336 boolean still_wait = TRUE;
3338 /* simulate releasing mouse button over last gadget, if still pressed */
3340 HandleGadgets(-1, -1, 0);
3342 button_status = MB_RELEASED;
3356 case EVENT_BUTTONPRESS:
3357 case EVENT_KEYPRESS:
3361 case EVENT_KEYRELEASE:
3362 ClearPlayerAction();
3366 HandleOtherEvents(&event);
3370 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3377 /* don't eat all CPU time */
3382 #define MAX_REQUEST_LINES 13
3383 #define MAX_REQUEST_LINE_FONT1_LEN 7
3384 #define MAX_REQUEST_LINE_FONT2_LEN 10
3386 static int RequestHandleEvents(unsigned int req_state)
3388 int last_game_status = game_status; /* save current game status */
3392 button_status = MB_RELEASED;
3394 request_gadget_id = -1;
3403 while (NextValidEvent(&event))
3407 case EVENT_BUTTONPRESS:
3408 case EVENT_BUTTONRELEASE:
3409 case EVENT_MOTIONNOTIFY:
3411 if (event.type == EVENT_MOTIONNOTIFY)
3413 if (!PointerInWindow(window))
3414 continue; /* window and pointer on different screens */
3419 motion_status = TRUE;
3420 mx = ((MotionEvent *) &event)->x;
3421 my = ((MotionEvent *) &event)->y;
3425 motion_status = FALSE;
3426 mx = ((ButtonEvent *) &event)->x;
3427 my = ((ButtonEvent *) &event)->y;
3428 if (event.type == EVENT_BUTTONPRESS)
3429 button_status = ((ButtonEvent *) &event)->button;
3431 button_status = MB_RELEASED;
3434 /* this sets 'request_gadget_id' */
3435 HandleGadgets(mx, my, button_status);
3437 switch (request_gadget_id)
3439 case TOOL_CTRL_ID_YES:
3442 case TOOL_CTRL_ID_NO:
3445 case TOOL_CTRL_ID_CONFIRM:
3446 result = TRUE | FALSE;
3449 case TOOL_CTRL_ID_PLAYER_1:
3452 case TOOL_CTRL_ID_PLAYER_2:
3455 case TOOL_CTRL_ID_PLAYER_3:
3458 case TOOL_CTRL_ID_PLAYER_4:
3469 case EVENT_KEYPRESS:
3470 switch (GetEventKey((KeyEvent *)&event, TRUE))
3473 if (req_state & REQ_CONFIRM)
3478 #if defined(TARGET_SDL2)
3485 #if defined(TARGET_SDL2)
3495 if (req_state & REQ_PLAYER)
3499 case EVENT_KEYRELEASE:
3500 ClearPlayerAction();
3504 HandleOtherEvents(&event);
3509 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3511 int joy = AnyJoystick();
3513 if (joy & JOY_BUTTON_1)
3515 else if (joy & JOY_BUTTON_2)
3519 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3521 HandleGameActions();
3527 if (!PendingEvent()) /* delay only if no pending events */
3531 game_status = GAME_MODE_PSEUDO_DOOR;
3535 game_status = last_game_status; /* restore current game status */
3541 static boolean RequestDoor(char *text, unsigned int req_state)
3543 unsigned int old_door_state;
3544 int last_game_status = game_status; /* save current game status */
3545 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3546 int font_nr = FONT_TEXT_2;
3551 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3553 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3554 font_nr = FONT_TEXT_1;
3557 if (game_status == GAME_MODE_PLAYING)
3558 BlitScreenToBitmap(backbuffer);
3560 /* disable deactivated drawing when quick-loading level tape recording */
3561 if (tape.playing && tape.deactivate_display)
3562 TapeDeactivateDisplayOff(TRUE);
3564 SetMouseCursor(CURSOR_DEFAULT);
3566 #if defined(NETWORK_AVALIABLE)
3567 /* pause network game while waiting for request to answer */
3568 if (options.network &&
3569 game_status == GAME_MODE_PLAYING &&
3570 req_state & REQUEST_WAIT_FOR_INPUT)
3571 SendToServer_PausePlaying();
3574 old_door_state = GetDoorState();
3576 /* simulate releasing mouse button over last gadget, if still pressed */
3578 HandleGadgets(-1, -1, 0);
3582 /* draw released gadget before proceeding */
3585 if (old_door_state & DOOR_OPEN_1)
3587 CloseDoor(DOOR_CLOSE_1);
3589 /* save old door content */
3590 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3591 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3594 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3595 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3597 /* clear door drawing field */
3598 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3600 /* force DOOR font inside door area */
3601 game_status = GAME_MODE_PSEUDO_DOOR;
3603 /* write text for request */
3604 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3606 char text_line[max_request_line_len + 1];
3612 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3614 tc = *(text_ptr + tx);
3615 // if (!tc || tc == ' ')
3616 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3620 if ((tc == '?' || tc == '!') && tl == 0)
3630 strncpy(text_line, text_ptr, tl);
3633 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3634 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3635 text_line, font_nr);
3637 text_ptr += tl + (tc == ' ' ? 1 : 0);
3638 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3641 game_status = last_game_status; /* restore current game status */
3643 if (req_state & REQ_ASK)
3645 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3646 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3648 else if (req_state & REQ_CONFIRM)
3650 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3652 else if (req_state & REQ_PLAYER)
3654 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3655 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3656 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3657 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3660 /* copy request gadgets to door backbuffer */
3661 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3663 OpenDoor(DOOR_OPEN_1);
3665 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3667 if (game_status == GAME_MODE_PLAYING)
3669 SetPanelBackground();
3670 SetDrawBackgroundMask(REDRAW_DOOR_1);
3674 SetDrawBackgroundMask(REDRAW_FIELD);
3680 if (game_status != GAME_MODE_MAIN)
3683 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3685 // ---------- handle request buttons ----------
3686 result = RequestHandleEvents(req_state);
3688 if (game_status != GAME_MODE_MAIN)
3693 if (!(req_state & REQ_STAY_OPEN))
3695 CloseDoor(DOOR_CLOSE_1);
3697 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3698 (req_state & REQ_REOPEN))
3699 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3704 if (game_status == GAME_MODE_PLAYING)
3706 SetPanelBackground();
3707 SetDrawBackgroundMask(REDRAW_DOOR_1);
3711 SetDrawBackgroundMask(REDRAW_FIELD);
3714 #if defined(NETWORK_AVALIABLE)
3715 /* continue network game after request */
3716 if (options.network &&
3717 game_status == GAME_MODE_PLAYING &&
3718 req_state & REQUEST_WAIT_FOR_INPUT)
3719 SendToServer_ContinuePlaying();
3722 /* restore deactivated drawing when quick-loading level tape recording */
3723 if (tape.playing && tape.deactivate_display)
3724 TapeDeactivateDisplayOn();
3729 static boolean RequestEnvelope(char *text, unsigned int req_state)
3733 if (game_status == GAME_MODE_PLAYING)
3734 BlitScreenToBitmap(backbuffer);
3736 /* disable deactivated drawing when quick-loading level tape recording */
3737 if (tape.playing && tape.deactivate_display)
3738 TapeDeactivateDisplayOff(TRUE);
3740 SetMouseCursor(CURSOR_DEFAULT);
3742 #if defined(NETWORK_AVALIABLE)
3743 /* pause network game while waiting for request to answer */
3744 if (options.network &&
3745 game_status == GAME_MODE_PLAYING &&
3746 req_state & REQUEST_WAIT_FOR_INPUT)
3747 SendToServer_PausePlaying();
3750 /* simulate releasing mouse button over last gadget, if still pressed */
3752 HandleGadgets(-1, -1, 0);
3756 // (replace with setting corresponding request background)
3757 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3758 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3760 /* clear door drawing field */
3761 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3763 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3765 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3767 if (game_status == GAME_MODE_PLAYING)
3769 SetPanelBackground();
3770 SetDrawBackgroundMask(REDRAW_DOOR_1);
3774 SetDrawBackgroundMask(REDRAW_FIELD);
3780 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3782 // ---------- handle request buttons ----------
3783 result = RequestHandleEvents(req_state);
3785 if (game_status != GAME_MODE_MAIN)
3790 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3794 if (game_status == GAME_MODE_PLAYING)
3796 SetPanelBackground();
3797 SetDrawBackgroundMask(REDRAW_DOOR_1);
3801 SetDrawBackgroundMask(REDRAW_FIELD);
3804 #if defined(NETWORK_AVALIABLE)
3805 /* continue network game after request */
3806 if (options.network &&
3807 game_status == GAME_MODE_PLAYING &&
3808 req_state & REQUEST_WAIT_FOR_INPUT)
3809 SendToServer_ContinuePlaying();
3812 /* restore deactivated drawing when quick-loading level tape recording */
3813 if (tape.playing && tape.deactivate_display)
3814 TapeDeactivateDisplayOn();
3819 boolean Request(char *text, unsigned int req_state)
3821 if (global.use_envelope_request)
3822 return RequestEnvelope(text, req_state);
3824 return RequestDoor(text, req_state);
3827 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3829 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3830 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3833 if (dpo1->sort_priority != dpo2->sort_priority)
3834 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3836 compare_result = dpo1->nr - dpo2->nr;
3838 return compare_result;
3841 void InitGraphicCompatibilityInfo_Doors()
3847 struct DoorInfo *door;
3851 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3852 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3854 { -1, -1, -1, NULL }
3856 struct Rect door_rect_list[] =
3858 { DX, DY, DXSIZE, DYSIZE },
3859 { VX, VY, VXSIZE, VYSIZE }
3863 for (i = 0; doors[i].door_token != -1; i++)
3865 int door_token = doors[i].door_token;
3866 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3867 int part_1 = doors[i].part_1;
3868 int part_8 = doors[i].part_8;
3869 int part_2 = part_1 + 1;
3870 int part_3 = part_1 + 2;
3871 struct DoorInfo *door = doors[i].door;
3872 struct Rect *door_rect = &door_rect_list[door_index];
3873 boolean door_gfx_redefined = FALSE;
3875 /* check if any door part graphic definitions have been redefined */
3877 for (j = 0; door_part_controls[j].door_token != -1; j++)
3879 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3880 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3882 if (dpc->door_token == door_token && fi->redefined)
3883 door_gfx_redefined = TRUE;
3886 /* check for old-style door graphic/animation modifications */
3888 if (!door_gfx_redefined)
3890 if (door->anim_mode & ANIM_STATIC_PANEL)
3892 door->panel.step_xoffset = 0;
3893 door->panel.step_yoffset = 0;
3896 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3898 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3899 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3900 int num_door_steps, num_panel_steps;
3902 /* remove door part graphics other than the two default wings */
3904 for (j = 0; door_part_controls[j].door_token != -1; j++)
3906 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3907 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3909 if (dpc->graphic >= part_3 &&
3910 dpc->graphic <= part_8)
3914 /* set graphics and screen positions of the default wings */
3916 g_part_1->width = door_rect->width;
3917 g_part_1->height = door_rect->height;
3918 g_part_2->width = door_rect->width;
3919 g_part_2->height = door_rect->height;
3920 g_part_2->src_x = door_rect->width;
3921 g_part_2->src_y = g_part_1->src_y;
3923 door->part_2.x = door->part_1.x;
3924 door->part_2.y = door->part_1.y;
3926 if (door->width != -1)
3928 g_part_1->width = door->width;
3929 g_part_2->width = door->width;
3931 // special treatment for graphics and screen position of right wing
3932 g_part_2->src_x += door_rect->width - door->width;
3933 door->part_2.x += door_rect->width - door->width;
3936 if (door->height != -1)
3938 g_part_1->height = door->height;
3939 g_part_2->height = door->height;
3941 // special treatment for graphics and screen position of bottom wing
3942 g_part_2->src_y += door_rect->height - door->height;
3943 door->part_2.y += door_rect->height - door->height;
3946 /* set animation delays for the default wings and panels */
3948 door->part_1.step_delay = door->step_delay;
3949 door->part_2.step_delay = door->step_delay;
3950 door->panel.step_delay = door->step_delay;
3952 /* set animation draw order for the default wings */
3954 door->part_1.sort_priority = 2; /* draw left wing over ... */
3955 door->part_2.sort_priority = 1; /* ... right wing */
3957 /* set animation draw offset for the default wings */
3959 if (door->anim_mode & ANIM_HORIZONTAL)
3961 door->part_1.step_xoffset = door->step_offset;
3962 door->part_1.step_yoffset = 0;
3963 door->part_2.step_xoffset = door->step_offset * -1;
3964 door->part_2.step_yoffset = 0;
3966 num_door_steps = g_part_1->width / door->step_offset;
3968 else // ANIM_VERTICAL
3970 door->part_1.step_xoffset = 0;
3971 door->part_1.step_yoffset = door->step_offset;
3972 door->part_2.step_xoffset = 0;
3973 door->part_2.step_yoffset = door->step_offset * -1;
3975 num_door_steps = g_part_1->height / door->step_offset;
3978 /* set animation draw offset for the default panels */
3980 if (door->step_offset > 1)
3982 num_panel_steps = 2 * door_rect->height / door->step_offset;
3983 door->panel.start_step = num_panel_steps - num_door_steps;
3984 door->panel.start_step_closing = door->panel.start_step;
3988 num_panel_steps = door_rect->height / door->step_offset;
3989 door->panel.start_step = num_panel_steps - num_door_steps / 2;
3990 door->panel.start_step_closing = door->panel.start_step;
3991 door->panel.step_delay *= 2;
4002 for (i = 0; door_part_controls[i].door_token != -1; i++)
4004 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4005 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4007 /* initialize "start_step_opening" and "start_step_closing", if needed */
4008 if (dpc->pos->start_step_opening == 0 &&
4009 dpc->pos->start_step_closing == 0)
4011 // dpc->pos->start_step_opening = dpc->pos->start_step;
4012 dpc->pos->start_step_closing = dpc->pos->start_step;
4015 /* fill structure for door part draw order (sorted below) */
4017 dpo->sort_priority = dpc->pos->sort_priority;
4020 /* sort door part controls according to sort_priority and graphic number */
4021 qsort(door_part_order, MAX_DOOR_PARTS,
4022 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4025 unsigned int OpenDoor(unsigned int door_state)
4027 if (door_state & DOOR_COPY_BACK)
4029 if (door_state & DOOR_OPEN_1)
4030 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4031 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4033 if (door_state & DOOR_OPEN_2)
4034 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4035 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4037 door_state &= ~DOOR_COPY_BACK;
4040 return MoveDoor(door_state);
4043 unsigned int CloseDoor(unsigned int door_state)
4045 unsigned int old_door_state = GetDoorState();
4047 if (!(door_state & DOOR_NO_COPY_BACK))
4049 if (old_door_state & DOOR_OPEN_1)
4050 BlitBitmap(backbuffer, bitmap_db_door_1,
4051 DX, DY, DXSIZE, DYSIZE, 0, 0);
4053 if (old_door_state & DOOR_OPEN_2)
4054 BlitBitmap(backbuffer, bitmap_db_door_2,
4055 VX, VY, VXSIZE, VYSIZE, 0, 0);
4057 door_state &= ~DOOR_NO_COPY_BACK;
4060 return MoveDoor(door_state);
4063 unsigned int GetDoorState()
4065 return MoveDoor(DOOR_GET_STATE);
4068 unsigned int SetDoorState(unsigned int door_state)
4070 return MoveDoor(door_state | DOOR_SET_STATE);
4073 int euclid(int a, int b)
4075 return (b ? euclid(b, a % b) : a);
4078 unsigned int MoveDoor(unsigned int door_state)
4080 struct Rect door_rect_list[] =
4082 { DX, DY, DXSIZE, DYSIZE },
4083 { VX, VY, VXSIZE, VYSIZE }
4085 static int door1 = DOOR_OPEN_1;
4086 static int door2 = DOOR_CLOSE_2;
4087 unsigned int door_delay = 0;
4088 unsigned int door_delay_value;
4091 if (door_state == DOOR_GET_STATE)
4092 return (door1 | door2);
4094 if (door_state & DOOR_SET_STATE)
4096 if (door_state & DOOR_ACTION_1)
4097 door1 = door_state & DOOR_ACTION_1;
4098 if (door_state & DOOR_ACTION_2)
4099 door2 = door_state & DOOR_ACTION_2;
4101 return (door1 | door2);
4104 if (!(door_state & DOOR_FORCE_REDRAW))
4106 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4107 door_state &= ~DOOR_OPEN_1;
4108 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4109 door_state &= ~DOOR_CLOSE_1;
4110 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4111 door_state &= ~DOOR_OPEN_2;
4112 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4113 door_state &= ~DOOR_CLOSE_2;
4116 if (global.autoplay_leveldir)
4118 door_state |= DOOR_NO_DELAY;
4119 door_state &= ~DOOR_CLOSE_ALL;
4122 if (game_status == GAME_MODE_EDITOR)
4123 door_state |= DOOR_NO_DELAY;
4125 if (door_state & DOOR_ACTION)
4127 boolean door_panel_drawn[NUM_DOORS];
4128 boolean panel_has_doors[NUM_DOORS];
4129 boolean door_part_skip[MAX_DOOR_PARTS];
4130 boolean door_part_done[MAX_DOOR_PARTS];
4131 boolean door_part_done_all;
4132 int num_steps[MAX_DOOR_PARTS];
4133 int max_move_delay = 0; // delay for complete animations of all doors
4134 int max_step_delay = 0; // delay (ms) between two animation frames
4135 int num_move_steps = 0; // number of animation steps for all doors
4136 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4137 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4138 int current_move_delay = 0;
4142 for (i = 0; i < NUM_DOORS; i++)
4143 panel_has_doors[i] = FALSE;
4145 for (i = 0; i < MAX_DOOR_PARTS; i++)
4147 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4148 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4149 int door_token = dpc->door_token;
4151 door_part_done[i] = FALSE;
4152 door_part_skip[i] = (!(door_state & door_token) ||
4156 for (i = 0; i < MAX_DOOR_PARTS; i++)
4158 int nr = door_part_order[i].nr;
4159 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4160 struct DoorPartPosInfo *pos = dpc->pos;
4161 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4162 int door_token = dpc->door_token;
4163 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4164 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4165 int step_xoffset = ABS(pos->step_xoffset);
4166 int step_yoffset = ABS(pos->step_yoffset);
4167 int step_delay = pos->step_delay;
4168 int current_door_state = door_state & door_token;
4169 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4170 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4171 boolean part_opening = (is_panel ? door_closing : door_opening);
4172 int start_step = (part_opening ? pos->start_step_opening :
4173 pos->start_step_closing);
4174 float move_xsize = (step_xoffset ? g->width : 0);
4175 float move_ysize = (step_yoffset ? g->height : 0);
4176 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4177 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4178 int move_steps = (move_xsteps && move_ysteps ?
4179 MIN(move_xsteps, move_ysteps) :
4180 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4181 int move_delay = move_steps * step_delay;
4183 if (door_part_skip[nr])
4186 max_move_delay = MAX(max_move_delay, move_delay);
4187 max_step_delay = (max_step_delay == 0 ? step_delay :
4188 euclid(max_step_delay, step_delay));
4189 num_steps[nr] = move_steps;
4193 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4195 panel_has_doors[door_index] = TRUE;
4199 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4201 num_move_steps = max_move_delay / max_step_delay;
4202 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4204 door_delay_value = max_step_delay;
4206 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4208 start = num_move_steps - 1;
4212 /* opening door sound has priority over simultaneously closing door */
4213 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4214 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4215 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4216 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4219 for (k = start; k < num_move_steps; k++)
4221 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4223 door_part_done_all = TRUE;
4225 for (i = 0; i < NUM_DOORS; i++)
4226 door_panel_drawn[i] = FALSE;
4228 for (i = 0; i < MAX_DOOR_PARTS; i++)
4230 int nr = door_part_order[i].nr;
4231 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4232 struct DoorPartPosInfo *pos = dpc->pos;
4233 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4234 int door_token = dpc->door_token;
4235 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4236 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4237 boolean is_panel_and_door_has_closed = FALSE;
4238 struct Rect *door_rect = &door_rect_list[door_index];
4239 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4241 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4242 int current_door_state = door_state & door_token;
4243 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4244 boolean door_closing = !door_opening;
4245 boolean part_opening = (is_panel ? door_closing : door_opening);
4246 boolean part_closing = !part_opening;
4247 int start_step = (part_opening ? pos->start_step_opening :
4248 pos->start_step_closing);
4249 int step_delay = pos->step_delay;
4250 int step_factor = step_delay / max_step_delay;
4251 int k1 = (step_factor ? k / step_factor + 1 : k);
4252 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4253 int kk = MAX(0, k2);
4256 int src_x, src_y, src_xx, src_yy;
4257 int dst_x, dst_y, dst_xx, dst_yy;
4260 if (door_part_skip[nr])
4263 if (!(door_state & door_token))
4271 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4272 int kk_door = MAX(0, k2_door);
4273 int sync_frame = kk_door * door_delay_value;
4274 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4276 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4281 if (!door_panel_drawn[door_index])
4283 ClearRectangle(drawto, door_rect->x, door_rect->y,
4284 door_rect->width, door_rect->height);
4286 door_panel_drawn[door_index] = TRUE;
4289 // draw opening or closing door parts
4291 if (pos->step_xoffset < 0) // door part on right side
4294 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4297 if (dst_xx + width > door_rect->width)
4298 width = door_rect->width - dst_xx;
4300 else // door part on left side
4303 dst_xx = pos->x - kk * pos->step_xoffset;
4307 src_xx = ABS(dst_xx);
4311 width = g->width - src_xx;
4313 // printf("::: k == %d [%d] \n", k, start_step);
4316 if (pos->step_yoffset < 0) // door part on bottom side
4319 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4322 if (dst_yy + height > door_rect->height)
4323 height = door_rect->height - dst_yy;
4325 else // door part on top side
4328 dst_yy = pos->y - kk * pos->step_yoffset;
4332 src_yy = ABS(dst_yy);
4336 height = g->height - src_yy;
4339 src_x = g_src_x + src_xx;
4340 src_y = g_src_y + src_yy;
4342 dst_x = door_rect->x + dst_xx;
4343 dst_y = door_rect->y + dst_yy;
4345 is_panel_and_door_has_closed =
4348 panel_has_doors[door_index] &&
4349 k >= num_move_steps_doors_only - 1);
4351 if (width >= 0 && width <= g->width &&
4352 height >= 0 && height <= g->height &&
4353 !is_panel_and_door_has_closed)
4355 if (is_panel || !pos->draw_masked)
4356 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4359 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4363 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4365 if ((part_opening && (width < 0 || height < 0)) ||
4366 (part_closing && (width >= g->width && height >= g->height)))
4367 door_part_done[nr] = TRUE;
4369 // continue door part animations, but not panel after door has closed
4370 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4371 door_part_done_all = FALSE;
4374 if (!(door_state & DOOR_NO_DELAY))
4378 if (game_status == GAME_MODE_MAIN)
4381 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4383 current_move_delay += max_step_delay;
4386 if (door_part_done_all)
4391 if (door_state & DOOR_ACTION_1)
4392 door1 = door_state & DOOR_ACTION_1;
4393 if (door_state & DOOR_ACTION_2)
4394 door2 = door_state & DOOR_ACTION_2;
4396 return (door1 | door2);
4399 void DrawSpecialEditorDoor()
4401 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4402 int top_border_width = gfx1->width;
4403 int top_border_height = gfx1->height;
4404 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4405 int ex = EX - outer_border;
4406 int ey = EY - outer_border;
4407 int vy = VY - outer_border;
4408 int exsize = EXSIZE + 2 * outer_border;
4410 /* draw bigger level editor toolbox window */
4411 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4412 top_border_width, top_border_height, ex, ey - top_border_height);
4413 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4414 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4416 redraw_mask |= REDRAW_ALL;
4419 void UndrawSpecialEditorDoor()
4421 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4422 int top_border_width = gfx1->width;
4423 int top_border_height = gfx1->height;
4424 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4425 int ex = EX - outer_border;
4426 int ey = EY - outer_border;
4427 int ey_top = ey - top_border_height;
4428 int exsize = EXSIZE + 2 * outer_border;
4429 int eysize = EYSIZE + 2 * outer_border;
4431 /* draw normal tape recorder window */
4432 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4434 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4435 ex, ey_top, top_border_width, top_border_height,
4437 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4438 ex, ey, exsize, eysize, ex, ey);
4442 // if screen background is set to "[NONE]", clear editor toolbox window
4443 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4444 ClearRectangle(drawto, ex, ey, exsize, eysize);
4447 redraw_mask |= REDRAW_ALL;
4451 /* ---------- new tool button stuff ---------------------------------------- */
4456 struct TextPosInfo *pos;
4459 } toolbutton_info[NUM_TOOL_BUTTONS] =
4462 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4463 TOOL_CTRL_ID_YES, "yes"
4466 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4467 TOOL_CTRL_ID_NO, "no"
4470 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4471 TOOL_CTRL_ID_CONFIRM, "confirm"
4474 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4475 TOOL_CTRL_ID_PLAYER_1, "player 1"
4478 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4479 TOOL_CTRL_ID_PLAYER_2, "player 2"
4482 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4483 TOOL_CTRL_ID_PLAYER_3, "player 3"
4486 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4487 TOOL_CTRL_ID_PLAYER_4, "player 4"
4491 void CreateToolButtons()
4495 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4497 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4498 struct TextPosInfo *pos = toolbutton_info[i].pos;
4499 struct GadgetInfo *gi;
4500 Bitmap *deco_bitmap = None;
4501 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4502 unsigned int event_mask = GD_EVENT_RELEASED;
4505 int gd_x = gfx->src_x;
4506 int gd_y = gfx->src_y;
4507 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4508 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4511 if (global.use_envelope_request)
4512 setRequestPosition(&dx, &dy, TRUE);
4514 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4516 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4518 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4519 pos->size, &deco_bitmap, &deco_x, &deco_y);
4520 deco_xpos = (gfx->width - pos->size) / 2;
4521 deco_ypos = (gfx->height - pos->size) / 2;
4524 gi = CreateGadget(GDI_CUSTOM_ID, id,
4525 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4526 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4527 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4528 GDI_WIDTH, gfx->width,
4529 GDI_HEIGHT, gfx->height,
4530 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4531 GDI_STATE, GD_BUTTON_UNPRESSED,
4532 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4533 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4534 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4535 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4536 GDI_DECORATION_SIZE, pos->size, pos->size,
4537 GDI_DECORATION_SHIFTING, 1, 1,
4538 GDI_DIRECT_DRAW, FALSE,
4539 GDI_EVENT_MASK, event_mask,
4540 GDI_CALLBACK_ACTION, HandleToolButtons,
4544 Error(ERR_EXIT, "cannot create gadget");
4546 tool_gadget[id] = gi;
4550 void FreeToolButtons()
4554 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4555 FreeGadget(tool_gadget[i]);
4558 static void UnmapToolButtons()
4562 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4563 UnmapGadget(tool_gadget[i]);
4566 static void HandleToolButtons(struct GadgetInfo *gi)
4568 request_gadget_id = gi->custom_id;
4571 static struct Mapping_EM_to_RND_object
4574 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4575 boolean is_backside; /* backside of moving element */
4581 em_object_mapping_list[] =
4584 Xblank, TRUE, FALSE,
4588 Yacid_splash_eB, FALSE, FALSE,
4589 EL_ACID_SPLASH_RIGHT, -1, -1
4592 Yacid_splash_wB, FALSE, FALSE,
4593 EL_ACID_SPLASH_LEFT, -1, -1
4596 #ifdef EM_ENGINE_BAD_ROLL
4598 Xstone_force_e, FALSE, FALSE,
4599 EL_ROCK, -1, MV_BIT_RIGHT
4602 Xstone_force_w, FALSE, FALSE,
4603 EL_ROCK, -1, MV_BIT_LEFT
4606 Xnut_force_e, FALSE, FALSE,
4607 EL_NUT, -1, MV_BIT_RIGHT
4610 Xnut_force_w, FALSE, FALSE,
4611 EL_NUT, -1, MV_BIT_LEFT
4614 Xspring_force_e, FALSE, FALSE,
4615 EL_SPRING, -1, MV_BIT_RIGHT
4618 Xspring_force_w, FALSE, FALSE,
4619 EL_SPRING, -1, MV_BIT_LEFT
4622 Xemerald_force_e, FALSE, FALSE,
4623 EL_EMERALD, -1, MV_BIT_RIGHT
4626 Xemerald_force_w, FALSE, FALSE,
4627 EL_EMERALD, -1, MV_BIT_LEFT
4630 Xdiamond_force_e, FALSE, FALSE,
4631 EL_DIAMOND, -1, MV_BIT_RIGHT
4634 Xdiamond_force_w, FALSE, FALSE,
4635 EL_DIAMOND, -1, MV_BIT_LEFT
4638 Xbomb_force_e, FALSE, FALSE,
4639 EL_BOMB, -1, MV_BIT_RIGHT
4642 Xbomb_force_w, FALSE, FALSE,
4643 EL_BOMB, -1, MV_BIT_LEFT
4645 #endif /* EM_ENGINE_BAD_ROLL */
4648 Xstone, TRUE, FALSE,
4652 Xstone_pause, FALSE, FALSE,
4656 Xstone_fall, FALSE, FALSE,
4660 Ystone_s, FALSE, FALSE,
4661 EL_ROCK, ACTION_FALLING, -1
4664 Ystone_sB, FALSE, TRUE,
4665 EL_ROCK, ACTION_FALLING, -1
4668 Ystone_e, FALSE, FALSE,
4669 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4672 Ystone_eB, FALSE, TRUE,
4673 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4676 Ystone_w, FALSE, FALSE,
4677 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4680 Ystone_wB, FALSE, TRUE,
4681 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4688 Xnut_pause, FALSE, FALSE,
4692 Xnut_fall, FALSE, FALSE,
4696 Ynut_s, FALSE, FALSE,
4697 EL_NUT, ACTION_FALLING, -1
4700 Ynut_sB, FALSE, TRUE,
4701 EL_NUT, ACTION_FALLING, -1
4704 Ynut_e, FALSE, FALSE,
4705 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4708 Ynut_eB, FALSE, TRUE,
4709 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4712 Ynut_w, FALSE, FALSE,
4713 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4716 Ynut_wB, FALSE, TRUE,
4717 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4720 Xbug_n, TRUE, FALSE,
4724 Xbug_e, TRUE, FALSE,
4725 EL_BUG_RIGHT, -1, -1
4728 Xbug_s, TRUE, FALSE,
4732 Xbug_w, TRUE, FALSE,
4736 Xbug_gon, FALSE, FALSE,
4740 Xbug_goe, FALSE, FALSE,
4741 EL_BUG_RIGHT, -1, -1
4744 Xbug_gos, FALSE, FALSE,
4748 Xbug_gow, FALSE, FALSE,
4752 Ybug_n, FALSE, FALSE,
4753 EL_BUG, ACTION_MOVING, MV_BIT_UP
4756 Ybug_nB, FALSE, TRUE,
4757 EL_BUG, ACTION_MOVING, MV_BIT_UP
4760 Ybug_e, FALSE, FALSE,
4761 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4764 Ybug_eB, FALSE, TRUE,
4765 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4768 Ybug_s, FALSE, FALSE,
4769 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4772 Ybug_sB, FALSE, TRUE,
4773 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4776 Ybug_w, FALSE, FALSE,
4777 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4780 Ybug_wB, FALSE, TRUE,
4781 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4784 Ybug_w_n, FALSE, FALSE,
4785 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4788 Ybug_n_e, FALSE, FALSE,
4789 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4792 Ybug_e_s, FALSE, FALSE,
4793 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4796 Ybug_s_w, FALSE, FALSE,
4797 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4800 Ybug_e_n, FALSE, FALSE,
4801 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4804 Ybug_s_e, FALSE, FALSE,
4805 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4808 Ybug_w_s, FALSE, FALSE,
4809 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4812 Ybug_n_w, FALSE, FALSE,
4813 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4816 Ybug_stone, FALSE, FALSE,
4817 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4820 Ybug_spring, FALSE, FALSE,
4821 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4824 Xtank_n, TRUE, FALSE,
4825 EL_SPACESHIP_UP, -1, -1
4828 Xtank_e, TRUE, FALSE,
4829 EL_SPACESHIP_RIGHT, -1, -1
4832 Xtank_s, TRUE, FALSE,
4833 EL_SPACESHIP_DOWN, -1, -1
4836 Xtank_w, TRUE, FALSE,
4837 EL_SPACESHIP_LEFT, -1, -1
4840 Xtank_gon, FALSE, FALSE,
4841 EL_SPACESHIP_UP, -1, -1
4844 Xtank_goe, FALSE, FALSE,
4845 EL_SPACESHIP_RIGHT, -1, -1
4848 Xtank_gos, FALSE, FALSE,
4849 EL_SPACESHIP_DOWN, -1, -1
4852 Xtank_gow, FALSE, FALSE,
4853 EL_SPACESHIP_LEFT, -1, -1
4856 Ytank_n, FALSE, FALSE,
4857 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4860 Ytank_nB, FALSE, TRUE,
4861 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4864 Ytank_e, FALSE, FALSE,
4865 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4868 Ytank_eB, FALSE, TRUE,
4869 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4872 Ytank_s, FALSE, FALSE,
4873 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4876 Ytank_sB, FALSE, TRUE,
4877 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4880 Ytank_w, FALSE, FALSE,
4881 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4884 Ytank_wB, FALSE, TRUE,
4885 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4888 Ytank_w_n, FALSE, FALSE,
4889 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4892 Ytank_n_e, FALSE, FALSE,
4893 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4896 Ytank_e_s, FALSE, FALSE,
4897 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4900 Ytank_s_w, FALSE, FALSE,
4901 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4904 Ytank_e_n, FALSE, FALSE,
4905 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4908 Ytank_s_e, FALSE, FALSE,
4909 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4912 Ytank_w_s, FALSE, FALSE,
4913 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4916 Ytank_n_w, FALSE, FALSE,
4917 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4920 Ytank_stone, FALSE, FALSE,
4921 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4924 Ytank_spring, FALSE, FALSE,
4925 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4928 Xandroid, TRUE, FALSE,
4929 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4932 Xandroid_1_n, FALSE, FALSE,
4933 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4936 Xandroid_2_n, FALSE, FALSE,
4937 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4940 Xandroid_1_e, FALSE, FALSE,
4941 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4944 Xandroid_2_e, FALSE, FALSE,
4945 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4948 Xandroid_1_w, FALSE, FALSE,
4949 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4952 Xandroid_2_w, FALSE, FALSE,
4953 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4956 Xandroid_1_s, FALSE, FALSE,
4957 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4960 Xandroid_2_s, FALSE, FALSE,
4961 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4964 Yandroid_n, FALSE, FALSE,
4965 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4968 Yandroid_nB, FALSE, TRUE,
4969 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4972 Yandroid_ne, FALSE, FALSE,
4973 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4976 Yandroid_neB, FALSE, TRUE,
4977 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4980 Yandroid_e, FALSE, FALSE,
4981 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4984 Yandroid_eB, FALSE, TRUE,
4985 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4988 Yandroid_se, FALSE, FALSE,
4989 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4992 Yandroid_seB, FALSE, TRUE,
4993 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4996 Yandroid_s, FALSE, FALSE,
4997 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5000 Yandroid_sB, FALSE, TRUE,
5001 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5004 Yandroid_sw, FALSE, FALSE,
5005 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5008 Yandroid_swB, FALSE, TRUE,
5009 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5012 Yandroid_w, FALSE, FALSE,
5013 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5016 Yandroid_wB, FALSE, TRUE,
5017 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5020 Yandroid_nw, FALSE, FALSE,
5021 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5024 Yandroid_nwB, FALSE, TRUE,
5025 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5028 Xspring, TRUE, FALSE,
5032 Xspring_pause, FALSE, FALSE,
5036 Xspring_e, FALSE, FALSE,
5040 Xspring_w, FALSE, FALSE,
5044 Xspring_fall, FALSE, FALSE,
5048 Yspring_s, FALSE, FALSE,
5049 EL_SPRING, ACTION_FALLING, -1
5052 Yspring_sB, FALSE, TRUE,
5053 EL_SPRING, ACTION_FALLING, -1
5056 Yspring_e, FALSE, FALSE,
5057 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5060 Yspring_eB, FALSE, TRUE,
5061 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5064 Yspring_w, FALSE, FALSE,
5065 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5068 Yspring_wB, FALSE, TRUE,
5069 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5072 Yspring_kill_e, FALSE, FALSE,
5073 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5076 Yspring_kill_eB, FALSE, TRUE,
5077 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5080 Yspring_kill_w, FALSE, FALSE,
5081 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5084 Yspring_kill_wB, FALSE, TRUE,
5085 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5088 Xeater_n, TRUE, FALSE,
5089 EL_YAMYAM_UP, -1, -1
5092 Xeater_e, TRUE, FALSE,
5093 EL_YAMYAM_RIGHT, -1, -1
5096 Xeater_w, TRUE, FALSE,
5097 EL_YAMYAM_LEFT, -1, -1
5100 Xeater_s, TRUE, FALSE,
5101 EL_YAMYAM_DOWN, -1, -1
5104 Yeater_n, FALSE, FALSE,
5105 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5108 Yeater_nB, FALSE, TRUE,
5109 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5112 Yeater_e, FALSE, FALSE,
5113 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5116 Yeater_eB, FALSE, TRUE,
5117 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5120 Yeater_s, FALSE, FALSE,
5121 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5124 Yeater_sB, FALSE, TRUE,
5125 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5128 Yeater_w, FALSE, FALSE,
5129 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5132 Yeater_wB, FALSE, TRUE,
5133 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5136 Yeater_stone, FALSE, FALSE,
5137 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5140 Yeater_spring, FALSE, FALSE,
5141 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5144 Xalien, TRUE, FALSE,
5148 Xalien_pause, FALSE, FALSE,
5152 Yalien_n, FALSE, FALSE,
5153 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5156 Yalien_nB, FALSE, TRUE,
5157 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5160 Yalien_e, FALSE, FALSE,
5161 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5164 Yalien_eB, FALSE, TRUE,
5165 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5168 Yalien_s, FALSE, FALSE,
5169 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5172 Yalien_sB, FALSE, TRUE,
5173 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5176 Yalien_w, FALSE, FALSE,
5177 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5180 Yalien_wB, FALSE, TRUE,
5181 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5184 Yalien_stone, FALSE, FALSE,
5185 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5188 Yalien_spring, FALSE, FALSE,
5189 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5192 Xemerald, TRUE, FALSE,
5196 Xemerald_pause, FALSE, FALSE,
5200 Xemerald_fall, FALSE, FALSE,
5204 Xemerald_shine, FALSE, FALSE,
5205 EL_EMERALD, ACTION_TWINKLING, -1
5208 Yemerald_s, FALSE, FALSE,
5209 EL_EMERALD, ACTION_FALLING, -1
5212 Yemerald_sB, FALSE, TRUE,
5213 EL_EMERALD, ACTION_FALLING, -1
5216 Yemerald_e, FALSE, FALSE,
5217 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5220 Yemerald_eB, FALSE, TRUE,
5221 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5224 Yemerald_w, FALSE, FALSE,
5225 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5228 Yemerald_wB, FALSE, TRUE,
5229 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5232 Yemerald_eat, FALSE, FALSE,
5233 EL_EMERALD, ACTION_COLLECTING, -1
5236 Yemerald_stone, FALSE, FALSE,
5237 EL_NUT, ACTION_BREAKING, -1
5240 Xdiamond, TRUE, FALSE,
5244 Xdiamond_pause, FALSE, FALSE,
5248 Xdiamond_fall, FALSE, FALSE,
5252 Xdiamond_shine, FALSE, FALSE,
5253 EL_DIAMOND, ACTION_TWINKLING, -1
5256 Ydiamond_s, FALSE, FALSE,
5257 EL_DIAMOND, ACTION_FALLING, -1
5260 Ydiamond_sB, FALSE, TRUE,
5261 EL_DIAMOND, ACTION_FALLING, -1
5264 Ydiamond_e, FALSE, FALSE,
5265 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5268 Ydiamond_eB, FALSE, TRUE,
5269 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5272 Ydiamond_w, FALSE, FALSE,
5273 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5276 Ydiamond_wB, FALSE, TRUE,
5277 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5280 Ydiamond_eat, FALSE, FALSE,
5281 EL_DIAMOND, ACTION_COLLECTING, -1
5284 Ydiamond_stone, FALSE, FALSE,
5285 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5288 Xdrip_fall, TRUE, FALSE,
5289 EL_AMOEBA_DROP, -1, -1
5292 Xdrip_stretch, FALSE, FALSE,
5293 EL_AMOEBA_DROP, ACTION_FALLING, -1
5296 Xdrip_stretchB, FALSE, TRUE,
5297 EL_AMOEBA_DROP, ACTION_FALLING, -1
5300 Xdrip_eat, FALSE, FALSE,
5301 EL_AMOEBA_DROP, ACTION_GROWING, -1
5304 Ydrip_s1, FALSE, FALSE,
5305 EL_AMOEBA_DROP, ACTION_FALLING, -1
5308 Ydrip_s1B, FALSE, TRUE,
5309 EL_AMOEBA_DROP, ACTION_FALLING, -1
5312 Ydrip_s2, FALSE, FALSE,
5313 EL_AMOEBA_DROP, ACTION_FALLING, -1
5316 Ydrip_s2B, FALSE, TRUE,
5317 EL_AMOEBA_DROP, ACTION_FALLING, -1
5324 Xbomb_pause, FALSE, FALSE,
5328 Xbomb_fall, FALSE, FALSE,
5332 Ybomb_s, FALSE, FALSE,
5333 EL_BOMB, ACTION_FALLING, -1
5336 Ybomb_sB, FALSE, TRUE,
5337 EL_BOMB, ACTION_FALLING, -1
5340 Ybomb_e, FALSE, FALSE,
5341 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5344 Ybomb_eB, FALSE, TRUE,
5345 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5348 Ybomb_w, FALSE, FALSE,
5349 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5352 Ybomb_wB, FALSE, TRUE,
5353 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5356 Ybomb_eat, FALSE, FALSE,
5357 EL_BOMB, ACTION_ACTIVATING, -1
5360 Xballoon, TRUE, FALSE,
5364 Yballoon_n, FALSE, FALSE,
5365 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5368 Yballoon_nB, FALSE, TRUE,
5369 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5372 Yballoon_e, FALSE, FALSE,
5373 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5376 Yballoon_eB, FALSE, TRUE,
5377 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5380 Yballoon_s, FALSE, FALSE,
5381 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5384 Yballoon_sB, FALSE, TRUE,
5385 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5388 Yballoon_w, FALSE, FALSE,
5389 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5392 Yballoon_wB, FALSE, TRUE,
5393 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5396 Xgrass, TRUE, FALSE,
5397 EL_EMC_GRASS, -1, -1
5400 Ygrass_nB, FALSE, FALSE,
5401 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5404 Ygrass_eB, FALSE, FALSE,
5405 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5408 Ygrass_sB, FALSE, FALSE,
5409 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5412 Ygrass_wB, FALSE, FALSE,
5413 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5420 Ydirt_nB, FALSE, FALSE,
5421 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5424 Ydirt_eB, FALSE, FALSE,
5425 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5428 Ydirt_sB, FALSE, FALSE,
5429 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5432 Ydirt_wB, FALSE, FALSE,
5433 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5436 Xacid_ne, TRUE, FALSE,
5437 EL_ACID_POOL_TOPRIGHT, -1, -1
5440 Xacid_se, TRUE, FALSE,
5441 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5444 Xacid_s, TRUE, FALSE,
5445 EL_ACID_POOL_BOTTOM, -1, -1
5448 Xacid_sw, TRUE, FALSE,
5449 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5452 Xacid_nw, TRUE, FALSE,
5453 EL_ACID_POOL_TOPLEFT, -1, -1
5456 Xacid_1, TRUE, FALSE,
5460 Xacid_2, FALSE, FALSE,
5464 Xacid_3, FALSE, FALSE,
5468 Xacid_4, FALSE, FALSE,
5472 Xacid_5, FALSE, FALSE,
5476 Xacid_6, FALSE, FALSE,
5480 Xacid_7, FALSE, FALSE,
5484 Xacid_8, FALSE, FALSE,
5488 Xball_1, TRUE, FALSE,
5489 EL_EMC_MAGIC_BALL, -1, -1
5492 Xball_1B, FALSE, FALSE,
5493 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5496 Xball_2, FALSE, FALSE,
5497 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5500 Xball_2B, FALSE, FALSE,
5501 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5504 Yball_eat, FALSE, FALSE,
5505 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5508 Ykey_1_eat, FALSE, FALSE,
5509 EL_EM_KEY_1, ACTION_COLLECTING, -1
5512 Ykey_2_eat, FALSE, FALSE,
5513 EL_EM_KEY_2, ACTION_COLLECTING, -1
5516 Ykey_3_eat, FALSE, FALSE,
5517 EL_EM_KEY_3, ACTION_COLLECTING, -1
5520 Ykey_4_eat, FALSE, FALSE,
5521 EL_EM_KEY_4, ACTION_COLLECTING, -1
5524 Ykey_5_eat, FALSE, FALSE,
5525 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5528 Ykey_6_eat, FALSE, FALSE,
5529 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5532 Ykey_7_eat, FALSE, FALSE,
5533 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5536 Ykey_8_eat, FALSE, FALSE,
5537 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5540 Ylenses_eat, FALSE, FALSE,
5541 EL_EMC_LENSES, ACTION_COLLECTING, -1
5544 Ymagnify_eat, FALSE, FALSE,
5545 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5548 Ygrass_eat, FALSE, FALSE,
5549 EL_EMC_GRASS, ACTION_SNAPPING, -1
5552 Ydirt_eat, FALSE, FALSE,
5553 EL_SAND, ACTION_SNAPPING, -1
5556 Xgrow_ns, TRUE, FALSE,
5557 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5560 Ygrow_ns_eat, FALSE, FALSE,
5561 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5564 Xgrow_ew, TRUE, FALSE,
5565 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5568 Ygrow_ew_eat, FALSE, FALSE,
5569 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5572 Xwonderwall, TRUE, FALSE,
5573 EL_MAGIC_WALL, -1, -1
5576 XwonderwallB, FALSE, FALSE,
5577 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5580 Xamoeba_1, TRUE, FALSE,
5581 EL_AMOEBA_DRY, ACTION_OTHER, -1
5584 Xamoeba_2, FALSE, FALSE,
5585 EL_AMOEBA_DRY, ACTION_OTHER, -1
5588 Xamoeba_3, FALSE, FALSE,
5589 EL_AMOEBA_DRY, ACTION_OTHER, -1
5592 Xamoeba_4, FALSE, FALSE,
5593 EL_AMOEBA_DRY, ACTION_OTHER, -1
5596 Xamoeba_5, TRUE, FALSE,
5597 EL_AMOEBA_WET, ACTION_OTHER, -1
5600 Xamoeba_6, FALSE, FALSE,
5601 EL_AMOEBA_WET, ACTION_OTHER, -1
5604 Xamoeba_7, FALSE, FALSE,
5605 EL_AMOEBA_WET, ACTION_OTHER, -1
5608 Xamoeba_8, FALSE, FALSE,
5609 EL_AMOEBA_WET, ACTION_OTHER, -1
5612 Xdoor_1, TRUE, FALSE,
5613 EL_EM_GATE_1, -1, -1
5616 Xdoor_2, TRUE, FALSE,
5617 EL_EM_GATE_2, -1, -1
5620 Xdoor_3, TRUE, FALSE,
5621 EL_EM_GATE_3, -1, -1
5624 Xdoor_4, TRUE, FALSE,
5625 EL_EM_GATE_4, -1, -1
5628 Xdoor_5, TRUE, FALSE,
5629 EL_EMC_GATE_5, -1, -1
5632 Xdoor_6, TRUE, FALSE,
5633 EL_EMC_GATE_6, -1, -1
5636 Xdoor_7, TRUE, FALSE,
5637 EL_EMC_GATE_7, -1, -1
5640 Xdoor_8, TRUE, FALSE,
5641 EL_EMC_GATE_8, -1, -1
5644 Xkey_1, TRUE, FALSE,
5648 Xkey_2, TRUE, FALSE,
5652 Xkey_3, TRUE, FALSE,
5656 Xkey_4, TRUE, FALSE,
5660 Xkey_5, TRUE, FALSE,
5661 EL_EMC_KEY_5, -1, -1
5664 Xkey_6, TRUE, FALSE,
5665 EL_EMC_KEY_6, -1, -1
5668 Xkey_7, TRUE, FALSE,
5669 EL_EMC_KEY_7, -1, -1
5672 Xkey_8, TRUE, FALSE,
5673 EL_EMC_KEY_8, -1, -1
5676 Xwind_n, TRUE, FALSE,
5677 EL_BALLOON_SWITCH_UP, -1, -1
5680 Xwind_e, TRUE, FALSE,
5681 EL_BALLOON_SWITCH_RIGHT, -1, -1
5684 Xwind_s, TRUE, FALSE,
5685 EL_BALLOON_SWITCH_DOWN, -1, -1
5688 Xwind_w, TRUE, FALSE,
5689 EL_BALLOON_SWITCH_LEFT, -1, -1
5692 Xwind_nesw, TRUE, FALSE,
5693 EL_BALLOON_SWITCH_ANY, -1, -1
5696 Xwind_stop, TRUE, FALSE,
5697 EL_BALLOON_SWITCH_NONE, -1, -1
5701 EL_EM_EXIT_CLOSED, -1, -1
5704 Xexit_1, TRUE, FALSE,
5705 EL_EM_EXIT_OPEN, -1, -1
5708 Xexit_2, FALSE, FALSE,
5709 EL_EM_EXIT_OPEN, -1, -1
5712 Xexit_3, FALSE, FALSE,
5713 EL_EM_EXIT_OPEN, -1, -1
5716 Xdynamite, TRUE, FALSE,
5717 EL_EM_DYNAMITE, -1, -1
5720 Ydynamite_eat, FALSE, FALSE,
5721 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5724 Xdynamite_1, TRUE, FALSE,
5725 EL_EM_DYNAMITE_ACTIVE, -1, -1
5728 Xdynamite_2, FALSE, FALSE,
5729 EL_EM_DYNAMITE_ACTIVE, -1, -1
5732 Xdynamite_3, FALSE, FALSE,
5733 EL_EM_DYNAMITE_ACTIVE, -1, -1
5736 Xdynamite_4, FALSE, FALSE,
5737 EL_EM_DYNAMITE_ACTIVE, -1, -1
5740 Xbumper, TRUE, FALSE,
5741 EL_EMC_SPRING_BUMPER, -1, -1
5744 XbumperB, FALSE, FALSE,
5745 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5748 Xwheel, TRUE, FALSE,
5749 EL_ROBOT_WHEEL, -1, -1
5752 XwheelB, FALSE, FALSE,
5753 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5756 Xswitch, TRUE, FALSE,
5757 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5760 XswitchB, FALSE, FALSE,
5761 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5765 EL_QUICKSAND_EMPTY, -1, -1
5768 Xsand_stone, TRUE, FALSE,
5769 EL_QUICKSAND_FULL, -1, -1
5772 Xsand_stonein_1, FALSE, TRUE,
5773 EL_ROCK, ACTION_FILLING, -1
5776 Xsand_stonein_2, FALSE, TRUE,
5777 EL_ROCK, ACTION_FILLING, -1
5780 Xsand_stonein_3, FALSE, TRUE,
5781 EL_ROCK, ACTION_FILLING, -1
5784 Xsand_stonein_4, FALSE, TRUE,
5785 EL_ROCK, ACTION_FILLING, -1
5788 Xsand_stonesand_1, FALSE, FALSE,
5789 EL_QUICKSAND_EMPTYING, -1, -1
5792 Xsand_stonesand_2, FALSE, FALSE,
5793 EL_QUICKSAND_EMPTYING, -1, -1
5796 Xsand_stonesand_3, FALSE, FALSE,
5797 EL_QUICKSAND_EMPTYING, -1, -1
5800 Xsand_stonesand_4, FALSE, FALSE,
5801 EL_QUICKSAND_EMPTYING, -1, -1
5804 Xsand_stonesand_quickout_1, FALSE, FALSE,
5805 EL_QUICKSAND_EMPTYING, -1, -1
5808 Xsand_stonesand_quickout_2, FALSE, FALSE,
5809 EL_QUICKSAND_EMPTYING, -1, -1
5812 Xsand_stoneout_1, FALSE, FALSE,
5813 EL_ROCK, ACTION_EMPTYING, -1
5816 Xsand_stoneout_2, FALSE, FALSE,
5817 EL_ROCK, ACTION_EMPTYING, -1
5820 Xsand_sandstone_1, FALSE, FALSE,
5821 EL_QUICKSAND_FILLING, -1, -1
5824 Xsand_sandstone_2, FALSE, FALSE,
5825 EL_QUICKSAND_FILLING, -1, -1
5828 Xsand_sandstone_3, FALSE, FALSE,
5829 EL_QUICKSAND_FILLING, -1, -1
5832 Xsand_sandstone_4, FALSE, FALSE,
5833 EL_QUICKSAND_FILLING, -1, -1
5836 Xplant, TRUE, FALSE,
5837 EL_EMC_PLANT, -1, -1
5840 Yplant, FALSE, FALSE,
5841 EL_EMC_PLANT, -1, -1
5844 Xlenses, TRUE, FALSE,
5845 EL_EMC_LENSES, -1, -1
5848 Xmagnify, TRUE, FALSE,
5849 EL_EMC_MAGNIFIER, -1, -1
5852 Xdripper, TRUE, FALSE,
5853 EL_EMC_DRIPPER, -1, -1
5856 XdripperB, FALSE, FALSE,
5857 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5860 Xfake_blank, TRUE, FALSE,
5861 EL_INVISIBLE_WALL, -1, -1
5864 Xfake_blankB, FALSE, FALSE,
5865 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5868 Xfake_grass, TRUE, FALSE,
5869 EL_EMC_FAKE_GRASS, -1, -1
5872 Xfake_grassB, FALSE, FALSE,
5873 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5876 Xfake_door_1, TRUE, FALSE,
5877 EL_EM_GATE_1_GRAY, -1, -1
5880 Xfake_door_2, TRUE, FALSE,
5881 EL_EM_GATE_2_GRAY, -1, -1
5884 Xfake_door_3, TRUE, FALSE,
5885 EL_EM_GATE_3_GRAY, -1, -1
5888 Xfake_door_4, TRUE, FALSE,
5889 EL_EM_GATE_4_GRAY, -1, -1
5892 Xfake_door_5, TRUE, FALSE,
5893 EL_EMC_GATE_5_GRAY, -1, -1
5896 Xfake_door_6, TRUE, FALSE,
5897 EL_EMC_GATE_6_GRAY, -1, -1
5900 Xfake_door_7, TRUE, FALSE,
5901 EL_EMC_GATE_7_GRAY, -1, -1
5904 Xfake_door_8, TRUE, FALSE,
5905 EL_EMC_GATE_8_GRAY, -1, -1
5908 Xfake_acid_1, TRUE, FALSE,
5909 EL_EMC_FAKE_ACID, -1, -1
5912 Xfake_acid_2, FALSE, FALSE,
5913 EL_EMC_FAKE_ACID, -1, -1
5916 Xfake_acid_3, FALSE, FALSE,
5917 EL_EMC_FAKE_ACID, -1, -1
5920 Xfake_acid_4, FALSE, FALSE,
5921 EL_EMC_FAKE_ACID, -1, -1
5924 Xfake_acid_5, FALSE, FALSE,
5925 EL_EMC_FAKE_ACID, -1, -1
5928 Xfake_acid_6, FALSE, FALSE,
5929 EL_EMC_FAKE_ACID, -1, -1
5932 Xfake_acid_7, FALSE, FALSE,
5933 EL_EMC_FAKE_ACID, -1, -1
5936 Xfake_acid_8, FALSE, FALSE,
5937 EL_EMC_FAKE_ACID, -1, -1
5940 Xsteel_1, TRUE, FALSE,
5941 EL_STEELWALL, -1, -1
5944 Xsteel_2, TRUE, FALSE,
5945 EL_EMC_STEELWALL_2, -1, -1
5948 Xsteel_3, TRUE, FALSE,
5949 EL_EMC_STEELWALL_3, -1, -1
5952 Xsteel_4, TRUE, FALSE,
5953 EL_EMC_STEELWALL_4, -1, -1
5956 Xwall_1, TRUE, FALSE,
5960 Xwall_2, TRUE, FALSE,
5961 EL_EMC_WALL_14, -1, -1
5964 Xwall_3, TRUE, FALSE,
5965 EL_EMC_WALL_15, -1, -1
5968 Xwall_4, TRUE, FALSE,
5969 EL_EMC_WALL_16, -1, -1
5972 Xround_wall_1, TRUE, FALSE,
5973 EL_WALL_SLIPPERY, -1, -1
5976 Xround_wall_2, TRUE, FALSE,
5977 EL_EMC_WALL_SLIPPERY_2, -1, -1
5980 Xround_wall_3, TRUE, FALSE,
5981 EL_EMC_WALL_SLIPPERY_3, -1, -1
5984 Xround_wall_4, TRUE, FALSE,
5985 EL_EMC_WALL_SLIPPERY_4, -1, -1
5988 Xdecor_1, TRUE, FALSE,
5989 EL_EMC_WALL_8, -1, -1
5992 Xdecor_2, TRUE, FALSE,
5993 EL_EMC_WALL_6, -1, -1
5996 Xdecor_3, TRUE, FALSE,
5997 EL_EMC_WALL_4, -1, -1
6000 Xdecor_4, TRUE, FALSE,
6001 EL_EMC_WALL_7, -1, -1
6004 Xdecor_5, TRUE, FALSE,
6005 EL_EMC_WALL_5, -1, -1
6008 Xdecor_6, TRUE, FALSE,
6009 EL_EMC_WALL_9, -1, -1
6012 Xdecor_7, TRUE, FALSE,
6013 EL_EMC_WALL_10, -1, -1
6016 Xdecor_8, TRUE, FALSE,
6017 EL_EMC_WALL_1, -1, -1
6020 Xdecor_9, TRUE, FALSE,
6021 EL_EMC_WALL_2, -1, -1
6024 Xdecor_10, TRUE, FALSE,
6025 EL_EMC_WALL_3, -1, -1
6028 Xdecor_11, TRUE, FALSE,
6029 EL_EMC_WALL_11, -1, -1
6032 Xdecor_12, TRUE, FALSE,
6033 EL_EMC_WALL_12, -1, -1
6036 Xalpha_0, TRUE, FALSE,
6037 EL_CHAR('0'), -1, -1
6040 Xalpha_1, TRUE, FALSE,
6041 EL_CHAR('1'), -1, -1
6044 Xalpha_2, TRUE, FALSE,
6045 EL_CHAR('2'), -1, -1
6048 Xalpha_3, TRUE, FALSE,
6049 EL_CHAR('3'), -1, -1
6052 Xalpha_4, TRUE, FALSE,
6053 EL_CHAR('4'), -1, -1
6056 Xalpha_5, TRUE, FALSE,
6057 EL_CHAR('5'), -1, -1
6060 Xalpha_6, TRUE, FALSE,
6061 EL_CHAR('6'), -1, -1
6064 Xalpha_7, TRUE, FALSE,
6065 EL_CHAR('7'), -1, -1
6068 Xalpha_8, TRUE, FALSE,
6069 EL_CHAR('8'), -1, -1
6072 Xalpha_9, TRUE, FALSE,
6073 EL_CHAR('9'), -1, -1
6076 Xalpha_excla, TRUE, FALSE,
6077 EL_CHAR('!'), -1, -1
6080 Xalpha_quote, TRUE, FALSE,
6081 EL_CHAR('"'), -1, -1
6084 Xalpha_comma, TRUE, FALSE,
6085 EL_CHAR(','), -1, -1
6088 Xalpha_minus, TRUE, FALSE,
6089 EL_CHAR('-'), -1, -1
6092 Xalpha_perio, TRUE, FALSE,
6093 EL_CHAR('.'), -1, -1
6096 Xalpha_colon, TRUE, FALSE,
6097 EL_CHAR(':'), -1, -1
6100 Xalpha_quest, TRUE, FALSE,
6101 EL_CHAR('?'), -1, -1
6104 Xalpha_a, TRUE, FALSE,
6105 EL_CHAR('A'), -1, -1
6108 Xalpha_b, TRUE, FALSE,
6109 EL_CHAR('B'), -1, -1
6112 Xalpha_c, TRUE, FALSE,
6113 EL_CHAR('C'), -1, -1
6116 Xalpha_d, TRUE, FALSE,
6117 EL_CHAR('D'), -1, -1
6120 Xalpha_e, TRUE, FALSE,
6121 EL_CHAR('E'), -1, -1
6124 Xalpha_f, TRUE, FALSE,
6125 EL_CHAR('F'), -1, -1
6128 Xalpha_g, TRUE, FALSE,
6129 EL_CHAR('G'), -1, -1
6132 Xalpha_h, TRUE, FALSE,
6133 EL_CHAR('H'), -1, -1
6136 Xalpha_i, TRUE, FALSE,
6137 EL_CHAR('I'), -1, -1
6140 Xalpha_j, TRUE, FALSE,
6141 EL_CHAR('J'), -1, -1
6144 Xalpha_k, TRUE, FALSE,
6145 EL_CHAR('K'), -1, -1
6148 Xalpha_l, TRUE, FALSE,
6149 EL_CHAR('L'), -1, -1
6152 Xalpha_m, TRUE, FALSE,
6153 EL_CHAR('M'), -1, -1
6156 Xalpha_n, TRUE, FALSE,
6157 EL_CHAR('N'), -1, -1
6160 Xalpha_o, TRUE, FALSE,
6161 EL_CHAR('O'), -1, -1
6164 Xalpha_p, TRUE, FALSE,
6165 EL_CHAR('P'), -1, -1
6168 Xalpha_q, TRUE, FALSE,
6169 EL_CHAR('Q'), -1, -1
6172 Xalpha_r, TRUE, FALSE,
6173 EL_CHAR('R'), -1, -1
6176 Xalpha_s, TRUE, FALSE,
6177 EL_CHAR('S'), -1, -1
6180 Xalpha_t, TRUE, FALSE,
6181 EL_CHAR('T'), -1, -1
6184 Xalpha_u, TRUE, FALSE,
6185 EL_CHAR('U'), -1, -1
6188 Xalpha_v, TRUE, FALSE,
6189 EL_CHAR('V'), -1, -1
6192 Xalpha_w, TRUE, FALSE,
6193 EL_CHAR('W'), -1, -1
6196 Xalpha_x, TRUE, FALSE,
6197 EL_CHAR('X'), -1, -1
6200 Xalpha_y, TRUE, FALSE,
6201 EL_CHAR('Y'), -1, -1
6204 Xalpha_z, TRUE, FALSE,
6205 EL_CHAR('Z'), -1, -1
6208 Xalpha_arrow_e, TRUE, FALSE,
6209 EL_CHAR('>'), -1, -1
6212 Xalpha_arrow_w, TRUE, FALSE,
6213 EL_CHAR('<'), -1, -1
6216 Xalpha_copyr, TRUE, FALSE,
6217 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6221 Xboom_bug, FALSE, FALSE,
6222 EL_BUG, ACTION_EXPLODING, -1
6225 Xboom_bomb, FALSE, FALSE,
6226 EL_BOMB, ACTION_EXPLODING, -1
6229 Xboom_android, FALSE, FALSE,
6230 EL_EMC_ANDROID, ACTION_OTHER, -1
6233 Xboom_1, FALSE, FALSE,
6234 EL_DEFAULT, ACTION_EXPLODING, -1
6237 Xboom_2, FALSE, FALSE,
6238 EL_DEFAULT, ACTION_EXPLODING, -1
6241 Znormal, FALSE, FALSE,
6245 Zdynamite, FALSE, FALSE,
6249 Zplayer, FALSE, FALSE,
6253 ZBORDER, FALSE, FALSE,
6263 static struct Mapping_EM_to_RND_player
6272 em_player_mapping_list[] =
6276 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6280 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6284 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6288 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6292 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6296 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6300 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6304 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6308 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6312 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6316 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6320 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6324 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6328 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6332 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6336 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6340 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6344 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6348 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6352 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6356 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6360 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6364 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6368 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6372 EL_PLAYER_1, ACTION_DEFAULT, -1,
6376 EL_PLAYER_2, ACTION_DEFAULT, -1,
6380 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6384 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6388 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6392 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6396 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6400 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6404 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6408 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6412 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6416 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6420 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6424 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6428 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6432 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6436 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6440 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6444 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6448 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6452 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6456 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6460 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6464 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6468 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6472 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6476 EL_PLAYER_3, ACTION_DEFAULT, -1,
6480 EL_PLAYER_4, ACTION_DEFAULT, -1,
6489 int map_element_RND_to_EM(int element_rnd)
6491 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6492 static boolean mapping_initialized = FALSE;
6494 if (!mapping_initialized)
6498 /* return "Xalpha_quest" for all undefined elements in mapping array */
6499 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6500 mapping_RND_to_EM[i] = Xalpha_quest;
6502 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6503 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6504 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6505 em_object_mapping_list[i].element_em;
6507 mapping_initialized = TRUE;
6510 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6511 return mapping_RND_to_EM[element_rnd];
6513 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6518 int map_element_EM_to_RND(int element_em)
6520 static unsigned short mapping_EM_to_RND[TILE_MAX];
6521 static boolean mapping_initialized = FALSE;
6523 if (!mapping_initialized)
6527 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6528 for (i = 0; i < TILE_MAX; i++)
6529 mapping_EM_to_RND[i] = EL_UNKNOWN;
6531 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6532 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6533 em_object_mapping_list[i].element_rnd;
6535 mapping_initialized = TRUE;
6538 if (element_em >= 0 && element_em < TILE_MAX)
6539 return mapping_EM_to_RND[element_em];
6541 Error(ERR_WARN, "invalid EM level element %d", element_em);
6546 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6548 struct LevelInfo_EM *level_em = level->native_em_level;
6549 struct LEVEL *lev = level_em->lev;
6552 for (i = 0; i < TILE_MAX; i++)
6553 lev->android_array[i] = Xblank;
6555 for (i = 0; i < level->num_android_clone_elements; i++)
6557 int element_rnd = level->android_clone_element[i];
6558 int element_em = map_element_RND_to_EM(element_rnd);
6560 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6561 if (em_object_mapping_list[j].element_rnd == element_rnd)
6562 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6566 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6568 struct LevelInfo_EM *level_em = level->native_em_level;
6569 struct LEVEL *lev = level_em->lev;
6572 level->num_android_clone_elements = 0;
6574 for (i = 0; i < TILE_MAX; i++)
6576 int element_em = lev->android_array[i];
6578 boolean element_found = FALSE;
6580 if (element_em == Xblank)
6583 element_rnd = map_element_EM_to_RND(element_em);
6585 for (j = 0; j < level->num_android_clone_elements; j++)
6586 if (level->android_clone_element[j] == element_rnd)
6587 element_found = TRUE;
6591 level->android_clone_element[level->num_android_clone_elements++] =
6594 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6599 if (level->num_android_clone_elements == 0)
6601 level->num_android_clone_elements = 1;
6602 level->android_clone_element[0] = EL_EMPTY;
6606 int map_direction_RND_to_EM(int direction)
6608 return (direction == MV_UP ? 0 :
6609 direction == MV_RIGHT ? 1 :
6610 direction == MV_DOWN ? 2 :
6611 direction == MV_LEFT ? 3 :
6615 int map_direction_EM_to_RND(int direction)
6617 return (direction == 0 ? MV_UP :
6618 direction == 1 ? MV_RIGHT :
6619 direction == 2 ? MV_DOWN :
6620 direction == 3 ? MV_LEFT :
6624 int map_element_RND_to_SP(int element_rnd)
6626 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6628 if (element_rnd >= EL_SP_START &&
6629 element_rnd <= EL_SP_END)
6630 element_sp = element_rnd - EL_SP_START;
6631 else if (element_rnd == EL_EMPTY_SPACE)
6633 else if (element_rnd == EL_INVISIBLE_WALL)
6639 int map_element_SP_to_RND(int element_sp)
6641 int element_rnd = EL_UNKNOWN;
6643 if (element_sp >= 0x00 &&
6645 element_rnd = EL_SP_START + element_sp;
6646 else if (element_sp == 0x28)
6647 element_rnd = EL_INVISIBLE_WALL;
6652 int map_action_SP_to_RND(int action_sp)
6656 case actActive: return ACTION_ACTIVE;
6657 case actImpact: return ACTION_IMPACT;
6658 case actExploding: return ACTION_EXPLODING;
6659 case actDigging: return ACTION_DIGGING;
6660 case actSnapping: return ACTION_SNAPPING;
6661 case actCollecting: return ACTION_COLLECTING;
6662 case actPassing: return ACTION_PASSING;
6663 case actPushing: return ACTION_PUSHING;
6664 case actDropping: return ACTION_DROPPING;
6666 default: return ACTION_DEFAULT;
6670 int get_next_element(int element)
6674 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6675 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6676 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6677 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6678 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6679 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6680 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6681 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6682 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6683 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6684 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6686 default: return element;
6690 int el_act_dir2img(int element, int action, int direction)
6692 element = GFX_ELEMENT(element);
6693 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6695 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6696 return element_info[element].direction_graphic[action][direction];
6699 static int el_act_dir2crm(int element, int action, int direction)
6701 element = GFX_ELEMENT(element);
6702 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6704 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6705 return element_info[element].direction_crumbled[action][direction];
6708 int el_act2img(int element, int action)
6710 element = GFX_ELEMENT(element);
6712 return element_info[element].graphic[action];
6715 int el_act2crm(int element, int action)
6717 element = GFX_ELEMENT(element);
6719 return element_info[element].crumbled[action];
6722 int el_dir2img(int element, int direction)
6724 element = GFX_ELEMENT(element);
6726 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6729 int el2baseimg(int element)
6731 return element_info[element].graphic[ACTION_DEFAULT];
6734 int el2img(int element)
6736 element = GFX_ELEMENT(element);
6738 return element_info[element].graphic[ACTION_DEFAULT];
6741 int el2edimg(int element)
6743 element = GFX_ELEMENT(element);
6745 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6748 int el2preimg(int element)
6750 element = GFX_ELEMENT(element);
6752 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6755 int el2panelimg(int element)
6757 element = GFX_ELEMENT(element);
6759 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6762 int font2baseimg(int font_nr)
6764 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6767 int getBeltNrFromBeltElement(int element)
6769 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6770 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6771 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6774 int getBeltNrFromBeltActiveElement(int element)
6776 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6777 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6778 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6781 int getBeltNrFromBeltSwitchElement(int element)
6783 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6784 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6785 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6788 int getBeltDirNrFromBeltElement(int element)
6790 static int belt_base_element[4] =
6792 EL_CONVEYOR_BELT_1_LEFT,
6793 EL_CONVEYOR_BELT_2_LEFT,
6794 EL_CONVEYOR_BELT_3_LEFT,
6795 EL_CONVEYOR_BELT_4_LEFT
6798 int belt_nr = getBeltNrFromBeltElement(element);
6799 int belt_dir_nr = element - belt_base_element[belt_nr];
6801 return (belt_dir_nr % 3);
6804 int getBeltDirNrFromBeltSwitchElement(int element)
6806 static int belt_base_element[4] =
6808 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6809 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6810 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6811 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6814 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6815 int belt_dir_nr = element - belt_base_element[belt_nr];
6817 return (belt_dir_nr % 3);
6820 int getBeltDirFromBeltElement(int element)
6822 static int belt_move_dir[3] =
6829 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6831 return belt_move_dir[belt_dir_nr];
6834 int getBeltDirFromBeltSwitchElement(int element)
6836 static int belt_move_dir[3] =
6843 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6845 return belt_move_dir[belt_dir_nr];
6848 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6850 static int belt_base_element[4] =
6852 EL_CONVEYOR_BELT_1_LEFT,
6853 EL_CONVEYOR_BELT_2_LEFT,
6854 EL_CONVEYOR_BELT_3_LEFT,
6855 EL_CONVEYOR_BELT_4_LEFT
6858 return belt_base_element[belt_nr] + belt_dir_nr;
6861 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6863 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6865 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6868 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6870 static int belt_base_element[4] =
6872 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6873 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6874 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6875 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6878 return belt_base_element[belt_nr] + belt_dir_nr;
6881 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6883 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6885 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6888 boolean getTeamMode_EM()
6890 return game.team_mode;
6893 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6895 int game_frame_delay_value;
6897 game_frame_delay_value =
6898 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6899 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6902 if (tape.playing && tape.warp_forward && !tape.pausing)
6903 game_frame_delay_value = 0;
6905 return game_frame_delay_value;
6908 unsigned int InitRND(int seed)
6910 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6911 return InitEngineRandom_EM(seed);
6912 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6913 return InitEngineRandom_SP(seed);
6915 return InitEngineRandom_RND(seed);
6918 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6919 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6921 inline static int get_effective_element_EM(int tile, int frame_em)
6923 int element = object_mapping[tile].element_rnd;
6924 int action = object_mapping[tile].action;
6925 boolean is_backside = object_mapping[tile].is_backside;
6926 boolean action_removing = (action == ACTION_DIGGING ||
6927 action == ACTION_SNAPPING ||
6928 action == ACTION_COLLECTING);
6934 case Yacid_splash_eB:
6935 case Yacid_splash_wB:
6936 return (frame_em > 5 ? EL_EMPTY : element);
6942 else /* frame_em == 7 */
6946 case Yacid_splash_eB:
6947 case Yacid_splash_wB:
6950 case Yemerald_stone:
6953 case Ydiamond_stone:
6957 case Xdrip_stretchB:
6976 case Xsand_stonein_1:
6977 case Xsand_stonein_2:
6978 case Xsand_stonein_3:
6979 case Xsand_stonein_4:
6983 return (is_backside || action_removing ? EL_EMPTY : element);
6988 inline static boolean check_linear_animation_EM(int tile)
6992 case Xsand_stonesand_1:
6993 case Xsand_stonesand_quickout_1:
6994 case Xsand_sandstone_1:
6995 case Xsand_stonein_1:
6996 case Xsand_stoneout_1:
7015 case Yacid_splash_eB:
7016 case Yacid_splash_wB:
7017 case Yemerald_stone:
7024 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7025 boolean has_crumbled_graphics,
7026 int crumbled, int sync_frame)
7028 /* if element can be crumbled, but certain action graphics are just empty
7029 space (like instantly snapping sand to empty space in 1 frame), do not
7030 treat these empty space graphics as crumbled graphics in EMC engine */
7031 if (crumbled == IMG_EMPTY_SPACE)
7032 has_crumbled_graphics = FALSE;
7034 if (has_crumbled_graphics)
7036 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7037 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7038 g_crumbled->anim_delay,
7039 g_crumbled->anim_mode,
7040 g_crumbled->anim_start_frame,
7043 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7044 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7046 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7048 g_em->has_crumbled_graphics = TRUE;
7052 g_em->crumbled_bitmap = NULL;
7053 g_em->crumbled_src_x = 0;
7054 g_em->crumbled_src_y = 0;
7055 g_em->crumbled_border_size = 0;
7057 g_em->has_crumbled_graphics = FALSE;
7061 void ResetGfxAnimation_EM(int x, int y, int tile)
7066 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7067 int tile, int frame_em, int x, int y)
7069 int action = object_mapping[tile].action;
7070 int direction = object_mapping[tile].direction;
7071 int effective_element = get_effective_element_EM(tile, frame_em);
7072 int graphic = (direction == MV_NONE ?
7073 el_act2img(effective_element, action) :
7074 el_act_dir2img(effective_element, action, direction));
7075 struct GraphicInfo *g = &graphic_info[graphic];
7077 boolean action_removing = (action == ACTION_DIGGING ||
7078 action == ACTION_SNAPPING ||
7079 action == ACTION_COLLECTING);
7080 boolean action_moving = (action == ACTION_FALLING ||
7081 action == ACTION_MOVING ||
7082 action == ACTION_PUSHING ||
7083 action == ACTION_EATING ||
7084 action == ACTION_FILLING ||
7085 action == ACTION_EMPTYING);
7086 boolean action_falling = (action == ACTION_FALLING ||
7087 action == ACTION_FILLING ||
7088 action == ACTION_EMPTYING);
7090 /* special case: graphic uses "2nd movement tile" and has defined
7091 7 frames for movement animation (or less) => use default graphic
7092 for last (8th) frame which ends the movement animation */
7093 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7095 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7096 graphic = (direction == MV_NONE ?
7097 el_act2img(effective_element, action) :
7098 el_act_dir2img(effective_element, action, direction));
7100 g = &graphic_info[graphic];
7103 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7107 else if (action_moving)
7109 boolean is_backside = object_mapping[tile].is_backside;
7113 int direction = object_mapping[tile].direction;
7114 int move_dir = (action_falling ? MV_DOWN : direction);
7119 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7120 if (g->double_movement && frame_em == 0)
7124 if (move_dir == MV_LEFT)
7125 GfxFrame[x - 1][y] = GfxFrame[x][y];
7126 else if (move_dir == MV_RIGHT)
7127 GfxFrame[x + 1][y] = GfxFrame[x][y];
7128 else if (move_dir == MV_UP)
7129 GfxFrame[x][y - 1] = GfxFrame[x][y];
7130 else if (move_dir == MV_DOWN)
7131 GfxFrame[x][y + 1] = GfxFrame[x][y];
7138 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7139 if (tile == Xsand_stonesand_quickout_1 ||
7140 tile == Xsand_stonesand_quickout_2)
7144 if (graphic_info[graphic].anim_global_sync)
7145 sync_frame = FrameCounter;
7146 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7147 sync_frame = GfxFrame[x][y];
7149 sync_frame = 0; /* playfield border (pseudo steel) */
7151 SetRandomAnimationValue(x, y);
7153 int frame = getAnimationFrame(g->anim_frames,
7156 g->anim_start_frame,
7159 g_em->unique_identifier =
7160 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7163 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7164 int tile, int frame_em, int x, int y)
7166 int action = object_mapping[tile].action;
7167 int direction = object_mapping[tile].direction;
7168 boolean is_backside = object_mapping[tile].is_backside;
7169 int effective_element = get_effective_element_EM(tile, frame_em);
7170 int effective_action = action;
7171 int graphic = (direction == MV_NONE ?
7172 el_act2img(effective_element, effective_action) :
7173 el_act_dir2img(effective_element, effective_action,
7175 int crumbled = (direction == MV_NONE ?
7176 el_act2crm(effective_element, effective_action) :
7177 el_act_dir2crm(effective_element, effective_action,
7179 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7180 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7181 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7182 struct GraphicInfo *g = &graphic_info[graphic];
7185 /* special case: graphic uses "2nd movement tile" and has defined
7186 7 frames for movement animation (or less) => use default graphic
7187 for last (8th) frame which ends the movement animation */
7188 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7190 effective_action = ACTION_DEFAULT;
7191 graphic = (direction == MV_NONE ?
7192 el_act2img(effective_element, effective_action) :
7193 el_act_dir2img(effective_element, effective_action,
7195 crumbled = (direction == MV_NONE ?
7196 el_act2crm(effective_element, effective_action) :
7197 el_act_dir2crm(effective_element, effective_action,
7200 g = &graphic_info[graphic];
7203 if (graphic_info[graphic].anim_global_sync)
7204 sync_frame = FrameCounter;
7205 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7206 sync_frame = GfxFrame[x][y];
7208 sync_frame = 0; /* playfield border (pseudo steel) */
7210 SetRandomAnimationValue(x, y);
7212 int frame = getAnimationFrame(g->anim_frames,
7215 g->anim_start_frame,
7218 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7219 g->double_movement && is_backside);
7221 /* (updating the "crumbled" graphic definitions is probably not really needed,
7222 as animations for crumbled graphics can't be longer than one EMC cycle) */
7223 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7227 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7228 int player_nr, int anim, int frame_em)
7230 int element = player_mapping[player_nr][anim].element_rnd;
7231 int action = player_mapping[player_nr][anim].action;
7232 int direction = player_mapping[player_nr][anim].direction;
7233 int graphic = (direction == MV_NONE ?
7234 el_act2img(element, action) :
7235 el_act_dir2img(element, action, direction));
7236 struct GraphicInfo *g = &graphic_info[graphic];
7239 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7241 stored_player[player_nr].StepFrame = frame_em;
7243 sync_frame = stored_player[player_nr].Frame;
7245 int frame = getAnimationFrame(g->anim_frames,
7248 g->anim_start_frame,
7251 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7252 &g_em->src_x, &g_em->src_y, FALSE);
7255 void InitGraphicInfo_EM(void)
7260 int num_em_gfx_errors = 0;
7262 if (graphic_info_em_object[0][0].bitmap == NULL)
7264 /* EM graphics not yet initialized in em_open_all() */
7269 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7272 /* always start with reliable default values */
7273 for (i = 0; i < TILE_MAX; i++)
7275 object_mapping[i].element_rnd = EL_UNKNOWN;
7276 object_mapping[i].is_backside = FALSE;
7277 object_mapping[i].action = ACTION_DEFAULT;
7278 object_mapping[i].direction = MV_NONE;
7281 /* always start with reliable default values */
7282 for (p = 0; p < MAX_PLAYERS; p++)
7284 for (i = 0; i < SPR_MAX; i++)
7286 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7287 player_mapping[p][i].action = ACTION_DEFAULT;
7288 player_mapping[p][i].direction = MV_NONE;
7292 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7294 int e = em_object_mapping_list[i].element_em;
7296 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7297 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7299 if (em_object_mapping_list[i].action != -1)
7300 object_mapping[e].action = em_object_mapping_list[i].action;
7302 if (em_object_mapping_list[i].direction != -1)
7303 object_mapping[e].direction =
7304 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7307 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7309 int a = em_player_mapping_list[i].action_em;
7310 int p = em_player_mapping_list[i].player_nr;
7312 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7314 if (em_player_mapping_list[i].action != -1)
7315 player_mapping[p][a].action = em_player_mapping_list[i].action;
7317 if (em_player_mapping_list[i].direction != -1)
7318 player_mapping[p][a].direction =
7319 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7322 for (i = 0; i < TILE_MAX; i++)
7324 int element = object_mapping[i].element_rnd;
7325 int action = object_mapping[i].action;
7326 int direction = object_mapping[i].direction;
7327 boolean is_backside = object_mapping[i].is_backside;
7328 boolean action_exploding = ((action == ACTION_EXPLODING ||
7329 action == ACTION_SMASHED_BY_ROCK ||
7330 action == ACTION_SMASHED_BY_SPRING) &&
7331 element != EL_DIAMOND);
7332 boolean action_active = (action == ACTION_ACTIVE);
7333 boolean action_other = (action == ACTION_OTHER);
7335 for (j = 0; j < 8; j++)
7337 int effective_element = get_effective_element_EM(i, j);
7338 int effective_action = (j < 7 ? action :
7339 i == Xdrip_stretch ? action :
7340 i == Xdrip_stretchB ? action :
7341 i == Ydrip_s1 ? action :
7342 i == Ydrip_s1B ? action :
7343 i == Xball_1B ? action :
7344 i == Xball_2 ? action :
7345 i == Xball_2B ? action :
7346 i == Yball_eat ? action :
7347 i == Ykey_1_eat ? action :
7348 i == Ykey_2_eat ? action :
7349 i == Ykey_3_eat ? action :
7350 i == Ykey_4_eat ? action :
7351 i == Ykey_5_eat ? action :
7352 i == Ykey_6_eat ? action :
7353 i == Ykey_7_eat ? action :
7354 i == Ykey_8_eat ? action :
7355 i == Ylenses_eat ? action :
7356 i == Ymagnify_eat ? action :
7357 i == Ygrass_eat ? action :
7358 i == Ydirt_eat ? action :
7359 i == Xsand_stonein_1 ? action :
7360 i == Xsand_stonein_2 ? action :
7361 i == Xsand_stonein_3 ? action :
7362 i == Xsand_stonein_4 ? action :
7363 i == Xsand_stoneout_1 ? action :
7364 i == Xsand_stoneout_2 ? action :
7365 i == Xboom_android ? ACTION_EXPLODING :
7366 action_exploding ? ACTION_EXPLODING :
7367 action_active ? action :
7368 action_other ? action :
7370 int graphic = (el_act_dir2img(effective_element, effective_action,
7372 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7374 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7375 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7376 boolean has_action_graphics = (graphic != base_graphic);
7377 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7378 struct GraphicInfo *g = &graphic_info[graphic];
7379 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7382 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7383 boolean special_animation = (action != ACTION_DEFAULT &&
7384 g->anim_frames == 3 &&
7385 g->anim_delay == 2 &&
7386 g->anim_mode & ANIM_LINEAR);
7387 int sync_frame = (i == Xdrip_stretch ? 7 :
7388 i == Xdrip_stretchB ? 7 :
7389 i == Ydrip_s2 ? j + 8 :
7390 i == Ydrip_s2B ? j + 8 :
7399 i == Xfake_acid_1 ? 0 :
7400 i == Xfake_acid_2 ? 10 :
7401 i == Xfake_acid_3 ? 20 :
7402 i == Xfake_acid_4 ? 30 :
7403 i == Xfake_acid_5 ? 40 :
7404 i == Xfake_acid_6 ? 50 :
7405 i == Xfake_acid_7 ? 60 :
7406 i == Xfake_acid_8 ? 70 :
7408 i == Xball_2B ? j + 8 :
7409 i == Yball_eat ? j + 1 :
7410 i == Ykey_1_eat ? j + 1 :
7411 i == Ykey_2_eat ? j + 1 :
7412 i == Ykey_3_eat ? j + 1 :
7413 i == Ykey_4_eat ? j + 1 :
7414 i == Ykey_5_eat ? j + 1 :
7415 i == Ykey_6_eat ? j + 1 :
7416 i == Ykey_7_eat ? j + 1 :
7417 i == Ykey_8_eat ? j + 1 :
7418 i == Ylenses_eat ? j + 1 :
7419 i == Ymagnify_eat ? j + 1 :
7420 i == Ygrass_eat ? j + 1 :
7421 i == Ydirt_eat ? j + 1 :
7422 i == Xamoeba_1 ? 0 :
7423 i == Xamoeba_2 ? 1 :
7424 i == Xamoeba_3 ? 2 :
7425 i == Xamoeba_4 ? 3 :
7426 i == Xamoeba_5 ? 0 :
7427 i == Xamoeba_6 ? 1 :
7428 i == Xamoeba_7 ? 2 :
7429 i == Xamoeba_8 ? 3 :
7430 i == Xexit_2 ? j + 8 :
7431 i == Xexit_3 ? j + 16 :
7432 i == Xdynamite_1 ? 0 :
7433 i == Xdynamite_2 ? 8 :
7434 i == Xdynamite_3 ? 16 :
7435 i == Xdynamite_4 ? 24 :
7436 i == Xsand_stonein_1 ? j + 1 :
7437 i == Xsand_stonein_2 ? j + 9 :
7438 i == Xsand_stonein_3 ? j + 17 :
7439 i == Xsand_stonein_4 ? j + 25 :
7440 i == Xsand_stoneout_1 && j == 0 ? 0 :
7441 i == Xsand_stoneout_1 && j == 1 ? 0 :
7442 i == Xsand_stoneout_1 && j == 2 ? 1 :
7443 i == Xsand_stoneout_1 && j == 3 ? 2 :
7444 i == Xsand_stoneout_1 && j == 4 ? 2 :
7445 i == Xsand_stoneout_1 && j == 5 ? 3 :
7446 i == Xsand_stoneout_1 && j == 6 ? 4 :
7447 i == Xsand_stoneout_1 && j == 7 ? 4 :
7448 i == Xsand_stoneout_2 && j == 0 ? 5 :
7449 i == Xsand_stoneout_2 && j == 1 ? 6 :
7450 i == Xsand_stoneout_2 && j == 2 ? 7 :
7451 i == Xsand_stoneout_2 && j == 3 ? 8 :
7452 i == Xsand_stoneout_2 && j == 4 ? 9 :
7453 i == Xsand_stoneout_2 && j == 5 ? 11 :
7454 i == Xsand_stoneout_2 && j == 6 ? 13 :
7455 i == Xsand_stoneout_2 && j == 7 ? 15 :
7456 i == Xboom_bug && j == 1 ? 2 :
7457 i == Xboom_bug && j == 2 ? 2 :
7458 i == Xboom_bug && j == 3 ? 4 :
7459 i == Xboom_bug && j == 4 ? 4 :
7460 i == Xboom_bug && j == 5 ? 2 :
7461 i == Xboom_bug && j == 6 ? 2 :
7462 i == Xboom_bug && j == 7 ? 0 :
7463 i == Xboom_bomb && j == 1 ? 2 :
7464 i == Xboom_bomb && j == 2 ? 2 :
7465 i == Xboom_bomb && j == 3 ? 4 :
7466 i == Xboom_bomb && j == 4 ? 4 :
7467 i == Xboom_bomb && j == 5 ? 2 :
7468 i == Xboom_bomb && j == 6 ? 2 :
7469 i == Xboom_bomb && j == 7 ? 0 :
7470 i == Xboom_android && j == 7 ? 6 :
7471 i == Xboom_1 && j == 1 ? 2 :
7472 i == Xboom_1 && j == 2 ? 2 :
7473 i == Xboom_1 && j == 3 ? 4 :
7474 i == Xboom_1 && j == 4 ? 4 :
7475 i == Xboom_1 && j == 5 ? 6 :
7476 i == Xboom_1 && j == 6 ? 6 :
7477 i == Xboom_1 && j == 7 ? 8 :
7478 i == Xboom_2 && j == 0 ? 8 :
7479 i == Xboom_2 && j == 1 ? 8 :
7480 i == Xboom_2 && j == 2 ? 10 :
7481 i == Xboom_2 && j == 3 ? 10 :
7482 i == Xboom_2 && j == 4 ? 10 :
7483 i == Xboom_2 && j == 5 ? 12 :
7484 i == Xboom_2 && j == 6 ? 12 :
7485 i == Xboom_2 && j == 7 ? 12 :
7486 special_animation && j == 4 ? 3 :
7487 effective_action != action ? 0 :
7491 Bitmap *debug_bitmap = g_em->bitmap;
7492 int debug_src_x = g_em->src_x;
7493 int debug_src_y = g_em->src_y;
7496 int frame = getAnimationFrame(g->anim_frames,
7499 g->anim_start_frame,
7502 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7503 g->double_movement && is_backside);
7505 g_em->bitmap = src_bitmap;
7506 g_em->src_x = src_x;
7507 g_em->src_y = src_y;
7508 g_em->src_offset_x = 0;
7509 g_em->src_offset_y = 0;
7510 g_em->dst_offset_x = 0;
7511 g_em->dst_offset_y = 0;
7512 g_em->width = TILEX;
7513 g_em->height = TILEY;
7515 g_em->preserve_background = FALSE;
7517 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7520 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7521 effective_action == ACTION_MOVING ||
7522 effective_action == ACTION_PUSHING ||
7523 effective_action == ACTION_EATING)) ||
7524 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7525 effective_action == ACTION_EMPTYING)))
7528 (effective_action == ACTION_FALLING ||
7529 effective_action == ACTION_FILLING ||
7530 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7531 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7532 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7533 int num_steps = (i == Ydrip_s1 ? 16 :
7534 i == Ydrip_s1B ? 16 :
7535 i == Ydrip_s2 ? 16 :
7536 i == Ydrip_s2B ? 16 :
7537 i == Xsand_stonein_1 ? 32 :
7538 i == Xsand_stonein_2 ? 32 :
7539 i == Xsand_stonein_3 ? 32 :
7540 i == Xsand_stonein_4 ? 32 :
7541 i == Xsand_stoneout_1 ? 16 :
7542 i == Xsand_stoneout_2 ? 16 : 8);
7543 int cx = ABS(dx) * (TILEX / num_steps);
7544 int cy = ABS(dy) * (TILEY / num_steps);
7545 int step_frame = (i == Ydrip_s2 ? j + 8 :
7546 i == Ydrip_s2B ? j + 8 :
7547 i == Xsand_stonein_2 ? j + 8 :
7548 i == Xsand_stonein_3 ? j + 16 :
7549 i == Xsand_stonein_4 ? j + 24 :
7550 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7551 int step = (is_backside ? step_frame : num_steps - step_frame);
7553 if (is_backside) /* tile where movement starts */
7555 if (dx < 0 || dy < 0)
7557 g_em->src_offset_x = cx * step;
7558 g_em->src_offset_y = cy * step;
7562 g_em->dst_offset_x = cx * step;
7563 g_em->dst_offset_y = cy * step;
7566 else /* tile where movement ends */
7568 if (dx < 0 || dy < 0)
7570 g_em->dst_offset_x = cx * step;
7571 g_em->dst_offset_y = cy * step;
7575 g_em->src_offset_x = cx * step;
7576 g_em->src_offset_y = cy * step;
7580 g_em->width = TILEX - cx * step;
7581 g_em->height = TILEY - cy * step;
7584 /* create unique graphic identifier to decide if tile must be redrawn */
7585 /* bit 31 - 16 (16 bit): EM style graphic
7586 bit 15 - 12 ( 4 bit): EM style frame
7587 bit 11 - 6 ( 6 bit): graphic width
7588 bit 5 - 0 ( 6 bit): graphic height */
7589 g_em->unique_identifier =
7590 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7594 /* skip check for EMC elements not contained in original EMC artwork */
7595 if (element == EL_EMC_FAKE_ACID)
7598 if (g_em->bitmap != debug_bitmap ||
7599 g_em->src_x != debug_src_x ||
7600 g_em->src_y != debug_src_y ||
7601 g_em->src_offset_x != 0 ||
7602 g_em->src_offset_y != 0 ||
7603 g_em->dst_offset_x != 0 ||
7604 g_em->dst_offset_y != 0 ||
7605 g_em->width != TILEX ||
7606 g_em->height != TILEY)
7608 static int last_i = -1;
7616 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7617 i, element, element_info[element].token_name,
7618 element_action_info[effective_action].suffix, direction);
7620 if (element != effective_element)
7621 printf(" [%d ('%s')]",
7623 element_info[effective_element].token_name);
7627 if (g_em->bitmap != debug_bitmap)
7628 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7629 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7631 if (g_em->src_x != debug_src_x ||
7632 g_em->src_y != debug_src_y)
7633 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7634 j, (is_backside ? 'B' : 'F'),
7635 g_em->src_x, g_em->src_y,
7636 g_em->src_x / 32, g_em->src_y / 32,
7637 debug_src_x, debug_src_y,
7638 debug_src_x / 32, debug_src_y / 32);
7640 if (g_em->src_offset_x != 0 ||
7641 g_em->src_offset_y != 0 ||
7642 g_em->dst_offset_x != 0 ||
7643 g_em->dst_offset_y != 0)
7644 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7646 g_em->src_offset_x, g_em->src_offset_y,
7647 g_em->dst_offset_x, g_em->dst_offset_y);
7649 if (g_em->width != TILEX ||
7650 g_em->height != TILEY)
7651 printf(" %d (%d): size %d,%d should be %d,%d\n",
7653 g_em->width, g_em->height, TILEX, TILEY);
7655 num_em_gfx_errors++;
7662 for (i = 0; i < TILE_MAX; i++)
7664 for (j = 0; j < 8; j++)
7666 int element = object_mapping[i].element_rnd;
7667 int action = object_mapping[i].action;
7668 int direction = object_mapping[i].direction;
7669 boolean is_backside = object_mapping[i].is_backside;
7670 int graphic_action = el_act_dir2img(element, action, direction);
7671 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7673 if ((action == ACTION_SMASHED_BY_ROCK ||
7674 action == ACTION_SMASHED_BY_SPRING ||
7675 action == ACTION_EATING) &&
7676 graphic_action == graphic_default)
7678 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7679 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7680 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7681 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7684 /* no separate animation for "smashed by rock" -- use rock instead */
7685 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7686 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7688 g_em->bitmap = g_xx->bitmap;
7689 g_em->src_x = g_xx->src_x;
7690 g_em->src_y = g_xx->src_y;
7691 g_em->src_offset_x = g_xx->src_offset_x;
7692 g_em->src_offset_y = g_xx->src_offset_y;
7693 g_em->dst_offset_x = g_xx->dst_offset_x;
7694 g_em->dst_offset_y = g_xx->dst_offset_y;
7695 g_em->width = g_xx->width;
7696 g_em->height = g_xx->height;
7697 g_em->unique_identifier = g_xx->unique_identifier;
7700 g_em->preserve_background = TRUE;
7705 for (p = 0; p < MAX_PLAYERS; p++)
7707 for (i = 0; i < SPR_MAX; i++)
7709 int element = player_mapping[p][i].element_rnd;
7710 int action = player_mapping[p][i].action;
7711 int direction = player_mapping[p][i].direction;
7713 for (j = 0; j < 8; j++)
7715 int effective_element = element;
7716 int effective_action = action;
7717 int graphic = (direction == MV_NONE ?
7718 el_act2img(effective_element, effective_action) :
7719 el_act_dir2img(effective_element, effective_action,
7721 struct GraphicInfo *g = &graphic_info[graphic];
7722 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7728 Bitmap *debug_bitmap = g_em->bitmap;
7729 int debug_src_x = g_em->src_x;
7730 int debug_src_y = g_em->src_y;
7733 int frame = getAnimationFrame(g->anim_frames,
7736 g->anim_start_frame,
7739 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7741 g_em->bitmap = src_bitmap;
7742 g_em->src_x = src_x;
7743 g_em->src_y = src_y;
7744 g_em->src_offset_x = 0;
7745 g_em->src_offset_y = 0;
7746 g_em->dst_offset_x = 0;
7747 g_em->dst_offset_y = 0;
7748 g_em->width = TILEX;
7749 g_em->height = TILEY;
7753 /* skip check for EMC elements not contained in original EMC artwork */
7754 if (element == EL_PLAYER_3 ||
7755 element == EL_PLAYER_4)
7758 if (g_em->bitmap != debug_bitmap ||
7759 g_em->src_x != debug_src_x ||
7760 g_em->src_y != debug_src_y)
7762 static int last_i = -1;
7770 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7771 p, i, element, element_info[element].token_name,
7772 element_action_info[effective_action].suffix, direction);
7774 if (element != effective_element)
7775 printf(" [%d ('%s')]",
7777 element_info[effective_element].token_name);
7781 if (g_em->bitmap != debug_bitmap)
7782 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7783 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7785 if (g_em->src_x != debug_src_x ||
7786 g_em->src_y != debug_src_y)
7787 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7789 g_em->src_x, g_em->src_y,
7790 g_em->src_x / 32, g_em->src_y / 32,
7791 debug_src_x, debug_src_y,
7792 debug_src_x / 32, debug_src_y / 32);
7794 num_em_gfx_errors++;
7804 printf("::: [%d errors found]\n", num_em_gfx_errors);
7810 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7811 boolean any_player_moving,
7812 boolean any_player_snapping,
7813 boolean any_player_dropping)
7815 static boolean player_was_waiting = TRUE;
7817 if (frame == 0 && !any_player_dropping)
7819 if (!player_was_waiting)
7821 if (!SaveEngineSnapshotToList())
7824 player_was_waiting = TRUE;
7827 else if (any_player_moving || any_player_snapping || any_player_dropping)
7829 player_was_waiting = FALSE;
7833 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7834 boolean murphy_is_dropping)
7836 static boolean player_was_waiting = TRUE;
7838 if (murphy_is_waiting)
7840 if (!player_was_waiting)
7842 if (!SaveEngineSnapshotToList())
7845 player_was_waiting = TRUE;
7850 player_was_waiting = FALSE;
7854 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7855 boolean any_player_moving,
7856 boolean any_player_snapping,
7857 boolean any_player_dropping)
7859 if (tape.single_step && tape.recording && !tape.pausing)
7860 if (frame == 0 && !any_player_dropping)
7861 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7863 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7864 any_player_snapping, any_player_dropping);
7867 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7868 boolean murphy_is_dropping)
7870 if (tape.single_step && tape.recording && !tape.pausing)
7871 if (murphy_is_waiting)
7872 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7874 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7877 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7878 int graphic, int sync_frame, int x, int y)
7880 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7882 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7885 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7887 return (IS_NEXT_FRAME(sync_frame, graphic));
7890 int getGraphicInfo_Delay(int graphic)
7892 return graphic_info[graphic].anim_delay;
7895 void PlayMenuSoundExt(int sound)
7897 if (sound == SND_UNDEFINED)
7900 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7901 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7904 if (IS_LOOP_SOUND(sound))
7905 PlaySoundLoop(sound);
7910 void PlayMenuSound()
7912 PlayMenuSoundExt(menu.sound[game_status]);
7915 void PlayMenuSoundStereo(int sound, int stereo_position)
7917 if (sound == SND_UNDEFINED)
7920 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7921 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7924 if (IS_LOOP_SOUND(sound))
7925 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7927 PlaySoundStereo(sound, stereo_position);
7930 void PlayMenuSoundIfLoopExt(int sound)
7932 if (sound == SND_UNDEFINED)
7935 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7936 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7939 if (IS_LOOP_SOUND(sound))
7940 PlaySoundLoop(sound);
7943 void PlayMenuSoundIfLoop()
7945 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7948 void PlayMenuMusicExt(int music)
7950 if (music == MUS_UNDEFINED)
7953 if (!setup.sound_music)
7959 void PlayMenuMusic()
7961 PlayMenuMusicExt(menu.music[game_status]);
7964 void PlaySoundActivating()
7967 PlaySound(SND_MENU_ITEM_ACTIVATING);
7971 void PlaySoundSelecting()
7974 PlaySound(SND_MENU_ITEM_SELECTING);
7978 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7980 boolean change_fullscreen = (setup.fullscreen !=
7981 video.fullscreen_enabled);
7982 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7983 !strEqual(setup.fullscreen_mode,
7984 video.fullscreen_mode_current));
7985 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7986 setup.window_scaling_percent !=
7987 video.window_scaling_percent);
7989 if (change_window_scaling_percent && video.fullscreen_enabled)
7992 if (!change_window_scaling_percent && !video.fullscreen_available)
7995 #if defined(TARGET_SDL2)
7996 if (change_window_scaling_percent)
7998 SDLSetWindowScaling(setup.window_scaling_percent);
8002 else if (change_fullscreen)
8004 SDLSetWindowFullscreen(setup.fullscreen);
8006 /* set setup value according to successfully changed fullscreen mode */
8007 setup.fullscreen = video.fullscreen_enabled;
8013 if (change_fullscreen ||
8014 change_fullscreen_mode ||
8015 change_window_scaling_percent)
8017 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8019 /* save backbuffer content which gets lost when toggling fullscreen mode */
8020 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8022 if (change_fullscreen_mode)
8024 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8025 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8028 if (change_window_scaling_percent)
8030 /* keep window mode, but change window scaling */
8031 video.fullscreen_enabled = TRUE; /* force new window scaling */
8034 /* toggle fullscreen */
8035 ChangeVideoModeIfNeeded(setup.fullscreen);
8037 /* set setup value according to successfully changed fullscreen mode */
8038 setup.fullscreen = video.fullscreen_enabled;
8040 /* restore backbuffer content from temporary backbuffer backup bitmap */
8041 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8043 FreeBitmap(tmp_backbuffer);
8045 /* update visible window/screen */
8046 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8050 void ChangeViewportPropertiesIfNeeded()
8052 int gfx_game_mode = game_status;
8053 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8055 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8056 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8057 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8058 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8059 int border_size = vp_playfield->border_size;
8060 int new_sx = vp_playfield->x + border_size;
8061 int new_sy = vp_playfield->y + border_size;
8062 int new_sxsize = vp_playfield->width - 2 * border_size;
8063 int new_sysize = vp_playfield->height - 2 * border_size;
8064 int new_real_sx = vp_playfield->x;
8065 int new_real_sy = vp_playfield->y;
8066 int new_full_sxsize = vp_playfield->width;
8067 int new_full_sysize = vp_playfield->height;
8068 int new_dx = vp_door_1->x;
8069 int new_dy = vp_door_1->y;
8070 int new_dxsize = vp_door_1->width;
8071 int new_dysize = vp_door_1->height;
8072 int new_vx = vp_door_2->x;
8073 int new_vy = vp_door_2->y;
8074 int new_vxsize = vp_door_2->width;
8075 int new_vysize = vp_door_2->height;
8076 int new_ex = vp_door_3->x;
8077 int new_ey = vp_door_3->y;
8078 int new_exsize = vp_door_3->width;
8079 int new_eysize = vp_door_3->height;
8080 int new_tilesize_var =
8081 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8083 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8084 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8085 int new_scr_fieldx = new_sxsize / tilesize;
8086 int new_scr_fieldy = new_sysize / tilesize;
8087 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8088 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8089 boolean init_gfx_buffers = FALSE;
8090 boolean init_video_buffer = FALSE;
8091 boolean init_gadgets_and_toons = FALSE;
8092 boolean init_em_graphics = FALSE;
8093 boolean drawing_area_changed = FALSE;
8095 if (viewport.window.width != WIN_XSIZE ||
8096 viewport.window.height != WIN_YSIZE)
8098 WIN_XSIZE = viewport.window.width;
8099 WIN_YSIZE = viewport.window.height;
8101 init_video_buffer = TRUE;
8102 init_gfx_buffers = TRUE;
8104 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8107 if (new_scr_fieldx != SCR_FIELDX ||
8108 new_scr_fieldy != SCR_FIELDY)
8110 /* this always toggles between MAIN and GAME when using small tile size */
8112 SCR_FIELDX = new_scr_fieldx;
8113 SCR_FIELDY = new_scr_fieldy;
8115 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8126 new_sxsize != SXSIZE ||
8127 new_sysize != SYSIZE ||
8128 new_dxsize != DXSIZE ||
8129 new_dysize != DYSIZE ||
8130 new_vxsize != VXSIZE ||
8131 new_vysize != VYSIZE ||
8132 new_exsize != EXSIZE ||
8133 new_eysize != EYSIZE ||
8134 new_real_sx != REAL_SX ||
8135 new_real_sy != REAL_SY ||
8136 new_full_sxsize != FULL_SXSIZE ||
8137 new_full_sysize != FULL_SYSIZE ||
8138 new_tilesize_var != TILESIZE_VAR
8141 if (new_tilesize_var != TILESIZE_VAR)
8143 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8145 // changing tile size invalidates scroll values of engine snapshots
8146 FreeEngineSnapshotSingle();
8148 // changing tile size requires update of graphic mapping for EM engine
8149 init_em_graphics = TRUE;
8154 new_sxsize != SXSIZE ||
8155 new_sysize != SYSIZE ||
8156 new_real_sx != REAL_SX ||
8157 new_real_sy != REAL_SY ||
8158 new_full_sxsize != FULL_SXSIZE ||
8159 new_full_sysize != FULL_SYSIZE)
8161 if (!init_video_buffer)
8162 drawing_area_changed = TRUE;
8173 SXSIZE = new_sxsize;
8174 SYSIZE = new_sysize;
8175 DXSIZE = new_dxsize;
8176 DYSIZE = new_dysize;
8177 VXSIZE = new_vxsize;
8178 VYSIZE = new_vysize;
8179 EXSIZE = new_exsize;
8180 EYSIZE = new_eysize;
8181 REAL_SX = new_real_sx;
8182 REAL_SY = new_real_sy;
8183 FULL_SXSIZE = new_full_sxsize;
8184 FULL_SYSIZE = new_full_sysize;
8185 TILESIZE_VAR = new_tilesize_var;
8187 init_gfx_buffers = TRUE;
8188 init_gadgets_and_toons = TRUE;
8190 // printf("::: viewports: init_gfx_buffers\n");
8191 // printf("::: viewports: init_gadgets_and_toons\n");
8194 if (init_gfx_buffers)
8196 // printf("::: init_gfx_buffers\n");
8198 SCR_FIELDX = new_scr_fieldx_buffers;
8199 SCR_FIELDY = new_scr_fieldy_buffers;
8203 SCR_FIELDX = new_scr_fieldx;
8204 SCR_FIELDY = new_scr_fieldy;
8206 gfx.drawing_area_changed = drawing_area_changed;
8208 SetDrawDeactivationMask(REDRAW_NONE);
8209 SetDrawBackgroundMask(REDRAW_FIELD);
8212 if (init_video_buffer)
8214 // printf("::: init_video_buffer\n");
8216 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8219 if (init_gadgets_and_toons)
8221 // printf("::: init_gadgets_and_toons\n");
8227 if (init_em_graphics)
8229 InitGraphicInfo_EM();