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;
3392 while (NextValidEvent(&event))
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 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);
3498 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3500 int joy = AnyJoystick();
3502 if (joy & JOY_BUTTON_1)
3504 else if (joy & JOY_BUTTON_2)
3508 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3510 HandleGameActions();
3516 if (!PendingEvent()) /* delay only if no pending events */
3520 game_status = GAME_MODE_PSEUDO_DOOR;
3524 game_status = last_game_status; /* restore current game status */
3530 static boolean RequestDoor(char *text, unsigned int req_state)
3532 unsigned int old_door_state;
3533 int last_game_status = game_status; /* save current game status */
3534 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3535 int font_nr = FONT_TEXT_2;
3540 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3542 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3543 font_nr = FONT_TEXT_1;
3546 if (game_status == GAME_MODE_PLAYING)
3547 BlitScreenToBitmap(backbuffer);
3549 /* disable deactivated drawing when quick-loading level tape recording */
3550 if (tape.playing && tape.deactivate_display)
3551 TapeDeactivateDisplayOff(TRUE);
3553 SetMouseCursor(CURSOR_DEFAULT);
3555 #if defined(NETWORK_AVALIABLE)
3556 /* pause network game while waiting for request to answer */
3557 if (options.network &&
3558 game_status == GAME_MODE_PLAYING &&
3559 req_state & REQUEST_WAIT_FOR_INPUT)
3560 SendToServer_PausePlaying();
3563 old_door_state = GetDoorState();
3565 /* simulate releasing mouse button over last gadget, if still pressed */
3567 HandleGadgets(-1, -1, 0);
3571 /* draw released gadget before proceeding */
3574 if (old_door_state & DOOR_OPEN_1)
3576 CloseDoor(DOOR_CLOSE_1);
3578 /* save old door content */
3579 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3580 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3583 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3584 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3586 /* clear door drawing field */
3587 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3589 /* force DOOR font inside door area */
3590 game_status = GAME_MODE_PSEUDO_DOOR;
3592 /* write text for request */
3593 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3595 char text_line[max_request_line_len + 1];
3601 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3603 tc = *(text_ptr + tx);
3604 // if (!tc || tc == ' ')
3605 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3609 if ((tc == '?' || tc == '!') && tl == 0)
3619 strncpy(text_line, text_ptr, tl);
3622 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3623 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3624 text_line, font_nr);
3626 text_ptr += tl + (tc == ' ' ? 1 : 0);
3627 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3630 game_status = last_game_status; /* restore current game status */
3632 if (req_state & REQ_ASK)
3634 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3635 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3637 else if (req_state & REQ_CONFIRM)
3639 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3641 else if (req_state & REQ_PLAYER)
3643 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3644 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3645 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3646 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3649 /* copy request gadgets to door backbuffer */
3650 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3652 OpenDoor(DOOR_OPEN_1);
3654 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3656 if (game_status == GAME_MODE_PLAYING)
3658 SetPanelBackground();
3659 SetDrawBackgroundMask(REDRAW_DOOR_1);
3663 SetDrawBackgroundMask(REDRAW_FIELD);
3669 if (game_status != GAME_MODE_MAIN)
3672 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3674 // ---------- handle request buttons ----------
3675 result = RequestHandleEvents(req_state);
3677 if (game_status != GAME_MODE_MAIN)
3682 if (!(req_state & REQ_STAY_OPEN))
3684 CloseDoor(DOOR_CLOSE_1);
3686 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3687 (req_state & REQ_REOPEN))
3688 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3693 if (game_status == GAME_MODE_PLAYING)
3695 SetPanelBackground();
3696 SetDrawBackgroundMask(REDRAW_DOOR_1);
3700 SetDrawBackgroundMask(REDRAW_FIELD);
3703 #if defined(NETWORK_AVALIABLE)
3704 /* continue network game after request */
3705 if (options.network &&
3706 game_status == GAME_MODE_PLAYING &&
3707 req_state & REQUEST_WAIT_FOR_INPUT)
3708 SendToServer_ContinuePlaying();
3711 /* restore deactivated drawing when quick-loading level tape recording */
3712 if (tape.playing && tape.deactivate_display)
3713 TapeDeactivateDisplayOn();
3718 static boolean RequestEnvelope(char *text, unsigned int req_state)
3722 if (game_status == GAME_MODE_PLAYING)
3723 BlitScreenToBitmap(backbuffer);
3725 /* disable deactivated drawing when quick-loading level tape recording */
3726 if (tape.playing && tape.deactivate_display)
3727 TapeDeactivateDisplayOff(TRUE);
3729 SetMouseCursor(CURSOR_DEFAULT);
3731 #if defined(NETWORK_AVALIABLE)
3732 /* pause network game while waiting for request to answer */
3733 if (options.network &&
3734 game_status == GAME_MODE_PLAYING &&
3735 req_state & REQUEST_WAIT_FOR_INPUT)
3736 SendToServer_PausePlaying();
3739 /* simulate releasing mouse button over last gadget, if still pressed */
3741 HandleGadgets(-1, -1, 0);
3745 // (replace with setting corresponding request background)
3746 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3747 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3749 /* clear door drawing field */
3750 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3752 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3754 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3756 if (game_status == GAME_MODE_PLAYING)
3758 SetPanelBackground();
3759 SetDrawBackgroundMask(REDRAW_DOOR_1);
3763 SetDrawBackgroundMask(REDRAW_FIELD);
3769 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3771 // ---------- handle request buttons ----------
3772 result = RequestHandleEvents(req_state);
3774 if (game_status != GAME_MODE_MAIN)
3779 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3783 if (game_status == GAME_MODE_PLAYING)
3785 SetPanelBackground();
3786 SetDrawBackgroundMask(REDRAW_DOOR_1);
3790 SetDrawBackgroundMask(REDRAW_FIELD);
3793 #if defined(NETWORK_AVALIABLE)
3794 /* continue network game after request */
3795 if (options.network &&
3796 game_status == GAME_MODE_PLAYING &&
3797 req_state & REQUEST_WAIT_FOR_INPUT)
3798 SendToServer_ContinuePlaying();
3801 /* restore deactivated drawing when quick-loading level tape recording */
3802 if (tape.playing && tape.deactivate_display)
3803 TapeDeactivateDisplayOn();
3808 boolean Request(char *text, unsigned int req_state)
3810 if (global.use_envelope_request)
3811 return RequestEnvelope(text, req_state);
3813 return RequestDoor(text, req_state);
3816 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3818 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3819 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3822 if (dpo1->sort_priority != dpo2->sort_priority)
3823 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3825 compare_result = dpo1->nr - dpo2->nr;
3827 return compare_result;
3830 void InitGraphicCompatibilityInfo_Doors()
3836 struct DoorInfo *door;
3840 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3841 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3843 { -1, -1, -1, NULL }
3845 struct Rect door_rect_list[] =
3847 { DX, DY, DXSIZE, DYSIZE },
3848 { VX, VY, VXSIZE, VYSIZE }
3852 for (i = 0; doors[i].door_token != -1; i++)
3854 int door_token = doors[i].door_token;
3855 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3856 int part_1 = doors[i].part_1;
3857 int part_8 = doors[i].part_8;
3858 int part_2 = part_1 + 1;
3859 int part_3 = part_1 + 2;
3860 struct DoorInfo *door = doors[i].door;
3861 struct Rect *door_rect = &door_rect_list[door_index];
3862 boolean door_gfx_redefined = FALSE;
3864 /* check if any door part graphic definitions have been redefined */
3866 for (j = 0; door_part_controls[j].door_token != -1; j++)
3868 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3869 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3871 if (dpc->door_token == door_token && fi->redefined)
3872 door_gfx_redefined = TRUE;
3875 /* check for old-style door graphic/animation modifications */
3877 if (!door_gfx_redefined)
3879 if (door->anim_mode & ANIM_STATIC_PANEL)
3881 door->panel.step_xoffset = 0;
3882 door->panel.step_yoffset = 0;
3885 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3887 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3888 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3889 int num_door_steps, num_panel_steps;
3891 /* remove door part graphics other than the two default wings */
3893 for (j = 0; door_part_controls[j].door_token != -1; j++)
3895 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3896 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3898 if (dpc->graphic >= part_3 &&
3899 dpc->graphic <= part_8)
3903 /* set graphics and screen positions of the default wings */
3905 g_part_1->width = door_rect->width;
3906 g_part_1->height = door_rect->height;
3907 g_part_2->width = door_rect->width;
3908 g_part_2->height = door_rect->height;
3909 g_part_2->src_x = door_rect->width;
3910 g_part_2->src_y = g_part_1->src_y;
3912 door->part_2.x = door->part_1.x;
3913 door->part_2.y = door->part_1.y;
3915 if (door->width != -1)
3917 g_part_1->width = door->width;
3918 g_part_2->width = door->width;
3920 // special treatment for graphics and screen position of right wing
3921 g_part_2->src_x += door_rect->width - door->width;
3922 door->part_2.x += door_rect->width - door->width;
3925 if (door->height != -1)
3927 g_part_1->height = door->height;
3928 g_part_2->height = door->height;
3930 // special treatment for graphics and screen position of bottom wing
3931 g_part_2->src_y += door_rect->height - door->height;
3932 door->part_2.y += door_rect->height - door->height;
3935 /* set animation delays for the default wings and panels */
3937 door->part_1.step_delay = door->step_delay;
3938 door->part_2.step_delay = door->step_delay;
3939 door->panel.step_delay = door->step_delay;
3941 /* set animation draw order for the default wings */
3943 door->part_1.sort_priority = 2; /* draw left wing over ... */
3944 door->part_2.sort_priority = 1; /* ... right wing */
3946 /* set animation draw offset for the default wings */
3948 if (door->anim_mode & ANIM_HORIZONTAL)
3950 door->part_1.step_xoffset = door->step_offset;
3951 door->part_1.step_yoffset = 0;
3952 door->part_2.step_xoffset = door->step_offset * -1;
3953 door->part_2.step_yoffset = 0;
3955 num_door_steps = g_part_1->width / door->step_offset;
3957 else // ANIM_VERTICAL
3959 door->part_1.step_xoffset = 0;
3960 door->part_1.step_yoffset = door->step_offset;
3961 door->part_2.step_xoffset = 0;
3962 door->part_2.step_yoffset = door->step_offset * -1;
3964 num_door_steps = g_part_1->height / door->step_offset;
3967 /* set animation draw offset for the default panels */
3969 if (door->step_offset > 1)
3971 num_panel_steps = 2 * door_rect->height / door->step_offset;
3972 door->panel.start_step = num_panel_steps - num_door_steps;
3973 door->panel.start_step_closing = door->panel.start_step;
3977 num_panel_steps = door_rect->height / door->step_offset;
3978 door->panel.start_step = num_panel_steps - num_door_steps / 2;
3979 door->panel.start_step_closing = door->panel.start_step;
3980 door->panel.step_delay *= 2;
3991 for (i = 0; door_part_controls[i].door_token != -1; i++)
3993 struct DoorPartControlInfo *dpc = &door_part_controls[i];
3994 struct DoorPartOrderInfo *dpo = &door_part_order[i];
3996 /* initialize "start_step_opening" and "start_step_closing", if needed */
3997 if (dpc->pos->start_step_opening == 0 &&
3998 dpc->pos->start_step_closing == 0)
4000 // dpc->pos->start_step_opening = dpc->pos->start_step;
4001 dpc->pos->start_step_closing = dpc->pos->start_step;
4004 /* fill structure for door part draw order (sorted below) */
4006 dpo->sort_priority = dpc->pos->sort_priority;
4009 /* sort door part controls according to sort_priority and graphic number */
4010 qsort(door_part_order, MAX_DOOR_PARTS,
4011 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4014 unsigned int OpenDoor(unsigned int door_state)
4016 if (door_state & DOOR_COPY_BACK)
4018 if (door_state & DOOR_OPEN_1)
4019 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4020 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4022 if (door_state & DOOR_OPEN_2)
4023 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4024 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4026 door_state &= ~DOOR_COPY_BACK;
4029 return MoveDoor(door_state);
4032 unsigned int CloseDoor(unsigned int door_state)
4034 unsigned int old_door_state = GetDoorState();
4036 if (!(door_state & DOOR_NO_COPY_BACK))
4038 if (old_door_state & DOOR_OPEN_1)
4039 BlitBitmap(backbuffer, bitmap_db_door_1,
4040 DX, DY, DXSIZE, DYSIZE, 0, 0);
4042 if (old_door_state & DOOR_OPEN_2)
4043 BlitBitmap(backbuffer, bitmap_db_door_2,
4044 VX, VY, VXSIZE, VYSIZE, 0, 0);
4046 door_state &= ~DOOR_NO_COPY_BACK;
4049 return MoveDoor(door_state);
4052 unsigned int GetDoorState()
4054 return MoveDoor(DOOR_GET_STATE);
4057 unsigned int SetDoorState(unsigned int door_state)
4059 return MoveDoor(door_state | DOOR_SET_STATE);
4062 int euclid(int a, int b)
4064 return (b ? euclid(b, a % b) : a);
4067 unsigned int MoveDoor(unsigned int door_state)
4069 struct Rect door_rect_list[] =
4071 { DX, DY, DXSIZE, DYSIZE },
4072 { VX, VY, VXSIZE, VYSIZE }
4074 static int door1 = DOOR_OPEN_1;
4075 static int door2 = DOOR_CLOSE_2;
4076 unsigned int door_delay = 0;
4077 unsigned int door_delay_value;
4080 if (door_state == DOOR_GET_STATE)
4081 return (door1 | door2);
4083 if (door_state & DOOR_SET_STATE)
4085 if (door_state & DOOR_ACTION_1)
4086 door1 = door_state & DOOR_ACTION_1;
4087 if (door_state & DOOR_ACTION_2)
4088 door2 = door_state & DOOR_ACTION_2;
4090 return (door1 | door2);
4093 if (!(door_state & DOOR_FORCE_REDRAW))
4095 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4096 door_state &= ~DOOR_OPEN_1;
4097 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4098 door_state &= ~DOOR_CLOSE_1;
4099 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4100 door_state &= ~DOOR_OPEN_2;
4101 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4102 door_state &= ~DOOR_CLOSE_2;
4105 if (global.autoplay_leveldir)
4107 door_state |= DOOR_NO_DELAY;
4108 door_state &= ~DOOR_CLOSE_ALL;
4111 if (game_status == GAME_MODE_EDITOR)
4112 door_state |= DOOR_NO_DELAY;
4114 if (door_state & DOOR_ACTION)
4116 boolean door_panel_drawn[NUM_DOORS];
4117 boolean panel_has_doors[NUM_DOORS];
4118 boolean door_part_skip[MAX_DOOR_PARTS];
4119 boolean door_part_done[MAX_DOOR_PARTS];
4120 boolean door_part_done_all;
4121 int num_steps[MAX_DOOR_PARTS];
4122 int max_move_delay = 0; // delay for complete animations of all doors
4123 int max_step_delay = 0; // delay (ms) between two animation frames
4124 int num_move_steps = 0; // number of animation steps for all doors
4125 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4126 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4127 int current_move_delay = 0;
4131 for (i = 0; i < NUM_DOORS; i++)
4132 panel_has_doors[i] = FALSE;
4134 for (i = 0; i < MAX_DOOR_PARTS; i++)
4136 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4137 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4138 int door_token = dpc->door_token;
4140 door_part_done[i] = FALSE;
4141 door_part_skip[i] = (!(door_state & door_token) ||
4145 for (i = 0; i < MAX_DOOR_PARTS; i++)
4147 int nr = door_part_order[i].nr;
4148 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4149 struct DoorPartPosInfo *pos = dpc->pos;
4150 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4151 int door_token = dpc->door_token;
4152 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4153 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4154 int step_xoffset = ABS(pos->step_xoffset);
4155 int step_yoffset = ABS(pos->step_yoffset);
4156 int step_delay = pos->step_delay;
4157 int current_door_state = door_state & door_token;
4158 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4159 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4160 boolean part_opening = (is_panel ? door_closing : door_opening);
4161 int start_step = (part_opening ? pos->start_step_opening :
4162 pos->start_step_closing);
4163 float move_xsize = (step_xoffset ? g->width : 0);
4164 float move_ysize = (step_yoffset ? g->height : 0);
4165 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4166 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4167 int move_steps = (move_xsteps && move_ysteps ?
4168 MIN(move_xsteps, move_ysteps) :
4169 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4170 int move_delay = move_steps * step_delay;
4172 if (door_part_skip[nr])
4175 max_move_delay = MAX(max_move_delay, move_delay);
4176 max_step_delay = (max_step_delay == 0 ? step_delay :
4177 euclid(max_step_delay, step_delay));
4178 num_steps[nr] = move_steps;
4182 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4184 panel_has_doors[door_index] = TRUE;
4188 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4190 num_move_steps = max_move_delay / max_step_delay;
4191 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4193 door_delay_value = max_step_delay;
4195 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4197 start = num_move_steps - 1;
4201 /* opening door sound has priority over simultaneously closing door */
4202 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4203 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4204 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4205 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4208 for (k = start; k < num_move_steps; k++)
4210 door_part_done_all = TRUE;
4212 for (i = 0; i < NUM_DOORS; i++)
4213 door_panel_drawn[i] = FALSE;
4215 for (i = 0; i < MAX_DOOR_PARTS; i++)
4217 int nr = door_part_order[i].nr;
4218 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4219 struct DoorPartPosInfo *pos = dpc->pos;
4220 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4221 int door_token = dpc->door_token;
4222 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4223 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4224 boolean is_panel_and_door_has_closed = FALSE;
4225 struct Rect *door_rect = &door_rect_list[door_index];
4226 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4228 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4229 int current_door_state = door_state & door_token;
4230 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4231 boolean door_closing = !door_opening;
4232 boolean part_opening = (is_panel ? door_closing : door_opening);
4233 boolean part_closing = !part_opening;
4234 int start_step = (part_opening ? pos->start_step_opening :
4235 pos->start_step_closing);
4236 int step_delay = pos->step_delay;
4237 int step_factor = step_delay / max_step_delay;
4238 int k1 = (step_factor ? k / step_factor + 1 : k);
4239 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4240 int kk = MAX(0, k2);
4243 int src_x, src_y, src_xx, src_yy;
4244 int dst_x, dst_y, dst_xx, dst_yy;
4247 if (door_part_skip[nr])
4250 if (!(door_state & door_token))
4258 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4259 int kk_door = MAX(0, k2_door);
4260 int sync_frame = kk_door * door_delay_value;
4261 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4263 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4268 if (!door_panel_drawn[door_index])
4270 ClearRectangle(drawto, door_rect->x, door_rect->y,
4271 door_rect->width, door_rect->height);
4273 door_panel_drawn[door_index] = TRUE;
4276 // draw opening or closing door parts
4278 if (pos->step_xoffset < 0) // door part on right side
4281 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4284 if (dst_xx + width > door_rect->width)
4285 width = door_rect->width - dst_xx;
4287 else // door part on left side
4290 dst_xx = pos->x - kk * pos->step_xoffset;
4294 src_xx = ABS(dst_xx);
4298 width = g->width - src_xx;
4300 // printf("::: k == %d [%d] \n", k, start_step);
4303 if (pos->step_yoffset < 0) // door part on bottom side
4306 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4309 if (dst_yy + height > door_rect->height)
4310 height = door_rect->height - dst_yy;
4312 else // door part on top side
4315 dst_yy = pos->y - kk * pos->step_yoffset;
4319 src_yy = ABS(dst_yy);
4323 height = g->height - src_yy;
4326 src_x = g_src_x + src_xx;
4327 src_y = g_src_y + src_yy;
4329 dst_x = door_rect->x + dst_xx;
4330 dst_y = door_rect->y + dst_yy;
4332 is_panel_and_door_has_closed =
4335 panel_has_doors[door_index] &&
4336 k >= num_move_steps_doors_only - 1);
4338 if (width >= 0 && width <= g->width &&
4339 height >= 0 && height <= g->height &&
4340 !is_panel_and_door_has_closed)
4342 if (is_panel || !pos->draw_masked)
4343 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4346 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4350 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4352 if ((part_opening && (width < 0 || height < 0)) ||
4353 (part_closing && (width >= g->width && height >= g->height)))
4354 door_part_done[nr] = TRUE;
4356 // continue door part animations, but not panel after door has closed
4357 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4358 door_part_done_all = FALSE;
4361 if (!(door_state & DOOR_NO_DELAY))
4365 if (game_status == GAME_MODE_MAIN)
4368 WaitUntilDelayReached(&door_delay, door_delay_value);
4370 current_move_delay += max_step_delay;
4373 if (door_part_done_all)
4378 if (door_state & DOOR_ACTION_1)
4379 door1 = door_state & DOOR_ACTION_1;
4380 if (door_state & DOOR_ACTION_2)
4381 door2 = door_state & DOOR_ACTION_2;
4383 return (door1 | door2);
4386 void DrawSpecialEditorDoor()
4388 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4389 int top_border_width = gfx1->width;
4390 int top_border_height = gfx1->height;
4391 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4392 int ex = EX - outer_border;
4393 int ey = EY - outer_border;
4394 int vy = VY - outer_border;
4395 int exsize = EXSIZE + 2 * outer_border;
4397 /* draw bigger level editor toolbox window */
4398 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4399 top_border_width, top_border_height, ex, ey - top_border_height);
4400 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4401 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4403 redraw_mask |= REDRAW_ALL;
4406 void UndrawSpecialEditorDoor()
4408 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4409 int top_border_width = gfx1->width;
4410 int top_border_height = gfx1->height;
4411 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4412 int ex = EX - outer_border;
4413 int ey = EY - outer_border;
4414 int ey_top = ey - top_border_height;
4415 int exsize = EXSIZE + 2 * outer_border;
4416 int eysize = EYSIZE + 2 * outer_border;
4418 /* draw normal tape recorder window */
4419 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4421 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4422 ex, ey_top, top_border_width, top_border_height,
4424 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4425 ex, ey, exsize, eysize, ex, ey);
4429 // if screen background is set to "[NONE]", clear editor toolbox window
4430 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4431 ClearRectangle(drawto, ex, ey, exsize, eysize);
4434 redraw_mask |= REDRAW_ALL;
4438 /* ---------- new tool button stuff ---------------------------------------- */
4443 struct TextPosInfo *pos;
4446 } toolbutton_info[NUM_TOOL_BUTTONS] =
4449 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4450 TOOL_CTRL_ID_YES, "yes"
4453 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4454 TOOL_CTRL_ID_NO, "no"
4457 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4458 TOOL_CTRL_ID_CONFIRM, "confirm"
4461 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4462 TOOL_CTRL_ID_PLAYER_1, "player 1"
4465 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4466 TOOL_CTRL_ID_PLAYER_2, "player 2"
4469 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4470 TOOL_CTRL_ID_PLAYER_3, "player 3"
4473 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4474 TOOL_CTRL_ID_PLAYER_4, "player 4"
4478 void CreateToolButtons()
4482 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4484 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4485 struct TextPosInfo *pos = toolbutton_info[i].pos;
4486 struct GadgetInfo *gi;
4487 Bitmap *deco_bitmap = None;
4488 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4489 unsigned int event_mask = GD_EVENT_RELEASED;
4492 int gd_x = gfx->src_x;
4493 int gd_y = gfx->src_y;
4494 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4495 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4498 if (global.use_envelope_request)
4499 setRequestPosition(&dx, &dy, TRUE);
4501 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4503 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4505 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4506 pos->size, &deco_bitmap, &deco_x, &deco_y);
4507 deco_xpos = (gfx->width - pos->size) / 2;
4508 deco_ypos = (gfx->height - pos->size) / 2;
4511 gi = CreateGadget(GDI_CUSTOM_ID, id,
4512 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4513 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4514 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4515 GDI_WIDTH, gfx->width,
4516 GDI_HEIGHT, gfx->height,
4517 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4518 GDI_STATE, GD_BUTTON_UNPRESSED,
4519 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4520 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4521 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4522 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4523 GDI_DECORATION_SIZE, pos->size, pos->size,
4524 GDI_DECORATION_SHIFTING, 1, 1,
4525 GDI_DIRECT_DRAW, FALSE,
4526 GDI_EVENT_MASK, event_mask,
4527 GDI_CALLBACK_ACTION, HandleToolButtons,
4531 Error(ERR_EXIT, "cannot create gadget");
4533 tool_gadget[id] = gi;
4537 void FreeToolButtons()
4541 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4542 FreeGadget(tool_gadget[i]);
4545 static void UnmapToolButtons()
4549 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4550 UnmapGadget(tool_gadget[i]);
4553 static void HandleToolButtons(struct GadgetInfo *gi)
4555 request_gadget_id = gi->custom_id;
4558 static struct Mapping_EM_to_RND_object
4561 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4562 boolean is_backside; /* backside of moving element */
4568 em_object_mapping_list[] =
4571 Xblank, TRUE, FALSE,
4575 Yacid_splash_eB, FALSE, FALSE,
4576 EL_ACID_SPLASH_RIGHT, -1, -1
4579 Yacid_splash_wB, FALSE, FALSE,
4580 EL_ACID_SPLASH_LEFT, -1, -1
4583 #ifdef EM_ENGINE_BAD_ROLL
4585 Xstone_force_e, FALSE, FALSE,
4586 EL_ROCK, -1, MV_BIT_RIGHT
4589 Xstone_force_w, FALSE, FALSE,
4590 EL_ROCK, -1, MV_BIT_LEFT
4593 Xnut_force_e, FALSE, FALSE,
4594 EL_NUT, -1, MV_BIT_RIGHT
4597 Xnut_force_w, FALSE, FALSE,
4598 EL_NUT, -1, MV_BIT_LEFT
4601 Xspring_force_e, FALSE, FALSE,
4602 EL_SPRING, -1, MV_BIT_RIGHT
4605 Xspring_force_w, FALSE, FALSE,
4606 EL_SPRING, -1, MV_BIT_LEFT
4609 Xemerald_force_e, FALSE, FALSE,
4610 EL_EMERALD, -1, MV_BIT_RIGHT
4613 Xemerald_force_w, FALSE, FALSE,
4614 EL_EMERALD, -1, MV_BIT_LEFT
4617 Xdiamond_force_e, FALSE, FALSE,
4618 EL_DIAMOND, -1, MV_BIT_RIGHT
4621 Xdiamond_force_w, FALSE, FALSE,
4622 EL_DIAMOND, -1, MV_BIT_LEFT
4625 Xbomb_force_e, FALSE, FALSE,
4626 EL_BOMB, -1, MV_BIT_RIGHT
4629 Xbomb_force_w, FALSE, FALSE,
4630 EL_BOMB, -1, MV_BIT_LEFT
4632 #endif /* EM_ENGINE_BAD_ROLL */
4635 Xstone, TRUE, FALSE,
4639 Xstone_pause, FALSE, FALSE,
4643 Xstone_fall, FALSE, FALSE,
4647 Ystone_s, FALSE, FALSE,
4648 EL_ROCK, ACTION_FALLING, -1
4651 Ystone_sB, FALSE, TRUE,
4652 EL_ROCK, ACTION_FALLING, -1
4655 Ystone_e, FALSE, FALSE,
4656 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4659 Ystone_eB, FALSE, TRUE,
4660 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4663 Ystone_w, FALSE, FALSE,
4664 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4667 Ystone_wB, FALSE, TRUE,
4668 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4675 Xnut_pause, FALSE, FALSE,
4679 Xnut_fall, FALSE, FALSE,
4683 Ynut_s, FALSE, FALSE,
4684 EL_NUT, ACTION_FALLING, -1
4687 Ynut_sB, FALSE, TRUE,
4688 EL_NUT, ACTION_FALLING, -1
4691 Ynut_e, FALSE, FALSE,
4692 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4695 Ynut_eB, FALSE, TRUE,
4696 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4699 Ynut_w, FALSE, FALSE,
4700 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4703 Ynut_wB, FALSE, TRUE,
4704 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4707 Xbug_n, TRUE, FALSE,
4711 Xbug_e, TRUE, FALSE,
4712 EL_BUG_RIGHT, -1, -1
4715 Xbug_s, TRUE, FALSE,
4719 Xbug_w, TRUE, FALSE,
4723 Xbug_gon, FALSE, FALSE,
4727 Xbug_goe, FALSE, FALSE,
4728 EL_BUG_RIGHT, -1, -1
4731 Xbug_gos, FALSE, FALSE,
4735 Xbug_gow, FALSE, FALSE,
4739 Ybug_n, FALSE, FALSE,
4740 EL_BUG, ACTION_MOVING, MV_BIT_UP
4743 Ybug_nB, FALSE, TRUE,
4744 EL_BUG, ACTION_MOVING, MV_BIT_UP
4747 Ybug_e, FALSE, FALSE,
4748 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4751 Ybug_eB, FALSE, TRUE,
4752 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4755 Ybug_s, FALSE, FALSE,
4756 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4759 Ybug_sB, FALSE, TRUE,
4760 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4763 Ybug_w, FALSE, FALSE,
4764 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4767 Ybug_wB, FALSE, TRUE,
4768 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4771 Ybug_w_n, FALSE, FALSE,
4772 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4775 Ybug_n_e, FALSE, FALSE,
4776 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4779 Ybug_e_s, FALSE, FALSE,
4780 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4783 Ybug_s_w, FALSE, FALSE,
4784 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4787 Ybug_e_n, FALSE, FALSE,
4788 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4791 Ybug_s_e, FALSE, FALSE,
4792 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4795 Ybug_w_s, FALSE, FALSE,
4796 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4799 Ybug_n_w, FALSE, FALSE,
4800 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4803 Ybug_stone, FALSE, FALSE,
4804 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4807 Ybug_spring, FALSE, FALSE,
4808 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4811 Xtank_n, TRUE, FALSE,
4812 EL_SPACESHIP_UP, -1, -1
4815 Xtank_e, TRUE, FALSE,
4816 EL_SPACESHIP_RIGHT, -1, -1
4819 Xtank_s, TRUE, FALSE,
4820 EL_SPACESHIP_DOWN, -1, -1
4823 Xtank_w, TRUE, FALSE,
4824 EL_SPACESHIP_LEFT, -1, -1
4827 Xtank_gon, FALSE, FALSE,
4828 EL_SPACESHIP_UP, -1, -1
4831 Xtank_goe, FALSE, FALSE,
4832 EL_SPACESHIP_RIGHT, -1, -1
4835 Xtank_gos, FALSE, FALSE,
4836 EL_SPACESHIP_DOWN, -1, -1
4839 Xtank_gow, FALSE, FALSE,
4840 EL_SPACESHIP_LEFT, -1, -1
4843 Ytank_n, FALSE, FALSE,
4844 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4847 Ytank_nB, FALSE, TRUE,
4848 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4851 Ytank_e, FALSE, FALSE,
4852 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4855 Ytank_eB, FALSE, TRUE,
4856 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4859 Ytank_s, FALSE, FALSE,
4860 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4863 Ytank_sB, FALSE, TRUE,
4864 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4867 Ytank_w, FALSE, FALSE,
4868 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4871 Ytank_wB, FALSE, TRUE,
4872 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4875 Ytank_w_n, FALSE, FALSE,
4876 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4879 Ytank_n_e, FALSE, FALSE,
4880 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4883 Ytank_e_s, FALSE, FALSE,
4884 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4887 Ytank_s_w, FALSE, FALSE,
4888 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4891 Ytank_e_n, FALSE, FALSE,
4892 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4895 Ytank_s_e, FALSE, FALSE,
4896 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4899 Ytank_w_s, FALSE, FALSE,
4900 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4903 Ytank_n_w, FALSE, FALSE,
4904 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4907 Ytank_stone, FALSE, FALSE,
4908 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4911 Ytank_spring, FALSE, FALSE,
4912 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4915 Xandroid, TRUE, FALSE,
4916 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4919 Xandroid_1_n, FALSE, FALSE,
4920 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4923 Xandroid_2_n, FALSE, FALSE,
4924 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4927 Xandroid_1_e, FALSE, FALSE,
4928 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4931 Xandroid_2_e, FALSE, FALSE,
4932 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4935 Xandroid_1_w, FALSE, FALSE,
4936 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4939 Xandroid_2_w, FALSE, FALSE,
4940 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4943 Xandroid_1_s, FALSE, FALSE,
4944 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4947 Xandroid_2_s, FALSE, FALSE,
4948 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4951 Yandroid_n, FALSE, FALSE,
4952 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4955 Yandroid_nB, FALSE, TRUE,
4956 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4959 Yandroid_ne, FALSE, FALSE,
4960 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4963 Yandroid_neB, FALSE, TRUE,
4964 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4967 Yandroid_e, FALSE, FALSE,
4968 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4971 Yandroid_eB, FALSE, TRUE,
4972 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4975 Yandroid_se, FALSE, FALSE,
4976 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4979 Yandroid_seB, FALSE, TRUE,
4980 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4983 Yandroid_s, FALSE, FALSE,
4984 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4987 Yandroid_sB, FALSE, TRUE,
4988 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4991 Yandroid_sw, FALSE, FALSE,
4992 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
4995 Yandroid_swB, FALSE, TRUE,
4996 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
4999 Yandroid_w, FALSE, FALSE,
5000 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5003 Yandroid_wB, FALSE, TRUE,
5004 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5007 Yandroid_nw, FALSE, FALSE,
5008 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5011 Yandroid_nwB, FALSE, TRUE,
5012 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5015 Xspring, TRUE, FALSE,
5019 Xspring_pause, FALSE, FALSE,
5023 Xspring_e, FALSE, FALSE,
5027 Xspring_w, FALSE, FALSE,
5031 Xspring_fall, FALSE, FALSE,
5035 Yspring_s, FALSE, FALSE,
5036 EL_SPRING, ACTION_FALLING, -1
5039 Yspring_sB, FALSE, TRUE,
5040 EL_SPRING, ACTION_FALLING, -1
5043 Yspring_e, FALSE, FALSE,
5044 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5047 Yspring_eB, FALSE, TRUE,
5048 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5051 Yspring_w, FALSE, FALSE,
5052 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5055 Yspring_wB, FALSE, TRUE,
5056 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5059 Yspring_kill_e, FALSE, FALSE,
5060 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5063 Yspring_kill_eB, FALSE, TRUE,
5064 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5067 Yspring_kill_w, FALSE, FALSE,
5068 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5071 Yspring_kill_wB, FALSE, TRUE,
5072 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5075 Xeater_n, TRUE, FALSE,
5076 EL_YAMYAM_UP, -1, -1
5079 Xeater_e, TRUE, FALSE,
5080 EL_YAMYAM_RIGHT, -1, -1
5083 Xeater_w, TRUE, FALSE,
5084 EL_YAMYAM_LEFT, -1, -1
5087 Xeater_s, TRUE, FALSE,
5088 EL_YAMYAM_DOWN, -1, -1
5091 Yeater_n, FALSE, FALSE,
5092 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5095 Yeater_nB, FALSE, TRUE,
5096 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5099 Yeater_e, FALSE, FALSE,
5100 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5103 Yeater_eB, FALSE, TRUE,
5104 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5107 Yeater_s, FALSE, FALSE,
5108 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5111 Yeater_sB, FALSE, TRUE,
5112 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5115 Yeater_w, FALSE, FALSE,
5116 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5119 Yeater_wB, FALSE, TRUE,
5120 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5123 Yeater_stone, FALSE, FALSE,
5124 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5127 Yeater_spring, FALSE, FALSE,
5128 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5131 Xalien, TRUE, FALSE,
5135 Xalien_pause, FALSE, FALSE,
5139 Yalien_n, FALSE, FALSE,
5140 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5143 Yalien_nB, FALSE, TRUE,
5144 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5147 Yalien_e, FALSE, FALSE,
5148 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5151 Yalien_eB, FALSE, TRUE,
5152 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5155 Yalien_s, FALSE, FALSE,
5156 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5159 Yalien_sB, FALSE, TRUE,
5160 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5163 Yalien_w, FALSE, FALSE,
5164 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5167 Yalien_wB, FALSE, TRUE,
5168 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5171 Yalien_stone, FALSE, FALSE,
5172 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5175 Yalien_spring, FALSE, FALSE,
5176 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5179 Xemerald, TRUE, FALSE,
5183 Xemerald_pause, FALSE, FALSE,
5187 Xemerald_fall, FALSE, FALSE,
5191 Xemerald_shine, FALSE, FALSE,
5192 EL_EMERALD, ACTION_TWINKLING, -1
5195 Yemerald_s, FALSE, FALSE,
5196 EL_EMERALD, ACTION_FALLING, -1
5199 Yemerald_sB, FALSE, TRUE,
5200 EL_EMERALD, ACTION_FALLING, -1
5203 Yemerald_e, FALSE, FALSE,
5204 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5207 Yemerald_eB, FALSE, TRUE,
5208 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5211 Yemerald_w, FALSE, FALSE,
5212 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5215 Yemerald_wB, FALSE, TRUE,
5216 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5219 Yemerald_eat, FALSE, FALSE,
5220 EL_EMERALD, ACTION_COLLECTING, -1
5223 Yemerald_stone, FALSE, FALSE,
5224 EL_NUT, ACTION_BREAKING, -1
5227 Xdiamond, TRUE, FALSE,
5231 Xdiamond_pause, FALSE, FALSE,
5235 Xdiamond_fall, FALSE, FALSE,
5239 Xdiamond_shine, FALSE, FALSE,
5240 EL_DIAMOND, ACTION_TWINKLING, -1
5243 Ydiamond_s, FALSE, FALSE,
5244 EL_DIAMOND, ACTION_FALLING, -1
5247 Ydiamond_sB, FALSE, TRUE,
5248 EL_DIAMOND, ACTION_FALLING, -1
5251 Ydiamond_e, FALSE, FALSE,
5252 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5255 Ydiamond_eB, FALSE, TRUE,
5256 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5259 Ydiamond_w, FALSE, FALSE,
5260 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5263 Ydiamond_wB, FALSE, TRUE,
5264 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5267 Ydiamond_eat, FALSE, FALSE,
5268 EL_DIAMOND, ACTION_COLLECTING, -1
5271 Ydiamond_stone, FALSE, FALSE,
5272 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5275 Xdrip_fall, TRUE, FALSE,
5276 EL_AMOEBA_DROP, -1, -1
5279 Xdrip_stretch, FALSE, FALSE,
5280 EL_AMOEBA_DROP, ACTION_FALLING, -1
5283 Xdrip_stretchB, FALSE, TRUE,
5284 EL_AMOEBA_DROP, ACTION_FALLING, -1
5287 Xdrip_eat, FALSE, FALSE,
5288 EL_AMOEBA_DROP, ACTION_GROWING, -1
5291 Ydrip_s1, FALSE, FALSE,
5292 EL_AMOEBA_DROP, ACTION_FALLING, -1
5295 Ydrip_s1B, FALSE, TRUE,
5296 EL_AMOEBA_DROP, ACTION_FALLING, -1
5299 Ydrip_s2, FALSE, FALSE,
5300 EL_AMOEBA_DROP, ACTION_FALLING, -1
5303 Ydrip_s2B, FALSE, TRUE,
5304 EL_AMOEBA_DROP, ACTION_FALLING, -1
5311 Xbomb_pause, FALSE, FALSE,
5315 Xbomb_fall, FALSE, FALSE,
5319 Ybomb_s, FALSE, FALSE,
5320 EL_BOMB, ACTION_FALLING, -1
5323 Ybomb_sB, FALSE, TRUE,
5324 EL_BOMB, ACTION_FALLING, -1
5327 Ybomb_e, FALSE, FALSE,
5328 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5331 Ybomb_eB, FALSE, TRUE,
5332 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5335 Ybomb_w, FALSE, FALSE,
5336 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5339 Ybomb_wB, FALSE, TRUE,
5340 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5343 Ybomb_eat, FALSE, FALSE,
5344 EL_BOMB, ACTION_ACTIVATING, -1
5347 Xballoon, TRUE, FALSE,
5351 Yballoon_n, FALSE, FALSE,
5352 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5355 Yballoon_nB, FALSE, TRUE,
5356 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5359 Yballoon_e, FALSE, FALSE,
5360 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5363 Yballoon_eB, FALSE, TRUE,
5364 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5367 Yballoon_s, FALSE, FALSE,
5368 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5371 Yballoon_sB, FALSE, TRUE,
5372 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5375 Yballoon_w, FALSE, FALSE,
5376 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5379 Yballoon_wB, FALSE, TRUE,
5380 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5383 Xgrass, TRUE, FALSE,
5384 EL_EMC_GRASS, -1, -1
5387 Ygrass_nB, FALSE, FALSE,
5388 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5391 Ygrass_eB, FALSE, FALSE,
5392 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5395 Ygrass_sB, FALSE, FALSE,
5396 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5399 Ygrass_wB, FALSE, FALSE,
5400 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5407 Ydirt_nB, FALSE, FALSE,
5408 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5411 Ydirt_eB, FALSE, FALSE,
5412 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5415 Ydirt_sB, FALSE, FALSE,
5416 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5419 Ydirt_wB, FALSE, FALSE,
5420 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5423 Xacid_ne, TRUE, FALSE,
5424 EL_ACID_POOL_TOPRIGHT, -1, -1
5427 Xacid_se, TRUE, FALSE,
5428 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5431 Xacid_s, TRUE, FALSE,
5432 EL_ACID_POOL_BOTTOM, -1, -1
5435 Xacid_sw, TRUE, FALSE,
5436 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5439 Xacid_nw, TRUE, FALSE,
5440 EL_ACID_POOL_TOPLEFT, -1, -1
5443 Xacid_1, TRUE, FALSE,
5447 Xacid_2, FALSE, FALSE,
5451 Xacid_3, FALSE, FALSE,
5455 Xacid_4, FALSE, FALSE,
5459 Xacid_5, FALSE, FALSE,
5463 Xacid_6, FALSE, FALSE,
5467 Xacid_7, FALSE, FALSE,
5471 Xacid_8, FALSE, FALSE,
5475 Xball_1, TRUE, FALSE,
5476 EL_EMC_MAGIC_BALL, -1, -1
5479 Xball_1B, FALSE, FALSE,
5480 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5483 Xball_2, FALSE, FALSE,
5484 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5487 Xball_2B, FALSE, FALSE,
5488 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5491 Yball_eat, FALSE, FALSE,
5492 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5495 Ykey_1_eat, FALSE, FALSE,
5496 EL_EM_KEY_1, ACTION_COLLECTING, -1
5499 Ykey_2_eat, FALSE, FALSE,
5500 EL_EM_KEY_2, ACTION_COLLECTING, -1
5503 Ykey_3_eat, FALSE, FALSE,
5504 EL_EM_KEY_3, ACTION_COLLECTING, -1
5507 Ykey_4_eat, FALSE, FALSE,
5508 EL_EM_KEY_4, ACTION_COLLECTING, -1
5511 Ykey_5_eat, FALSE, FALSE,
5512 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5515 Ykey_6_eat, FALSE, FALSE,
5516 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5519 Ykey_7_eat, FALSE, FALSE,
5520 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5523 Ykey_8_eat, FALSE, FALSE,
5524 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5527 Ylenses_eat, FALSE, FALSE,
5528 EL_EMC_LENSES, ACTION_COLLECTING, -1
5531 Ymagnify_eat, FALSE, FALSE,
5532 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5535 Ygrass_eat, FALSE, FALSE,
5536 EL_EMC_GRASS, ACTION_SNAPPING, -1
5539 Ydirt_eat, FALSE, FALSE,
5540 EL_SAND, ACTION_SNAPPING, -1
5543 Xgrow_ns, TRUE, FALSE,
5544 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5547 Ygrow_ns_eat, FALSE, FALSE,
5548 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5551 Xgrow_ew, TRUE, FALSE,
5552 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5555 Ygrow_ew_eat, FALSE, FALSE,
5556 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5559 Xwonderwall, TRUE, FALSE,
5560 EL_MAGIC_WALL, -1, -1
5563 XwonderwallB, FALSE, FALSE,
5564 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5567 Xamoeba_1, TRUE, FALSE,
5568 EL_AMOEBA_DRY, ACTION_OTHER, -1
5571 Xamoeba_2, FALSE, FALSE,
5572 EL_AMOEBA_DRY, ACTION_OTHER, -1
5575 Xamoeba_3, FALSE, FALSE,
5576 EL_AMOEBA_DRY, ACTION_OTHER, -1
5579 Xamoeba_4, FALSE, FALSE,
5580 EL_AMOEBA_DRY, ACTION_OTHER, -1
5583 Xamoeba_5, TRUE, FALSE,
5584 EL_AMOEBA_WET, ACTION_OTHER, -1
5587 Xamoeba_6, FALSE, FALSE,
5588 EL_AMOEBA_WET, ACTION_OTHER, -1
5591 Xamoeba_7, FALSE, FALSE,
5592 EL_AMOEBA_WET, ACTION_OTHER, -1
5595 Xamoeba_8, FALSE, FALSE,
5596 EL_AMOEBA_WET, ACTION_OTHER, -1
5599 Xdoor_1, TRUE, FALSE,
5600 EL_EM_GATE_1, -1, -1
5603 Xdoor_2, TRUE, FALSE,
5604 EL_EM_GATE_2, -1, -1
5607 Xdoor_3, TRUE, FALSE,
5608 EL_EM_GATE_3, -1, -1
5611 Xdoor_4, TRUE, FALSE,
5612 EL_EM_GATE_4, -1, -1
5615 Xdoor_5, TRUE, FALSE,
5616 EL_EMC_GATE_5, -1, -1
5619 Xdoor_6, TRUE, FALSE,
5620 EL_EMC_GATE_6, -1, -1
5623 Xdoor_7, TRUE, FALSE,
5624 EL_EMC_GATE_7, -1, -1
5627 Xdoor_8, TRUE, FALSE,
5628 EL_EMC_GATE_8, -1, -1
5631 Xkey_1, TRUE, FALSE,
5635 Xkey_2, TRUE, FALSE,
5639 Xkey_3, TRUE, FALSE,
5643 Xkey_4, TRUE, FALSE,
5647 Xkey_5, TRUE, FALSE,
5648 EL_EMC_KEY_5, -1, -1
5651 Xkey_6, TRUE, FALSE,
5652 EL_EMC_KEY_6, -1, -1
5655 Xkey_7, TRUE, FALSE,
5656 EL_EMC_KEY_7, -1, -1
5659 Xkey_8, TRUE, FALSE,
5660 EL_EMC_KEY_8, -1, -1
5663 Xwind_n, TRUE, FALSE,
5664 EL_BALLOON_SWITCH_UP, -1, -1
5667 Xwind_e, TRUE, FALSE,
5668 EL_BALLOON_SWITCH_RIGHT, -1, -1
5671 Xwind_s, TRUE, FALSE,
5672 EL_BALLOON_SWITCH_DOWN, -1, -1
5675 Xwind_w, TRUE, FALSE,
5676 EL_BALLOON_SWITCH_LEFT, -1, -1
5679 Xwind_nesw, TRUE, FALSE,
5680 EL_BALLOON_SWITCH_ANY, -1, -1
5683 Xwind_stop, TRUE, FALSE,
5684 EL_BALLOON_SWITCH_NONE, -1, -1
5688 EL_EM_EXIT_CLOSED, -1, -1
5691 Xexit_1, TRUE, FALSE,
5692 EL_EM_EXIT_OPEN, -1, -1
5695 Xexit_2, FALSE, FALSE,
5696 EL_EM_EXIT_OPEN, -1, -1
5699 Xexit_3, FALSE, FALSE,
5700 EL_EM_EXIT_OPEN, -1, -1
5703 Xdynamite, TRUE, FALSE,
5704 EL_EM_DYNAMITE, -1, -1
5707 Ydynamite_eat, FALSE, FALSE,
5708 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5711 Xdynamite_1, TRUE, FALSE,
5712 EL_EM_DYNAMITE_ACTIVE, -1, -1
5715 Xdynamite_2, FALSE, FALSE,
5716 EL_EM_DYNAMITE_ACTIVE, -1, -1
5719 Xdynamite_3, FALSE, FALSE,
5720 EL_EM_DYNAMITE_ACTIVE, -1, -1
5723 Xdynamite_4, FALSE, FALSE,
5724 EL_EM_DYNAMITE_ACTIVE, -1, -1
5727 Xbumper, TRUE, FALSE,
5728 EL_EMC_SPRING_BUMPER, -1, -1
5731 XbumperB, FALSE, FALSE,
5732 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5735 Xwheel, TRUE, FALSE,
5736 EL_ROBOT_WHEEL, -1, -1
5739 XwheelB, FALSE, FALSE,
5740 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5743 Xswitch, TRUE, FALSE,
5744 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5747 XswitchB, FALSE, FALSE,
5748 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5752 EL_QUICKSAND_EMPTY, -1, -1
5755 Xsand_stone, TRUE, FALSE,
5756 EL_QUICKSAND_FULL, -1, -1
5759 Xsand_stonein_1, FALSE, TRUE,
5760 EL_ROCK, ACTION_FILLING, -1
5763 Xsand_stonein_2, FALSE, TRUE,
5764 EL_ROCK, ACTION_FILLING, -1
5767 Xsand_stonein_3, FALSE, TRUE,
5768 EL_ROCK, ACTION_FILLING, -1
5771 Xsand_stonein_4, FALSE, TRUE,
5772 EL_ROCK, ACTION_FILLING, -1
5775 Xsand_stonesand_1, FALSE, FALSE,
5776 EL_QUICKSAND_EMPTYING, -1, -1
5779 Xsand_stonesand_2, FALSE, FALSE,
5780 EL_QUICKSAND_EMPTYING, -1, -1
5783 Xsand_stonesand_3, FALSE, FALSE,
5784 EL_QUICKSAND_EMPTYING, -1, -1
5787 Xsand_stonesand_4, FALSE, FALSE,
5788 EL_QUICKSAND_EMPTYING, -1, -1
5791 Xsand_stonesand_quickout_1, FALSE, FALSE,
5792 EL_QUICKSAND_EMPTYING, -1, -1
5795 Xsand_stonesand_quickout_2, FALSE, FALSE,
5796 EL_QUICKSAND_EMPTYING, -1, -1
5799 Xsand_stoneout_1, FALSE, FALSE,
5800 EL_ROCK, ACTION_EMPTYING, -1
5803 Xsand_stoneout_2, FALSE, FALSE,
5804 EL_ROCK, ACTION_EMPTYING, -1
5807 Xsand_sandstone_1, FALSE, FALSE,
5808 EL_QUICKSAND_FILLING, -1, -1
5811 Xsand_sandstone_2, FALSE, FALSE,
5812 EL_QUICKSAND_FILLING, -1, -1
5815 Xsand_sandstone_3, FALSE, FALSE,
5816 EL_QUICKSAND_FILLING, -1, -1
5819 Xsand_sandstone_4, FALSE, FALSE,
5820 EL_QUICKSAND_FILLING, -1, -1
5823 Xplant, TRUE, FALSE,
5824 EL_EMC_PLANT, -1, -1
5827 Yplant, FALSE, FALSE,
5828 EL_EMC_PLANT, -1, -1
5831 Xlenses, TRUE, FALSE,
5832 EL_EMC_LENSES, -1, -1
5835 Xmagnify, TRUE, FALSE,
5836 EL_EMC_MAGNIFIER, -1, -1
5839 Xdripper, TRUE, FALSE,
5840 EL_EMC_DRIPPER, -1, -1
5843 XdripperB, FALSE, FALSE,
5844 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5847 Xfake_blank, TRUE, FALSE,
5848 EL_INVISIBLE_WALL, -1, -1
5851 Xfake_blankB, FALSE, FALSE,
5852 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5855 Xfake_grass, TRUE, FALSE,
5856 EL_EMC_FAKE_GRASS, -1, -1
5859 Xfake_grassB, FALSE, FALSE,
5860 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5863 Xfake_door_1, TRUE, FALSE,
5864 EL_EM_GATE_1_GRAY, -1, -1
5867 Xfake_door_2, TRUE, FALSE,
5868 EL_EM_GATE_2_GRAY, -1, -1
5871 Xfake_door_3, TRUE, FALSE,
5872 EL_EM_GATE_3_GRAY, -1, -1
5875 Xfake_door_4, TRUE, FALSE,
5876 EL_EM_GATE_4_GRAY, -1, -1
5879 Xfake_door_5, TRUE, FALSE,
5880 EL_EMC_GATE_5_GRAY, -1, -1
5883 Xfake_door_6, TRUE, FALSE,
5884 EL_EMC_GATE_6_GRAY, -1, -1
5887 Xfake_door_7, TRUE, FALSE,
5888 EL_EMC_GATE_7_GRAY, -1, -1
5891 Xfake_door_8, TRUE, FALSE,
5892 EL_EMC_GATE_8_GRAY, -1, -1
5895 Xfake_acid_1, TRUE, FALSE,
5896 EL_EMC_FAKE_ACID, -1, -1
5899 Xfake_acid_2, FALSE, FALSE,
5900 EL_EMC_FAKE_ACID, -1, -1
5903 Xfake_acid_3, FALSE, FALSE,
5904 EL_EMC_FAKE_ACID, -1, -1
5907 Xfake_acid_4, FALSE, FALSE,
5908 EL_EMC_FAKE_ACID, -1, -1
5911 Xfake_acid_5, FALSE, FALSE,
5912 EL_EMC_FAKE_ACID, -1, -1
5915 Xfake_acid_6, FALSE, FALSE,
5916 EL_EMC_FAKE_ACID, -1, -1
5919 Xfake_acid_7, FALSE, FALSE,
5920 EL_EMC_FAKE_ACID, -1, -1
5923 Xfake_acid_8, FALSE, FALSE,
5924 EL_EMC_FAKE_ACID, -1, -1
5927 Xsteel_1, TRUE, FALSE,
5928 EL_STEELWALL, -1, -1
5931 Xsteel_2, TRUE, FALSE,
5932 EL_EMC_STEELWALL_2, -1, -1
5935 Xsteel_3, TRUE, FALSE,
5936 EL_EMC_STEELWALL_3, -1, -1
5939 Xsteel_4, TRUE, FALSE,
5940 EL_EMC_STEELWALL_4, -1, -1
5943 Xwall_1, TRUE, FALSE,
5947 Xwall_2, TRUE, FALSE,
5948 EL_EMC_WALL_14, -1, -1
5951 Xwall_3, TRUE, FALSE,
5952 EL_EMC_WALL_15, -1, -1
5955 Xwall_4, TRUE, FALSE,
5956 EL_EMC_WALL_16, -1, -1
5959 Xround_wall_1, TRUE, FALSE,
5960 EL_WALL_SLIPPERY, -1, -1
5963 Xround_wall_2, TRUE, FALSE,
5964 EL_EMC_WALL_SLIPPERY_2, -1, -1
5967 Xround_wall_3, TRUE, FALSE,
5968 EL_EMC_WALL_SLIPPERY_3, -1, -1
5971 Xround_wall_4, TRUE, FALSE,
5972 EL_EMC_WALL_SLIPPERY_4, -1, -1
5975 Xdecor_1, TRUE, FALSE,
5976 EL_EMC_WALL_8, -1, -1
5979 Xdecor_2, TRUE, FALSE,
5980 EL_EMC_WALL_6, -1, -1
5983 Xdecor_3, TRUE, FALSE,
5984 EL_EMC_WALL_4, -1, -1
5987 Xdecor_4, TRUE, FALSE,
5988 EL_EMC_WALL_7, -1, -1
5991 Xdecor_5, TRUE, FALSE,
5992 EL_EMC_WALL_5, -1, -1
5995 Xdecor_6, TRUE, FALSE,
5996 EL_EMC_WALL_9, -1, -1
5999 Xdecor_7, TRUE, FALSE,
6000 EL_EMC_WALL_10, -1, -1
6003 Xdecor_8, TRUE, FALSE,
6004 EL_EMC_WALL_1, -1, -1
6007 Xdecor_9, TRUE, FALSE,
6008 EL_EMC_WALL_2, -1, -1
6011 Xdecor_10, TRUE, FALSE,
6012 EL_EMC_WALL_3, -1, -1
6015 Xdecor_11, TRUE, FALSE,
6016 EL_EMC_WALL_11, -1, -1
6019 Xdecor_12, TRUE, FALSE,
6020 EL_EMC_WALL_12, -1, -1
6023 Xalpha_0, TRUE, FALSE,
6024 EL_CHAR('0'), -1, -1
6027 Xalpha_1, TRUE, FALSE,
6028 EL_CHAR('1'), -1, -1
6031 Xalpha_2, TRUE, FALSE,
6032 EL_CHAR('2'), -1, -1
6035 Xalpha_3, TRUE, FALSE,
6036 EL_CHAR('3'), -1, -1
6039 Xalpha_4, TRUE, FALSE,
6040 EL_CHAR('4'), -1, -1
6043 Xalpha_5, TRUE, FALSE,
6044 EL_CHAR('5'), -1, -1
6047 Xalpha_6, TRUE, FALSE,
6048 EL_CHAR('6'), -1, -1
6051 Xalpha_7, TRUE, FALSE,
6052 EL_CHAR('7'), -1, -1
6055 Xalpha_8, TRUE, FALSE,
6056 EL_CHAR('8'), -1, -1
6059 Xalpha_9, TRUE, FALSE,
6060 EL_CHAR('9'), -1, -1
6063 Xalpha_excla, TRUE, FALSE,
6064 EL_CHAR('!'), -1, -1
6067 Xalpha_quote, TRUE, FALSE,
6068 EL_CHAR('"'), -1, -1
6071 Xalpha_comma, TRUE, FALSE,
6072 EL_CHAR(','), -1, -1
6075 Xalpha_minus, TRUE, FALSE,
6076 EL_CHAR('-'), -1, -1
6079 Xalpha_perio, TRUE, FALSE,
6080 EL_CHAR('.'), -1, -1
6083 Xalpha_colon, TRUE, FALSE,
6084 EL_CHAR(':'), -1, -1
6087 Xalpha_quest, TRUE, FALSE,
6088 EL_CHAR('?'), -1, -1
6091 Xalpha_a, TRUE, FALSE,
6092 EL_CHAR('A'), -1, -1
6095 Xalpha_b, TRUE, FALSE,
6096 EL_CHAR('B'), -1, -1
6099 Xalpha_c, TRUE, FALSE,
6100 EL_CHAR('C'), -1, -1
6103 Xalpha_d, TRUE, FALSE,
6104 EL_CHAR('D'), -1, -1
6107 Xalpha_e, TRUE, FALSE,
6108 EL_CHAR('E'), -1, -1
6111 Xalpha_f, TRUE, FALSE,
6112 EL_CHAR('F'), -1, -1
6115 Xalpha_g, TRUE, FALSE,
6116 EL_CHAR('G'), -1, -1
6119 Xalpha_h, TRUE, FALSE,
6120 EL_CHAR('H'), -1, -1
6123 Xalpha_i, TRUE, FALSE,
6124 EL_CHAR('I'), -1, -1
6127 Xalpha_j, TRUE, FALSE,
6128 EL_CHAR('J'), -1, -1
6131 Xalpha_k, TRUE, FALSE,
6132 EL_CHAR('K'), -1, -1
6135 Xalpha_l, TRUE, FALSE,
6136 EL_CHAR('L'), -1, -1
6139 Xalpha_m, TRUE, FALSE,
6140 EL_CHAR('M'), -1, -1
6143 Xalpha_n, TRUE, FALSE,
6144 EL_CHAR('N'), -1, -1
6147 Xalpha_o, TRUE, FALSE,
6148 EL_CHAR('O'), -1, -1
6151 Xalpha_p, TRUE, FALSE,
6152 EL_CHAR('P'), -1, -1
6155 Xalpha_q, TRUE, FALSE,
6156 EL_CHAR('Q'), -1, -1
6159 Xalpha_r, TRUE, FALSE,
6160 EL_CHAR('R'), -1, -1
6163 Xalpha_s, TRUE, FALSE,
6164 EL_CHAR('S'), -1, -1
6167 Xalpha_t, TRUE, FALSE,
6168 EL_CHAR('T'), -1, -1
6171 Xalpha_u, TRUE, FALSE,
6172 EL_CHAR('U'), -1, -1
6175 Xalpha_v, TRUE, FALSE,
6176 EL_CHAR('V'), -1, -1
6179 Xalpha_w, TRUE, FALSE,
6180 EL_CHAR('W'), -1, -1
6183 Xalpha_x, TRUE, FALSE,
6184 EL_CHAR('X'), -1, -1
6187 Xalpha_y, TRUE, FALSE,
6188 EL_CHAR('Y'), -1, -1
6191 Xalpha_z, TRUE, FALSE,
6192 EL_CHAR('Z'), -1, -1
6195 Xalpha_arrow_e, TRUE, FALSE,
6196 EL_CHAR('>'), -1, -1
6199 Xalpha_arrow_w, TRUE, FALSE,
6200 EL_CHAR('<'), -1, -1
6203 Xalpha_copyr, TRUE, FALSE,
6204 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6208 Xboom_bug, FALSE, FALSE,
6209 EL_BUG, ACTION_EXPLODING, -1
6212 Xboom_bomb, FALSE, FALSE,
6213 EL_BOMB, ACTION_EXPLODING, -1
6216 Xboom_android, FALSE, FALSE,
6217 EL_EMC_ANDROID, ACTION_OTHER, -1
6220 Xboom_1, FALSE, FALSE,
6221 EL_DEFAULT, ACTION_EXPLODING, -1
6224 Xboom_2, FALSE, FALSE,
6225 EL_DEFAULT, ACTION_EXPLODING, -1
6228 Znormal, FALSE, FALSE,
6232 Zdynamite, FALSE, FALSE,
6236 Zplayer, FALSE, FALSE,
6240 ZBORDER, FALSE, FALSE,
6250 static struct Mapping_EM_to_RND_player
6259 em_player_mapping_list[] =
6263 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6267 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6271 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6275 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6279 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6283 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6287 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6291 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6295 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6299 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6303 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6307 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6311 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6315 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6319 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6323 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6327 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6331 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6335 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6339 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6343 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6347 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6351 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6355 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6359 EL_PLAYER_1, ACTION_DEFAULT, -1,
6363 EL_PLAYER_2, ACTION_DEFAULT, -1,
6367 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6371 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6375 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6379 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6383 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6387 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6391 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6395 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6399 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6403 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6407 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6411 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6415 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6419 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6423 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6427 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6431 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6435 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6439 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6443 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6447 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6451 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6455 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6459 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6463 EL_PLAYER_3, ACTION_DEFAULT, -1,
6467 EL_PLAYER_4, ACTION_DEFAULT, -1,
6476 int map_element_RND_to_EM(int element_rnd)
6478 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6479 static boolean mapping_initialized = FALSE;
6481 if (!mapping_initialized)
6485 /* return "Xalpha_quest" for all undefined elements in mapping array */
6486 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6487 mapping_RND_to_EM[i] = Xalpha_quest;
6489 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6490 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6491 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6492 em_object_mapping_list[i].element_em;
6494 mapping_initialized = TRUE;
6497 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6498 return mapping_RND_to_EM[element_rnd];
6500 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6505 int map_element_EM_to_RND(int element_em)
6507 static unsigned short mapping_EM_to_RND[TILE_MAX];
6508 static boolean mapping_initialized = FALSE;
6510 if (!mapping_initialized)
6514 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6515 for (i = 0; i < TILE_MAX; i++)
6516 mapping_EM_to_RND[i] = EL_UNKNOWN;
6518 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6519 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6520 em_object_mapping_list[i].element_rnd;
6522 mapping_initialized = TRUE;
6525 if (element_em >= 0 && element_em < TILE_MAX)
6526 return mapping_EM_to_RND[element_em];
6528 Error(ERR_WARN, "invalid EM level element %d", element_em);
6533 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6535 struct LevelInfo_EM *level_em = level->native_em_level;
6536 struct LEVEL *lev = level_em->lev;
6539 for (i = 0; i < TILE_MAX; i++)
6540 lev->android_array[i] = Xblank;
6542 for (i = 0; i < level->num_android_clone_elements; i++)
6544 int element_rnd = level->android_clone_element[i];
6545 int element_em = map_element_RND_to_EM(element_rnd);
6547 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6548 if (em_object_mapping_list[j].element_rnd == element_rnd)
6549 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6553 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6555 struct LevelInfo_EM *level_em = level->native_em_level;
6556 struct LEVEL *lev = level_em->lev;
6559 level->num_android_clone_elements = 0;
6561 for (i = 0; i < TILE_MAX; i++)
6563 int element_em = lev->android_array[i];
6565 boolean element_found = FALSE;
6567 if (element_em == Xblank)
6570 element_rnd = map_element_EM_to_RND(element_em);
6572 for (j = 0; j < level->num_android_clone_elements; j++)
6573 if (level->android_clone_element[j] == element_rnd)
6574 element_found = TRUE;
6578 level->android_clone_element[level->num_android_clone_elements++] =
6581 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6586 if (level->num_android_clone_elements == 0)
6588 level->num_android_clone_elements = 1;
6589 level->android_clone_element[0] = EL_EMPTY;
6593 int map_direction_RND_to_EM(int direction)
6595 return (direction == MV_UP ? 0 :
6596 direction == MV_RIGHT ? 1 :
6597 direction == MV_DOWN ? 2 :
6598 direction == MV_LEFT ? 3 :
6602 int map_direction_EM_to_RND(int direction)
6604 return (direction == 0 ? MV_UP :
6605 direction == 1 ? MV_RIGHT :
6606 direction == 2 ? MV_DOWN :
6607 direction == 3 ? MV_LEFT :
6611 int map_element_RND_to_SP(int element_rnd)
6613 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6615 if (element_rnd >= EL_SP_START &&
6616 element_rnd <= EL_SP_END)
6617 element_sp = element_rnd - EL_SP_START;
6618 else if (element_rnd == EL_EMPTY_SPACE)
6620 else if (element_rnd == EL_INVISIBLE_WALL)
6626 int map_element_SP_to_RND(int element_sp)
6628 int element_rnd = EL_UNKNOWN;
6630 if (element_sp >= 0x00 &&
6632 element_rnd = EL_SP_START + element_sp;
6633 else if (element_sp == 0x28)
6634 element_rnd = EL_INVISIBLE_WALL;
6639 int map_action_SP_to_RND(int action_sp)
6643 case actActive: return ACTION_ACTIVE;
6644 case actImpact: return ACTION_IMPACT;
6645 case actExploding: return ACTION_EXPLODING;
6646 case actDigging: return ACTION_DIGGING;
6647 case actSnapping: return ACTION_SNAPPING;
6648 case actCollecting: return ACTION_COLLECTING;
6649 case actPassing: return ACTION_PASSING;
6650 case actPushing: return ACTION_PUSHING;
6651 case actDropping: return ACTION_DROPPING;
6653 default: return ACTION_DEFAULT;
6657 int get_next_element(int element)
6661 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6662 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6663 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6664 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6665 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6666 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6667 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6668 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6669 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6670 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6671 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6673 default: return element;
6677 int el_act_dir2img(int element, int action, int direction)
6679 element = GFX_ELEMENT(element);
6680 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6682 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6683 return element_info[element].direction_graphic[action][direction];
6686 static int el_act_dir2crm(int element, int action, int direction)
6688 element = GFX_ELEMENT(element);
6689 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6691 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6692 return element_info[element].direction_crumbled[action][direction];
6695 int el_act2img(int element, int action)
6697 element = GFX_ELEMENT(element);
6699 return element_info[element].graphic[action];
6702 int el_act2crm(int element, int action)
6704 element = GFX_ELEMENT(element);
6706 return element_info[element].crumbled[action];
6709 int el_dir2img(int element, int direction)
6711 element = GFX_ELEMENT(element);
6713 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6716 int el2baseimg(int element)
6718 return element_info[element].graphic[ACTION_DEFAULT];
6721 int el2img(int element)
6723 element = GFX_ELEMENT(element);
6725 return element_info[element].graphic[ACTION_DEFAULT];
6728 int el2edimg(int element)
6730 element = GFX_ELEMENT(element);
6732 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6735 int el2preimg(int element)
6737 element = GFX_ELEMENT(element);
6739 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6742 int el2panelimg(int element)
6744 element = GFX_ELEMENT(element);
6746 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6749 int font2baseimg(int font_nr)
6751 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6754 int getBeltNrFromBeltElement(int element)
6756 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6757 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6758 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6761 int getBeltNrFromBeltActiveElement(int element)
6763 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6764 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6765 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6768 int getBeltNrFromBeltSwitchElement(int element)
6770 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6771 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6772 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6775 int getBeltDirNrFromBeltElement(int element)
6777 static int belt_base_element[4] =
6779 EL_CONVEYOR_BELT_1_LEFT,
6780 EL_CONVEYOR_BELT_2_LEFT,
6781 EL_CONVEYOR_BELT_3_LEFT,
6782 EL_CONVEYOR_BELT_4_LEFT
6785 int belt_nr = getBeltNrFromBeltElement(element);
6786 int belt_dir_nr = element - belt_base_element[belt_nr];
6788 return (belt_dir_nr % 3);
6791 int getBeltDirNrFromBeltSwitchElement(int element)
6793 static int belt_base_element[4] =
6795 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6796 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6797 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6798 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6801 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6802 int belt_dir_nr = element - belt_base_element[belt_nr];
6804 return (belt_dir_nr % 3);
6807 int getBeltDirFromBeltElement(int element)
6809 static int belt_move_dir[3] =
6816 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6818 return belt_move_dir[belt_dir_nr];
6821 int getBeltDirFromBeltSwitchElement(int element)
6823 static int belt_move_dir[3] =
6830 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6832 return belt_move_dir[belt_dir_nr];
6835 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6837 static int belt_base_element[4] =
6839 EL_CONVEYOR_BELT_1_LEFT,
6840 EL_CONVEYOR_BELT_2_LEFT,
6841 EL_CONVEYOR_BELT_3_LEFT,
6842 EL_CONVEYOR_BELT_4_LEFT
6845 return belt_base_element[belt_nr] + belt_dir_nr;
6848 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6850 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6852 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6855 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6857 static int belt_base_element[4] =
6859 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6860 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6861 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6862 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6865 return belt_base_element[belt_nr] + belt_dir_nr;
6868 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6870 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6872 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6875 boolean getTeamMode_EM()
6877 return game.team_mode;
6880 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6882 int game_frame_delay_value;
6884 game_frame_delay_value =
6885 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6886 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6889 if (tape.playing && tape.warp_forward && !tape.pausing)
6890 game_frame_delay_value = 0;
6892 return game_frame_delay_value;
6895 unsigned int InitRND(int seed)
6897 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6898 return InitEngineRandom_EM(seed);
6899 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6900 return InitEngineRandom_SP(seed);
6902 return InitEngineRandom_RND(seed);
6905 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6906 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6908 inline static int get_effective_element_EM(int tile, int frame_em)
6910 int element = object_mapping[tile].element_rnd;
6911 int action = object_mapping[tile].action;
6912 boolean is_backside = object_mapping[tile].is_backside;
6913 boolean action_removing = (action == ACTION_DIGGING ||
6914 action == ACTION_SNAPPING ||
6915 action == ACTION_COLLECTING);
6921 case Yacid_splash_eB:
6922 case Yacid_splash_wB:
6923 return (frame_em > 5 ? EL_EMPTY : element);
6929 else /* frame_em == 7 */
6933 case Yacid_splash_eB:
6934 case Yacid_splash_wB:
6937 case Yemerald_stone:
6940 case Ydiamond_stone:
6944 case Xdrip_stretchB:
6963 case Xsand_stonein_1:
6964 case Xsand_stonein_2:
6965 case Xsand_stonein_3:
6966 case Xsand_stonein_4:
6970 return (is_backside || action_removing ? EL_EMPTY : element);
6975 inline static boolean check_linear_animation_EM(int tile)
6979 case Xsand_stonesand_1:
6980 case Xsand_stonesand_quickout_1:
6981 case Xsand_sandstone_1:
6982 case Xsand_stonein_1:
6983 case Xsand_stoneout_1:
7002 case Yacid_splash_eB:
7003 case Yacid_splash_wB:
7004 case Yemerald_stone:
7011 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7012 boolean has_crumbled_graphics,
7013 int crumbled, int sync_frame)
7015 /* if element can be crumbled, but certain action graphics are just empty
7016 space (like instantly snapping sand to empty space in 1 frame), do not
7017 treat these empty space graphics as crumbled graphics in EMC engine */
7018 if (crumbled == IMG_EMPTY_SPACE)
7019 has_crumbled_graphics = FALSE;
7021 if (has_crumbled_graphics)
7023 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7024 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7025 g_crumbled->anim_delay,
7026 g_crumbled->anim_mode,
7027 g_crumbled->anim_start_frame,
7030 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7031 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7033 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7035 g_em->has_crumbled_graphics = TRUE;
7039 g_em->crumbled_bitmap = NULL;
7040 g_em->crumbled_src_x = 0;
7041 g_em->crumbled_src_y = 0;
7042 g_em->crumbled_border_size = 0;
7044 g_em->has_crumbled_graphics = FALSE;
7048 void ResetGfxAnimation_EM(int x, int y, int tile)
7053 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7054 int tile, int frame_em, int x, int y)
7056 int action = object_mapping[tile].action;
7057 int direction = object_mapping[tile].direction;
7058 int effective_element = get_effective_element_EM(tile, frame_em);
7059 int graphic = (direction == MV_NONE ?
7060 el_act2img(effective_element, action) :
7061 el_act_dir2img(effective_element, action, direction));
7062 struct GraphicInfo *g = &graphic_info[graphic];
7064 boolean action_removing = (action == ACTION_DIGGING ||
7065 action == ACTION_SNAPPING ||
7066 action == ACTION_COLLECTING);
7067 boolean action_moving = (action == ACTION_FALLING ||
7068 action == ACTION_MOVING ||
7069 action == ACTION_PUSHING ||
7070 action == ACTION_EATING ||
7071 action == ACTION_FILLING ||
7072 action == ACTION_EMPTYING);
7073 boolean action_falling = (action == ACTION_FALLING ||
7074 action == ACTION_FILLING ||
7075 action == ACTION_EMPTYING);
7077 /* special case: graphic uses "2nd movement tile" and has defined
7078 7 frames for movement animation (or less) => use default graphic
7079 for last (8th) frame which ends the movement animation */
7080 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7082 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7083 graphic = (direction == MV_NONE ?
7084 el_act2img(effective_element, action) :
7085 el_act_dir2img(effective_element, action, direction));
7087 g = &graphic_info[graphic];
7090 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7094 else if (action_moving)
7096 boolean is_backside = object_mapping[tile].is_backside;
7100 int direction = object_mapping[tile].direction;
7101 int move_dir = (action_falling ? MV_DOWN : direction);
7106 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7107 if (g->double_movement && frame_em == 0)
7111 if (move_dir == MV_LEFT)
7112 GfxFrame[x - 1][y] = GfxFrame[x][y];
7113 else if (move_dir == MV_RIGHT)
7114 GfxFrame[x + 1][y] = GfxFrame[x][y];
7115 else if (move_dir == MV_UP)
7116 GfxFrame[x][y - 1] = GfxFrame[x][y];
7117 else if (move_dir == MV_DOWN)
7118 GfxFrame[x][y + 1] = GfxFrame[x][y];
7125 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7126 if (tile == Xsand_stonesand_quickout_1 ||
7127 tile == Xsand_stonesand_quickout_2)
7131 if (graphic_info[graphic].anim_global_sync)
7132 sync_frame = FrameCounter;
7133 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7134 sync_frame = GfxFrame[x][y];
7136 sync_frame = 0; /* playfield border (pseudo steel) */
7138 SetRandomAnimationValue(x, y);
7140 int frame = getAnimationFrame(g->anim_frames,
7143 g->anim_start_frame,
7146 g_em->unique_identifier =
7147 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7150 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7151 int tile, int frame_em, int x, int y)
7153 int action = object_mapping[tile].action;
7154 int direction = object_mapping[tile].direction;
7155 boolean is_backside = object_mapping[tile].is_backside;
7156 int effective_element = get_effective_element_EM(tile, frame_em);
7157 int effective_action = action;
7158 int graphic = (direction == MV_NONE ?
7159 el_act2img(effective_element, effective_action) :
7160 el_act_dir2img(effective_element, effective_action,
7162 int crumbled = (direction == MV_NONE ?
7163 el_act2crm(effective_element, effective_action) :
7164 el_act_dir2crm(effective_element, effective_action,
7166 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7167 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7168 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7169 struct GraphicInfo *g = &graphic_info[graphic];
7172 /* special case: graphic uses "2nd movement tile" and has defined
7173 7 frames for movement animation (or less) => use default graphic
7174 for last (8th) frame which ends the movement animation */
7175 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7177 effective_action = ACTION_DEFAULT;
7178 graphic = (direction == MV_NONE ?
7179 el_act2img(effective_element, effective_action) :
7180 el_act_dir2img(effective_element, effective_action,
7182 crumbled = (direction == MV_NONE ?
7183 el_act2crm(effective_element, effective_action) :
7184 el_act_dir2crm(effective_element, effective_action,
7187 g = &graphic_info[graphic];
7190 if (graphic_info[graphic].anim_global_sync)
7191 sync_frame = FrameCounter;
7192 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7193 sync_frame = GfxFrame[x][y];
7195 sync_frame = 0; /* playfield border (pseudo steel) */
7197 SetRandomAnimationValue(x, y);
7199 int frame = getAnimationFrame(g->anim_frames,
7202 g->anim_start_frame,
7205 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7206 g->double_movement && is_backside);
7208 /* (updating the "crumbled" graphic definitions is probably not really needed,
7209 as animations for crumbled graphics can't be longer than one EMC cycle) */
7210 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7214 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7215 int player_nr, int anim, int frame_em)
7217 int element = player_mapping[player_nr][anim].element_rnd;
7218 int action = player_mapping[player_nr][anim].action;
7219 int direction = player_mapping[player_nr][anim].direction;
7220 int graphic = (direction == MV_NONE ?
7221 el_act2img(element, action) :
7222 el_act_dir2img(element, action, direction));
7223 struct GraphicInfo *g = &graphic_info[graphic];
7226 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7228 stored_player[player_nr].StepFrame = frame_em;
7230 sync_frame = stored_player[player_nr].Frame;
7232 int frame = getAnimationFrame(g->anim_frames,
7235 g->anim_start_frame,
7238 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7239 &g_em->src_x, &g_em->src_y, FALSE);
7242 void InitGraphicInfo_EM(void)
7247 int num_em_gfx_errors = 0;
7249 if (graphic_info_em_object[0][0].bitmap == NULL)
7251 /* EM graphics not yet initialized in em_open_all() */
7256 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7259 /* always start with reliable default values */
7260 for (i = 0; i < TILE_MAX; i++)
7262 object_mapping[i].element_rnd = EL_UNKNOWN;
7263 object_mapping[i].is_backside = FALSE;
7264 object_mapping[i].action = ACTION_DEFAULT;
7265 object_mapping[i].direction = MV_NONE;
7268 /* always start with reliable default values */
7269 for (p = 0; p < MAX_PLAYERS; p++)
7271 for (i = 0; i < SPR_MAX; i++)
7273 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7274 player_mapping[p][i].action = ACTION_DEFAULT;
7275 player_mapping[p][i].direction = MV_NONE;
7279 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7281 int e = em_object_mapping_list[i].element_em;
7283 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7284 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7286 if (em_object_mapping_list[i].action != -1)
7287 object_mapping[e].action = em_object_mapping_list[i].action;
7289 if (em_object_mapping_list[i].direction != -1)
7290 object_mapping[e].direction =
7291 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7294 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7296 int a = em_player_mapping_list[i].action_em;
7297 int p = em_player_mapping_list[i].player_nr;
7299 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7301 if (em_player_mapping_list[i].action != -1)
7302 player_mapping[p][a].action = em_player_mapping_list[i].action;
7304 if (em_player_mapping_list[i].direction != -1)
7305 player_mapping[p][a].direction =
7306 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7309 for (i = 0; i < TILE_MAX; i++)
7311 int element = object_mapping[i].element_rnd;
7312 int action = object_mapping[i].action;
7313 int direction = object_mapping[i].direction;
7314 boolean is_backside = object_mapping[i].is_backside;
7315 boolean action_exploding = ((action == ACTION_EXPLODING ||
7316 action == ACTION_SMASHED_BY_ROCK ||
7317 action == ACTION_SMASHED_BY_SPRING) &&
7318 element != EL_DIAMOND);
7319 boolean action_active = (action == ACTION_ACTIVE);
7320 boolean action_other = (action == ACTION_OTHER);
7322 for (j = 0; j < 8; j++)
7324 int effective_element = get_effective_element_EM(i, j);
7325 int effective_action = (j < 7 ? action :
7326 i == Xdrip_stretch ? action :
7327 i == Xdrip_stretchB ? action :
7328 i == Ydrip_s1 ? action :
7329 i == Ydrip_s1B ? action :
7330 i == Xball_1B ? action :
7331 i == Xball_2 ? action :
7332 i == Xball_2B ? action :
7333 i == Yball_eat ? action :
7334 i == Ykey_1_eat ? action :
7335 i == Ykey_2_eat ? action :
7336 i == Ykey_3_eat ? action :
7337 i == Ykey_4_eat ? action :
7338 i == Ykey_5_eat ? action :
7339 i == Ykey_6_eat ? action :
7340 i == Ykey_7_eat ? action :
7341 i == Ykey_8_eat ? action :
7342 i == Ylenses_eat ? action :
7343 i == Ymagnify_eat ? action :
7344 i == Ygrass_eat ? action :
7345 i == Ydirt_eat ? action :
7346 i == Xsand_stonein_1 ? action :
7347 i == Xsand_stonein_2 ? action :
7348 i == Xsand_stonein_3 ? action :
7349 i == Xsand_stonein_4 ? action :
7350 i == Xsand_stoneout_1 ? action :
7351 i == Xsand_stoneout_2 ? action :
7352 i == Xboom_android ? ACTION_EXPLODING :
7353 action_exploding ? ACTION_EXPLODING :
7354 action_active ? action :
7355 action_other ? action :
7357 int graphic = (el_act_dir2img(effective_element, effective_action,
7359 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7361 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7362 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7363 boolean has_action_graphics = (graphic != base_graphic);
7364 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7365 struct GraphicInfo *g = &graphic_info[graphic];
7366 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7369 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7370 boolean special_animation = (action != ACTION_DEFAULT &&
7371 g->anim_frames == 3 &&
7372 g->anim_delay == 2 &&
7373 g->anim_mode & ANIM_LINEAR);
7374 int sync_frame = (i == Xdrip_stretch ? 7 :
7375 i == Xdrip_stretchB ? 7 :
7376 i == Ydrip_s2 ? j + 8 :
7377 i == Ydrip_s2B ? j + 8 :
7386 i == Xfake_acid_1 ? 0 :
7387 i == Xfake_acid_2 ? 10 :
7388 i == Xfake_acid_3 ? 20 :
7389 i == Xfake_acid_4 ? 30 :
7390 i == Xfake_acid_5 ? 40 :
7391 i == Xfake_acid_6 ? 50 :
7392 i == Xfake_acid_7 ? 60 :
7393 i == Xfake_acid_8 ? 70 :
7395 i == Xball_2B ? j + 8 :
7396 i == Yball_eat ? j + 1 :
7397 i == Ykey_1_eat ? j + 1 :
7398 i == Ykey_2_eat ? j + 1 :
7399 i == Ykey_3_eat ? j + 1 :
7400 i == Ykey_4_eat ? j + 1 :
7401 i == Ykey_5_eat ? j + 1 :
7402 i == Ykey_6_eat ? j + 1 :
7403 i == Ykey_7_eat ? j + 1 :
7404 i == Ykey_8_eat ? j + 1 :
7405 i == Ylenses_eat ? j + 1 :
7406 i == Ymagnify_eat ? j + 1 :
7407 i == Ygrass_eat ? j + 1 :
7408 i == Ydirt_eat ? j + 1 :
7409 i == Xamoeba_1 ? 0 :
7410 i == Xamoeba_2 ? 1 :
7411 i == Xamoeba_3 ? 2 :
7412 i == Xamoeba_4 ? 3 :
7413 i == Xamoeba_5 ? 0 :
7414 i == Xamoeba_6 ? 1 :
7415 i == Xamoeba_7 ? 2 :
7416 i == Xamoeba_8 ? 3 :
7417 i == Xexit_2 ? j + 8 :
7418 i == Xexit_3 ? j + 16 :
7419 i == Xdynamite_1 ? 0 :
7420 i == Xdynamite_2 ? 8 :
7421 i == Xdynamite_3 ? 16 :
7422 i == Xdynamite_4 ? 24 :
7423 i == Xsand_stonein_1 ? j + 1 :
7424 i == Xsand_stonein_2 ? j + 9 :
7425 i == Xsand_stonein_3 ? j + 17 :
7426 i == Xsand_stonein_4 ? j + 25 :
7427 i == Xsand_stoneout_1 && j == 0 ? 0 :
7428 i == Xsand_stoneout_1 && j == 1 ? 0 :
7429 i == Xsand_stoneout_1 && j == 2 ? 1 :
7430 i == Xsand_stoneout_1 && j == 3 ? 2 :
7431 i == Xsand_stoneout_1 && j == 4 ? 2 :
7432 i == Xsand_stoneout_1 && j == 5 ? 3 :
7433 i == Xsand_stoneout_1 && j == 6 ? 4 :
7434 i == Xsand_stoneout_1 && j == 7 ? 4 :
7435 i == Xsand_stoneout_2 && j == 0 ? 5 :
7436 i == Xsand_stoneout_2 && j == 1 ? 6 :
7437 i == Xsand_stoneout_2 && j == 2 ? 7 :
7438 i == Xsand_stoneout_2 && j == 3 ? 8 :
7439 i == Xsand_stoneout_2 && j == 4 ? 9 :
7440 i == Xsand_stoneout_2 && j == 5 ? 11 :
7441 i == Xsand_stoneout_2 && j == 6 ? 13 :
7442 i == Xsand_stoneout_2 && j == 7 ? 15 :
7443 i == Xboom_bug && j == 1 ? 2 :
7444 i == Xboom_bug && j == 2 ? 2 :
7445 i == Xboom_bug && j == 3 ? 4 :
7446 i == Xboom_bug && j == 4 ? 4 :
7447 i == Xboom_bug && j == 5 ? 2 :
7448 i == Xboom_bug && j == 6 ? 2 :
7449 i == Xboom_bug && j == 7 ? 0 :
7450 i == Xboom_bomb && j == 1 ? 2 :
7451 i == Xboom_bomb && j == 2 ? 2 :
7452 i == Xboom_bomb && j == 3 ? 4 :
7453 i == Xboom_bomb && j == 4 ? 4 :
7454 i == Xboom_bomb && j == 5 ? 2 :
7455 i == Xboom_bomb && j == 6 ? 2 :
7456 i == Xboom_bomb && j == 7 ? 0 :
7457 i == Xboom_android && j == 7 ? 6 :
7458 i == Xboom_1 && j == 1 ? 2 :
7459 i == Xboom_1 && j == 2 ? 2 :
7460 i == Xboom_1 && j == 3 ? 4 :
7461 i == Xboom_1 && j == 4 ? 4 :
7462 i == Xboom_1 && j == 5 ? 6 :
7463 i == Xboom_1 && j == 6 ? 6 :
7464 i == Xboom_1 && j == 7 ? 8 :
7465 i == Xboom_2 && j == 0 ? 8 :
7466 i == Xboom_2 && j == 1 ? 8 :
7467 i == Xboom_2 && j == 2 ? 10 :
7468 i == Xboom_2 && j == 3 ? 10 :
7469 i == Xboom_2 && j == 4 ? 10 :
7470 i == Xboom_2 && j == 5 ? 12 :
7471 i == Xboom_2 && j == 6 ? 12 :
7472 i == Xboom_2 && j == 7 ? 12 :
7473 special_animation && j == 4 ? 3 :
7474 effective_action != action ? 0 :
7478 Bitmap *debug_bitmap = g_em->bitmap;
7479 int debug_src_x = g_em->src_x;
7480 int debug_src_y = g_em->src_y;
7483 int frame = getAnimationFrame(g->anim_frames,
7486 g->anim_start_frame,
7489 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7490 g->double_movement && is_backside);
7492 g_em->bitmap = src_bitmap;
7493 g_em->src_x = src_x;
7494 g_em->src_y = src_y;
7495 g_em->src_offset_x = 0;
7496 g_em->src_offset_y = 0;
7497 g_em->dst_offset_x = 0;
7498 g_em->dst_offset_y = 0;
7499 g_em->width = TILEX;
7500 g_em->height = TILEY;
7502 g_em->preserve_background = FALSE;
7504 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7507 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7508 effective_action == ACTION_MOVING ||
7509 effective_action == ACTION_PUSHING ||
7510 effective_action == ACTION_EATING)) ||
7511 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7512 effective_action == ACTION_EMPTYING)))
7515 (effective_action == ACTION_FALLING ||
7516 effective_action == ACTION_FILLING ||
7517 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7518 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7519 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7520 int num_steps = (i == Ydrip_s1 ? 16 :
7521 i == Ydrip_s1B ? 16 :
7522 i == Ydrip_s2 ? 16 :
7523 i == Ydrip_s2B ? 16 :
7524 i == Xsand_stonein_1 ? 32 :
7525 i == Xsand_stonein_2 ? 32 :
7526 i == Xsand_stonein_3 ? 32 :
7527 i == Xsand_stonein_4 ? 32 :
7528 i == Xsand_stoneout_1 ? 16 :
7529 i == Xsand_stoneout_2 ? 16 : 8);
7530 int cx = ABS(dx) * (TILEX / num_steps);
7531 int cy = ABS(dy) * (TILEY / num_steps);
7532 int step_frame = (i == Ydrip_s2 ? j + 8 :
7533 i == Ydrip_s2B ? j + 8 :
7534 i == Xsand_stonein_2 ? j + 8 :
7535 i == Xsand_stonein_3 ? j + 16 :
7536 i == Xsand_stonein_4 ? j + 24 :
7537 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7538 int step = (is_backside ? step_frame : num_steps - step_frame);
7540 if (is_backside) /* tile where movement starts */
7542 if (dx < 0 || dy < 0)
7544 g_em->src_offset_x = cx * step;
7545 g_em->src_offset_y = cy * step;
7549 g_em->dst_offset_x = cx * step;
7550 g_em->dst_offset_y = cy * step;
7553 else /* tile where movement ends */
7555 if (dx < 0 || dy < 0)
7557 g_em->dst_offset_x = cx * step;
7558 g_em->dst_offset_y = cy * step;
7562 g_em->src_offset_x = cx * step;
7563 g_em->src_offset_y = cy * step;
7567 g_em->width = TILEX - cx * step;
7568 g_em->height = TILEY - cy * step;
7571 /* create unique graphic identifier to decide if tile must be redrawn */
7572 /* bit 31 - 16 (16 bit): EM style graphic
7573 bit 15 - 12 ( 4 bit): EM style frame
7574 bit 11 - 6 ( 6 bit): graphic width
7575 bit 5 - 0 ( 6 bit): graphic height */
7576 g_em->unique_identifier =
7577 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7581 /* skip check for EMC elements not contained in original EMC artwork */
7582 if (element == EL_EMC_FAKE_ACID)
7585 if (g_em->bitmap != debug_bitmap ||
7586 g_em->src_x != debug_src_x ||
7587 g_em->src_y != debug_src_y ||
7588 g_em->src_offset_x != 0 ||
7589 g_em->src_offset_y != 0 ||
7590 g_em->dst_offset_x != 0 ||
7591 g_em->dst_offset_y != 0 ||
7592 g_em->width != TILEX ||
7593 g_em->height != TILEY)
7595 static int last_i = -1;
7603 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7604 i, element, element_info[element].token_name,
7605 element_action_info[effective_action].suffix, direction);
7607 if (element != effective_element)
7608 printf(" [%d ('%s')]",
7610 element_info[effective_element].token_name);
7614 if (g_em->bitmap != debug_bitmap)
7615 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7616 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7618 if (g_em->src_x != debug_src_x ||
7619 g_em->src_y != debug_src_y)
7620 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7621 j, (is_backside ? 'B' : 'F'),
7622 g_em->src_x, g_em->src_y,
7623 g_em->src_x / 32, g_em->src_y / 32,
7624 debug_src_x, debug_src_y,
7625 debug_src_x / 32, debug_src_y / 32);
7627 if (g_em->src_offset_x != 0 ||
7628 g_em->src_offset_y != 0 ||
7629 g_em->dst_offset_x != 0 ||
7630 g_em->dst_offset_y != 0)
7631 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7633 g_em->src_offset_x, g_em->src_offset_y,
7634 g_em->dst_offset_x, g_em->dst_offset_y);
7636 if (g_em->width != TILEX ||
7637 g_em->height != TILEY)
7638 printf(" %d (%d): size %d,%d should be %d,%d\n",
7640 g_em->width, g_em->height, TILEX, TILEY);
7642 num_em_gfx_errors++;
7649 for (i = 0; i < TILE_MAX; i++)
7651 for (j = 0; j < 8; j++)
7653 int element = object_mapping[i].element_rnd;
7654 int action = object_mapping[i].action;
7655 int direction = object_mapping[i].direction;
7656 boolean is_backside = object_mapping[i].is_backside;
7657 int graphic_action = el_act_dir2img(element, action, direction);
7658 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7660 if ((action == ACTION_SMASHED_BY_ROCK ||
7661 action == ACTION_SMASHED_BY_SPRING ||
7662 action == ACTION_EATING) &&
7663 graphic_action == graphic_default)
7665 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7666 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7667 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7668 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7671 /* no separate animation for "smashed by rock" -- use rock instead */
7672 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7673 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7675 g_em->bitmap = g_xx->bitmap;
7676 g_em->src_x = g_xx->src_x;
7677 g_em->src_y = g_xx->src_y;
7678 g_em->src_offset_x = g_xx->src_offset_x;
7679 g_em->src_offset_y = g_xx->src_offset_y;
7680 g_em->dst_offset_x = g_xx->dst_offset_x;
7681 g_em->dst_offset_y = g_xx->dst_offset_y;
7682 g_em->width = g_xx->width;
7683 g_em->height = g_xx->height;
7684 g_em->unique_identifier = g_xx->unique_identifier;
7687 g_em->preserve_background = TRUE;
7692 for (p = 0; p < MAX_PLAYERS; p++)
7694 for (i = 0; i < SPR_MAX; i++)
7696 int element = player_mapping[p][i].element_rnd;
7697 int action = player_mapping[p][i].action;
7698 int direction = player_mapping[p][i].direction;
7700 for (j = 0; j < 8; j++)
7702 int effective_element = element;
7703 int effective_action = action;
7704 int graphic = (direction == MV_NONE ?
7705 el_act2img(effective_element, effective_action) :
7706 el_act_dir2img(effective_element, effective_action,
7708 struct GraphicInfo *g = &graphic_info[graphic];
7709 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7715 Bitmap *debug_bitmap = g_em->bitmap;
7716 int debug_src_x = g_em->src_x;
7717 int debug_src_y = g_em->src_y;
7720 int frame = getAnimationFrame(g->anim_frames,
7723 g->anim_start_frame,
7726 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7728 g_em->bitmap = src_bitmap;
7729 g_em->src_x = src_x;
7730 g_em->src_y = src_y;
7731 g_em->src_offset_x = 0;
7732 g_em->src_offset_y = 0;
7733 g_em->dst_offset_x = 0;
7734 g_em->dst_offset_y = 0;
7735 g_em->width = TILEX;
7736 g_em->height = TILEY;
7740 /* skip check for EMC elements not contained in original EMC artwork */
7741 if (element == EL_PLAYER_3 ||
7742 element == EL_PLAYER_4)
7745 if (g_em->bitmap != debug_bitmap ||
7746 g_em->src_x != debug_src_x ||
7747 g_em->src_y != debug_src_y)
7749 static int last_i = -1;
7757 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7758 p, i, element, element_info[element].token_name,
7759 element_action_info[effective_action].suffix, direction);
7761 if (element != effective_element)
7762 printf(" [%d ('%s')]",
7764 element_info[effective_element].token_name);
7768 if (g_em->bitmap != debug_bitmap)
7769 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7770 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7772 if (g_em->src_x != debug_src_x ||
7773 g_em->src_y != debug_src_y)
7774 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7776 g_em->src_x, g_em->src_y,
7777 g_em->src_x / 32, g_em->src_y / 32,
7778 debug_src_x, debug_src_y,
7779 debug_src_x / 32, debug_src_y / 32);
7781 num_em_gfx_errors++;
7791 printf("::: [%d errors found]\n", num_em_gfx_errors);
7797 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7798 boolean any_player_moving,
7799 boolean player_is_dropping)
7801 if (tape.single_step && tape.recording && !tape.pausing)
7802 if (frame == 0 && !player_is_dropping)
7803 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7806 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7807 boolean murphy_is_dropping)
7809 if (tape.single_step && tape.recording && !tape.pausing)
7810 if (murphy_is_waiting)
7811 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7814 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7815 int graphic, int sync_frame, int x, int y)
7817 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7819 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7822 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7824 return (IS_NEXT_FRAME(sync_frame, graphic));
7827 int getGraphicInfo_Delay(int graphic)
7829 return graphic_info[graphic].anim_delay;
7832 void PlayMenuSoundExt(int sound)
7834 if (sound == SND_UNDEFINED)
7837 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7838 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7841 if (IS_LOOP_SOUND(sound))
7842 PlaySoundLoop(sound);
7847 void PlayMenuSound()
7849 PlayMenuSoundExt(menu.sound[game_status]);
7852 void PlayMenuSoundStereo(int sound, int stereo_position)
7854 if (sound == SND_UNDEFINED)
7857 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7858 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7861 if (IS_LOOP_SOUND(sound))
7862 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7864 PlaySoundStereo(sound, stereo_position);
7867 void PlayMenuSoundIfLoopExt(int sound)
7869 if (sound == SND_UNDEFINED)
7872 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7873 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7876 if (IS_LOOP_SOUND(sound))
7877 PlaySoundLoop(sound);
7880 void PlayMenuSoundIfLoop()
7882 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7885 void PlayMenuMusicExt(int music)
7887 if (music == MUS_UNDEFINED)
7890 if (!setup.sound_music)
7896 void PlayMenuMusic()
7898 PlayMenuMusicExt(menu.music[game_status]);
7901 void PlaySoundActivating()
7904 PlaySound(SND_MENU_ITEM_ACTIVATING);
7908 void PlaySoundSelecting()
7911 PlaySound(SND_MENU_ITEM_SELECTING);
7915 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7917 boolean change_fullscreen = (setup.fullscreen !=
7918 video.fullscreen_enabled);
7919 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7920 !strEqual(setup.fullscreen_mode,
7921 video.fullscreen_mode_current));
7922 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7923 setup.window_scaling_percent !=
7924 video.window_scaling_percent);
7926 if (change_window_scaling_percent && video.fullscreen_enabled)
7929 if (!change_window_scaling_percent && !video.fullscreen_available)
7932 #if defined(TARGET_SDL2)
7933 if (change_window_scaling_percent)
7935 SDLSetWindowScaling(setup.window_scaling_percent);
7939 else if (change_fullscreen)
7941 SDLSetWindowFullscreen(setup.fullscreen);
7943 /* set setup value according to successfully changed fullscreen mode */
7944 setup.fullscreen = video.fullscreen_enabled;
7950 if (change_fullscreen ||
7951 change_fullscreen_mode ||
7952 change_window_scaling_percent)
7954 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
7956 /* save backbuffer content which gets lost when toggling fullscreen mode */
7957 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7959 if (change_fullscreen_mode)
7961 /* keep fullscreen, but change fullscreen mode (screen resolution) */
7962 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
7965 if (change_window_scaling_percent)
7967 /* keep window mode, but change window scaling */
7968 video.fullscreen_enabled = TRUE; /* force new window scaling */
7971 /* toggle fullscreen */
7972 ChangeVideoModeIfNeeded(setup.fullscreen);
7974 /* set setup value according to successfully changed fullscreen mode */
7975 setup.fullscreen = video.fullscreen_enabled;
7977 /* restore backbuffer content from temporary backbuffer backup bitmap */
7978 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7980 FreeBitmap(tmp_backbuffer);
7982 /* update visible window/screen */
7983 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7987 void ChangeViewportPropertiesIfNeeded()
7989 int gfx_game_mode = game_status;
7990 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
7992 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
7993 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
7994 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
7995 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
7996 int border_size = vp_playfield->border_size;
7997 int new_sx = vp_playfield->x + border_size;
7998 int new_sy = vp_playfield->y + border_size;
7999 int new_sxsize = vp_playfield->width - 2 * border_size;
8000 int new_sysize = vp_playfield->height - 2 * border_size;
8001 int new_real_sx = vp_playfield->x;
8002 int new_real_sy = vp_playfield->y;
8003 int new_full_sxsize = vp_playfield->width;
8004 int new_full_sysize = vp_playfield->height;
8005 int new_dx = vp_door_1->x;
8006 int new_dy = vp_door_1->y;
8007 int new_dxsize = vp_door_1->width;
8008 int new_dysize = vp_door_1->height;
8009 int new_vx = vp_door_2->x;
8010 int new_vy = vp_door_2->y;
8011 int new_vxsize = vp_door_2->width;
8012 int new_vysize = vp_door_2->height;
8013 int new_ex = vp_door_3->x;
8014 int new_ey = vp_door_3->y;
8015 int new_exsize = vp_door_3->width;
8016 int new_eysize = vp_door_3->height;
8017 int new_tilesize_var =
8018 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8020 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8021 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8022 int new_scr_fieldx = new_sxsize / tilesize;
8023 int new_scr_fieldy = new_sysize / tilesize;
8024 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8025 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8026 boolean init_gfx_buffers = FALSE;
8027 boolean init_video_buffer = FALSE;
8028 boolean init_gadgets_and_toons = FALSE;
8029 boolean init_em_graphics = FALSE;
8030 boolean drawing_area_changed = FALSE;
8032 if (viewport.window.width != WIN_XSIZE ||
8033 viewport.window.height != WIN_YSIZE)
8035 WIN_XSIZE = viewport.window.width;
8036 WIN_YSIZE = viewport.window.height;
8038 init_video_buffer = TRUE;
8039 init_gfx_buffers = TRUE;
8041 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8044 if (new_scr_fieldx != SCR_FIELDX ||
8045 new_scr_fieldy != SCR_FIELDY)
8047 /* this always toggles between MAIN and GAME when using small tile size */
8049 SCR_FIELDX = new_scr_fieldx;
8050 SCR_FIELDY = new_scr_fieldy;
8052 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8063 new_sxsize != SXSIZE ||
8064 new_sysize != SYSIZE ||
8065 new_dxsize != DXSIZE ||
8066 new_dysize != DYSIZE ||
8067 new_vxsize != VXSIZE ||
8068 new_vysize != VYSIZE ||
8069 new_exsize != EXSIZE ||
8070 new_eysize != EYSIZE ||
8071 new_real_sx != REAL_SX ||
8072 new_real_sy != REAL_SY ||
8073 new_full_sxsize != FULL_SXSIZE ||
8074 new_full_sysize != FULL_SYSIZE ||
8075 new_tilesize_var != TILESIZE_VAR
8078 if (new_tilesize_var != TILESIZE_VAR)
8080 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8082 // changing tile size invalidates scroll values of engine snapshots
8083 FreeEngineSnapshot();
8085 // changing tile size requires update of graphic mapping for EM engine
8086 init_em_graphics = TRUE;
8091 new_sxsize != SXSIZE ||
8092 new_sysize != SYSIZE ||
8093 new_real_sx != REAL_SX ||
8094 new_real_sy != REAL_SY ||
8095 new_full_sxsize != FULL_SXSIZE ||
8096 new_full_sysize != FULL_SYSIZE)
8098 if (!init_video_buffer)
8099 drawing_area_changed = TRUE;
8110 SXSIZE = new_sxsize;
8111 SYSIZE = new_sysize;
8112 DXSIZE = new_dxsize;
8113 DYSIZE = new_dysize;
8114 VXSIZE = new_vxsize;
8115 VYSIZE = new_vysize;
8116 EXSIZE = new_exsize;
8117 EYSIZE = new_eysize;
8118 REAL_SX = new_real_sx;
8119 REAL_SY = new_real_sy;
8120 FULL_SXSIZE = new_full_sxsize;
8121 FULL_SYSIZE = new_full_sysize;
8122 TILESIZE_VAR = new_tilesize_var;
8124 init_gfx_buffers = TRUE;
8125 init_gadgets_and_toons = TRUE;
8127 // printf("::: viewports: init_gfx_buffers\n");
8128 // printf("::: viewports: init_gadgets_and_toons\n");
8131 if (init_gfx_buffers)
8133 // printf("::: init_gfx_buffers\n");
8135 SCR_FIELDX = new_scr_fieldx_buffers;
8136 SCR_FIELDY = new_scr_fieldy_buffers;
8140 SCR_FIELDX = new_scr_fieldx;
8141 SCR_FIELDY = new_scr_fieldy;
8143 gfx.drawing_area_changed = drawing_area_changed;
8145 SetDrawDeactivationMask(REDRAW_NONE);
8146 SetDrawBackgroundMask(REDRAW_FIELD);
8149 if (init_video_buffer)
8151 // printf("::: init_video_buffer\n");
8153 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8156 if (init_gadgets_and_toons)
8158 // printf("::: init_gadgets_and_toons\n");
8164 if (init_em_graphics)
8166 InitGraphicInfo_EM();