1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static char *print_if_not_empty(int element)
178 static char *s = NULL;
179 char *token_name = element_info[element].token_name;
184 s = checked_malloc(strlen(token_name) + 10 + 1);
186 if (element != EL_EMPTY)
187 sprintf(s, "%d\t['%s']", element, token_name);
189 sprintf(s, "%d", element);
194 void DumpTile(int x, int y)
199 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
205 printf_line("-", 79);
206 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
207 printf_line("-", 79);
209 if (!IN_LEV_FIELD(x, y))
211 printf("(not in level field)\n");
217 printf(" Feld: %d\t['%s']\n", Feld[x][y],
218 element_info[Feld[x][y]].token_name);
219 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
220 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
221 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
222 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
223 printf(" MovPos: %d\n", MovPos[x][y]);
224 printf(" MovDir: %d\n", MovDir[x][y]);
225 printf(" MovDelay: %d\n", MovDelay[x][y]);
226 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
227 printf(" CustomValue: %d\n", CustomValue[x][y]);
228 printf(" GfxElement: %d\n", GfxElement[x][y]);
229 printf(" GfxAction: %d\n", GfxAction[x][y]);
230 printf(" GfxFrame: %d\n", GfxFrame[x][y]);
234 void SetDrawtoField(int mode)
236 if (mode == DRAW_BUFFERED && setup.soft_scrolling)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
247 drawto_field = fieldbuffer;
249 else /* DRAW_BACKBUFFER */
255 BX2 = SCR_FIELDX - 1;
256 BY2 = SCR_FIELDY - 1;
260 drawto_field = backbuffer;
264 static void RedrawPlayfield_RND()
266 if (game.envelope_active)
269 DrawLevel(REDRAW_ALL);
273 void RedrawPlayfield()
275 if (game_status != GAME_MODE_PLAYING)
278 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
279 RedrawPlayfield_EM(TRUE);
280 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
281 RedrawPlayfield_SP(TRUE);
282 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
283 RedrawPlayfield_RND();
285 BlitScreenToBitmap(backbuffer);
287 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
291 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
293 Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
295 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
298 void DrawMaskedBorder_FIELD()
300 if (global.border_status >= GAME_MODE_TITLE &&
301 global.border_status <= GAME_MODE_PLAYING &&
302 border.draw_masked[global.border_status])
303 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
306 void DrawMaskedBorder_DOOR_1()
308 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
309 (global.border_status != GAME_MODE_EDITOR ||
310 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
311 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
314 void DrawMaskedBorder_DOOR_2()
316 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
317 global.border_status != GAME_MODE_EDITOR)
318 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
321 void DrawMaskedBorder_DOOR_3()
323 /* currently not available */
326 void DrawMaskedBorder_ALL()
328 DrawMaskedBorder_FIELD();
329 DrawMaskedBorder_DOOR_1();
330 DrawMaskedBorder_DOOR_2();
331 DrawMaskedBorder_DOOR_3();
334 void DrawMaskedBorder(int redraw_mask)
336 /* never draw masked screen borders on borderless screens */
337 if (effectiveGameStatus() == GAME_MODE_LOADING ||
338 effectiveGameStatus() == GAME_MODE_TITLE)
341 /* never draw masked screen borders when displaying request outside door */
342 if (effectiveGameStatus() == GAME_MODE_PSEUDO_DOOR &&
343 global.use_envelope_request)
346 if (redraw_mask & REDRAW_ALL)
347 DrawMaskedBorder_ALL();
350 if (redraw_mask & REDRAW_FIELD)
351 DrawMaskedBorder_FIELD();
352 if (redraw_mask & REDRAW_DOOR_1)
353 DrawMaskedBorder_DOOR_1();
354 if (redraw_mask & REDRAW_DOOR_2)
355 DrawMaskedBorder_DOOR_2();
356 if (redraw_mask & REDRAW_DOOR_3)
357 DrawMaskedBorder_DOOR_3();
361 static void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
363 DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
364 int fx = FX, fy = FY;
365 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
366 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
368 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
369 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
370 int dx_var = dx * TILESIZE_VAR / TILESIZE;
371 int dy_var = dy * TILESIZE_VAR / TILESIZE;
374 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
375 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
377 if (EVEN(SCR_FIELDX))
379 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
380 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
382 fx += (dx_var > 0 ? TILEX_VAR : 0);
389 if (EVEN(SCR_FIELDY))
391 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
392 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
394 fy += (dy_var > 0 ? TILEY_VAR : 0);
401 if (full_lev_fieldx <= SCR_FIELDX)
403 if (EVEN(SCR_FIELDX))
404 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
406 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
409 if (full_lev_fieldy <= SCR_FIELDY)
411 if (EVEN(SCR_FIELDY))
412 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
414 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
417 if (border.draw_masked[GAME_MODE_PLAYING])
419 if (buffer != backbuffer)
421 /* copy playfield buffer to backbuffer to add masked border */
422 BlitBitmap(buffer, backbuffer, fx, fy, SXSIZE, SYSIZE, SX, SY);
423 DrawMaskedBorder(REDRAW_FIELD);
426 BlitBitmap(backbuffer, target_bitmap,
427 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
432 BlitBitmap(buffer, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
436 void BlitScreenToBitmap(Bitmap *target_bitmap)
438 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
439 BlitScreenToBitmap_EM(target_bitmap);
440 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
441 BlitScreenToBitmap_SP(target_bitmap);
442 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
443 BlitScreenToBitmap_RND(target_bitmap);
449 DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
451 if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
452 redraw_mask |= REDRAW_FIELD;
455 // never redraw single tiles, always redraw the whole field
456 // (redrawing single tiles up to a certain threshold was faster on old,
457 // now legacy graphics, but slows things down on modern graphics now)
458 // UPDATE: this is now globally defined by value of REDRAWTILES_THRESHOLD
459 if (redraw_mask & REDRAW_TILES)
460 redraw_mask |= REDRAW_FIELD;
464 /* !!! TEST ONLY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
465 /* (force full redraw) */
466 if (game_status == GAME_MODE_PLAYING)
467 redraw_mask |= REDRAW_FIELD;
470 if (redraw_mask & REDRAW_FIELD)
471 redraw_mask &= ~REDRAW_TILES;
473 if (redraw_mask == REDRAW_NONE)
478 if (redraw_mask & REDRAW_ALL)
479 printf("[REDRAW_ALL]");
480 if (redraw_mask & REDRAW_FIELD)
481 printf("[REDRAW_FIELD]");
482 if (redraw_mask & REDRAW_TILES)
483 printf("[REDRAW_TILES]");
484 if (redraw_mask & REDRAW_DOOR_1)
485 printf("[REDRAW_DOOR_1]");
486 if (redraw_mask & REDRAW_DOOR_2)
487 printf("[REDRAW_DOOR_2]");
488 if (redraw_mask & REDRAW_FROM_BACKBUFFER)
489 printf("[REDRAW_FROM_BACKBUFFER]");
490 printf(" [%d]\n", FrameCounter);
493 if (redraw_mask & REDRAW_TILES &&
494 game_status == GAME_MODE_PLAYING &&
495 border.draw_masked[GAME_MODE_PLAYING])
496 redraw_mask |= REDRAW_FIELD;
498 if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
500 static boolean last_frame_skipped = FALSE;
501 boolean skip_even_when_not_scrolling = TRUE;
502 boolean just_scrolling = (ScreenMovDir != 0);
503 boolean verbose = FALSE;
505 if (global.fps_slowdown_factor > 1 &&
506 (FrameCounter % global.fps_slowdown_factor) &&
507 (just_scrolling || skip_even_when_not_scrolling))
509 redraw_mask &= ~REDRAW_MAIN;
511 last_frame_skipped = TRUE;
514 printf("FRAME SKIPPED\n");
518 if (last_frame_skipped)
519 redraw_mask |= REDRAW_FIELD;
521 last_frame_skipped = FALSE;
524 printf("frame not skipped\n");
528 /* synchronize X11 graphics at this point; if we would synchronize the
529 display immediately after the buffer switching (after the XFlush),
530 this could mean that we have to wait for the graphics to complete,
531 although we could go on doing calculations for the next frame */
535 /* never draw masked border to backbuffer when using playfield buffer */
536 if (game_status != GAME_MODE_PLAYING ||
537 redraw_mask & REDRAW_FROM_BACKBUFFER ||
538 buffer == backbuffer)
539 DrawMaskedBorder(redraw_mask);
541 DrawMaskedBorder(redraw_mask & REDRAW_DOORS);
543 if (redraw_mask & REDRAW_ALL)
545 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
547 redraw_mask = REDRAW_NONE;
550 if (redraw_mask & REDRAW_FIELD)
552 if (game_status != GAME_MODE_PLAYING ||
553 redraw_mask & REDRAW_FROM_BACKBUFFER)
555 BlitBitmap(backbuffer, window,
556 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
560 BlitScreenToBitmap_RND(window);
563 redraw_mask &= ~REDRAW_MAIN;
566 if (redraw_mask & REDRAW_DOORS)
568 if (redraw_mask & REDRAW_DOOR_1)
569 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
571 if (redraw_mask & REDRAW_DOOR_2)
572 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
574 if (redraw_mask & REDRAW_DOOR_3)
575 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
577 redraw_mask &= ~REDRAW_DOORS;
580 if (redraw_mask & REDRAW_MICROLEVEL)
582 BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
583 SX, SY + 10 * TILEY);
585 redraw_mask &= ~REDRAW_MICROLEVEL;
588 if (redraw_mask & REDRAW_TILES)
594 int dx_var = dx * TILESIZE_VAR / TILESIZE;
595 int dy_var = dy * TILESIZE_VAR / TILESIZE;
597 int fx = FX, fy = FY;
599 int scr_fieldx = SCR_FIELDX + (EVEN(SCR_FIELDX) ? 2 : 0);
600 int scr_fieldy = SCR_FIELDY + (EVEN(SCR_FIELDY) ? 2 : 0);
602 InitGfxClipRegion(TRUE, SX, SY, SXSIZE, SYSIZE);
604 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
605 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
607 if (EVEN(SCR_FIELDX))
609 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
611 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
620 fx += (dx_var > 0 ? TILEX_VAR : 0);
624 if (EVEN(SCR_FIELDY))
626 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
628 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
637 fy += (dy_var > 0 ? TILEY_VAR : 0);
641 for (x = 0; x < scr_fieldx; x++)
642 for (y = 0 ; y < scr_fieldy; y++)
643 if (redraw[redraw_x1 + x][redraw_y1 + y])
644 BlitBitmap(buffer, window,
645 FX + x * TILEX_VAR, FY + y * TILEY_VAR,
646 TILEX_VAR, TILEY_VAR,
647 sx + x * TILEX_VAR, sy + y * TILEY_VAR);
649 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
652 if (redraw_mask & REDRAW_FPS) /* display frames per second */
657 sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
658 if (!global.fps_slowdown)
661 sprintf(text, "%04.1f fps%s", global.frames_per_second, info1);
663 DrawTextExt(window, SX + SXSIZE + SX, 0, text, FONT_TEXT_2, BLIT_OPAQUE);
666 for (x = 0; x < MAX_BUF_XSIZE; x++)
667 for (y = 0; y < MAX_BUF_YSIZE; y++)
670 redraw_mask = REDRAW_NONE;
673 static void FadeCrossSaveBackbuffer()
675 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
678 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
680 static int fade_type_skip = FADE_TYPE_NONE;
681 void (*draw_border_function)(void) = NULL;
682 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
683 int x, y, width, height;
684 int fade_delay, post_delay;
686 if (fade_type == FADE_TYPE_FADE_OUT)
688 if (fade_type_skip != FADE_TYPE_NONE)
690 /* skip all fade operations until specified fade operation */
691 if (fade_type & fade_type_skip)
692 fade_type_skip = FADE_TYPE_NONE;
697 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
699 FadeCrossSaveBackbuffer();
705 redraw_mask |= fade_mask;
707 if (fade_type == FADE_TYPE_SKIP)
709 fade_type_skip = fade_mode;
714 fade_delay = fading.fade_delay;
715 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
717 if (fade_type_skip != FADE_TYPE_NONE)
719 /* skip all fade operations until specified fade operation */
720 if (fade_type & fade_type_skip)
721 fade_type_skip = FADE_TYPE_NONE;
726 if (global.autoplay_leveldir)
731 if (fade_mask == REDRAW_FIELD)
736 height = FULL_SYSIZE;
738 if (border.draw_masked_when_fading)
739 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
741 DrawMaskedBorder_FIELD(); /* draw once */
743 else /* REDRAW_ALL */
751 if (!setup.fade_screens ||
753 fading.fade_mode == FADE_MODE_NONE)
755 if (fade_mode == FADE_MODE_FADE_OUT)
758 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
760 redraw_mask &= ~fade_mask;
765 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
766 draw_border_function);
768 redraw_mask &= ~fade_mask;
771 void FadeIn(int fade_mask)
773 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
774 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
776 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
779 void FadeOut(int fade_mask)
781 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
782 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
784 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
786 global.border_status = game_status;
789 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
791 static struct TitleFadingInfo fading_leave_stored;
794 fading_leave_stored = fading_leave;
796 fading = fading_leave_stored;
799 void FadeSetEnterMenu()
801 fading = menu.enter_menu;
803 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
806 void FadeSetLeaveMenu()
808 fading = menu.leave_menu;
810 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
813 void FadeSetEnterScreen()
815 fading = menu.enter_screen[game_status];
817 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
820 void FadeSetNextScreen()
822 fading = menu.next_screen;
824 // (do not overwrite fade mode set by FadeSetEnterScreen)
825 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
828 void FadeSetLeaveScreen()
830 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
833 void FadeSetFromType(int type)
835 if (type & TYPE_ENTER_SCREEN)
836 FadeSetEnterScreen();
837 else if (type & TYPE_ENTER)
839 else if (type & TYPE_LEAVE)
843 void FadeSetDisabled()
845 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
847 fading = fading_none;
850 void FadeSkipNextFadeIn()
852 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
855 void FadeSkipNextFadeOut()
857 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
860 void SetWindowBackgroundImageIfDefined(int graphic)
862 if (graphic_info[graphic].bitmap)
863 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
866 void SetMainBackgroundImageIfDefined(int graphic)
868 if (graphic_info[graphic].bitmap)
869 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
872 void SetDoorBackgroundImageIfDefined(int graphic)
874 if (graphic_info[graphic].bitmap)
875 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
878 void SetWindowBackgroundImage(int graphic)
880 SetWindowBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
881 graphic_info[graphic].bitmap ?
882 graphic_info[graphic].bitmap :
883 graphic_info[IMG_BACKGROUND].bitmap);
886 void SetMainBackgroundImage(int graphic)
888 SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
889 graphic_info[graphic].bitmap ?
890 graphic_info[graphic].bitmap :
891 graphic_info[IMG_BACKGROUND].bitmap);
894 void SetDoorBackgroundImage(int graphic)
896 SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
897 graphic_info[graphic].bitmap ?
898 graphic_info[graphic].bitmap :
899 graphic_info[IMG_BACKGROUND].bitmap);
902 void SetPanelBackground()
904 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
906 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
907 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
909 SetDoorBackgroundBitmap(bitmap_db_panel);
912 void DrawBackground(int x, int y, int width, int height)
914 /* "drawto" might still point to playfield buffer here (hall of fame) */
915 ClearRectangleOnBackground(backbuffer, x, y, width, height);
917 if (IN_GFX_FIELD_FULL(x, y))
918 redraw_mask |= REDRAW_FIELD;
919 else if (IN_GFX_DOOR_1(x, y))
920 redraw_mask |= REDRAW_DOOR_1;
921 else if (IN_GFX_DOOR_2(x, y))
922 redraw_mask |= REDRAW_DOOR_2;
923 else if (IN_GFX_DOOR_3(x, y))
924 redraw_mask |= REDRAW_DOOR_3;
927 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
929 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
931 if (font->bitmap == NULL)
934 DrawBackground(x, y, width, height);
937 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
939 struct GraphicInfo *g = &graphic_info[graphic];
941 if (g->bitmap == NULL)
944 DrawBackground(x, y, width, height);
949 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
950 /* (when entering hall of fame after playing) */
951 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
953 /* !!! maybe this should be done before clearing the background !!! */
954 if (setup.soft_scrolling && game_status == GAME_MODE_PLAYING)
956 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
957 SetDrawtoField(DRAW_BUFFERED);
960 SetDrawtoField(DRAW_BACKBUFFER);
963 void MarkTileDirty(int x, int y)
965 int xx = redraw_x1 + x;
966 int yy = redraw_y1 + y;
971 redraw[xx][yy] = TRUE;
972 redraw_mask |= REDRAW_TILES;
975 void SetBorderElement()
979 BorderElement = EL_EMPTY;
981 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
983 for (x = 0; x < lev_fieldx; x++)
985 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
986 BorderElement = EL_STEELWALL;
988 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
994 void FloodFillLevel(int from_x, int from_y, int fill_element,
995 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
996 int max_fieldx, int max_fieldy)
1000 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1001 static int safety = 0;
1003 /* check if starting field still has the desired content */
1004 if (field[from_x][from_y] == fill_element)
1009 if (safety > max_fieldx * max_fieldy)
1010 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1012 old_element = field[from_x][from_y];
1013 field[from_x][from_y] = fill_element;
1015 for (i = 0; i < 4; i++)
1017 x = from_x + check[i][0];
1018 y = from_y + check[i][1];
1020 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1021 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1027 void SetRandomAnimationValue(int x, int y)
1029 gfx.anim_random_frame = GfxRandom[x][y];
1032 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
1034 /* animation synchronized with global frame counter, not move position */
1035 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1036 sync_frame = FrameCounter;
1038 return getAnimationFrame(graphic_info[graphic].anim_frames,
1039 graphic_info[graphic].anim_delay,
1040 graphic_info[graphic].anim_mode,
1041 graphic_info[graphic].anim_start_frame,
1045 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1046 Bitmap **bitmap, int *x, int *y,
1047 boolean get_backside)
1049 struct GraphicInfo *g = &graphic_info[graphic];
1050 Bitmap *src_bitmap = g->bitmap;
1051 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1052 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1053 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1055 // if no in-game graphics defined, always use standard graphic size
1056 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1057 tilesize = TILESIZE;
1059 if (tilesize == gfx.standard_tile_size)
1060 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1061 else if (tilesize == game.tile_size)
1062 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1064 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1066 if (g->offset_y == 0) /* frames are ordered horizontally */
1068 int max_width = g->anim_frames_per_line * g->width;
1069 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1071 src_x = pos % max_width;
1072 src_y = src_y % g->height + pos / max_width * g->height;
1074 else if (g->offset_x == 0) /* frames are ordered vertically */
1076 int max_height = g->anim_frames_per_line * g->height;
1077 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1079 src_x = src_x % g->width + pos / max_height * g->width;
1080 src_y = pos % max_height;
1082 else /* frames are ordered diagonally */
1084 src_x = src_x + frame * g->offset_x;
1085 src_y = src_y + frame * g->offset_y;
1088 *bitmap = src_bitmap;
1089 *x = src_x * tilesize / TILESIZE;
1090 *y = src_y * tilesize / TILESIZE;
1093 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1094 int *x, int *y, boolean get_backside)
1096 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1100 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1101 Bitmap **bitmap, int *x, int *y)
1103 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1106 void getFixedGraphicSource(int graphic, int frame,
1107 Bitmap **bitmap, int *x, int *y)
1109 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1112 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1114 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1117 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1118 int *x, int *y, boolean get_backside)
1120 struct GraphicInfo *g = &graphic_info[graphic];
1121 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1122 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1124 if (TILESIZE_VAR != TILESIZE)
1125 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1128 *bitmap = g->bitmap;
1130 if (g->offset_y == 0) /* frames are ordered horizontally */
1132 int max_width = g->anim_frames_per_line * g->width;
1133 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1135 *x = pos % max_width;
1136 *y = src_y % g->height + pos / max_width * g->height;
1138 else if (g->offset_x == 0) /* frames are ordered vertically */
1140 int max_height = g->anim_frames_per_line * g->height;
1141 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1143 *x = src_x % g->width + pos / max_height * g->width;
1144 *y = pos % max_height;
1146 else /* frames are ordered diagonally */
1148 *x = src_x + frame * g->offset_x;
1149 *y = src_y + frame * g->offset_y;
1153 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1155 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1158 void DrawGraphic(int x, int y, int graphic, int frame)
1161 if (!IN_SCR_FIELD(x, y))
1163 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1164 printf("DrawGraphic(): This should never happen!\n");
1169 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1172 MarkTileDirty(x, y);
1175 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1178 if (!IN_SCR_FIELD(x, y))
1180 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1181 printf("DrawGraphic(): This should never happen!\n");
1186 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1188 MarkTileDirty(x, y);
1191 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1197 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1199 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1202 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1208 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1209 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1212 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1215 if (!IN_SCR_FIELD(x, y))
1217 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1218 printf("DrawGraphicThruMask(): This should never happen!\n");
1223 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1226 MarkTileDirty(x, y);
1229 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1232 if (!IN_SCR_FIELD(x, y))
1234 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1235 printf("DrawGraphicThruMask(): This should never happen!\n");
1240 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1242 MarkTileDirty(x, y);
1245 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1251 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1253 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1257 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1258 int graphic, int frame)
1260 struct GraphicInfo *g = &graphic_info[graphic];
1264 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1266 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1270 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1272 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1274 MarkTileDirty(x / tilesize, y / tilesize);
1277 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1283 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1284 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1287 void DrawMiniGraphic(int x, int y, int graphic)
1289 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1290 MarkTileDirty(x / 2, y / 2);
1293 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1298 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1299 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1302 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1303 int graphic, int frame,
1304 int cut_mode, int mask_mode)
1309 int width = TILEX, height = TILEY;
1312 if (dx || dy) /* shifted graphic */
1314 if (x < BX1) /* object enters playfield from the left */
1321 else if (x > BX2) /* object enters playfield from the right */
1327 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1333 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1335 else if (dx) /* general horizontal movement */
1336 MarkTileDirty(x + SIGN(dx), y);
1338 if (y < BY1) /* object enters playfield from the top */
1340 if (cut_mode==CUT_BELOW) /* object completely above top border */
1348 else if (y > BY2) /* object enters playfield from the bottom */
1354 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1360 else if (dy > 0 && cut_mode == CUT_ABOVE)
1362 if (y == BY2) /* object completely above bottom border */
1368 MarkTileDirty(x, y + 1);
1369 } /* object leaves playfield to the bottom */
1370 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1372 else if (dy) /* general vertical movement */
1373 MarkTileDirty(x, y + SIGN(dy));
1377 if (!IN_SCR_FIELD(x, y))
1379 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1380 printf("DrawGraphicShifted(): This should never happen!\n");
1385 width = width * TILESIZE_VAR / TILESIZE;
1386 height = height * TILESIZE_VAR / TILESIZE;
1387 cx = cx * TILESIZE_VAR / TILESIZE;
1388 cy = cy * TILESIZE_VAR / TILESIZE;
1389 dx = dx * TILESIZE_VAR / TILESIZE;
1390 dy = dy * TILESIZE_VAR / TILESIZE;
1392 if (width > 0 && height > 0)
1394 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1399 dst_x = FX + x * TILEX_VAR + dx;
1400 dst_y = FY + y * TILEY_VAR + dy;
1402 if (mask_mode == USE_MASKING)
1403 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1406 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1409 MarkTileDirty(x, y);
1413 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1414 int graphic, int frame,
1415 int cut_mode, int mask_mode)
1420 int width = TILEX_VAR, height = TILEY_VAR;
1423 int x2 = x + SIGN(dx);
1424 int y2 = y + SIGN(dy);
1426 /* movement with two-tile animations must be sync'ed with movement position,
1427 not with current GfxFrame (which can be higher when using slow movement) */
1428 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1429 int anim_frames = graphic_info[graphic].anim_frames;
1431 /* (we also need anim_delay here for movement animations with less frames) */
1432 int anim_delay = graphic_info[graphic].anim_delay;
1433 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1435 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1436 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1438 /* re-calculate animation frame for two-tile movement animation */
1439 frame = getGraphicAnimationFrame(graphic, sync_frame);
1441 /* check if movement start graphic inside screen area and should be drawn */
1442 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1444 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1446 dst_x = FX + x1 * TILEX_VAR;
1447 dst_y = FY + y1 * TILEY_VAR;
1449 if (mask_mode == USE_MASKING)
1450 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1453 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1456 MarkTileDirty(x1, y1);
1459 /* check if movement end graphic inside screen area and should be drawn */
1460 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1462 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1464 dst_x = FX + x2 * TILEX_VAR;
1465 dst_y = FY + y2 * TILEY_VAR;
1467 if (mask_mode == USE_MASKING)
1468 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1471 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1474 MarkTileDirty(x2, y2);
1478 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1479 int graphic, int frame,
1480 int cut_mode, int mask_mode)
1484 DrawGraphic(x, y, graphic, frame);
1489 if (graphic_info[graphic].double_movement) /* EM style movement images */
1490 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1492 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1495 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1496 int frame, int cut_mode)
1498 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1501 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1502 int cut_mode, int mask_mode)
1504 int lx = LEVELX(x), ly = LEVELY(y);
1508 if (IN_LEV_FIELD(lx, ly))
1510 SetRandomAnimationValue(lx, ly);
1512 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1513 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1515 /* do not use double (EM style) movement graphic when not moving */
1516 if (graphic_info[graphic].double_movement && !dx && !dy)
1518 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1519 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1522 else /* border element */
1524 graphic = el2img(element);
1525 frame = getGraphicAnimationFrame(graphic, -1);
1528 if (element == EL_EXPANDABLE_WALL)
1530 boolean left_stopped = FALSE, right_stopped = FALSE;
1532 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1533 left_stopped = TRUE;
1534 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1535 right_stopped = TRUE;
1537 if (left_stopped && right_stopped)
1539 else if (left_stopped)
1541 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1542 frame = graphic_info[graphic].anim_frames - 1;
1544 else if (right_stopped)
1546 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1547 frame = graphic_info[graphic].anim_frames - 1;
1552 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1553 else if (mask_mode == USE_MASKING)
1554 DrawGraphicThruMask(x, y, graphic, frame);
1556 DrawGraphic(x, y, graphic, frame);
1559 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1560 int cut_mode, int mask_mode)
1562 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1563 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1564 cut_mode, mask_mode);
1567 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1570 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1573 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1576 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1579 void DrawLevelElementThruMask(int x, int y, int element)
1581 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1584 void DrawLevelFieldThruMask(int x, int y)
1586 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1589 /* !!! implementation of quicksand is totally broken !!! */
1590 #define IS_CRUMBLED_TILE(x, y, e) \
1591 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1592 !IS_MOVING(x, y) || \
1593 (e) == EL_QUICKSAND_EMPTYING || \
1594 (e) == EL_QUICKSAND_FAST_EMPTYING))
1596 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1601 int width, height, cx, cy;
1602 int sx = SCREENX(x), sy = SCREENY(y);
1603 int crumbled_border_size = graphic_info[graphic].border_size;
1606 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1608 for (i = 1; i < 4; i++)
1610 int dxx = (i & 1 ? dx : 0);
1611 int dyy = (i & 2 ? dy : 0);
1614 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1617 /* check if neighbour field is of same crumble type */
1618 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1619 graphic_info[graphic].class ==
1620 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1622 /* return if check prevents inner corner */
1623 if (same == (dxx == dx && dyy == dy))
1627 /* if we reach this point, we have an inner corner */
1629 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1631 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1632 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1633 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1634 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1636 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1637 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1640 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1645 int width, height, bx, by, cx, cy;
1646 int sx = SCREENX(x), sy = SCREENY(y);
1647 int crumbled_border_size = graphic_info[graphic].border_size;
1648 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1649 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1652 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1654 /* draw simple, sloppy, non-corner-accurate crumbled border */
1656 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1657 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1658 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1659 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1661 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1662 FX + sx * TILEX_VAR + cx,
1663 FY + sy * TILEY_VAR + cy);
1665 /* (remaining middle border part must be at least as big as corner part) */
1666 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1667 crumbled_border_size >= TILESIZE / 3)
1670 /* correct corners of crumbled border, if needed */
1672 for (i = -1; i <= 1; i += 2)
1674 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1675 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1676 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1679 /* check if neighbour field is of same crumble type */
1680 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1681 graphic_info[graphic].class ==
1682 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1684 /* no crumbled corner, but continued crumbled border */
1686 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1687 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1688 int b1 = (i == 1 ? crumbled_border_size_var :
1689 TILESIZE_VAR - 2 * crumbled_border_size_var);
1691 width = crumbled_border_size_var;
1692 height = crumbled_border_size_var;
1694 if (dir == 1 || dir == 2)
1709 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1711 FX + sx * TILEX_VAR + cx,
1712 FY + sy * TILEY_VAR + cy);
1717 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1719 int sx = SCREENX(x), sy = SCREENY(y);
1722 static int xy[4][2] =
1730 if (!IN_LEV_FIELD(x, y))
1733 element = TILE_GFX_ELEMENT(x, y);
1735 /* crumble field itself */
1736 if (IS_CRUMBLED_TILE(x, y, element))
1738 if (!IN_SCR_FIELD(sx, sy))
1741 for (i = 0; i < 4; i++)
1743 int xx = x + xy[i][0];
1744 int yy = y + xy[i][1];
1746 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1749 /* check if neighbour field is of same crumble type */
1750 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1751 graphic_info[graphic].class ==
1752 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1755 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1758 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1759 graphic_info[graphic].anim_frames == 2)
1761 for (i = 0; i < 4; i++)
1763 int dx = (i & 1 ? +1 : -1);
1764 int dy = (i & 2 ? +1 : -1);
1766 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1770 MarkTileDirty(sx, sy);
1772 else /* center field not crumbled -- crumble neighbour fields */
1774 for (i = 0; i < 4; i++)
1776 int xx = x + xy[i][0];
1777 int yy = y + xy[i][1];
1778 int sxx = sx + xy[i][0];
1779 int syy = sy + xy[i][1];
1781 if (!IN_LEV_FIELD(xx, yy) ||
1782 !IN_SCR_FIELD(sxx, syy))
1785 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1788 element = TILE_GFX_ELEMENT(xx, yy);
1790 if (!IS_CRUMBLED_TILE(xx, yy, element))
1793 graphic = el_act2crm(element, ACTION_DEFAULT);
1795 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1797 MarkTileDirty(sxx, syy);
1802 void DrawLevelFieldCrumbled(int x, int y)
1806 if (!IN_LEV_FIELD(x, y))
1809 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1810 GfxElement[x][y] != EL_UNDEFINED &&
1811 GFX_CRUMBLED(GfxElement[x][y]))
1813 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1818 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1820 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1823 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1826 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1827 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1828 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1829 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1830 int sx = SCREENX(x), sy = SCREENY(y);
1832 DrawGraphic(sx, sy, graphic1, frame1);
1833 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1836 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1838 int sx = SCREENX(x), sy = SCREENY(y);
1839 static int xy[4][2] =
1848 for (i = 0; i < 4; i++)
1850 int xx = x + xy[i][0];
1851 int yy = y + xy[i][1];
1852 int sxx = sx + xy[i][0];
1853 int syy = sy + xy[i][1];
1855 if (!IN_LEV_FIELD(xx, yy) ||
1856 !IN_SCR_FIELD(sxx, syy) ||
1857 !GFX_CRUMBLED(Feld[xx][yy]) ||
1861 DrawLevelField(xx, yy);
1865 static int getBorderElement(int x, int y)
1869 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1870 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1871 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1872 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1873 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1874 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1875 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1877 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1878 int steel_position = (x == -1 && y == -1 ? 0 :
1879 x == lev_fieldx && y == -1 ? 1 :
1880 x == -1 && y == lev_fieldy ? 2 :
1881 x == lev_fieldx && y == lev_fieldy ? 3 :
1882 x == -1 || x == lev_fieldx ? 4 :
1883 y == -1 || y == lev_fieldy ? 5 : 6);
1885 return border[steel_position][steel_type];
1888 void DrawScreenElement(int x, int y, int element)
1890 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1891 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1894 void DrawLevelElement(int x, int y, int element)
1896 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1897 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1900 void DrawScreenField(int x, int y)
1902 int lx = LEVELX(x), ly = LEVELY(y);
1903 int element, content;
1905 if (!IN_LEV_FIELD(lx, ly))
1907 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1910 element = getBorderElement(lx, ly);
1912 DrawScreenElement(x, y, element);
1917 element = Feld[lx][ly];
1918 content = Store[lx][ly];
1920 if (IS_MOVING(lx, ly))
1922 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1923 boolean cut_mode = NO_CUTTING;
1925 if (element == EL_QUICKSAND_EMPTYING ||
1926 element == EL_QUICKSAND_FAST_EMPTYING ||
1927 element == EL_MAGIC_WALL_EMPTYING ||
1928 element == EL_BD_MAGIC_WALL_EMPTYING ||
1929 element == EL_DC_MAGIC_WALL_EMPTYING ||
1930 element == EL_AMOEBA_DROPPING)
1931 cut_mode = CUT_ABOVE;
1932 else if (element == EL_QUICKSAND_FILLING ||
1933 element == EL_QUICKSAND_FAST_FILLING ||
1934 element == EL_MAGIC_WALL_FILLING ||
1935 element == EL_BD_MAGIC_WALL_FILLING ||
1936 element == EL_DC_MAGIC_WALL_FILLING)
1937 cut_mode = CUT_BELOW;
1939 if (cut_mode == CUT_ABOVE)
1940 DrawScreenElement(x, y, element);
1942 DrawScreenElement(x, y, EL_EMPTY);
1945 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1946 else if (cut_mode == NO_CUTTING)
1947 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1950 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1952 if (cut_mode == CUT_BELOW &&
1953 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1954 DrawLevelElement(lx, ly + 1, element);
1957 if (content == EL_ACID)
1959 int dir = MovDir[lx][ly];
1960 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1961 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1963 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1966 else if (IS_BLOCKED(lx, ly))
1971 boolean cut_mode = NO_CUTTING;
1972 int element_old, content_old;
1974 Blocked2Moving(lx, ly, &oldx, &oldy);
1977 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1978 MovDir[oldx][oldy] == MV_RIGHT);
1980 element_old = Feld[oldx][oldy];
1981 content_old = Store[oldx][oldy];
1983 if (element_old == EL_QUICKSAND_EMPTYING ||
1984 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1985 element_old == EL_MAGIC_WALL_EMPTYING ||
1986 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1987 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1988 element_old == EL_AMOEBA_DROPPING)
1989 cut_mode = CUT_ABOVE;
1991 DrawScreenElement(x, y, EL_EMPTY);
1994 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1996 else if (cut_mode == NO_CUTTING)
1997 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2000 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2003 else if (IS_DRAWABLE(element))
2004 DrawScreenElement(x, y, element);
2006 DrawScreenElement(x, y, EL_EMPTY);
2009 void DrawLevelField(int x, int y)
2011 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2012 DrawScreenField(SCREENX(x), SCREENY(y));
2013 else if (IS_MOVING(x, y))
2017 Moving2Blocked(x, y, &newx, &newy);
2018 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2019 DrawScreenField(SCREENX(newx), SCREENY(newy));
2021 else if (IS_BLOCKED(x, y))
2025 Blocked2Moving(x, y, &oldx, &oldy);
2026 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2027 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2031 void DrawSizedElement(int x, int y, int element, int tilesize)
2035 graphic = el2edimg(element);
2036 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2039 void DrawMiniElement(int x, int y, int element)
2043 graphic = el2edimg(element);
2044 DrawMiniGraphic(x, y, graphic);
2047 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2050 int x = sx + scroll_x, y = sy + scroll_y;
2052 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2053 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2054 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2055 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2057 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2060 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2062 int x = sx + scroll_x, y = sy + scroll_y;
2064 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2065 DrawMiniElement(sx, sy, EL_EMPTY);
2066 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2067 DrawMiniElement(sx, sy, Feld[x][y]);
2069 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2072 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2073 int x, int y, int xsize, int ysize,
2074 int tile_width, int tile_height)
2078 int dst_x = startx + x * tile_width;
2079 int dst_y = starty + y * tile_height;
2080 int width = graphic_info[graphic].width;
2081 int height = graphic_info[graphic].height;
2082 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2083 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2084 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2085 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2086 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2087 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2088 boolean draw_masked = graphic_info[graphic].draw_masked;
2090 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2092 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2094 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2098 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2099 inner_sx + (x - 1) * tile_width % inner_width);
2100 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2101 inner_sy + (y - 1) * tile_height % inner_height);
2104 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2107 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2111 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2112 int x, int y, int xsize, int ysize, int font_nr)
2114 int font_width = getFontWidth(font_nr);
2115 int font_height = getFontHeight(font_nr);
2117 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2118 font_width, font_height);
2121 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2123 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2124 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2125 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2126 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2127 boolean no_delay = (tape.warp_forward);
2128 unsigned int anim_delay = 0;
2129 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2130 int anim_delay_value = (no_delay ? 0 : frame_delay_value);
2131 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2132 int font_width = getFontWidth(font_nr);
2133 int font_height = getFontHeight(font_nr);
2134 int max_xsize = level.envelope[envelope_nr].xsize;
2135 int max_ysize = level.envelope[envelope_nr].ysize;
2136 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2137 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2138 int xend = max_xsize;
2139 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2140 int xstep = (xstart < xend ? 1 : 0);
2141 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2144 for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2146 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2147 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2148 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2149 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2152 SetDrawtoField(DRAW_BUFFERED);
2154 BlitScreenToBitmap(backbuffer);
2156 SetDrawtoField(DRAW_BACKBUFFER);
2158 for (yy = 0; yy < ysize; yy++)
2159 for (xx = 0; xx < xsize; xx++)
2160 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2162 DrawTextBuffer(sx + font_width, sy + font_height,
2163 level.envelope[envelope_nr].text, font_nr, max_xsize,
2164 xsize - 2, ysize - 2, 0, mask_mode,
2165 level.envelope[envelope_nr].autowrap,
2166 level.envelope[envelope_nr].centered, FALSE);
2168 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2171 WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2175 void ShowEnvelope(int envelope_nr)
2177 int element = EL_ENVELOPE_1 + envelope_nr;
2178 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2179 int sound_opening = element_info[element].sound[ACTION_OPENING];
2180 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2181 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2182 boolean no_delay = (tape.warp_forward);
2183 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2184 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2185 int anim_mode = graphic_info[graphic].anim_mode;
2186 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2187 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2189 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2191 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2193 if (anim_mode == ANIM_DEFAULT)
2194 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2196 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2199 Delay(wait_delay_value);
2201 WaitForEventToContinue();
2203 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2205 if (anim_mode != ANIM_NONE)
2206 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2208 if (anim_mode == ANIM_DEFAULT)
2209 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2211 game.envelope_active = FALSE;
2213 SetDrawtoField(DRAW_BUFFERED);
2215 redraw_mask |= REDRAW_FIELD;
2219 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2221 int border_size = request.border_size;
2222 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2223 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2224 int sx = sx_center - request.width / 2;
2225 int sy = sy_center - request.height / 2;
2227 if (add_border_size)
2237 void DrawEnvelopeRequest(char *text)
2239 char *text_final = text;
2240 char *text_door_style = NULL;
2241 int graphic = IMG_BACKGROUND_REQUEST;
2242 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2243 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2244 int font_nr = FONT_REQUEST;
2245 int font_width = getFontWidth(font_nr);
2246 int font_height = getFontHeight(font_nr);
2247 int border_size = request.border_size;
2248 int line_spacing = request.line_spacing;
2249 int line_height = font_height + line_spacing;
2250 int text_width = request.width - 2 * border_size;
2251 int text_height = request.height - 2 * border_size;
2252 int line_length = text_width / font_width;
2253 int max_lines = text_height / line_height;
2254 int width = request.width;
2255 int height = request.height;
2256 int tile_size = request.step_offset;
2257 int x_steps = width / tile_size;
2258 int y_steps = height / tile_size;
2262 if (request.wrap_single_words)
2264 char *src_text_ptr, *dst_text_ptr;
2266 text_door_style = checked_malloc(2 * strlen(text) + 1);
2268 src_text_ptr = text;
2269 dst_text_ptr = text_door_style;
2271 while (*src_text_ptr)
2273 if (*src_text_ptr == ' ' ||
2274 *src_text_ptr == '?' ||
2275 *src_text_ptr == '!')
2276 *dst_text_ptr++ = '\n';
2278 if (*src_text_ptr != ' ')
2279 *dst_text_ptr++ = *src_text_ptr;
2284 *dst_text_ptr = '\0';
2286 text_final = text_door_style;
2289 setRequestPosition(&sx, &sy, FALSE);
2291 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2293 for (y = 0; y < y_steps; y++)
2294 for (x = 0; x < x_steps; x++)
2295 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2296 x, y, x_steps, y_steps,
2297 tile_size, tile_size);
2299 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2300 line_length, -1, max_lines, line_spacing, mask_mode,
2301 request.autowrap, request.centered, FALSE);
2303 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2304 RedrawGadget(tool_gadget[i]);
2306 // store readily prepared envelope request for later use when animating
2307 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2309 if (text_door_style)
2310 free(text_door_style);
2313 void AnimateEnvelopeRequest(int anim_mode, int action)
2315 int graphic = IMG_BACKGROUND_REQUEST;
2316 boolean draw_masked = graphic_info[graphic].draw_masked;
2317 int delay_value_normal = request.step_delay;
2318 int delay_value_fast = delay_value_normal / 2;
2319 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2320 boolean no_delay = (tape.warp_forward);
2321 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2322 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0);
2323 unsigned int anim_delay = 0;
2325 int width = request.width;
2326 int height = request.height;
2327 int tile_size = request.step_offset;
2328 int max_xsize = width / tile_size;
2329 int max_ysize = height / tile_size;
2330 int max_xsize_inner = max_xsize - 2;
2331 int max_ysize_inner = max_ysize - 2;
2333 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2334 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2335 int xend = max_xsize_inner;
2336 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2337 int xstep = (xstart < xend ? 1 : 0);
2338 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2341 if (setup.quick_doors)
2348 if (action == ACTION_OPENING)
2349 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2350 else if (action == ACTION_CLOSING)
2351 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2354 for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2356 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2357 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2358 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2359 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2360 int src_x = sx_center - width / 2;
2361 int src_y = sy_center - height / 2;
2362 int dst_x = sx_center - xsize * tile_size / 2;
2363 int dst_y = sy_center - ysize * tile_size / 2;
2364 int xsize_size_left = (xsize - 1) * tile_size;
2365 int ysize_size_top = (ysize - 1) * tile_size;
2366 int max_xsize_pos = (max_xsize - 1) * tile_size;
2367 int max_ysize_pos = (max_ysize - 1) * tile_size;
2370 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2372 for (yy = 0; yy < 2; yy++)
2374 for (xx = 0; xx < 2; xx++)
2376 int src_xx = src_x + xx * max_xsize_pos;
2377 int src_yy = src_y + yy * max_ysize_pos;
2378 int dst_xx = dst_x + xx * xsize_size_left;
2379 int dst_yy = dst_y + yy * ysize_size_top;
2380 int xx_size = (xx ? tile_size : xsize_size_left);
2381 int yy_size = (yy ? tile_size : ysize_size_top);
2384 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2385 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2387 BlitBitmap(bitmap_db_cross, backbuffer,
2388 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2392 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2397 WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2402 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2404 int last_game_status = game_status; /* save current game status */
2405 int graphic = IMG_BACKGROUND_REQUEST;
2406 int sound_opening = SND_REQUEST_OPENING;
2407 int sound_closing = SND_REQUEST_CLOSING;
2408 int anim_mode = graphic_info[graphic].anim_mode;
2409 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2410 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2412 if (game_status == GAME_MODE_PLAYING)
2413 BlitScreenToBitmap(backbuffer);
2415 SetDrawtoField(DRAW_BACKBUFFER);
2417 // SetDrawBackgroundMask(REDRAW_NONE);
2419 if (action == ACTION_OPENING)
2421 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2423 if (req_state & REQ_ASK)
2425 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2426 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2428 else if (req_state & REQ_CONFIRM)
2430 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2432 else if (req_state & REQ_PLAYER)
2434 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2435 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2436 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2437 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2440 DrawEnvelopeRequest(text);
2442 if (game_status != GAME_MODE_MAIN)
2446 /* force DOOR font inside door area */
2447 game_status = GAME_MODE_PSEUDO_DOOR;
2449 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2451 if (action == ACTION_OPENING)
2453 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2455 if (anim_mode == ANIM_DEFAULT)
2456 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2458 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2463 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2465 if (anim_mode != ANIM_NONE)
2466 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2468 if (anim_mode == ANIM_DEFAULT)
2469 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2472 game.envelope_active = FALSE;
2474 game_status = last_game_status; /* restore current game status */
2476 if (action == ACTION_CLOSING)
2478 if (game_status != GAME_MODE_MAIN)
2481 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2484 // SetDrawBackgroundMask(last_draw_background_mask);
2486 redraw_mask |= REDRAW_FIELD;
2488 if (game_status == GAME_MODE_MAIN)
2493 if (action == ACTION_CLOSING &&
2494 game_status == GAME_MODE_PLAYING &&
2495 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2496 SetDrawtoField(DRAW_BUFFERED);
2499 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2503 int graphic = el2preimg(element);
2505 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2506 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2509 void DrawLevel(int draw_background_mask)
2513 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2514 SetDrawBackgroundMask(draw_background_mask);
2518 for (x = BX1; x <= BX2; x++)
2519 for (y = BY1; y <= BY2; y++)
2520 DrawScreenField(x, y);
2522 redraw_mask |= REDRAW_FIELD;
2525 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2530 for (x = 0; x < size_x; x++)
2531 for (y = 0; y < size_y; y++)
2532 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2534 redraw_mask |= REDRAW_FIELD;
2537 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2541 for (x = 0; x < size_x; x++)
2542 for (y = 0; y < size_y; y++)
2543 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2545 redraw_mask |= REDRAW_FIELD;
2548 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2550 boolean show_level_border = (BorderElement != EL_EMPTY);
2551 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2552 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2553 int tile_size = preview.tile_size;
2554 int preview_width = preview.xsize * tile_size;
2555 int preview_height = preview.ysize * tile_size;
2556 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2557 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2558 int real_preview_width = real_preview_xsize * tile_size;
2559 int real_preview_height = real_preview_ysize * tile_size;
2560 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2561 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2564 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2567 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2569 dst_x += (preview_width - real_preview_width) / 2;
2570 dst_y += (preview_height - real_preview_height) / 2;
2572 for (x = 0; x < real_preview_xsize; x++)
2574 for (y = 0; y < real_preview_ysize; y++)
2576 int lx = from_x + x + (show_level_border ? -1 : 0);
2577 int ly = from_y + y + (show_level_border ? -1 : 0);
2578 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2579 getBorderElement(lx, ly));
2581 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2582 element, tile_size);
2586 redraw_mask |= REDRAW_MICROLEVEL;
2589 #define MICROLABEL_EMPTY 0
2590 #define MICROLABEL_LEVEL_NAME 1
2591 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2592 #define MICROLABEL_LEVEL_AUTHOR 3
2593 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2594 #define MICROLABEL_IMPORTED_FROM 5
2595 #define MICROLABEL_IMPORTED_BY_HEAD 6
2596 #define MICROLABEL_IMPORTED_BY 7
2598 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2600 int max_text_width = SXSIZE;
2601 int font_width = getFontWidth(font_nr);
2603 if (pos->align == ALIGN_CENTER)
2604 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2605 else if (pos->align == ALIGN_RIGHT)
2606 max_text_width = pos->x;
2608 max_text_width = SXSIZE - pos->x;
2610 return max_text_width / font_width;
2613 static void DrawPreviewLevelLabelExt(int mode)
2615 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2616 char label_text[MAX_OUTPUT_LINESIZE + 1];
2617 int max_len_label_text;
2618 int font_nr = pos->font;
2621 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2624 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2625 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2626 mode == MICROLABEL_IMPORTED_BY_HEAD)
2627 font_nr = pos->font_alt;
2629 max_len_label_text = getMaxTextLength(pos, font_nr);
2631 if (pos->size != -1)
2632 max_len_label_text = pos->size;
2634 for (i = 0; i < max_len_label_text; i++)
2635 label_text[i] = ' ';
2636 label_text[max_len_label_text] = '\0';
2638 if (strlen(label_text) > 0)
2639 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2642 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2643 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2644 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2645 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2646 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2647 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2648 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2649 max_len_label_text);
2650 label_text[max_len_label_text] = '\0';
2652 if (strlen(label_text) > 0)
2653 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2655 redraw_mask |= REDRAW_MICROLEVEL;
2658 static void DrawPreviewLevelExt(boolean restart)
2660 static unsigned int scroll_delay = 0;
2661 static unsigned int label_delay = 0;
2662 static int from_x, from_y, scroll_direction;
2663 static int label_state, label_counter;
2664 unsigned int scroll_delay_value = preview.step_delay;
2665 boolean show_level_border = (BorderElement != EL_EMPTY);
2666 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2667 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2668 int last_game_status = game_status; /* save current game status */
2675 if (preview.anim_mode == ANIM_CENTERED)
2677 if (level_xsize > preview.xsize)
2678 from_x = (level_xsize - preview.xsize) / 2;
2679 if (level_ysize > preview.ysize)
2680 from_y = (level_ysize - preview.ysize) / 2;
2683 from_x += preview.xoffset;
2684 from_y += preview.yoffset;
2686 scroll_direction = MV_RIGHT;
2690 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2691 DrawPreviewLevelLabelExt(label_state);
2693 /* initialize delay counters */
2694 DelayReached(&scroll_delay, 0);
2695 DelayReached(&label_delay, 0);
2697 if (leveldir_current->name)
2699 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2700 char label_text[MAX_OUTPUT_LINESIZE + 1];
2701 int font_nr = pos->font;
2702 int max_len_label_text = getMaxTextLength(pos, font_nr);
2704 if (pos->size != -1)
2705 max_len_label_text = pos->size;
2707 strncpy(label_text, leveldir_current->name, max_len_label_text);
2708 label_text[max_len_label_text] = '\0';
2710 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2711 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2714 game_status = last_game_status; /* restore current game status */
2719 /* scroll preview level, if needed */
2720 if (preview.anim_mode != ANIM_NONE &&
2721 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2722 DelayReached(&scroll_delay, scroll_delay_value))
2724 switch (scroll_direction)
2729 from_x -= preview.step_offset;
2730 from_x = (from_x < 0 ? 0 : from_x);
2733 scroll_direction = MV_UP;
2737 if (from_x < level_xsize - preview.xsize)
2739 from_x += preview.step_offset;
2740 from_x = (from_x > level_xsize - preview.xsize ?
2741 level_xsize - preview.xsize : from_x);
2744 scroll_direction = MV_DOWN;
2750 from_y -= preview.step_offset;
2751 from_y = (from_y < 0 ? 0 : from_y);
2754 scroll_direction = MV_RIGHT;
2758 if (from_y < level_ysize - preview.ysize)
2760 from_y += preview.step_offset;
2761 from_y = (from_y > level_ysize - preview.ysize ?
2762 level_ysize - preview.ysize : from_y);
2765 scroll_direction = MV_LEFT;
2772 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2775 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2776 /* redraw micro level label, if needed */
2777 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2778 !strEqual(level.author, ANONYMOUS_NAME) &&
2779 !strEqual(level.author, leveldir_current->name) &&
2780 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2782 int max_label_counter = 23;
2784 if (leveldir_current->imported_from != NULL &&
2785 strlen(leveldir_current->imported_from) > 0)
2786 max_label_counter += 14;
2787 if (leveldir_current->imported_by != NULL &&
2788 strlen(leveldir_current->imported_by) > 0)
2789 max_label_counter += 14;
2791 label_counter = (label_counter + 1) % max_label_counter;
2792 label_state = (label_counter >= 0 && label_counter <= 7 ?
2793 MICROLABEL_LEVEL_NAME :
2794 label_counter >= 9 && label_counter <= 12 ?
2795 MICROLABEL_LEVEL_AUTHOR_HEAD :
2796 label_counter >= 14 && label_counter <= 21 ?
2797 MICROLABEL_LEVEL_AUTHOR :
2798 label_counter >= 23 && label_counter <= 26 ?
2799 MICROLABEL_IMPORTED_FROM_HEAD :
2800 label_counter >= 28 && label_counter <= 35 ?
2801 MICROLABEL_IMPORTED_FROM :
2802 label_counter >= 37 && label_counter <= 40 ?
2803 MICROLABEL_IMPORTED_BY_HEAD :
2804 label_counter >= 42 && label_counter <= 49 ?
2805 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2807 if (leveldir_current->imported_from == NULL &&
2808 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2809 label_state == MICROLABEL_IMPORTED_FROM))
2810 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2811 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2813 DrawPreviewLevelLabelExt(label_state);
2816 game_status = last_game_status; /* restore current game status */
2819 void DrawPreviewLevelInitial()
2821 DrawPreviewLevelExt(TRUE);
2824 void DrawPreviewLevelAnimation()
2826 DrawPreviewLevelExt(FALSE);
2829 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2830 int graphic, int sync_frame, int mask_mode)
2832 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2834 if (mask_mode == USE_MASKING)
2835 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2837 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2840 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2841 int graphic, int sync_frame,
2844 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2846 if (mask_mode == USE_MASKING)
2847 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2849 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2852 inline void DrawGraphicAnimation(int x, int y, int graphic)
2854 int lx = LEVELX(x), ly = LEVELY(y);
2856 if (!IN_SCR_FIELD(x, y))
2859 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2860 graphic, GfxFrame[lx][ly], NO_MASKING);
2862 MarkTileDirty(x, y);
2865 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2867 int lx = LEVELX(x), ly = LEVELY(y);
2869 if (!IN_SCR_FIELD(x, y))
2872 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2873 graphic, GfxFrame[lx][ly], NO_MASKING);
2874 MarkTileDirty(x, y);
2877 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2879 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2882 void DrawLevelElementAnimation(int x, int y, int element)
2884 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2886 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2889 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2891 int sx = SCREENX(x), sy = SCREENY(y);
2893 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2896 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2899 DrawGraphicAnimation(sx, sy, graphic);
2902 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2903 DrawLevelFieldCrumbled(x, y);
2905 if (GFX_CRUMBLED(Feld[x][y]))
2906 DrawLevelFieldCrumbled(x, y);
2910 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2912 int sx = SCREENX(x), sy = SCREENY(y);
2915 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2918 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2920 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2923 DrawGraphicAnimation(sx, sy, graphic);
2925 if (GFX_CRUMBLED(element))
2926 DrawLevelFieldCrumbled(x, y);
2929 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2931 if (player->use_murphy)
2933 /* this works only because currently only one player can be "murphy" ... */
2934 static int last_horizontal_dir = MV_LEFT;
2935 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2937 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2938 last_horizontal_dir = move_dir;
2940 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2942 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2944 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2950 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2953 static boolean equalGraphics(int graphic1, int graphic2)
2955 struct GraphicInfo *g1 = &graphic_info[graphic1];
2956 struct GraphicInfo *g2 = &graphic_info[graphic2];
2958 return (g1->bitmap == g2->bitmap &&
2959 g1->src_x == g2->src_x &&
2960 g1->src_y == g2->src_y &&
2961 g1->anim_frames == g2->anim_frames &&
2962 g1->anim_delay == g2->anim_delay &&
2963 g1->anim_mode == g2->anim_mode);
2966 void DrawAllPlayers()
2970 for (i = 0; i < MAX_PLAYERS; i++)
2971 if (stored_player[i].active)
2972 DrawPlayer(&stored_player[i]);
2975 void DrawPlayerField(int x, int y)
2977 if (!IS_PLAYER(x, y))
2980 DrawPlayer(PLAYERINFO(x, y));
2983 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
2985 void DrawPlayer(struct PlayerInfo *player)
2987 int jx = player->jx;
2988 int jy = player->jy;
2989 int move_dir = player->MovDir;
2990 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
2991 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
2992 int last_jx = (player->is_moving ? jx - dx : jx);
2993 int last_jy = (player->is_moving ? jy - dy : jy);
2994 int next_jx = jx + dx;
2995 int next_jy = jy + dy;
2996 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
2997 boolean player_is_opaque = FALSE;
2998 int sx = SCREENX(jx), sy = SCREENY(jy);
2999 int sxx = 0, syy = 0;
3000 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3002 int action = ACTION_DEFAULT;
3003 int last_player_graphic = getPlayerGraphic(player, move_dir);
3004 int last_player_frame = player->Frame;
3007 /* GfxElement[][] is set to the element the player is digging or collecting;
3008 remove also for off-screen player if the player is not moving anymore */
3009 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3010 GfxElement[jx][jy] = EL_UNDEFINED;
3012 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3016 if (!IN_LEV_FIELD(jx, jy))
3018 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3019 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3020 printf("DrawPlayerField(): This should never happen!\n");
3025 if (element == EL_EXPLOSION)
3028 action = (player->is_pushing ? ACTION_PUSHING :
3029 player->is_digging ? ACTION_DIGGING :
3030 player->is_collecting ? ACTION_COLLECTING :
3031 player->is_moving ? ACTION_MOVING :
3032 player->is_snapping ? ACTION_SNAPPING :
3033 player->is_dropping ? ACTION_DROPPING :
3034 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3036 if (player->is_waiting)
3037 move_dir = player->dir_waiting;
3039 InitPlayerGfxAnimation(player, action, move_dir);
3041 /* ----------------------------------------------------------------------- */
3042 /* draw things in the field the player is leaving, if needed */
3043 /* ----------------------------------------------------------------------- */
3045 if (player->is_moving)
3047 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3049 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3051 if (last_element == EL_DYNAMITE_ACTIVE ||
3052 last_element == EL_EM_DYNAMITE_ACTIVE ||
3053 last_element == EL_SP_DISK_RED_ACTIVE)
3054 DrawDynamite(last_jx, last_jy);
3056 DrawLevelFieldThruMask(last_jx, last_jy);
3058 else if (last_element == EL_DYNAMITE_ACTIVE ||
3059 last_element == EL_EM_DYNAMITE_ACTIVE ||
3060 last_element == EL_SP_DISK_RED_ACTIVE)
3061 DrawDynamite(last_jx, last_jy);
3063 /* !!! this is not enough to prevent flickering of players which are
3064 moving next to each others without a free tile between them -- this
3065 can only be solved by drawing all players layer by layer (first the
3066 background, then the foreground etc.) !!! => TODO */
3067 else if (!IS_PLAYER(last_jx, last_jy))
3068 DrawLevelField(last_jx, last_jy);
3071 DrawLevelField(last_jx, last_jy);
3074 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3075 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3078 if (!IN_SCR_FIELD(sx, sy))
3081 /* ----------------------------------------------------------------------- */
3082 /* draw things behind the player, if needed */
3083 /* ----------------------------------------------------------------------- */
3086 DrawLevelElement(jx, jy, Back[jx][jy]);
3087 else if (IS_ACTIVE_BOMB(element))
3088 DrawLevelElement(jx, jy, EL_EMPTY);
3091 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3093 int old_element = GfxElement[jx][jy];
3094 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3095 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3097 if (GFX_CRUMBLED(old_element))
3098 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3100 DrawGraphic(sx, sy, old_graphic, frame);
3102 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3103 player_is_opaque = TRUE;
3107 GfxElement[jx][jy] = EL_UNDEFINED;
3109 /* make sure that pushed elements are drawn with correct frame rate */
3110 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3112 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3113 GfxFrame[jx][jy] = player->StepFrame;
3115 DrawLevelField(jx, jy);
3119 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3120 /* ----------------------------------------------------------------------- */
3121 /* draw player himself */
3122 /* ----------------------------------------------------------------------- */
3124 graphic = getPlayerGraphic(player, move_dir);
3126 /* in the case of changed player action or direction, prevent the current
3127 animation frame from being restarted for identical animations */
3128 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3129 player->Frame = last_player_frame;
3131 frame = getGraphicAnimationFrame(graphic, player->Frame);
3135 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3136 sxx = player->GfxPos;
3138 syy = player->GfxPos;
3141 if (!setup.soft_scrolling && ScreenMovPos)
3144 if (player_is_opaque)
3145 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3147 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3149 if (SHIELD_ON(player))
3151 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3152 IMG_SHIELD_NORMAL_ACTIVE);
3153 int frame = getGraphicAnimationFrame(graphic, -1);
3155 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3159 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3162 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3163 sxx = player->GfxPos;
3165 syy = player->GfxPos;
3169 /* ----------------------------------------------------------------------- */
3170 /* draw things the player is pushing, if needed */
3171 /* ----------------------------------------------------------------------- */
3173 if (player->is_pushing && player->is_moving)
3175 int px = SCREENX(jx), py = SCREENY(jy);
3176 int pxx = (TILEX - ABS(sxx)) * dx;
3177 int pyy = (TILEY - ABS(syy)) * dy;
3178 int gfx_frame = GfxFrame[jx][jy];
3184 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3186 element = Feld[next_jx][next_jy];
3187 gfx_frame = GfxFrame[next_jx][next_jy];
3190 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3192 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3193 frame = getGraphicAnimationFrame(graphic, sync_frame);
3195 /* draw background element under pushed element (like the Sokoban field) */
3196 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3198 /* this allows transparent pushing animation over non-black background */
3201 DrawLevelElement(jx, jy, Back[jx][jy]);
3203 DrawLevelElement(jx, jy, EL_EMPTY);
3205 if (Back[next_jx][next_jy])
3206 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3208 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3210 else if (Back[next_jx][next_jy])
3211 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3214 /* do not draw (EM style) pushing animation when pushing is finished */
3215 /* (two-tile animations usually do not contain start and end frame) */
3216 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3217 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3219 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3221 /* masked drawing is needed for EMC style (double) movement graphics */
3222 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3223 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3227 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3228 /* ----------------------------------------------------------------------- */
3229 /* draw player himself */
3230 /* ----------------------------------------------------------------------- */
3232 graphic = getPlayerGraphic(player, move_dir);
3234 /* in the case of changed player action or direction, prevent the current
3235 animation frame from being restarted for identical animations */
3236 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3237 player->Frame = last_player_frame;
3239 frame = getGraphicAnimationFrame(graphic, player->Frame);
3243 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3244 sxx = player->GfxPos;
3246 syy = player->GfxPos;
3249 if (!setup.soft_scrolling && ScreenMovPos)
3252 if (player_is_opaque)
3253 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3255 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3257 if (SHIELD_ON(player))
3259 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3260 IMG_SHIELD_NORMAL_ACTIVE);
3261 int frame = getGraphicAnimationFrame(graphic, -1);
3263 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3267 /* ----------------------------------------------------------------------- */
3268 /* draw things in front of player (active dynamite or dynabombs) */
3269 /* ----------------------------------------------------------------------- */
3271 if (IS_ACTIVE_BOMB(element))
3273 graphic = el2img(element);
3274 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3276 if (game.emulation == EMU_SUPAPLEX)
3277 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3279 DrawGraphicThruMask(sx, sy, graphic, frame);
3282 if (player_is_moving && last_element == EL_EXPLOSION)
3284 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3285 GfxElement[last_jx][last_jy] : EL_EMPTY);
3286 int graphic = el_act2img(element, ACTION_EXPLODING);
3287 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3288 int phase = ExplodePhase[last_jx][last_jy] - 1;
3289 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3292 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3295 /* ----------------------------------------------------------------------- */
3296 /* draw elements the player is just walking/passing through/under */
3297 /* ----------------------------------------------------------------------- */
3299 if (player_is_moving)
3301 /* handle the field the player is leaving ... */
3302 if (IS_ACCESSIBLE_INSIDE(last_element))
3303 DrawLevelField(last_jx, last_jy);
3304 else if (IS_ACCESSIBLE_UNDER(last_element))
3305 DrawLevelFieldThruMask(last_jx, last_jy);
3308 /* do not redraw accessible elements if the player is just pushing them */
3309 if (!player_is_moving || !player->is_pushing)
3311 /* ... and the field the player is entering */
3312 if (IS_ACCESSIBLE_INSIDE(element))
3313 DrawLevelField(jx, jy);
3314 else if (IS_ACCESSIBLE_UNDER(element))
3315 DrawLevelFieldThruMask(jx, jy);
3318 MarkTileDirty(sx, sy);
3321 /* ------------------------------------------------------------------------- */
3323 void WaitForEventToContinue()
3325 boolean still_wait = TRUE;
3327 /* simulate releasing mouse button over last gadget, if still pressed */
3329 HandleGadgets(-1, -1, 0);
3331 button_status = MB_RELEASED;
3345 case EVENT_BUTTONPRESS:
3346 case EVENT_KEYPRESS:
3350 case EVENT_KEYRELEASE:
3351 ClearPlayerAction();
3355 HandleOtherEvents(&event);
3359 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3366 /* don't eat all CPU time */
3371 #define MAX_REQUEST_LINES 13
3372 #define MAX_REQUEST_LINE_FONT1_LEN 7
3373 #define MAX_REQUEST_LINE_FONT2_LEN 10
3375 static int RequestHandleEvents(unsigned int req_state)
3377 int last_game_status = game_status; /* save current game status */
3381 button_status = MB_RELEASED;
3383 request_gadget_id = -1;
3396 case EVENT_BUTTONPRESS:
3397 case EVENT_BUTTONRELEASE:
3398 case EVENT_MOTIONNOTIFY:
3400 if (event.type == EVENT_MOTIONNOTIFY)
3402 if (!PointerInWindow(window))
3403 continue; /* window and pointer are on different screens */
3408 motion_status = TRUE;
3409 mx = ((MotionEvent *) &event)->x;
3410 my = ((MotionEvent *) &event)->y;
3414 motion_status = FALSE;
3415 mx = ((ButtonEvent *) &event)->x;
3416 my = ((ButtonEvent *) &event)->y;
3417 if (event.type == EVENT_BUTTONPRESS)
3418 button_status = ((ButtonEvent *) &event)->button;
3420 button_status = MB_RELEASED;
3423 /* this sets 'request_gadget_id' */
3424 HandleGadgets(mx, my, button_status);
3426 switch (request_gadget_id)
3428 case TOOL_CTRL_ID_YES:
3431 case TOOL_CTRL_ID_NO:
3434 case TOOL_CTRL_ID_CONFIRM:
3435 result = TRUE | FALSE;
3438 case TOOL_CTRL_ID_PLAYER_1:
3441 case TOOL_CTRL_ID_PLAYER_2:
3444 case TOOL_CTRL_ID_PLAYER_3:
3447 case TOOL_CTRL_ID_PLAYER_4:
3458 case EVENT_KEYPRESS:
3459 switch (GetEventKey((KeyEvent *)&event, TRUE))
3462 if (req_state & REQ_CONFIRM)
3467 #if defined(TARGET_SDL2)
3474 #if defined(TARGET_SDL2)
3484 if (req_state & REQ_PLAYER)
3488 case EVENT_KEYRELEASE:
3489 ClearPlayerAction();
3493 HandleOtherEvents(&event);
3497 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3499 int joy = AnyJoystick();
3501 if (joy & JOY_BUTTON_1)
3503 else if (joy & JOY_BUTTON_2)
3507 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3509 HandleGameActions();
3515 if (!PendingEvent()) /* delay only if no pending events */
3519 game_status = GAME_MODE_PSEUDO_DOOR;
3523 game_status = last_game_status; /* restore current game status */
3529 static boolean RequestDoor(char *text, unsigned int req_state)
3531 unsigned int old_door_state;
3532 int last_game_status = game_status; /* save current game status */
3533 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3534 int font_nr = FONT_TEXT_2;
3539 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3541 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3542 font_nr = FONT_TEXT_1;
3545 if (game_status == GAME_MODE_PLAYING)
3546 BlitScreenToBitmap(backbuffer);
3548 /* disable deactivated drawing when quick-loading level tape recording */
3549 if (tape.playing && tape.deactivate_display)
3550 TapeDeactivateDisplayOff(TRUE);
3552 SetMouseCursor(CURSOR_DEFAULT);
3554 #if defined(NETWORK_AVALIABLE)
3555 /* pause network game while waiting for request to answer */
3556 if (options.network &&
3557 game_status == GAME_MODE_PLAYING &&
3558 req_state & REQUEST_WAIT_FOR_INPUT)
3559 SendToServer_PausePlaying();
3562 old_door_state = GetDoorState();
3564 /* simulate releasing mouse button over last gadget, if still pressed */
3566 HandleGadgets(-1, -1, 0);
3570 /* draw released gadget before proceeding */
3573 if (old_door_state & DOOR_OPEN_1)
3575 CloseDoor(DOOR_CLOSE_1);
3577 /* save old door content */
3578 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3579 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3582 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3583 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3585 /* clear door drawing field */
3586 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3588 /* force DOOR font inside door area */
3589 game_status = GAME_MODE_PSEUDO_DOOR;
3591 /* write text for request */
3592 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3594 char text_line[max_request_line_len + 1];
3600 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3602 tc = *(text_ptr + tx);
3603 // if (!tc || tc == ' ')
3604 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3608 if ((tc == '?' || tc == '!') && tl == 0)
3618 strncpy(text_line, text_ptr, tl);
3621 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3622 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3623 text_line, font_nr);
3625 text_ptr += tl + (tc == ' ' ? 1 : 0);
3626 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3629 game_status = last_game_status; /* restore current game status */
3631 if (req_state & REQ_ASK)
3633 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3634 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3636 else if (req_state & REQ_CONFIRM)
3638 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3640 else if (req_state & REQ_PLAYER)
3642 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3643 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3644 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3645 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3648 /* copy request gadgets to door backbuffer */
3649 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3651 OpenDoor(DOOR_OPEN_1);
3653 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3655 if (game_status == GAME_MODE_PLAYING)
3657 SetPanelBackground();
3658 SetDrawBackgroundMask(REDRAW_DOOR_1);
3662 SetDrawBackgroundMask(REDRAW_FIELD);
3668 if (game_status != GAME_MODE_MAIN)
3671 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3673 // ---------- handle request buttons ----------
3674 result = RequestHandleEvents(req_state);
3676 if (game_status != GAME_MODE_MAIN)
3681 if (!(req_state & REQ_STAY_OPEN))
3683 CloseDoor(DOOR_CLOSE_1);
3685 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3686 (req_state & REQ_REOPEN))
3687 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3692 if (game_status == GAME_MODE_PLAYING)
3694 SetPanelBackground();
3695 SetDrawBackgroundMask(REDRAW_DOOR_1);
3699 SetDrawBackgroundMask(REDRAW_FIELD);
3702 #if defined(NETWORK_AVALIABLE)
3703 /* continue network game after request */
3704 if (options.network &&
3705 game_status == GAME_MODE_PLAYING &&
3706 req_state & REQUEST_WAIT_FOR_INPUT)
3707 SendToServer_ContinuePlaying();
3710 /* restore deactivated drawing when quick-loading level tape recording */
3711 if (tape.playing && tape.deactivate_display)
3712 TapeDeactivateDisplayOn();
3717 static boolean RequestEnvelope(char *text, unsigned int req_state)
3721 if (game_status == GAME_MODE_PLAYING)
3722 BlitScreenToBitmap(backbuffer);
3724 /* disable deactivated drawing when quick-loading level tape recording */
3725 if (tape.playing && tape.deactivate_display)
3726 TapeDeactivateDisplayOff(TRUE);
3728 SetMouseCursor(CURSOR_DEFAULT);
3730 #if defined(NETWORK_AVALIABLE)
3731 /* pause network game while waiting for request to answer */
3732 if (options.network &&
3733 game_status == GAME_MODE_PLAYING &&
3734 req_state & REQUEST_WAIT_FOR_INPUT)
3735 SendToServer_PausePlaying();
3738 /* simulate releasing mouse button over last gadget, if still pressed */
3740 HandleGadgets(-1, -1, 0);
3744 // (replace with setting corresponding request background)
3745 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3746 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3748 /* clear door drawing field */
3749 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3751 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3753 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3755 if (game_status == GAME_MODE_PLAYING)
3757 SetPanelBackground();
3758 SetDrawBackgroundMask(REDRAW_DOOR_1);
3762 SetDrawBackgroundMask(REDRAW_FIELD);
3768 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3770 // ---------- handle request buttons ----------
3771 result = RequestHandleEvents(req_state);
3773 if (game_status != GAME_MODE_MAIN)
3778 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3782 if (game_status == GAME_MODE_PLAYING)
3784 SetPanelBackground();
3785 SetDrawBackgroundMask(REDRAW_DOOR_1);
3789 SetDrawBackgroundMask(REDRAW_FIELD);
3792 #if defined(NETWORK_AVALIABLE)
3793 /* continue network game after request */
3794 if (options.network &&
3795 game_status == GAME_MODE_PLAYING &&
3796 req_state & REQUEST_WAIT_FOR_INPUT)
3797 SendToServer_ContinuePlaying();
3800 /* restore deactivated drawing when quick-loading level tape recording */
3801 if (tape.playing && tape.deactivate_display)
3802 TapeDeactivateDisplayOn();
3807 boolean Request(char *text, unsigned int req_state)
3809 if (global.use_envelope_request)
3810 return RequestEnvelope(text, req_state);
3812 return RequestDoor(text, req_state);
3815 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3817 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3818 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3821 if (dpo1->sort_priority != dpo2->sort_priority)
3822 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3824 compare_result = dpo1->nr - dpo2->nr;
3826 return compare_result;
3829 void InitGraphicCompatibilityInfo_Doors()
3835 struct DoorInfo *door;
3839 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3840 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3842 { -1, -1, -1, NULL }
3844 struct Rect door_rect_list[] =
3846 { DX, DY, DXSIZE, DYSIZE },
3847 { VX, VY, VXSIZE, VYSIZE }
3851 for (i = 0; doors[i].door_token != -1; i++)
3853 int door_token = doors[i].door_token;
3854 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3855 int part_1 = doors[i].part_1;
3856 int part_8 = doors[i].part_8;
3857 int part_2 = part_1 + 1;
3858 int part_3 = part_1 + 2;
3859 struct DoorInfo *door = doors[i].door;
3860 struct Rect *door_rect = &door_rect_list[door_index];
3861 boolean door_gfx_redefined = FALSE;
3863 /* check if any door part graphic definitions have been redefined */
3865 for (j = 0; door_part_controls[j].door_token != -1; j++)
3867 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3868 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3870 if (dpc->door_token == door_token && fi->redefined)
3871 door_gfx_redefined = TRUE;
3874 /* check for old-style door graphic/animation modifications */
3876 if (!door_gfx_redefined)
3878 if (door->anim_mode & ANIM_STATIC_PANEL)
3880 door->panel.step_xoffset = 0;
3881 door->panel.step_yoffset = 0;
3884 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3886 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3887 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3888 int num_door_steps, num_panel_steps;
3890 /* remove door part graphics other than the two default wings */
3892 for (j = 0; door_part_controls[j].door_token != -1; j++)
3894 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3895 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3897 if (dpc->graphic >= part_3 &&
3898 dpc->graphic <= part_8)
3902 /* set graphics and screen positions of the default wings */
3904 g_part_1->width = door_rect->width;
3905 g_part_1->height = door_rect->height;
3906 g_part_2->width = door_rect->width;
3907 g_part_2->height = door_rect->height;
3908 g_part_2->src_x = door_rect->width;
3909 g_part_2->src_y = g_part_1->src_y;
3911 door->part_2.x = door->part_1.x;
3912 door->part_2.y = door->part_1.y;
3914 if (door->width != -1)
3916 g_part_1->width = door->width;
3917 g_part_2->width = door->width;
3919 // special treatment for graphics and screen position of right wing
3920 g_part_2->src_x += door_rect->width - door->width;
3921 door->part_2.x += door_rect->width - door->width;
3924 if (door->height != -1)
3926 g_part_1->height = door->height;
3927 g_part_2->height = door->height;
3929 // special treatment for graphics and screen position of bottom wing
3930 g_part_2->src_y += door_rect->height - door->height;
3931 door->part_2.y += door_rect->height - door->height;
3934 /* set animation delays for the default wings and panels */
3936 door->part_1.step_delay = door->step_delay;
3937 door->part_2.step_delay = door->step_delay;
3938 door->panel.step_delay = door->step_delay;
3940 /* set animation draw order for the default wings */
3942 door->part_1.sort_priority = 2; /* draw left wing over ... */
3943 door->part_2.sort_priority = 1; /* ... right wing */
3945 /* set animation draw offset for the default wings */
3947 if (door->anim_mode & ANIM_HORIZONTAL)
3949 door->part_1.step_xoffset = door->step_offset;
3950 door->part_1.step_yoffset = 0;
3951 door->part_2.step_xoffset = door->step_offset * -1;
3952 door->part_2.step_yoffset = 0;
3954 num_door_steps = g_part_1->width / door->step_offset;
3956 else // ANIM_VERTICAL
3958 door->part_1.step_xoffset = 0;
3959 door->part_1.step_yoffset = door->step_offset;
3960 door->part_2.step_xoffset = 0;
3961 door->part_2.step_yoffset = door->step_offset * -1;
3963 num_door_steps = g_part_1->height / door->step_offset;
3966 /* set animation draw offset for the default panels */
3968 if (door->step_offset > 1)
3970 num_panel_steps = 2 * door_rect->height / door->step_offset;
3971 door->panel.start_step = num_panel_steps - num_door_steps;
3972 door->panel.start_step_closing = door->panel.start_step;
3976 num_panel_steps = door_rect->height / door->step_offset;
3977 door->panel.start_step = num_panel_steps - num_door_steps / 2;
3978 door->panel.start_step_closing = door->panel.start_step;
3979 door->panel.step_delay *= 2;
3990 for (i = 0; door_part_controls[i].door_token != -1; i++)
3992 struct DoorPartControlInfo *dpc = &door_part_controls[i];
3993 struct DoorPartOrderInfo *dpo = &door_part_order[i];
3995 /* initialize "start_step_opening" and "start_step_closing", if needed */
3996 if (dpc->pos->start_step_opening == 0 &&
3997 dpc->pos->start_step_closing == 0)
3999 // dpc->pos->start_step_opening = dpc->pos->start_step;
4000 dpc->pos->start_step_closing = dpc->pos->start_step;
4003 /* fill structure for door part draw order (sorted below) */
4005 dpo->sort_priority = dpc->pos->sort_priority;
4008 /* sort door part controls according to sort_priority and graphic number */
4009 qsort(door_part_order, MAX_DOOR_PARTS,
4010 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4013 unsigned int OpenDoor(unsigned int door_state)
4015 if (door_state & DOOR_COPY_BACK)
4017 if (door_state & DOOR_OPEN_1)
4018 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4019 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4021 if (door_state & DOOR_OPEN_2)
4022 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4023 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4025 door_state &= ~DOOR_COPY_BACK;
4028 return MoveDoor(door_state);
4031 unsigned int CloseDoor(unsigned int door_state)
4033 unsigned int old_door_state = GetDoorState();
4035 if (!(door_state & DOOR_NO_COPY_BACK))
4037 if (old_door_state & DOOR_OPEN_1)
4038 BlitBitmap(backbuffer, bitmap_db_door_1,
4039 DX, DY, DXSIZE, DYSIZE, 0, 0);
4041 if (old_door_state & DOOR_OPEN_2)
4042 BlitBitmap(backbuffer, bitmap_db_door_2,
4043 VX, VY, VXSIZE, VYSIZE, 0, 0);
4045 door_state &= ~DOOR_NO_COPY_BACK;
4048 return MoveDoor(door_state);
4051 unsigned int GetDoorState()
4053 return MoveDoor(DOOR_GET_STATE);
4056 unsigned int SetDoorState(unsigned int door_state)
4058 return MoveDoor(door_state | DOOR_SET_STATE);
4061 int euclid(int a, int b)
4063 return (b ? euclid(b, a % b) : a);
4066 unsigned int MoveDoor(unsigned int door_state)
4068 struct Rect door_rect_list[] =
4070 { DX, DY, DXSIZE, DYSIZE },
4071 { VX, VY, VXSIZE, VYSIZE }
4073 static int door1 = DOOR_OPEN_1;
4074 static int door2 = DOOR_CLOSE_2;
4075 unsigned int door_delay = 0;
4076 unsigned int door_delay_value;
4079 if (door_state == DOOR_GET_STATE)
4080 return (door1 | door2);
4082 if (door_state & DOOR_SET_STATE)
4084 if (door_state & DOOR_ACTION_1)
4085 door1 = door_state & DOOR_ACTION_1;
4086 if (door_state & DOOR_ACTION_2)
4087 door2 = door_state & DOOR_ACTION_2;
4089 return (door1 | door2);
4092 if (!(door_state & DOOR_FORCE_REDRAW))
4094 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4095 door_state &= ~DOOR_OPEN_1;
4096 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4097 door_state &= ~DOOR_CLOSE_1;
4098 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4099 door_state &= ~DOOR_OPEN_2;
4100 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4101 door_state &= ~DOOR_CLOSE_2;
4104 if (global.autoplay_leveldir)
4106 door_state |= DOOR_NO_DELAY;
4107 door_state &= ~DOOR_CLOSE_ALL;
4110 if (game_status == GAME_MODE_EDITOR)
4111 door_state |= DOOR_NO_DELAY;
4113 if (door_state & DOOR_ACTION)
4115 boolean door_panel_drawn[NUM_DOORS];
4116 boolean panel_has_doors[NUM_DOORS];
4117 boolean door_part_skip[MAX_DOOR_PARTS];
4118 boolean door_part_done[MAX_DOOR_PARTS];
4119 boolean door_part_done_all;
4120 int num_steps[MAX_DOOR_PARTS];
4121 int max_move_delay = 0; // delay for complete animations of all doors
4122 int max_step_delay = 0; // delay (ms) between two animation frames
4123 int num_move_steps = 0; // number of animation steps for all doors
4124 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4125 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4126 int current_move_delay = 0;
4130 for (i = 0; i < NUM_DOORS; i++)
4131 panel_has_doors[i] = FALSE;
4133 for (i = 0; i < MAX_DOOR_PARTS; i++)
4135 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4136 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4137 int door_token = dpc->door_token;
4139 door_part_done[i] = FALSE;
4140 door_part_skip[i] = (!(door_state & door_token) ||
4144 for (i = 0; i < MAX_DOOR_PARTS; i++)
4146 int nr = door_part_order[i].nr;
4147 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4148 struct DoorPartPosInfo *pos = dpc->pos;
4149 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4150 int door_token = dpc->door_token;
4151 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4152 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4153 int step_xoffset = ABS(pos->step_xoffset);
4154 int step_yoffset = ABS(pos->step_yoffset);
4155 int step_delay = pos->step_delay;
4156 int current_door_state = door_state & door_token;
4157 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4158 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4159 boolean part_opening = (is_panel ? door_closing : door_opening);
4160 int start_step = (part_opening ? pos->start_step_opening :
4161 pos->start_step_closing);
4162 float move_xsize = (step_xoffset ? g->width : 0);
4163 float move_ysize = (step_yoffset ? g->height : 0);
4164 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4165 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4166 int move_steps = (move_xsteps && move_ysteps ?
4167 MIN(move_xsteps, move_ysteps) :
4168 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4169 int move_delay = move_steps * step_delay;
4171 if (door_part_skip[nr])
4174 max_move_delay = MAX(max_move_delay, move_delay);
4175 max_step_delay = (max_step_delay == 0 ? step_delay :
4176 euclid(max_step_delay, step_delay));
4177 num_steps[nr] = move_steps;
4181 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4183 panel_has_doors[door_index] = TRUE;
4187 num_move_steps = max_move_delay / max_step_delay;
4188 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4190 door_delay_value = max_step_delay;
4192 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4194 start = num_move_steps - 1;
4198 /* opening door sound has priority over simultaneously closing door */
4199 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4200 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4201 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4202 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4205 for (k = start; k < num_move_steps; k++)
4207 door_part_done_all = TRUE;
4209 for (i = 0; i < NUM_DOORS; i++)
4210 door_panel_drawn[i] = FALSE;
4212 for (i = 0; i < MAX_DOOR_PARTS; i++)
4214 int nr = door_part_order[i].nr;
4215 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4216 struct DoorPartPosInfo *pos = dpc->pos;
4217 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4218 int door_token = dpc->door_token;
4219 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4220 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4221 boolean is_panel_and_door_has_closed = FALSE;
4222 struct Rect *door_rect = &door_rect_list[door_index];
4223 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4225 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4226 int current_door_state = door_state & door_token;
4227 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4228 boolean door_closing = !door_opening;
4229 boolean part_opening = (is_panel ? door_closing : door_opening);
4230 boolean part_closing = !part_opening;
4231 int start_step = (part_opening ? pos->start_step_opening :
4232 pos->start_step_closing);
4233 int step_delay = pos->step_delay;
4234 int step_factor = step_delay / max_step_delay;
4235 int k1 = (step_factor ? k / step_factor + 1 : k);
4236 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4237 int kk = MAX(0, k2);
4240 int src_x, src_y, src_xx, src_yy;
4241 int dst_x, dst_y, dst_xx, dst_yy;
4244 if (door_part_skip[nr])
4247 if (!(door_state & door_token))
4255 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4256 int kk_door = MAX(0, k2_door);
4257 int sync_frame = kk_door * door_delay_value;
4258 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4260 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4265 if (!door_panel_drawn[door_index])
4267 ClearRectangle(drawto, door_rect->x, door_rect->y,
4268 door_rect->width, door_rect->height);
4270 door_panel_drawn[door_index] = TRUE;
4273 // draw opening or closing door parts
4275 if (pos->step_xoffset < 0) // door part on right side
4278 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4281 if (dst_xx + width > door_rect->width)
4282 width = door_rect->width - dst_xx;
4284 else // door part on left side
4287 dst_xx = pos->x - kk * pos->step_xoffset;
4291 src_xx = ABS(dst_xx);
4295 width = g->width - src_xx;
4297 // printf("::: k == %d [%d] \n", k, start_step);
4300 if (pos->step_yoffset < 0) // door part on bottom side
4303 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4306 if (dst_yy + height > door_rect->height)
4307 height = door_rect->height - dst_yy;
4309 else // door part on top side
4312 dst_yy = pos->y - kk * pos->step_yoffset;
4316 src_yy = ABS(dst_yy);
4320 height = g->height - src_yy;
4323 src_x = g_src_x + src_xx;
4324 src_y = g_src_y + src_yy;
4326 dst_x = door_rect->x + dst_xx;
4327 dst_y = door_rect->y + dst_yy;
4329 is_panel_and_door_has_closed =
4332 panel_has_doors[door_index] &&
4333 k >= num_move_steps_doors_only - 1);
4335 if (width >= 0 && width <= g->width &&
4336 height >= 0 && height <= g->height &&
4337 !is_panel_and_door_has_closed)
4339 if (is_panel || !pos->draw_masked)
4340 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4343 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4347 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4349 if ((part_opening && (width < 0 || height < 0)) ||
4350 (part_closing && (width >= g->width && height >= g->height)))
4351 door_part_done[nr] = TRUE;
4353 // continue door part animations, but not panel after door has closed
4354 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4355 door_part_done_all = FALSE;
4358 if (!(door_state & DOOR_NO_DELAY))
4362 if (game_status == GAME_MODE_MAIN)
4365 WaitUntilDelayReached(&door_delay, door_delay_value);
4367 current_move_delay += max_step_delay;
4370 if (door_part_done_all)
4375 if (door_state & DOOR_ACTION_1)
4376 door1 = door_state & DOOR_ACTION_1;
4377 if (door_state & DOOR_ACTION_2)
4378 door2 = door_state & DOOR_ACTION_2;
4380 return (door1 | door2);
4383 void DrawSpecialEditorDoor()
4385 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4386 int top_border_width = gfx1->width;
4387 int top_border_height = gfx1->height;
4388 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4389 int ex = EX - outer_border;
4390 int ey = EY - outer_border;
4391 int vy = VY - outer_border;
4392 int exsize = EXSIZE + 2 * outer_border;
4394 /* draw bigger level editor toolbox window */
4395 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4396 top_border_width, top_border_height, ex, ey - top_border_height);
4397 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4398 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4400 redraw_mask |= REDRAW_ALL;
4403 void UndrawSpecialEditorDoor()
4405 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4406 int top_border_width = gfx1->width;
4407 int top_border_height = gfx1->height;
4408 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4409 int ex = EX - outer_border;
4410 int ey = EY - outer_border;
4411 int ey_top = ey - top_border_height;
4412 int exsize = EXSIZE + 2 * outer_border;
4413 int eysize = EYSIZE + 2 * outer_border;
4415 /* draw normal tape recorder window */
4416 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4418 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4419 ex, ey_top, top_border_width, top_border_height,
4421 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4422 ex, ey, exsize, eysize, ex, ey);
4426 // if screen background is set to "[NONE]", clear editor toolbox window
4427 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4428 ClearRectangle(drawto, ex, ey, exsize, eysize);
4431 redraw_mask |= REDRAW_ALL;
4435 /* ---------- new tool button stuff ---------------------------------------- */
4440 struct TextPosInfo *pos;
4443 } toolbutton_info[NUM_TOOL_BUTTONS] =
4446 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4447 TOOL_CTRL_ID_YES, "yes"
4450 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4451 TOOL_CTRL_ID_NO, "no"
4454 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4455 TOOL_CTRL_ID_CONFIRM, "confirm"
4458 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4459 TOOL_CTRL_ID_PLAYER_1, "player 1"
4462 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4463 TOOL_CTRL_ID_PLAYER_2, "player 2"
4466 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4467 TOOL_CTRL_ID_PLAYER_3, "player 3"
4470 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4471 TOOL_CTRL_ID_PLAYER_4, "player 4"
4475 void CreateToolButtons()
4479 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4481 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4482 struct TextPosInfo *pos = toolbutton_info[i].pos;
4483 struct GadgetInfo *gi;
4484 Bitmap *deco_bitmap = None;
4485 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4486 unsigned int event_mask = GD_EVENT_RELEASED;
4489 int gd_x = gfx->src_x;
4490 int gd_y = gfx->src_y;
4491 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4492 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4495 if (global.use_envelope_request)
4496 setRequestPosition(&dx, &dy, TRUE);
4498 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4500 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4502 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4503 pos->size, &deco_bitmap, &deco_x, &deco_y);
4504 deco_xpos = (gfx->width - pos->size) / 2;
4505 deco_ypos = (gfx->height - pos->size) / 2;
4508 gi = CreateGadget(GDI_CUSTOM_ID, id,
4509 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4510 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4511 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4512 GDI_WIDTH, gfx->width,
4513 GDI_HEIGHT, gfx->height,
4514 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4515 GDI_STATE, GD_BUTTON_UNPRESSED,
4516 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4517 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4518 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4519 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4520 GDI_DECORATION_SIZE, pos->size, pos->size,
4521 GDI_DECORATION_SHIFTING, 1, 1,
4522 GDI_DIRECT_DRAW, FALSE,
4523 GDI_EVENT_MASK, event_mask,
4524 GDI_CALLBACK_ACTION, HandleToolButtons,
4528 Error(ERR_EXIT, "cannot create gadget");
4530 tool_gadget[id] = gi;
4534 void FreeToolButtons()
4538 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4539 FreeGadget(tool_gadget[i]);
4542 static void UnmapToolButtons()
4546 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4547 UnmapGadget(tool_gadget[i]);
4550 static void HandleToolButtons(struct GadgetInfo *gi)
4552 request_gadget_id = gi->custom_id;
4555 static struct Mapping_EM_to_RND_object
4558 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4559 boolean is_backside; /* backside of moving element */
4565 em_object_mapping_list[] =
4568 Xblank, TRUE, FALSE,
4572 Yacid_splash_eB, FALSE, FALSE,
4573 EL_ACID_SPLASH_RIGHT, -1, -1
4576 Yacid_splash_wB, FALSE, FALSE,
4577 EL_ACID_SPLASH_LEFT, -1, -1
4580 #ifdef EM_ENGINE_BAD_ROLL
4582 Xstone_force_e, FALSE, FALSE,
4583 EL_ROCK, -1, MV_BIT_RIGHT
4586 Xstone_force_w, FALSE, FALSE,
4587 EL_ROCK, -1, MV_BIT_LEFT
4590 Xnut_force_e, FALSE, FALSE,
4591 EL_NUT, -1, MV_BIT_RIGHT
4594 Xnut_force_w, FALSE, FALSE,
4595 EL_NUT, -1, MV_BIT_LEFT
4598 Xspring_force_e, FALSE, FALSE,
4599 EL_SPRING, -1, MV_BIT_RIGHT
4602 Xspring_force_w, FALSE, FALSE,
4603 EL_SPRING, -1, MV_BIT_LEFT
4606 Xemerald_force_e, FALSE, FALSE,
4607 EL_EMERALD, -1, MV_BIT_RIGHT
4610 Xemerald_force_w, FALSE, FALSE,
4611 EL_EMERALD, -1, MV_BIT_LEFT
4614 Xdiamond_force_e, FALSE, FALSE,
4615 EL_DIAMOND, -1, MV_BIT_RIGHT
4618 Xdiamond_force_w, FALSE, FALSE,
4619 EL_DIAMOND, -1, MV_BIT_LEFT
4622 Xbomb_force_e, FALSE, FALSE,
4623 EL_BOMB, -1, MV_BIT_RIGHT
4626 Xbomb_force_w, FALSE, FALSE,
4627 EL_BOMB, -1, MV_BIT_LEFT
4629 #endif /* EM_ENGINE_BAD_ROLL */
4632 Xstone, TRUE, FALSE,
4636 Xstone_pause, FALSE, FALSE,
4640 Xstone_fall, FALSE, FALSE,
4644 Ystone_s, FALSE, FALSE,
4645 EL_ROCK, ACTION_FALLING, -1
4648 Ystone_sB, FALSE, TRUE,
4649 EL_ROCK, ACTION_FALLING, -1
4652 Ystone_e, FALSE, FALSE,
4653 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4656 Ystone_eB, FALSE, TRUE,
4657 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4660 Ystone_w, FALSE, FALSE,
4661 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4664 Ystone_wB, FALSE, TRUE,
4665 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4672 Xnut_pause, FALSE, FALSE,
4676 Xnut_fall, FALSE, FALSE,
4680 Ynut_s, FALSE, FALSE,
4681 EL_NUT, ACTION_FALLING, -1
4684 Ynut_sB, FALSE, TRUE,
4685 EL_NUT, ACTION_FALLING, -1
4688 Ynut_e, FALSE, FALSE,
4689 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4692 Ynut_eB, FALSE, TRUE,
4693 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4696 Ynut_w, FALSE, FALSE,
4697 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4700 Ynut_wB, FALSE, TRUE,
4701 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4704 Xbug_n, TRUE, FALSE,
4708 Xbug_e, TRUE, FALSE,
4709 EL_BUG_RIGHT, -1, -1
4712 Xbug_s, TRUE, FALSE,
4716 Xbug_w, TRUE, FALSE,
4720 Xbug_gon, FALSE, FALSE,
4724 Xbug_goe, FALSE, FALSE,
4725 EL_BUG_RIGHT, -1, -1
4728 Xbug_gos, FALSE, FALSE,
4732 Xbug_gow, FALSE, FALSE,
4736 Ybug_n, FALSE, FALSE,
4737 EL_BUG, ACTION_MOVING, MV_BIT_UP
4740 Ybug_nB, FALSE, TRUE,
4741 EL_BUG, ACTION_MOVING, MV_BIT_UP
4744 Ybug_e, FALSE, FALSE,
4745 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4748 Ybug_eB, FALSE, TRUE,
4749 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4752 Ybug_s, FALSE, FALSE,
4753 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4756 Ybug_sB, FALSE, TRUE,
4757 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4760 Ybug_w, FALSE, FALSE,
4761 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4764 Ybug_wB, FALSE, TRUE,
4765 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4768 Ybug_w_n, FALSE, FALSE,
4769 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4772 Ybug_n_e, FALSE, FALSE,
4773 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4776 Ybug_e_s, FALSE, FALSE,
4777 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4780 Ybug_s_w, FALSE, FALSE,
4781 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4784 Ybug_e_n, FALSE, FALSE,
4785 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4788 Ybug_s_e, FALSE, FALSE,
4789 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4792 Ybug_w_s, FALSE, FALSE,
4793 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4796 Ybug_n_w, FALSE, FALSE,
4797 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4800 Ybug_stone, FALSE, FALSE,
4801 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4804 Ybug_spring, FALSE, FALSE,
4805 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4808 Xtank_n, TRUE, FALSE,
4809 EL_SPACESHIP_UP, -1, -1
4812 Xtank_e, TRUE, FALSE,
4813 EL_SPACESHIP_RIGHT, -1, -1
4816 Xtank_s, TRUE, FALSE,
4817 EL_SPACESHIP_DOWN, -1, -1
4820 Xtank_w, TRUE, FALSE,
4821 EL_SPACESHIP_LEFT, -1, -1
4824 Xtank_gon, FALSE, FALSE,
4825 EL_SPACESHIP_UP, -1, -1
4828 Xtank_goe, FALSE, FALSE,
4829 EL_SPACESHIP_RIGHT, -1, -1
4832 Xtank_gos, FALSE, FALSE,
4833 EL_SPACESHIP_DOWN, -1, -1
4836 Xtank_gow, FALSE, FALSE,
4837 EL_SPACESHIP_LEFT, -1, -1
4840 Ytank_n, FALSE, FALSE,
4841 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4844 Ytank_nB, FALSE, TRUE,
4845 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4848 Ytank_e, FALSE, FALSE,
4849 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4852 Ytank_eB, FALSE, TRUE,
4853 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4856 Ytank_s, FALSE, FALSE,
4857 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4860 Ytank_sB, FALSE, TRUE,
4861 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4864 Ytank_w, FALSE, FALSE,
4865 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4868 Ytank_wB, FALSE, TRUE,
4869 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4872 Ytank_w_n, FALSE, FALSE,
4873 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4876 Ytank_n_e, FALSE, FALSE,
4877 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4880 Ytank_e_s, FALSE, FALSE,
4881 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4884 Ytank_s_w, FALSE, FALSE,
4885 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4888 Ytank_e_n, FALSE, FALSE,
4889 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4892 Ytank_s_e, FALSE, FALSE,
4893 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4896 Ytank_w_s, FALSE, FALSE,
4897 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4900 Ytank_n_w, FALSE, FALSE,
4901 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4904 Ytank_stone, FALSE, FALSE,
4905 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4908 Ytank_spring, FALSE, FALSE,
4909 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4912 Xandroid, TRUE, FALSE,
4913 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4916 Xandroid_1_n, FALSE, FALSE,
4917 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4920 Xandroid_2_n, FALSE, FALSE,
4921 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4924 Xandroid_1_e, FALSE, FALSE,
4925 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4928 Xandroid_2_e, FALSE, FALSE,
4929 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4932 Xandroid_1_w, FALSE, FALSE,
4933 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4936 Xandroid_2_w, FALSE, FALSE,
4937 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4940 Xandroid_1_s, FALSE, FALSE,
4941 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4944 Xandroid_2_s, FALSE, FALSE,
4945 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4948 Yandroid_n, FALSE, FALSE,
4949 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4952 Yandroid_nB, FALSE, TRUE,
4953 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4956 Yandroid_ne, FALSE, FALSE,
4957 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4960 Yandroid_neB, FALSE, TRUE,
4961 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4964 Yandroid_e, FALSE, FALSE,
4965 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4968 Yandroid_eB, FALSE, TRUE,
4969 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4972 Yandroid_se, FALSE, FALSE,
4973 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4976 Yandroid_seB, FALSE, TRUE,
4977 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4980 Yandroid_s, FALSE, FALSE,
4981 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4984 Yandroid_sB, FALSE, TRUE,
4985 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4988 Yandroid_sw, FALSE, FALSE,
4989 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
4992 Yandroid_swB, FALSE, TRUE,
4993 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
4996 Yandroid_w, FALSE, FALSE,
4997 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5000 Yandroid_wB, FALSE, TRUE,
5001 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5004 Yandroid_nw, FALSE, FALSE,
5005 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5008 Yandroid_nwB, FALSE, TRUE,
5009 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5012 Xspring, TRUE, FALSE,
5016 Xspring_pause, FALSE, FALSE,
5020 Xspring_e, FALSE, FALSE,
5024 Xspring_w, FALSE, FALSE,
5028 Xspring_fall, FALSE, FALSE,
5032 Yspring_s, FALSE, FALSE,
5033 EL_SPRING, ACTION_FALLING, -1
5036 Yspring_sB, FALSE, TRUE,
5037 EL_SPRING, ACTION_FALLING, -1
5040 Yspring_e, FALSE, FALSE,
5041 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5044 Yspring_eB, FALSE, TRUE,
5045 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5048 Yspring_w, FALSE, FALSE,
5049 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5052 Yspring_wB, FALSE, TRUE,
5053 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5056 Yspring_kill_e, FALSE, FALSE,
5057 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5060 Yspring_kill_eB, FALSE, TRUE,
5061 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5064 Yspring_kill_w, FALSE, FALSE,
5065 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5068 Yspring_kill_wB, FALSE, TRUE,
5069 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5072 Xeater_n, TRUE, FALSE,
5073 EL_YAMYAM_UP, -1, -1
5076 Xeater_e, TRUE, FALSE,
5077 EL_YAMYAM_RIGHT, -1, -1
5080 Xeater_w, TRUE, FALSE,
5081 EL_YAMYAM_LEFT, -1, -1
5084 Xeater_s, TRUE, FALSE,
5085 EL_YAMYAM_DOWN, -1, -1
5088 Yeater_n, FALSE, FALSE,
5089 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5092 Yeater_nB, FALSE, TRUE,
5093 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5096 Yeater_e, FALSE, FALSE,
5097 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5100 Yeater_eB, FALSE, TRUE,
5101 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5104 Yeater_s, FALSE, FALSE,
5105 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5108 Yeater_sB, FALSE, TRUE,
5109 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5112 Yeater_w, FALSE, FALSE,
5113 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5116 Yeater_wB, FALSE, TRUE,
5117 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5120 Yeater_stone, FALSE, FALSE,
5121 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5124 Yeater_spring, FALSE, FALSE,
5125 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5128 Xalien, TRUE, FALSE,
5132 Xalien_pause, FALSE, FALSE,
5136 Yalien_n, FALSE, FALSE,
5137 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5140 Yalien_nB, FALSE, TRUE,
5141 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5144 Yalien_e, FALSE, FALSE,
5145 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5148 Yalien_eB, FALSE, TRUE,
5149 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5152 Yalien_s, FALSE, FALSE,
5153 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5156 Yalien_sB, FALSE, TRUE,
5157 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5160 Yalien_w, FALSE, FALSE,
5161 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5164 Yalien_wB, FALSE, TRUE,
5165 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5168 Yalien_stone, FALSE, FALSE,
5169 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5172 Yalien_spring, FALSE, FALSE,
5173 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5176 Xemerald, TRUE, FALSE,
5180 Xemerald_pause, FALSE, FALSE,
5184 Xemerald_fall, FALSE, FALSE,
5188 Xemerald_shine, FALSE, FALSE,
5189 EL_EMERALD, ACTION_TWINKLING, -1
5192 Yemerald_s, FALSE, FALSE,
5193 EL_EMERALD, ACTION_FALLING, -1
5196 Yemerald_sB, FALSE, TRUE,
5197 EL_EMERALD, ACTION_FALLING, -1
5200 Yemerald_e, FALSE, FALSE,
5201 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5204 Yemerald_eB, FALSE, TRUE,
5205 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5208 Yemerald_w, FALSE, FALSE,
5209 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5212 Yemerald_wB, FALSE, TRUE,
5213 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5216 Yemerald_eat, FALSE, FALSE,
5217 EL_EMERALD, ACTION_COLLECTING, -1
5220 Yemerald_stone, FALSE, FALSE,
5221 EL_NUT, ACTION_BREAKING, -1
5224 Xdiamond, TRUE, FALSE,
5228 Xdiamond_pause, FALSE, FALSE,
5232 Xdiamond_fall, FALSE, FALSE,
5236 Xdiamond_shine, FALSE, FALSE,
5237 EL_DIAMOND, ACTION_TWINKLING, -1
5240 Ydiamond_s, FALSE, FALSE,
5241 EL_DIAMOND, ACTION_FALLING, -1
5244 Ydiamond_sB, FALSE, TRUE,
5245 EL_DIAMOND, ACTION_FALLING, -1
5248 Ydiamond_e, FALSE, FALSE,
5249 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5252 Ydiamond_eB, FALSE, TRUE,
5253 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5256 Ydiamond_w, FALSE, FALSE,
5257 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5260 Ydiamond_wB, FALSE, TRUE,
5261 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5264 Ydiamond_eat, FALSE, FALSE,
5265 EL_DIAMOND, ACTION_COLLECTING, -1
5268 Ydiamond_stone, FALSE, FALSE,
5269 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5272 Xdrip_fall, TRUE, FALSE,
5273 EL_AMOEBA_DROP, -1, -1
5276 Xdrip_stretch, FALSE, FALSE,
5277 EL_AMOEBA_DROP, ACTION_FALLING, -1
5280 Xdrip_stretchB, FALSE, TRUE,
5281 EL_AMOEBA_DROP, ACTION_FALLING, -1
5284 Xdrip_eat, FALSE, FALSE,
5285 EL_AMOEBA_DROP, ACTION_GROWING, -1
5288 Ydrip_s1, FALSE, FALSE,
5289 EL_AMOEBA_DROP, ACTION_FALLING, -1
5292 Ydrip_s1B, FALSE, TRUE,
5293 EL_AMOEBA_DROP, ACTION_FALLING, -1
5296 Ydrip_s2, FALSE, FALSE,
5297 EL_AMOEBA_DROP, ACTION_FALLING, -1
5300 Ydrip_s2B, FALSE, TRUE,
5301 EL_AMOEBA_DROP, ACTION_FALLING, -1
5308 Xbomb_pause, FALSE, FALSE,
5312 Xbomb_fall, FALSE, FALSE,
5316 Ybomb_s, FALSE, FALSE,
5317 EL_BOMB, ACTION_FALLING, -1
5320 Ybomb_sB, FALSE, TRUE,
5321 EL_BOMB, ACTION_FALLING, -1
5324 Ybomb_e, FALSE, FALSE,
5325 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5328 Ybomb_eB, FALSE, TRUE,
5329 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5332 Ybomb_w, FALSE, FALSE,
5333 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5336 Ybomb_wB, FALSE, TRUE,
5337 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5340 Ybomb_eat, FALSE, FALSE,
5341 EL_BOMB, ACTION_ACTIVATING, -1
5344 Xballoon, TRUE, FALSE,
5348 Yballoon_n, FALSE, FALSE,
5349 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5352 Yballoon_nB, FALSE, TRUE,
5353 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5356 Yballoon_e, FALSE, FALSE,
5357 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5360 Yballoon_eB, FALSE, TRUE,
5361 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5364 Yballoon_s, FALSE, FALSE,
5365 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5368 Yballoon_sB, FALSE, TRUE,
5369 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5372 Yballoon_w, FALSE, FALSE,
5373 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5376 Yballoon_wB, FALSE, TRUE,
5377 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5380 Xgrass, TRUE, FALSE,
5381 EL_EMC_GRASS, -1, -1
5384 Ygrass_nB, FALSE, FALSE,
5385 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5388 Ygrass_eB, FALSE, FALSE,
5389 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5392 Ygrass_sB, FALSE, FALSE,
5393 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5396 Ygrass_wB, FALSE, FALSE,
5397 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5404 Ydirt_nB, FALSE, FALSE,
5405 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5408 Ydirt_eB, FALSE, FALSE,
5409 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5412 Ydirt_sB, FALSE, FALSE,
5413 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5416 Ydirt_wB, FALSE, FALSE,
5417 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5420 Xacid_ne, TRUE, FALSE,
5421 EL_ACID_POOL_TOPRIGHT, -1, -1
5424 Xacid_se, TRUE, FALSE,
5425 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5428 Xacid_s, TRUE, FALSE,
5429 EL_ACID_POOL_BOTTOM, -1, -1
5432 Xacid_sw, TRUE, FALSE,
5433 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5436 Xacid_nw, TRUE, FALSE,
5437 EL_ACID_POOL_TOPLEFT, -1, -1
5440 Xacid_1, TRUE, FALSE,
5444 Xacid_2, FALSE, FALSE,
5448 Xacid_3, FALSE, FALSE,
5452 Xacid_4, FALSE, FALSE,
5456 Xacid_5, FALSE, FALSE,
5460 Xacid_6, FALSE, FALSE,
5464 Xacid_7, FALSE, FALSE,
5468 Xacid_8, FALSE, FALSE,
5472 Xball_1, TRUE, FALSE,
5473 EL_EMC_MAGIC_BALL, -1, -1
5476 Xball_1B, FALSE, FALSE,
5477 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5480 Xball_2, FALSE, FALSE,
5481 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5484 Xball_2B, FALSE, FALSE,
5485 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5488 Yball_eat, FALSE, FALSE,
5489 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5492 Ykey_1_eat, FALSE, FALSE,
5493 EL_EM_KEY_1, ACTION_COLLECTING, -1
5496 Ykey_2_eat, FALSE, FALSE,
5497 EL_EM_KEY_2, ACTION_COLLECTING, -1
5500 Ykey_3_eat, FALSE, FALSE,
5501 EL_EM_KEY_3, ACTION_COLLECTING, -1
5504 Ykey_4_eat, FALSE, FALSE,
5505 EL_EM_KEY_4, ACTION_COLLECTING, -1
5508 Ykey_5_eat, FALSE, FALSE,
5509 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5512 Ykey_6_eat, FALSE, FALSE,
5513 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5516 Ykey_7_eat, FALSE, FALSE,
5517 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5520 Ykey_8_eat, FALSE, FALSE,
5521 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5524 Ylenses_eat, FALSE, FALSE,
5525 EL_EMC_LENSES, ACTION_COLLECTING, -1
5528 Ymagnify_eat, FALSE, FALSE,
5529 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5532 Ygrass_eat, FALSE, FALSE,
5533 EL_EMC_GRASS, ACTION_SNAPPING, -1
5536 Ydirt_eat, FALSE, FALSE,
5537 EL_SAND, ACTION_SNAPPING, -1
5540 Xgrow_ns, TRUE, FALSE,
5541 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5544 Ygrow_ns_eat, FALSE, FALSE,
5545 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5548 Xgrow_ew, TRUE, FALSE,
5549 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5552 Ygrow_ew_eat, FALSE, FALSE,
5553 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5556 Xwonderwall, TRUE, FALSE,
5557 EL_MAGIC_WALL, -1, -1
5560 XwonderwallB, FALSE, FALSE,
5561 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5564 Xamoeba_1, TRUE, FALSE,
5565 EL_AMOEBA_DRY, ACTION_OTHER, -1
5568 Xamoeba_2, FALSE, FALSE,
5569 EL_AMOEBA_DRY, ACTION_OTHER, -1
5572 Xamoeba_3, FALSE, FALSE,
5573 EL_AMOEBA_DRY, ACTION_OTHER, -1
5576 Xamoeba_4, FALSE, FALSE,
5577 EL_AMOEBA_DRY, ACTION_OTHER, -1
5580 Xamoeba_5, TRUE, FALSE,
5581 EL_AMOEBA_WET, ACTION_OTHER, -1
5584 Xamoeba_6, FALSE, FALSE,
5585 EL_AMOEBA_WET, ACTION_OTHER, -1
5588 Xamoeba_7, FALSE, FALSE,
5589 EL_AMOEBA_WET, ACTION_OTHER, -1
5592 Xamoeba_8, FALSE, FALSE,
5593 EL_AMOEBA_WET, ACTION_OTHER, -1
5596 Xdoor_1, TRUE, FALSE,
5597 EL_EM_GATE_1, -1, -1
5600 Xdoor_2, TRUE, FALSE,
5601 EL_EM_GATE_2, -1, -1
5604 Xdoor_3, TRUE, FALSE,
5605 EL_EM_GATE_3, -1, -1
5608 Xdoor_4, TRUE, FALSE,
5609 EL_EM_GATE_4, -1, -1
5612 Xdoor_5, TRUE, FALSE,
5613 EL_EMC_GATE_5, -1, -1
5616 Xdoor_6, TRUE, FALSE,
5617 EL_EMC_GATE_6, -1, -1
5620 Xdoor_7, TRUE, FALSE,
5621 EL_EMC_GATE_7, -1, -1
5624 Xdoor_8, TRUE, FALSE,
5625 EL_EMC_GATE_8, -1, -1
5628 Xkey_1, TRUE, FALSE,
5632 Xkey_2, TRUE, FALSE,
5636 Xkey_3, TRUE, FALSE,
5640 Xkey_4, TRUE, FALSE,
5644 Xkey_5, TRUE, FALSE,
5645 EL_EMC_KEY_5, -1, -1
5648 Xkey_6, TRUE, FALSE,
5649 EL_EMC_KEY_6, -1, -1
5652 Xkey_7, TRUE, FALSE,
5653 EL_EMC_KEY_7, -1, -1
5656 Xkey_8, TRUE, FALSE,
5657 EL_EMC_KEY_8, -1, -1
5660 Xwind_n, TRUE, FALSE,
5661 EL_BALLOON_SWITCH_UP, -1, -1
5664 Xwind_e, TRUE, FALSE,
5665 EL_BALLOON_SWITCH_RIGHT, -1, -1
5668 Xwind_s, TRUE, FALSE,
5669 EL_BALLOON_SWITCH_DOWN, -1, -1
5672 Xwind_w, TRUE, FALSE,
5673 EL_BALLOON_SWITCH_LEFT, -1, -1
5676 Xwind_nesw, TRUE, FALSE,
5677 EL_BALLOON_SWITCH_ANY, -1, -1
5680 Xwind_stop, TRUE, FALSE,
5681 EL_BALLOON_SWITCH_NONE, -1, -1
5685 EL_EM_EXIT_CLOSED, -1, -1
5688 Xexit_1, TRUE, FALSE,
5689 EL_EM_EXIT_OPEN, -1, -1
5692 Xexit_2, FALSE, FALSE,
5693 EL_EM_EXIT_OPEN, -1, -1
5696 Xexit_3, FALSE, FALSE,
5697 EL_EM_EXIT_OPEN, -1, -1
5700 Xdynamite, TRUE, FALSE,
5701 EL_EM_DYNAMITE, -1, -1
5704 Ydynamite_eat, FALSE, FALSE,
5705 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5708 Xdynamite_1, TRUE, FALSE,
5709 EL_EM_DYNAMITE_ACTIVE, -1, -1
5712 Xdynamite_2, FALSE, FALSE,
5713 EL_EM_DYNAMITE_ACTIVE, -1, -1
5716 Xdynamite_3, FALSE, FALSE,
5717 EL_EM_DYNAMITE_ACTIVE, -1, -1
5720 Xdynamite_4, FALSE, FALSE,
5721 EL_EM_DYNAMITE_ACTIVE, -1, -1
5724 Xbumper, TRUE, FALSE,
5725 EL_EMC_SPRING_BUMPER, -1, -1
5728 XbumperB, FALSE, FALSE,
5729 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5732 Xwheel, TRUE, FALSE,
5733 EL_ROBOT_WHEEL, -1, -1
5736 XwheelB, FALSE, FALSE,
5737 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5740 Xswitch, TRUE, FALSE,
5741 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5744 XswitchB, FALSE, FALSE,
5745 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5749 EL_QUICKSAND_EMPTY, -1, -1
5752 Xsand_stone, TRUE, FALSE,
5753 EL_QUICKSAND_FULL, -1, -1
5756 Xsand_stonein_1, FALSE, TRUE,
5757 EL_ROCK, ACTION_FILLING, -1
5760 Xsand_stonein_2, FALSE, TRUE,
5761 EL_ROCK, ACTION_FILLING, -1
5764 Xsand_stonein_3, FALSE, TRUE,
5765 EL_ROCK, ACTION_FILLING, -1
5768 Xsand_stonein_4, FALSE, TRUE,
5769 EL_ROCK, ACTION_FILLING, -1
5772 Xsand_stonesand_1, FALSE, FALSE,
5773 EL_QUICKSAND_EMPTYING, -1, -1
5776 Xsand_stonesand_2, FALSE, FALSE,
5777 EL_QUICKSAND_EMPTYING, -1, -1
5780 Xsand_stonesand_3, FALSE, FALSE,
5781 EL_QUICKSAND_EMPTYING, -1, -1
5784 Xsand_stonesand_4, FALSE, FALSE,
5785 EL_QUICKSAND_EMPTYING, -1, -1
5788 Xsand_stonesand_quickout_1, FALSE, FALSE,
5789 EL_QUICKSAND_EMPTYING, -1, -1
5792 Xsand_stonesand_quickout_2, FALSE, FALSE,
5793 EL_QUICKSAND_EMPTYING, -1, -1
5796 Xsand_stoneout_1, FALSE, FALSE,
5797 EL_ROCK, ACTION_EMPTYING, -1
5800 Xsand_stoneout_2, FALSE, FALSE,
5801 EL_ROCK, ACTION_EMPTYING, -1
5804 Xsand_sandstone_1, FALSE, FALSE,
5805 EL_QUICKSAND_FILLING, -1, -1
5808 Xsand_sandstone_2, FALSE, FALSE,
5809 EL_QUICKSAND_FILLING, -1, -1
5812 Xsand_sandstone_3, FALSE, FALSE,
5813 EL_QUICKSAND_FILLING, -1, -1
5816 Xsand_sandstone_4, FALSE, FALSE,
5817 EL_QUICKSAND_FILLING, -1, -1
5820 Xplant, TRUE, FALSE,
5821 EL_EMC_PLANT, -1, -1
5824 Yplant, FALSE, FALSE,
5825 EL_EMC_PLANT, -1, -1
5828 Xlenses, TRUE, FALSE,
5829 EL_EMC_LENSES, -1, -1
5832 Xmagnify, TRUE, FALSE,
5833 EL_EMC_MAGNIFIER, -1, -1
5836 Xdripper, TRUE, FALSE,
5837 EL_EMC_DRIPPER, -1, -1
5840 XdripperB, FALSE, FALSE,
5841 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5844 Xfake_blank, TRUE, FALSE,
5845 EL_INVISIBLE_WALL, -1, -1
5848 Xfake_blankB, FALSE, FALSE,
5849 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5852 Xfake_grass, TRUE, FALSE,
5853 EL_EMC_FAKE_GRASS, -1, -1
5856 Xfake_grassB, FALSE, FALSE,
5857 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5860 Xfake_door_1, TRUE, FALSE,
5861 EL_EM_GATE_1_GRAY, -1, -1
5864 Xfake_door_2, TRUE, FALSE,
5865 EL_EM_GATE_2_GRAY, -1, -1
5868 Xfake_door_3, TRUE, FALSE,
5869 EL_EM_GATE_3_GRAY, -1, -1
5872 Xfake_door_4, TRUE, FALSE,
5873 EL_EM_GATE_4_GRAY, -1, -1
5876 Xfake_door_5, TRUE, FALSE,
5877 EL_EMC_GATE_5_GRAY, -1, -1
5880 Xfake_door_6, TRUE, FALSE,
5881 EL_EMC_GATE_6_GRAY, -1, -1
5884 Xfake_door_7, TRUE, FALSE,
5885 EL_EMC_GATE_7_GRAY, -1, -1
5888 Xfake_door_8, TRUE, FALSE,
5889 EL_EMC_GATE_8_GRAY, -1, -1
5892 Xfake_acid_1, TRUE, FALSE,
5893 EL_EMC_FAKE_ACID, -1, -1
5896 Xfake_acid_2, FALSE, FALSE,
5897 EL_EMC_FAKE_ACID, -1, -1
5900 Xfake_acid_3, FALSE, FALSE,
5901 EL_EMC_FAKE_ACID, -1, -1
5904 Xfake_acid_4, FALSE, FALSE,
5905 EL_EMC_FAKE_ACID, -1, -1
5908 Xfake_acid_5, FALSE, FALSE,
5909 EL_EMC_FAKE_ACID, -1, -1
5912 Xfake_acid_6, FALSE, FALSE,
5913 EL_EMC_FAKE_ACID, -1, -1
5916 Xfake_acid_7, FALSE, FALSE,
5917 EL_EMC_FAKE_ACID, -1, -1
5920 Xfake_acid_8, FALSE, FALSE,
5921 EL_EMC_FAKE_ACID, -1, -1
5924 Xsteel_1, TRUE, FALSE,
5925 EL_STEELWALL, -1, -1
5928 Xsteel_2, TRUE, FALSE,
5929 EL_EMC_STEELWALL_2, -1, -1
5932 Xsteel_3, TRUE, FALSE,
5933 EL_EMC_STEELWALL_3, -1, -1
5936 Xsteel_4, TRUE, FALSE,
5937 EL_EMC_STEELWALL_4, -1, -1
5940 Xwall_1, TRUE, FALSE,
5944 Xwall_2, TRUE, FALSE,
5945 EL_EMC_WALL_14, -1, -1
5948 Xwall_3, TRUE, FALSE,
5949 EL_EMC_WALL_15, -1, -1
5952 Xwall_4, TRUE, FALSE,
5953 EL_EMC_WALL_16, -1, -1
5956 Xround_wall_1, TRUE, FALSE,
5957 EL_WALL_SLIPPERY, -1, -1
5960 Xround_wall_2, TRUE, FALSE,
5961 EL_EMC_WALL_SLIPPERY_2, -1, -1
5964 Xround_wall_3, TRUE, FALSE,
5965 EL_EMC_WALL_SLIPPERY_3, -1, -1
5968 Xround_wall_4, TRUE, FALSE,
5969 EL_EMC_WALL_SLIPPERY_4, -1, -1
5972 Xdecor_1, TRUE, FALSE,
5973 EL_EMC_WALL_8, -1, -1
5976 Xdecor_2, TRUE, FALSE,
5977 EL_EMC_WALL_6, -1, -1
5980 Xdecor_3, TRUE, FALSE,
5981 EL_EMC_WALL_4, -1, -1
5984 Xdecor_4, TRUE, FALSE,
5985 EL_EMC_WALL_7, -1, -1
5988 Xdecor_5, TRUE, FALSE,
5989 EL_EMC_WALL_5, -1, -1
5992 Xdecor_6, TRUE, FALSE,
5993 EL_EMC_WALL_9, -1, -1
5996 Xdecor_7, TRUE, FALSE,
5997 EL_EMC_WALL_10, -1, -1
6000 Xdecor_8, TRUE, FALSE,
6001 EL_EMC_WALL_1, -1, -1
6004 Xdecor_9, TRUE, FALSE,
6005 EL_EMC_WALL_2, -1, -1
6008 Xdecor_10, TRUE, FALSE,
6009 EL_EMC_WALL_3, -1, -1
6012 Xdecor_11, TRUE, FALSE,
6013 EL_EMC_WALL_11, -1, -1
6016 Xdecor_12, TRUE, FALSE,
6017 EL_EMC_WALL_12, -1, -1
6020 Xalpha_0, TRUE, FALSE,
6021 EL_CHAR('0'), -1, -1
6024 Xalpha_1, TRUE, FALSE,
6025 EL_CHAR('1'), -1, -1
6028 Xalpha_2, TRUE, FALSE,
6029 EL_CHAR('2'), -1, -1
6032 Xalpha_3, TRUE, FALSE,
6033 EL_CHAR('3'), -1, -1
6036 Xalpha_4, TRUE, FALSE,
6037 EL_CHAR('4'), -1, -1
6040 Xalpha_5, TRUE, FALSE,
6041 EL_CHAR('5'), -1, -1
6044 Xalpha_6, TRUE, FALSE,
6045 EL_CHAR('6'), -1, -1
6048 Xalpha_7, TRUE, FALSE,
6049 EL_CHAR('7'), -1, -1
6052 Xalpha_8, TRUE, FALSE,
6053 EL_CHAR('8'), -1, -1
6056 Xalpha_9, TRUE, FALSE,
6057 EL_CHAR('9'), -1, -1
6060 Xalpha_excla, TRUE, FALSE,
6061 EL_CHAR('!'), -1, -1
6064 Xalpha_quote, TRUE, FALSE,
6065 EL_CHAR('"'), -1, -1
6068 Xalpha_comma, TRUE, FALSE,
6069 EL_CHAR(','), -1, -1
6072 Xalpha_minus, TRUE, FALSE,
6073 EL_CHAR('-'), -1, -1
6076 Xalpha_perio, TRUE, FALSE,
6077 EL_CHAR('.'), -1, -1
6080 Xalpha_colon, TRUE, FALSE,
6081 EL_CHAR(':'), -1, -1
6084 Xalpha_quest, TRUE, FALSE,
6085 EL_CHAR('?'), -1, -1
6088 Xalpha_a, TRUE, FALSE,
6089 EL_CHAR('A'), -1, -1
6092 Xalpha_b, TRUE, FALSE,
6093 EL_CHAR('B'), -1, -1
6096 Xalpha_c, TRUE, FALSE,
6097 EL_CHAR('C'), -1, -1
6100 Xalpha_d, TRUE, FALSE,
6101 EL_CHAR('D'), -1, -1
6104 Xalpha_e, TRUE, FALSE,
6105 EL_CHAR('E'), -1, -1
6108 Xalpha_f, TRUE, FALSE,
6109 EL_CHAR('F'), -1, -1
6112 Xalpha_g, TRUE, FALSE,
6113 EL_CHAR('G'), -1, -1
6116 Xalpha_h, TRUE, FALSE,
6117 EL_CHAR('H'), -1, -1
6120 Xalpha_i, TRUE, FALSE,
6121 EL_CHAR('I'), -1, -1
6124 Xalpha_j, TRUE, FALSE,
6125 EL_CHAR('J'), -1, -1
6128 Xalpha_k, TRUE, FALSE,
6129 EL_CHAR('K'), -1, -1
6132 Xalpha_l, TRUE, FALSE,
6133 EL_CHAR('L'), -1, -1
6136 Xalpha_m, TRUE, FALSE,
6137 EL_CHAR('M'), -1, -1
6140 Xalpha_n, TRUE, FALSE,
6141 EL_CHAR('N'), -1, -1
6144 Xalpha_o, TRUE, FALSE,
6145 EL_CHAR('O'), -1, -1
6148 Xalpha_p, TRUE, FALSE,
6149 EL_CHAR('P'), -1, -1
6152 Xalpha_q, TRUE, FALSE,
6153 EL_CHAR('Q'), -1, -1
6156 Xalpha_r, TRUE, FALSE,
6157 EL_CHAR('R'), -1, -1
6160 Xalpha_s, TRUE, FALSE,
6161 EL_CHAR('S'), -1, -1
6164 Xalpha_t, TRUE, FALSE,
6165 EL_CHAR('T'), -1, -1
6168 Xalpha_u, TRUE, FALSE,
6169 EL_CHAR('U'), -1, -1
6172 Xalpha_v, TRUE, FALSE,
6173 EL_CHAR('V'), -1, -1
6176 Xalpha_w, TRUE, FALSE,
6177 EL_CHAR('W'), -1, -1
6180 Xalpha_x, TRUE, FALSE,
6181 EL_CHAR('X'), -1, -1
6184 Xalpha_y, TRUE, FALSE,
6185 EL_CHAR('Y'), -1, -1
6188 Xalpha_z, TRUE, FALSE,
6189 EL_CHAR('Z'), -1, -1
6192 Xalpha_arrow_e, TRUE, FALSE,
6193 EL_CHAR('>'), -1, -1
6196 Xalpha_arrow_w, TRUE, FALSE,
6197 EL_CHAR('<'), -1, -1
6200 Xalpha_copyr, TRUE, FALSE,
6201 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6205 Xboom_bug, FALSE, FALSE,
6206 EL_BUG, ACTION_EXPLODING, -1
6209 Xboom_bomb, FALSE, FALSE,
6210 EL_BOMB, ACTION_EXPLODING, -1
6213 Xboom_android, FALSE, FALSE,
6214 EL_EMC_ANDROID, ACTION_OTHER, -1
6217 Xboom_1, FALSE, FALSE,
6218 EL_DEFAULT, ACTION_EXPLODING, -1
6221 Xboom_2, FALSE, FALSE,
6222 EL_DEFAULT, ACTION_EXPLODING, -1
6225 Znormal, FALSE, FALSE,
6229 Zdynamite, FALSE, FALSE,
6233 Zplayer, FALSE, FALSE,
6237 ZBORDER, FALSE, FALSE,
6247 static struct Mapping_EM_to_RND_player
6256 em_player_mapping_list[] =
6260 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6264 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6268 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6272 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6276 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6280 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6284 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6288 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6292 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6296 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6300 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6304 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6308 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6312 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6316 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6320 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6324 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6328 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6332 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6336 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6340 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6344 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6348 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6352 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6356 EL_PLAYER_1, ACTION_DEFAULT, -1,
6360 EL_PLAYER_2, ACTION_DEFAULT, -1,
6364 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6368 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6372 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6376 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6380 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6384 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6388 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6392 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6396 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6400 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6404 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6408 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6412 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6416 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6420 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6424 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6428 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6432 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6436 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6440 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6444 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6448 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6452 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6456 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6460 EL_PLAYER_3, ACTION_DEFAULT, -1,
6464 EL_PLAYER_4, ACTION_DEFAULT, -1,
6473 int map_element_RND_to_EM(int element_rnd)
6475 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6476 static boolean mapping_initialized = FALSE;
6478 if (!mapping_initialized)
6482 /* return "Xalpha_quest" for all undefined elements in mapping array */
6483 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6484 mapping_RND_to_EM[i] = Xalpha_quest;
6486 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6487 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6488 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6489 em_object_mapping_list[i].element_em;
6491 mapping_initialized = TRUE;
6494 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6495 return mapping_RND_to_EM[element_rnd];
6497 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6502 int map_element_EM_to_RND(int element_em)
6504 static unsigned short mapping_EM_to_RND[TILE_MAX];
6505 static boolean mapping_initialized = FALSE;
6507 if (!mapping_initialized)
6511 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6512 for (i = 0; i < TILE_MAX; i++)
6513 mapping_EM_to_RND[i] = EL_UNKNOWN;
6515 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6516 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6517 em_object_mapping_list[i].element_rnd;
6519 mapping_initialized = TRUE;
6522 if (element_em >= 0 && element_em < TILE_MAX)
6523 return mapping_EM_to_RND[element_em];
6525 Error(ERR_WARN, "invalid EM level element %d", element_em);
6530 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6532 struct LevelInfo_EM *level_em = level->native_em_level;
6533 struct LEVEL *lev = level_em->lev;
6536 for (i = 0; i < TILE_MAX; i++)
6537 lev->android_array[i] = Xblank;
6539 for (i = 0; i < level->num_android_clone_elements; i++)
6541 int element_rnd = level->android_clone_element[i];
6542 int element_em = map_element_RND_to_EM(element_rnd);
6544 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6545 if (em_object_mapping_list[j].element_rnd == element_rnd)
6546 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6550 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6552 struct LevelInfo_EM *level_em = level->native_em_level;
6553 struct LEVEL *lev = level_em->lev;
6556 level->num_android_clone_elements = 0;
6558 for (i = 0; i < TILE_MAX; i++)
6560 int element_em = lev->android_array[i];
6562 boolean element_found = FALSE;
6564 if (element_em == Xblank)
6567 element_rnd = map_element_EM_to_RND(element_em);
6569 for (j = 0; j < level->num_android_clone_elements; j++)
6570 if (level->android_clone_element[j] == element_rnd)
6571 element_found = TRUE;
6575 level->android_clone_element[level->num_android_clone_elements++] =
6578 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6583 if (level->num_android_clone_elements == 0)
6585 level->num_android_clone_elements = 1;
6586 level->android_clone_element[0] = EL_EMPTY;
6590 int map_direction_RND_to_EM(int direction)
6592 return (direction == MV_UP ? 0 :
6593 direction == MV_RIGHT ? 1 :
6594 direction == MV_DOWN ? 2 :
6595 direction == MV_LEFT ? 3 :
6599 int map_direction_EM_to_RND(int direction)
6601 return (direction == 0 ? MV_UP :
6602 direction == 1 ? MV_RIGHT :
6603 direction == 2 ? MV_DOWN :
6604 direction == 3 ? MV_LEFT :
6608 int map_element_RND_to_SP(int element_rnd)
6610 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6612 if (element_rnd >= EL_SP_START &&
6613 element_rnd <= EL_SP_END)
6614 element_sp = element_rnd - EL_SP_START;
6615 else if (element_rnd == EL_EMPTY_SPACE)
6617 else if (element_rnd == EL_INVISIBLE_WALL)
6623 int map_element_SP_to_RND(int element_sp)
6625 int element_rnd = EL_UNKNOWN;
6627 if (element_sp >= 0x00 &&
6629 element_rnd = EL_SP_START + element_sp;
6630 else if (element_sp == 0x28)
6631 element_rnd = EL_INVISIBLE_WALL;
6636 int map_action_SP_to_RND(int action_sp)
6640 case actActive: return ACTION_ACTIVE;
6641 case actImpact: return ACTION_IMPACT;
6642 case actExploding: return ACTION_EXPLODING;
6643 case actDigging: return ACTION_DIGGING;
6644 case actSnapping: return ACTION_SNAPPING;
6645 case actCollecting: return ACTION_COLLECTING;
6646 case actPassing: return ACTION_PASSING;
6647 case actPushing: return ACTION_PUSHING;
6648 case actDropping: return ACTION_DROPPING;
6650 default: return ACTION_DEFAULT;
6654 int get_next_element(int element)
6658 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6659 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6660 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6661 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6662 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6663 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6664 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6665 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6666 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6667 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6668 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6670 default: return element;
6674 int el_act_dir2img(int element, int action, int direction)
6676 element = GFX_ELEMENT(element);
6677 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6679 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6680 return element_info[element].direction_graphic[action][direction];
6683 static int el_act_dir2crm(int element, int action, int direction)
6685 element = GFX_ELEMENT(element);
6686 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6688 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6689 return element_info[element].direction_crumbled[action][direction];
6692 int el_act2img(int element, int action)
6694 element = GFX_ELEMENT(element);
6696 return element_info[element].graphic[action];
6699 int el_act2crm(int element, int action)
6701 element = GFX_ELEMENT(element);
6703 return element_info[element].crumbled[action];
6706 int el_dir2img(int element, int direction)
6708 element = GFX_ELEMENT(element);
6710 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6713 int el2baseimg(int element)
6715 return element_info[element].graphic[ACTION_DEFAULT];
6718 int el2img(int element)
6720 element = GFX_ELEMENT(element);
6722 return element_info[element].graphic[ACTION_DEFAULT];
6725 int el2edimg(int element)
6727 element = GFX_ELEMENT(element);
6729 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6732 int el2preimg(int element)
6734 element = GFX_ELEMENT(element);
6736 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6739 int el2panelimg(int element)
6741 element = GFX_ELEMENT(element);
6743 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6746 int font2baseimg(int font_nr)
6748 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6751 int getBeltNrFromBeltElement(int element)
6753 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6754 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6755 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6758 int getBeltNrFromBeltActiveElement(int element)
6760 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6761 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6762 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6765 int getBeltNrFromBeltSwitchElement(int element)
6767 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6768 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6769 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6772 int getBeltDirNrFromBeltElement(int element)
6774 static int belt_base_element[4] =
6776 EL_CONVEYOR_BELT_1_LEFT,
6777 EL_CONVEYOR_BELT_2_LEFT,
6778 EL_CONVEYOR_BELT_3_LEFT,
6779 EL_CONVEYOR_BELT_4_LEFT
6782 int belt_nr = getBeltNrFromBeltElement(element);
6783 int belt_dir_nr = element - belt_base_element[belt_nr];
6785 return (belt_dir_nr % 3);
6788 int getBeltDirNrFromBeltSwitchElement(int element)
6790 static int belt_base_element[4] =
6792 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6793 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6794 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6795 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6798 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6799 int belt_dir_nr = element - belt_base_element[belt_nr];
6801 return (belt_dir_nr % 3);
6804 int getBeltDirFromBeltElement(int element)
6806 static int belt_move_dir[3] =
6813 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6815 return belt_move_dir[belt_dir_nr];
6818 int getBeltDirFromBeltSwitchElement(int element)
6820 static int belt_move_dir[3] =
6827 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6829 return belt_move_dir[belt_dir_nr];
6832 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6834 static int belt_base_element[4] =
6836 EL_CONVEYOR_BELT_1_LEFT,
6837 EL_CONVEYOR_BELT_2_LEFT,
6838 EL_CONVEYOR_BELT_3_LEFT,
6839 EL_CONVEYOR_BELT_4_LEFT
6842 return belt_base_element[belt_nr] + belt_dir_nr;
6845 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6847 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6849 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6852 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6854 static int belt_base_element[4] =
6856 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6857 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6858 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6859 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6862 return belt_base_element[belt_nr] + belt_dir_nr;
6865 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6867 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6869 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6872 boolean getTeamMode_EM()
6874 return game.team_mode;
6877 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6879 int game_frame_delay_value;
6881 game_frame_delay_value =
6882 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6883 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6886 if (tape.playing && tape.warp_forward && !tape.pausing)
6887 game_frame_delay_value = 0;
6889 return game_frame_delay_value;
6892 unsigned int InitRND(int seed)
6894 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6895 return InitEngineRandom_EM(seed);
6896 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6897 return InitEngineRandom_SP(seed);
6899 return InitEngineRandom_RND(seed);
6902 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6903 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6905 inline static int get_effective_element_EM(int tile, int frame_em)
6907 int element = object_mapping[tile].element_rnd;
6908 int action = object_mapping[tile].action;
6909 boolean is_backside = object_mapping[tile].is_backside;
6910 boolean action_removing = (action == ACTION_DIGGING ||
6911 action == ACTION_SNAPPING ||
6912 action == ACTION_COLLECTING);
6918 case Yacid_splash_eB:
6919 case Yacid_splash_wB:
6920 return (frame_em > 5 ? EL_EMPTY : element);
6926 else /* frame_em == 7 */
6930 case Yacid_splash_eB:
6931 case Yacid_splash_wB:
6934 case Yemerald_stone:
6937 case Ydiamond_stone:
6941 case Xdrip_stretchB:
6960 case Xsand_stonein_1:
6961 case Xsand_stonein_2:
6962 case Xsand_stonein_3:
6963 case Xsand_stonein_4:
6967 return (is_backside || action_removing ? EL_EMPTY : element);
6972 inline static boolean check_linear_animation_EM(int tile)
6976 case Xsand_stonesand_1:
6977 case Xsand_stonesand_quickout_1:
6978 case Xsand_sandstone_1:
6979 case Xsand_stonein_1:
6980 case Xsand_stoneout_1:
6999 case Yacid_splash_eB:
7000 case Yacid_splash_wB:
7001 case Yemerald_stone:
7008 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7009 boolean has_crumbled_graphics,
7010 int crumbled, int sync_frame)
7012 /* if element can be crumbled, but certain action graphics are just empty
7013 space (like instantly snapping sand to empty space in 1 frame), do not
7014 treat these empty space graphics as crumbled graphics in EMC engine */
7015 if (crumbled == IMG_EMPTY_SPACE)
7016 has_crumbled_graphics = FALSE;
7018 if (has_crumbled_graphics)
7020 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7021 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7022 g_crumbled->anim_delay,
7023 g_crumbled->anim_mode,
7024 g_crumbled->anim_start_frame,
7027 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7028 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7030 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7032 g_em->has_crumbled_graphics = TRUE;
7036 g_em->crumbled_bitmap = NULL;
7037 g_em->crumbled_src_x = 0;
7038 g_em->crumbled_src_y = 0;
7039 g_em->crumbled_border_size = 0;
7041 g_em->has_crumbled_graphics = FALSE;
7045 void ResetGfxAnimation_EM(int x, int y, int tile)
7050 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7051 int tile, int frame_em, int x, int y)
7053 int action = object_mapping[tile].action;
7054 int direction = object_mapping[tile].direction;
7055 int effective_element = get_effective_element_EM(tile, frame_em);
7056 int graphic = (direction == MV_NONE ?
7057 el_act2img(effective_element, action) :
7058 el_act_dir2img(effective_element, action, direction));
7059 struct GraphicInfo *g = &graphic_info[graphic];
7061 boolean action_removing = (action == ACTION_DIGGING ||
7062 action == ACTION_SNAPPING ||
7063 action == ACTION_COLLECTING);
7064 boolean action_moving = (action == ACTION_FALLING ||
7065 action == ACTION_MOVING ||
7066 action == ACTION_PUSHING ||
7067 action == ACTION_EATING ||
7068 action == ACTION_FILLING ||
7069 action == ACTION_EMPTYING);
7070 boolean action_falling = (action == ACTION_FALLING ||
7071 action == ACTION_FILLING ||
7072 action == ACTION_EMPTYING);
7074 /* special case: graphic uses "2nd movement tile" and has defined
7075 7 frames for movement animation (or less) => use default graphic
7076 for last (8th) frame which ends the movement animation */
7077 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7079 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7080 graphic = (direction == MV_NONE ?
7081 el_act2img(effective_element, action) :
7082 el_act_dir2img(effective_element, action, direction));
7084 g = &graphic_info[graphic];
7087 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7091 else if (action_moving)
7093 boolean is_backside = object_mapping[tile].is_backside;
7097 int direction = object_mapping[tile].direction;
7098 int move_dir = (action_falling ? MV_DOWN : direction);
7103 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7104 if (g->double_movement && frame_em == 0)
7108 if (move_dir == MV_LEFT)
7109 GfxFrame[x - 1][y] = GfxFrame[x][y];
7110 else if (move_dir == MV_RIGHT)
7111 GfxFrame[x + 1][y] = GfxFrame[x][y];
7112 else if (move_dir == MV_UP)
7113 GfxFrame[x][y - 1] = GfxFrame[x][y];
7114 else if (move_dir == MV_DOWN)
7115 GfxFrame[x][y + 1] = GfxFrame[x][y];
7122 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7123 if (tile == Xsand_stonesand_quickout_1 ||
7124 tile == Xsand_stonesand_quickout_2)
7128 if (graphic_info[graphic].anim_global_sync)
7129 sync_frame = FrameCounter;
7130 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7131 sync_frame = GfxFrame[x][y];
7133 sync_frame = 0; /* playfield border (pseudo steel) */
7135 SetRandomAnimationValue(x, y);
7137 int frame = getAnimationFrame(g->anim_frames,
7140 g->anim_start_frame,
7143 g_em->unique_identifier =
7144 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7147 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7148 int tile, int frame_em, int x, int y)
7150 int action = object_mapping[tile].action;
7151 int direction = object_mapping[tile].direction;
7152 boolean is_backside = object_mapping[tile].is_backside;
7153 int effective_element = get_effective_element_EM(tile, frame_em);
7154 int effective_action = action;
7155 int graphic = (direction == MV_NONE ?
7156 el_act2img(effective_element, effective_action) :
7157 el_act_dir2img(effective_element, effective_action,
7159 int crumbled = (direction == MV_NONE ?
7160 el_act2crm(effective_element, effective_action) :
7161 el_act_dir2crm(effective_element, effective_action,
7163 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7164 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7165 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7166 struct GraphicInfo *g = &graphic_info[graphic];
7169 /* special case: graphic uses "2nd movement tile" and has defined
7170 7 frames for movement animation (or less) => use default graphic
7171 for last (8th) frame which ends the movement animation */
7172 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7174 effective_action = ACTION_DEFAULT;
7175 graphic = (direction == MV_NONE ?
7176 el_act2img(effective_element, effective_action) :
7177 el_act_dir2img(effective_element, effective_action,
7179 crumbled = (direction == MV_NONE ?
7180 el_act2crm(effective_element, effective_action) :
7181 el_act_dir2crm(effective_element, effective_action,
7184 g = &graphic_info[graphic];
7187 if (graphic_info[graphic].anim_global_sync)
7188 sync_frame = FrameCounter;
7189 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7190 sync_frame = GfxFrame[x][y];
7192 sync_frame = 0; /* playfield border (pseudo steel) */
7194 SetRandomAnimationValue(x, y);
7196 int frame = getAnimationFrame(g->anim_frames,
7199 g->anim_start_frame,
7202 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7203 g->double_movement && is_backside);
7205 /* (updating the "crumbled" graphic definitions is probably not really needed,
7206 as animations for crumbled graphics can't be longer than one EMC cycle) */
7207 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7211 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7212 int player_nr, int anim, int frame_em)
7214 int element = player_mapping[player_nr][anim].element_rnd;
7215 int action = player_mapping[player_nr][anim].action;
7216 int direction = player_mapping[player_nr][anim].direction;
7217 int graphic = (direction == MV_NONE ?
7218 el_act2img(element, action) :
7219 el_act_dir2img(element, action, direction));
7220 struct GraphicInfo *g = &graphic_info[graphic];
7223 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7225 stored_player[player_nr].StepFrame = frame_em;
7227 sync_frame = stored_player[player_nr].Frame;
7229 int frame = getAnimationFrame(g->anim_frames,
7232 g->anim_start_frame,
7235 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7236 &g_em->src_x, &g_em->src_y, FALSE);
7239 void InitGraphicInfo_EM(void)
7244 int num_em_gfx_errors = 0;
7246 if (graphic_info_em_object[0][0].bitmap == NULL)
7248 /* EM graphics not yet initialized in em_open_all() */
7253 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7256 /* always start with reliable default values */
7257 for (i = 0; i < TILE_MAX; i++)
7259 object_mapping[i].element_rnd = EL_UNKNOWN;
7260 object_mapping[i].is_backside = FALSE;
7261 object_mapping[i].action = ACTION_DEFAULT;
7262 object_mapping[i].direction = MV_NONE;
7265 /* always start with reliable default values */
7266 for (p = 0; p < MAX_PLAYERS; p++)
7268 for (i = 0; i < SPR_MAX; i++)
7270 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7271 player_mapping[p][i].action = ACTION_DEFAULT;
7272 player_mapping[p][i].direction = MV_NONE;
7276 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7278 int e = em_object_mapping_list[i].element_em;
7280 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7281 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7283 if (em_object_mapping_list[i].action != -1)
7284 object_mapping[e].action = em_object_mapping_list[i].action;
7286 if (em_object_mapping_list[i].direction != -1)
7287 object_mapping[e].direction =
7288 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7291 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7293 int a = em_player_mapping_list[i].action_em;
7294 int p = em_player_mapping_list[i].player_nr;
7296 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7298 if (em_player_mapping_list[i].action != -1)
7299 player_mapping[p][a].action = em_player_mapping_list[i].action;
7301 if (em_player_mapping_list[i].direction != -1)
7302 player_mapping[p][a].direction =
7303 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7306 for (i = 0; i < TILE_MAX; i++)
7308 int element = object_mapping[i].element_rnd;
7309 int action = object_mapping[i].action;
7310 int direction = object_mapping[i].direction;
7311 boolean is_backside = object_mapping[i].is_backside;
7312 boolean action_exploding = ((action == ACTION_EXPLODING ||
7313 action == ACTION_SMASHED_BY_ROCK ||
7314 action == ACTION_SMASHED_BY_SPRING) &&
7315 element != EL_DIAMOND);
7316 boolean action_active = (action == ACTION_ACTIVE);
7317 boolean action_other = (action == ACTION_OTHER);
7319 for (j = 0; j < 8; j++)
7321 int effective_element = get_effective_element_EM(i, j);
7322 int effective_action = (j < 7 ? action :
7323 i == Xdrip_stretch ? action :
7324 i == Xdrip_stretchB ? action :
7325 i == Ydrip_s1 ? action :
7326 i == Ydrip_s1B ? action :
7327 i == Xball_1B ? action :
7328 i == Xball_2 ? action :
7329 i == Xball_2B ? action :
7330 i == Yball_eat ? action :
7331 i == Ykey_1_eat ? action :
7332 i == Ykey_2_eat ? action :
7333 i == Ykey_3_eat ? action :
7334 i == Ykey_4_eat ? action :
7335 i == Ykey_5_eat ? action :
7336 i == Ykey_6_eat ? action :
7337 i == Ykey_7_eat ? action :
7338 i == Ykey_8_eat ? action :
7339 i == Ylenses_eat ? action :
7340 i == Ymagnify_eat ? action :
7341 i == Ygrass_eat ? action :
7342 i == Ydirt_eat ? action :
7343 i == Xsand_stonein_1 ? action :
7344 i == Xsand_stonein_2 ? action :
7345 i == Xsand_stonein_3 ? action :
7346 i == Xsand_stonein_4 ? action :
7347 i == Xsand_stoneout_1 ? action :
7348 i == Xsand_stoneout_2 ? action :
7349 i == Xboom_android ? ACTION_EXPLODING :
7350 action_exploding ? ACTION_EXPLODING :
7351 action_active ? action :
7352 action_other ? action :
7354 int graphic = (el_act_dir2img(effective_element, effective_action,
7356 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7358 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7359 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7360 boolean has_action_graphics = (graphic != base_graphic);
7361 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7362 struct GraphicInfo *g = &graphic_info[graphic];
7363 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7366 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7367 boolean special_animation = (action != ACTION_DEFAULT &&
7368 g->anim_frames == 3 &&
7369 g->anim_delay == 2 &&
7370 g->anim_mode & ANIM_LINEAR);
7371 int sync_frame = (i == Xdrip_stretch ? 7 :
7372 i == Xdrip_stretchB ? 7 :
7373 i == Ydrip_s2 ? j + 8 :
7374 i == Ydrip_s2B ? j + 8 :
7383 i == Xfake_acid_1 ? 0 :
7384 i == Xfake_acid_2 ? 10 :
7385 i == Xfake_acid_3 ? 20 :
7386 i == Xfake_acid_4 ? 30 :
7387 i == Xfake_acid_5 ? 40 :
7388 i == Xfake_acid_6 ? 50 :
7389 i == Xfake_acid_7 ? 60 :
7390 i == Xfake_acid_8 ? 70 :
7392 i == Xball_2B ? j + 8 :
7393 i == Yball_eat ? j + 1 :
7394 i == Ykey_1_eat ? j + 1 :
7395 i == Ykey_2_eat ? j + 1 :
7396 i == Ykey_3_eat ? j + 1 :
7397 i == Ykey_4_eat ? j + 1 :
7398 i == Ykey_5_eat ? j + 1 :
7399 i == Ykey_6_eat ? j + 1 :
7400 i == Ykey_7_eat ? j + 1 :
7401 i == Ykey_8_eat ? j + 1 :
7402 i == Ylenses_eat ? j + 1 :
7403 i == Ymagnify_eat ? j + 1 :
7404 i == Ygrass_eat ? j + 1 :
7405 i == Ydirt_eat ? j + 1 :
7406 i == Xamoeba_1 ? 0 :
7407 i == Xamoeba_2 ? 1 :
7408 i == Xamoeba_3 ? 2 :
7409 i == Xamoeba_4 ? 3 :
7410 i == Xamoeba_5 ? 0 :
7411 i == Xamoeba_6 ? 1 :
7412 i == Xamoeba_7 ? 2 :
7413 i == Xamoeba_8 ? 3 :
7414 i == Xexit_2 ? j + 8 :
7415 i == Xexit_3 ? j + 16 :
7416 i == Xdynamite_1 ? 0 :
7417 i == Xdynamite_2 ? 8 :
7418 i == Xdynamite_3 ? 16 :
7419 i == Xdynamite_4 ? 24 :
7420 i == Xsand_stonein_1 ? j + 1 :
7421 i == Xsand_stonein_2 ? j + 9 :
7422 i == Xsand_stonein_3 ? j + 17 :
7423 i == Xsand_stonein_4 ? j + 25 :
7424 i == Xsand_stoneout_1 && j == 0 ? 0 :
7425 i == Xsand_stoneout_1 && j == 1 ? 0 :
7426 i == Xsand_stoneout_1 && j == 2 ? 1 :
7427 i == Xsand_stoneout_1 && j == 3 ? 2 :
7428 i == Xsand_stoneout_1 && j == 4 ? 2 :
7429 i == Xsand_stoneout_1 && j == 5 ? 3 :
7430 i == Xsand_stoneout_1 && j == 6 ? 4 :
7431 i == Xsand_stoneout_1 && j == 7 ? 4 :
7432 i == Xsand_stoneout_2 && j == 0 ? 5 :
7433 i == Xsand_stoneout_2 && j == 1 ? 6 :
7434 i == Xsand_stoneout_2 && j == 2 ? 7 :
7435 i == Xsand_stoneout_2 && j == 3 ? 8 :
7436 i == Xsand_stoneout_2 && j == 4 ? 9 :
7437 i == Xsand_stoneout_2 && j == 5 ? 11 :
7438 i == Xsand_stoneout_2 && j == 6 ? 13 :
7439 i == Xsand_stoneout_2 && j == 7 ? 15 :
7440 i == Xboom_bug && j == 1 ? 2 :
7441 i == Xboom_bug && j == 2 ? 2 :
7442 i == Xboom_bug && j == 3 ? 4 :
7443 i == Xboom_bug && j == 4 ? 4 :
7444 i == Xboom_bug && j == 5 ? 2 :
7445 i == Xboom_bug && j == 6 ? 2 :
7446 i == Xboom_bug && j == 7 ? 0 :
7447 i == Xboom_bomb && j == 1 ? 2 :
7448 i == Xboom_bomb && j == 2 ? 2 :
7449 i == Xboom_bomb && j == 3 ? 4 :
7450 i == Xboom_bomb && j == 4 ? 4 :
7451 i == Xboom_bomb && j == 5 ? 2 :
7452 i == Xboom_bomb && j == 6 ? 2 :
7453 i == Xboom_bomb && j == 7 ? 0 :
7454 i == Xboom_android && j == 7 ? 6 :
7455 i == Xboom_1 && j == 1 ? 2 :
7456 i == Xboom_1 && j == 2 ? 2 :
7457 i == Xboom_1 && j == 3 ? 4 :
7458 i == Xboom_1 && j == 4 ? 4 :
7459 i == Xboom_1 && j == 5 ? 6 :
7460 i == Xboom_1 && j == 6 ? 6 :
7461 i == Xboom_1 && j == 7 ? 8 :
7462 i == Xboom_2 && j == 0 ? 8 :
7463 i == Xboom_2 && j == 1 ? 8 :
7464 i == Xboom_2 && j == 2 ? 10 :
7465 i == Xboom_2 && j == 3 ? 10 :
7466 i == Xboom_2 && j == 4 ? 10 :
7467 i == Xboom_2 && j == 5 ? 12 :
7468 i == Xboom_2 && j == 6 ? 12 :
7469 i == Xboom_2 && j == 7 ? 12 :
7470 special_animation && j == 4 ? 3 :
7471 effective_action != action ? 0 :
7475 Bitmap *debug_bitmap = g_em->bitmap;
7476 int debug_src_x = g_em->src_x;
7477 int debug_src_y = g_em->src_y;
7480 int frame = getAnimationFrame(g->anim_frames,
7483 g->anim_start_frame,
7486 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7487 g->double_movement && is_backside);
7489 g_em->bitmap = src_bitmap;
7490 g_em->src_x = src_x;
7491 g_em->src_y = src_y;
7492 g_em->src_offset_x = 0;
7493 g_em->src_offset_y = 0;
7494 g_em->dst_offset_x = 0;
7495 g_em->dst_offset_y = 0;
7496 g_em->width = TILEX;
7497 g_em->height = TILEY;
7499 g_em->preserve_background = FALSE;
7501 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7504 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7505 effective_action == ACTION_MOVING ||
7506 effective_action == ACTION_PUSHING ||
7507 effective_action == ACTION_EATING)) ||
7508 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7509 effective_action == ACTION_EMPTYING)))
7512 (effective_action == ACTION_FALLING ||
7513 effective_action == ACTION_FILLING ||
7514 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7515 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7516 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7517 int num_steps = (i == Ydrip_s1 ? 16 :
7518 i == Ydrip_s1B ? 16 :
7519 i == Ydrip_s2 ? 16 :
7520 i == Ydrip_s2B ? 16 :
7521 i == Xsand_stonein_1 ? 32 :
7522 i == Xsand_stonein_2 ? 32 :
7523 i == Xsand_stonein_3 ? 32 :
7524 i == Xsand_stonein_4 ? 32 :
7525 i == Xsand_stoneout_1 ? 16 :
7526 i == Xsand_stoneout_2 ? 16 : 8);
7527 int cx = ABS(dx) * (TILEX / num_steps);
7528 int cy = ABS(dy) * (TILEY / num_steps);
7529 int step_frame = (i == Ydrip_s2 ? j + 8 :
7530 i == Ydrip_s2B ? j + 8 :
7531 i == Xsand_stonein_2 ? j + 8 :
7532 i == Xsand_stonein_3 ? j + 16 :
7533 i == Xsand_stonein_4 ? j + 24 :
7534 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7535 int step = (is_backside ? step_frame : num_steps - step_frame);
7537 if (is_backside) /* tile where movement starts */
7539 if (dx < 0 || dy < 0)
7541 g_em->src_offset_x = cx * step;
7542 g_em->src_offset_y = cy * step;
7546 g_em->dst_offset_x = cx * step;
7547 g_em->dst_offset_y = cy * step;
7550 else /* tile where movement ends */
7552 if (dx < 0 || dy < 0)
7554 g_em->dst_offset_x = cx * step;
7555 g_em->dst_offset_y = cy * step;
7559 g_em->src_offset_x = cx * step;
7560 g_em->src_offset_y = cy * step;
7564 g_em->width = TILEX - cx * step;
7565 g_em->height = TILEY - cy * step;
7568 /* create unique graphic identifier to decide if tile must be redrawn */
7569 /* bit 31 - 16 (16 bit): EM style graphic
7570 bit 15 - 12 ( 4 bit): EM style frame
7571 bit 11 - 6 ( 6 bit): graphic width
7572 bit 5 - 0 ( 6 bit): graphic height */
7573 g_em->unique_identifier =
7574 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7578 /* skip check for EMC elements not contained in original EMC artwork */
7579 if (element == EL_EMC_FAKE_ACID)
7582 if (g_em->bitmap != debug_bitmap ||
7583 g_em->src_x != debug_src_x ||
7584 g_em->src_y != debug_src_y ||
7585 g_em->src_offset_x != 0 ||
7586 g_em->src_offset_y != 0 ||
7587 g_em->dst_offset_x != 0 ||
7588 g_em->dst_offset_y != 0 ||
7589 g_em->width != TILEX ||
7590 g_em->height != TILEY)
7592 static int last_i = -1;
7600 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7601 i, element, element_info[element].token_name,
7602 element_action_info[effective_action].suffix, direction);
7604 if (element != effective_element)
7605 printf(" [%d ('%s')]",
7607 element_info[effective_element].token_name);
7611 if (g_em->bitmap != debug_bitmap)
7612 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7613 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7615 if (g_em->src_x != debug_src_x ||
7616 g_em->src_y != debug_src_y)
7617 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7618 j, (is_backside ? 'B' : 'F'),
7619 g_em->src_x, g_em->src_y,
7620 g_em->src_x / 32, g_em->src_y / 32,
7621 debug_src_x, debug_src_y,
7622 debug_src_x / 32, debug_src_y / 32);
7624 if (g_em->src_offset_x != 0 ||
7625 g_em->src_offset_y != 0 ||
7626 g_em->dst_offset_x != 0 ||
7627 g_em->dst_offset_y != 0)
7628 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7630 g_em->src_offset_x, g_em->src_offset_y,
7631 g_em->dst_offset_x, g_em->dst_offset_y);
7633 if (g_em->width != TILEX ||
7634 g_em->height != TILEY)
7635 printf(" %d (%d): size %d,%d should be %d,%d\n",
7637 g_em->width, g_em->height, TILEX, TILEY);
7639 num_em_gfx_errors++;
7646 for (i = 0; i < TILE_MAX; i++)
7648 for (j = 0; j < 8; j++)
7650 int element = object_mapping[i].element_rnd;
7651 int action = object_mapping[i].action;
7652 int direction = object_mapping[i].direction;
7653 boolean is_backside = object_mapping[i].is_backside;
7654 int graphic_action = el_act_dir2img(element, action, direction);
7655 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7657 if ((action == ACTION_SMASHED_BY_ROCK ||
7658 action == ACTION_SMASHED_BY_SPRING ||
7659 action == ACTION_EATING) &&
7660 graphic_action == graphic_default)
7662 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7663 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7664 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7665 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7668 /* no separate animation for "smashed by rock" -- use rock instead */
7669 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7670 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7672 g_em->bitmap = g_xx->bitmap;
7673 g_em->src_x = g_xx->src_x;
7674 g_em->src_y = g_xx->src_y;
7675 g_em->src_offset_x = g_xx->src_offset_x;
7676 g_em->src_offset_y = g_xx->src_offset_y;
7677 g_em->dst_offset_x = g_xx->dst_offset_x;
7678 g_em->dst_offset_y = g_xx->dst_offset_y;
7679 g_em->width = g_xx->width;
7680 g_em->height = g_xx->height;
7681 g_em->unique_identifier = g_xx->unique_identifier;
7684 g_em->preserve_background = TRUE;
7689 for (p = 0; p < MAX_PLAYERS; p++)
7691 for (i = 0; i < SPR_MAX; i++)
7693 int element = player_mapping[p][i].element_rnd;
7694 int action = player_mapping[p][i].action;
7695 int direction = player_mapping[p][i].direction;
7697 for (j = 0; j < 8; j++)
7699 int effective_element = element;
7700 int effective_action = action;
7701 int graphic = (direction == MV_NONE ?
7702 el_act2img(effective_element, effective_action) :
7703 el_act_dir2img(effective_element, effective_action,
7705 struct GraphicInfo *g = &graphic_info[graphic];
7706 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7712 Bitmap *debug_bitmap = g_em->bitmap;
7713 int debug_src_x = g_em->src_x;
7714 int debug_src_y = g_em->src_y;
7717 int frame = getAnimationFrame(g->anim_frames,
7720 g->anim_start_frame,
7723 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7725 g_em->bitmap = src_bitmap;
7726 g_em->src_x = src_x;
7727 g_em->src_y = src_y;
7728 g_em->src_offset_x = 0;
7729 g_em->src_offset_y = 0;
7730 g_em->dst_offset_x = 0;
7731 g_em->dst_offset_y = 0;
7732 g_em->width = TILEX;
7733 g_em->height = TILEY;
7737 /* skip check for EMC elements not contained in original EMC artwork */
7738 if (element == EL_PLAYER_3 ||
7739 element == EL_PLAYER_4)
7742 if (g_em->bitmap != debug_bitmap ||
7743 g_em->src_x != debug_src_x ||
7744 g_em->src_y != debug_src_y)
7746 static int last_i = -1;
7754 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7755 p, i, element, element_info[element].token_name,
7756 element_action_info[effective_action].suffix, direction);
7758 if (element != effective_element)
7759 printf(" [%d ('%s')]",
7761 element_info[effective_element].token_name);
7765 if (g_em->bitmap != debug_bitmap)
7766 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7767 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7769 if (g_em->src_x != debug_src_x ||
7770 g_em->src_y != debug_src_y)
7771 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7773 g_em->src_x, g_em->src_y,
7774 g_em->src_x / 32, g_em->src_y / 32,
7775 debug_src_x, debug_src_y,
7776 debug_src_x / 32, debug_src_y / 32);
7778 num_em_gfx_errors++;
7788 printf("::: [%d errors found]\n", num_em_gfx_errors);
7794 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7795 boolean any_player_moving,
7796 boolean player_is_dropping)
7798 if (tape.single_step && tape.recording && !tape.pausing)
7799 if (frame == 0 && !player_is_dropping)
7800 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7803 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7804 boolean murphy_is_dropping)
7806 if (tape.single_step && tape.recording && !tape.pausing)
7807 if (murphy_is_waiting)
7808 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7811 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7812 int graphic, int sync_frame, int x, int y)
7814 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7816 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7819 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7821 return (IS_NEXT_FRAME(sync_frame, graphic));
7824 int getGraphicInfo_Delay(int graphic)
7826 return graphic_info[graphic].anim_delay;
7829 void PlayMenuSoundExt(int sound)
7831 if (sound == SND_UNDEFINED)
7834 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7835 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7838 if (IS_LOOP_SOUND(sound))
7839 PlaySoundLoop(sound);
7844 void PlayMenuSound()
7846 PlayMenuSoundExt(menu.sound[game_status]);
7849 void PlayMenuSoundStereo(int sound, int stereo_position)
7851 if (sound == SND_UNDEFINED)
7854 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7855 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7858 if (IS_LOOP_SOUND(sound))
7859 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7861 PlaySoundStereo(sound, stereo_position);
7864 void PlayMenuSoundIfLoopExt(int sound)
7866 if (sound == SND_UNDEFINED)
7869 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7870 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7873 if (IS_LOOP_SOUND(sound))
7874 PlaySoundLoop(sound);
7877 void PlayMenuSoundIfLoop()
7879 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7882 void PlayMenuMusicExt(int music)
7884 if (music == MUS_UNDEFINED)
7887 if (!setup.sound_music)
7893 void PlayMenuMusic()
7895 PlayMenuMusicExt(menu.music[game_status]);
7898 void PlaySoundActivating()
7901 PlaySound(SND_MENU_ITEM_ACTIVATING);
7905 void PlaySoundSelecting()
7908 PlaySound(SND_MENU_ITEM_SELECTING);
7912 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7914 boolean change_fullscreen = (setup.fullscreen !=
7915 video.fullscreen_enabled);
7916 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7917 !strEqual(setup.fullscreen_mode,
7918 video.fullscreen_mode_current));
7919 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7920 setup.window_scaling_percent !=
7921 video.window_scaling_percent);
7923 if (change_window_scaling_percent && video.fullscreen_enabled)
7926 if (!change_window_scaling_percent && !video.fullscreen_available)
7929 #if defined(TARGET_SDL2)
7930 if (change_window_scaling_percent)
7932 SDLSetWindowScaling(setup.window_scaling_percent);
7936 else if (change_fullscreen)
7938 SDLSetWindowFullscreen(setup.fullscreen);
7940 /* set setup value according to successfully changed fullscreen mode */
7941 setup.fullscreen = video.fullscreen_enabled;
7947 if (change_fullscreen ||
7948 change_fullscreen_mode ||
7949 change_window_scaling_percent)
7951 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
7953 /* save backbuffer content which gets lost when toggling fullscreen mode */
7954 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7956 if (change_fullscreen_mode)
7958 /* keep fullscreen, but change fullscreen mode (screen resolution) */
7959 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
7962 if (change_window_scaling_percent)
7964 /* keep window mode, but change window scaling */
7965 video.fullscreen_enabled = TRUE; /* force new window scaling */
7968 /* toggle fullscreen */
7969 ChangeVideoModeIfNeeded(setup.fullscreen);
7971 /* set setup value according to successfully changed fullscreen mode */
7972 setup.fullscreen = video.fullscreen_enabled;
7974 /* restore backbuffer content from temporary backbuffer backup bitmap */
7975 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7977 FreeBitmap(tmp_backbuffer);
7979 /* update visible window/screen */
7980 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7984 void ChangeViewportPropertiesIfNeeded()
7986 int gfx_game_mode = game_status;
7987 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
7989 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
7990 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
7991 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
7992 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
7993 int border_size = vp_playfield->border_size;
7994 int new_sx = vp_playfield->x + border_size;
7995 int new_sy = vp_playfield->y + border_size;
7996 int new_sxsize = vp_playfield->width - 2 * border_size;
7997 int new_sysize = vp_playfield->height - 2 * border_size;
7998 int new_real_sx = vp_playfield->x;
7999 int new_real_sy = vp_playfield->y;
8000 int new_full_sxsize = vp_playfield->width;
8001 int new_full_sysize = vp_playfield->height;
8002 int new_dx = vp_door_1->x;
8003 int new_dy = vp_door_1->y;
8004 int new_dxsize = vp_door_1->width;
8005 int new_dysize = vp_door_1->height;
8006 int new_vx = vp_door_2->x;
8007 int new_vy = vp_door_2->y;
8008 int new_vxsize = vp_door_2->width;
8009 int new_vysize = vp_door_2->height;
8010 int new_ex = vp_door_3->x;
8011 int new_ey = vp_door_3->y;
8012 int new_exsize = vp_door_3->width;
8013 int new_eysize = vp_door_3->height;
8014 int new_tilesize_var =
8015 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8017 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8018 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8019 int new_scr_fieldx = new_sxsize / tilesize;
8020 int new_scr_fieldy = new_sysize / tilesize;
8021 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8022 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8023 boolean init_gfx_buffers = FALSE;
8024 boolean init_video_buffer = FALSE;
8025 boolean init_gadgets_and_toons = FALSE;
8026 boolean init_em_graphics = FALSE;
8027 boolean drawing_area_changed = FALSE;
8029 if (viewport.window.width != WIN_XSIZE ||
8030 viewport.window.height != WIN_YSIZE)
8032 WIN_XSIZE = viewport.window.width;
8033 WIN_YSIZE = viewport.window.height;
8035 init_video_buffer = TRUE;
8036 init_gfx_buffers = TRUE;
8038 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8041 if (new_scr_fieldx != SCR_FIELDX ||
8042 new_scr_fieldy != SCR_FIELDY)
8044 /* this always toggles between MAIN and GAME when using small tile size */
8046 SCR_FIELDX = new_scr_fieldx;
8047 SCR_FIELDY = new_scr_fieldy;
8049 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8060 new_sxsize != SXSIZE ||
8061 new_sysize != SYSIZE ||
8062 new_dxsize != DXSIZE ||
8063 new_dysize != DYSIZE ||
8064 new_vxsize != VXSIZE ||
8065 new_vysize != VYSIZE ||
8066 new_exsize != EXSIZE ||
8067 new_eysize != EYSIZE ||
8068 new_real_sx != REAL_SX ||
8069 new_real_sy != REAL_SY ||
8070 new_full_sxsize != FULL_SXSIZE ||
8071 new_full_sysize != FULL_SYSIZE ||
8072 new_tilesize_var != TILESIZE_VAR
8075 if (new_tilesize_var != TILESIZE_VAR)
8077 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8079 // changing tile size invalidates scroll values of engine snapshots
8080 FreeEngineSnapshot();
8082 // changing tile size requires update of graphic mapping for EM engine
8083 init_em_graphics = TRUE;
8088 new_sxsize != SXSIZE ||
8089 new_sysize != SYSIZE ||
8090 new_real_sx != REAL_SX ||
8091 new_real_sy != REAL_SY ||
8092 new_full_sxsize != FULL_SXSIZE ||
8093 new_full_sysize != FULL_SYSIZE)
8095 if (!init_video_buffer)
8096 drawing_area_changed = TRUE;
8107 SXSIZE = new_sxsize;
8108 SYSIZE = new_sysize;
8109 DXSIZE = new_dxsize;
8110 DYSIZE = new_dysize;
8111 VXSIZE = new_vxsize;
8112 VYSIZE = new_vysize;
8113 EXSIZE = new_exsize;
8114 EYSIZE = new_eysize;
8115 REAL_SX = new_real_sx;
8116 REAL_SY = new_real_sy;
8117 FULL_SXSIZE = new_full_sxsize;
8118 FULL_SYSIZE = new_full_sysize;
8119 TILESIZE_VAR = new_tilesize_var;
8121 init_gfx_buffers = TRUE;
8122 init_gadgets_and_toons = TRUE;
8124 // printf("::: viewports: init_gfx_buffers\n");
8125 // printf("::: viewports: init_gadgets_and_toons\n");
8128 if (init_gfx_buffers)
8130 // printf("::: init_gfx_buffers\n");
8132 SCR_FIELDX = new_scr_fieldx_buffers;
8133 SCR_FIELDY = new_scr_fieldy_buffers;
8137 SCR_FIELDX = new_scr_fieldx;
8138 SCR_FIELDY = new_scr_fieldy;
8140 gfx.drawing_area_changed = drawing_area_changed;
8142 SetDrawDeactivationMask(REDRAW_NONE);
8143 SetDrawBackgroundMask(REDRAW_FIELD);
8146 if (init_video_buffer)
8148 // printf("::: init_video_buffer\n");
8150 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8153 if (init_gadgets_and_toons)
8155 // printf("::: init_gadgets_and_toons\n");
8161 if (init_em_graphics)
8163 InitGraphicInfo_EM();