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_FIELDBUFFER)
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 (game_status == GAME_MODE_PLAYING)
956 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
957 SetDrawtoField(DRAW_FIELDBUFFER);
960 SetDrawtoField(DRAW_BACKBUFFER);
963 void MarkTileDirty(int x, int y)
965 int xx = redraw_x1 + x;
966 int yy = redraw_y1 + y;
971 redraw[xx][yy] = TRUE;
972 redraw_mask |= REDRAW_TILES;
975 void SetBorderElement()
979 BorderElement = EL_EMPTY;
981 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
983 for (x = 0; x < lev_fieldx; x++)
985 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
986 BorderElement = EL_STEELWALL;
988 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
994 void FloodFillLevel(int from_x, int from_y, int fill_element,
995 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
996 int max_fieldx, int max_fieldy)
1000 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1001 static int safety = 0;
1003 /* check if starting field still has the desired content */
1004 if (field[from_x][from_y] == fill_element)
1009 if (safety > max_fieldx * max_fieldy)
1010 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1012 old_element = field[from_x][from_y];
1013 field[from_x][from_y] = fill_element;
1015 for (i = 0; i < 4; i++)
1017 x = from_x + check[i][0];
1018 y = from_y + check[i][1];
1020 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1021 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1027 void SetRandomAnimationValue(int x, int y)
1029 gfx.anim_random_frame = GfxRandom[x][y];
1032 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
1034 /* animation synchronized with global frame counter, not move position */
1035 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1036 sync_frame = FrameCounter;
1038 return getAnimationFrame(graphic_info[graphic].anim_frames,
1039 graphic_info[graphic].anim_delay,
1040 graphic_info[graphic].anim_mode,
1041 graphic_info[graphic].anim_start_frame,
1045 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1046 Bitmap **bitmap, int *x, int *y,
1047 boolean get_backside)
1049 struct GraphicInfo *g = &graphic_info[graphic];
1050 Bitmap *src_bitmap = g->bitmap;
1051 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1052 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1053 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1055 // if no in-game graphics defined, always use standard graphic size
1056 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1057 tilesize = TILESIZE;
1059 if (tilesize == gfx.standard_tile_size)
1060 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1061 else if (tilesize == game.tile_size)
1062 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1064 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1066 if (g->offset_y == 0) /* frames are ordered horizontally */
1068 int max_width = g->anim_frames_per_line * g->width;
1069 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1071 src_x = pos % max_width;
1072 src_y = src_y % g->height + pos / max_width * g->height;
1074 else if (g->offset_x == 0) /* frames are ordered vertically */
1076 int max_height = g->anim_frames_per_line * g->height;
1077 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1079 src_x = src_x % g->width + pos / max_height * g->width;
1080 src_y = pos % max_height;
1082 else /* frames are ordered diagonally */
1084 src_x = src_x + frame * g->offset_x;
1085 src_y = src_y + frame * g->offset_y;
1088 *bitmap = src_bitmap;
1089 *x = src_x * tilesize / TILESIZE;
1090 *y = src_y * tilesize / TILESIZE;
1093 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1094 int *x, int *y, boolean get_backside)
1096 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1100 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1101 Bitmap **bitmap, int *x, int *y)
1103 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1106 void getFixedGraphicSource(int graphic, int frame,
1107 Bitmap **bitmap, int *x, int *y)
1109 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1112 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1114 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1117 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1118 int *x, int *y, boolean get_backside)
1120 struct GraphicInfo *g = &graphic_info[graphic];
1121 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1122 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1124 if (TILESIZE_VAR != TILESIZE)
1125 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1128 *bitmap = g->bitmap;
1130 if (g->offset_y == 0) /* frames are ordered horizontally */
1132 int max_width = g->anim_frames_per_line * g->width;
1133 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1135 *x = pos % max_width;
1136 *y = src_y % g->height + pos / max_width * g->height;
1138 else if (g->offset_x == 0) /* frames are ordered vertically */
1140 int max_height = g->anim_frames_per_line * g->height;
1141 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1143 *x = src_x % g->width + pos / max_height * g->width;
1144 *y = pos % max_height;
1146 else /* frames are ordered diagonally */
1148 *x = src_x + frame * g->offset_x;
1149 *y = src_y + frame * g->offset_y;
1153 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1155 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1158 void DrawGraphic(int x, int y, int graphic, int frame)
1161 if (!IN_SCR_FIELD(x, y))
1163 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1164 printf("DrawGraphic(): This should never happen!\n");
1169 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1172 MarkTileDirty(x, y);
1175 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1178 if (!IN_SCR_FIELD(x, y))
1180 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1181 printf("DrawGraphic(): This should never happen!\n");
1186 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1188 MarkTileDirty(x, y);
1191 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1197 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1199 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1202 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1208 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1209 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1212 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1215 if (!IN_SCR_FIELD(x, y))
1217 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1218 printf("DrawGraphicThruMask(): This should never happen!\n");
1223 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1226 MarkTileDirty(x, y);
1229 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1232 if (!IN_SCR_FIELD(x, y))
1234 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1235 printf("DrawGraphicThruMask(): This should never happen!\n");
1240 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1242 MarkTileDirty(x, y);
1245 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1251 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1253 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1257 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1258 int graphic, int frame)
1260 struct GraphicInfo *g = &graphic_info[graphic];
1264 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1266 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1270 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1272 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1274 MarkTileDirty(x / tilesize, y / tilesize);
1277 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1283 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1284 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1287 void DrawMiniGraphic(int x, int y, int graphic)
1289 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1290 MarkTileDirty(x / 2, y / 2);
1293 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1298 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1299 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1302 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1303 int graphic, int frame,
1304 int cut_mode, int mask_mode)
1309 int width = TILEX, height = TILEY;
1312 if (dx || dy) /* shifted graphic */
1314 if (x < BX1) /* object enters playfield from the left */
1321 else if (x > BX2) /* object enters playfield from the right */
1327 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1333 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1335 else if (dx) /* general horizontal movement */
1336 MarkTileDirty(x + SIGN(dx), y);
1338 if (y < BY1) /* object enters playfield from the top */
1340 if (cut_mode==CUT_BELOW) /* object completely above top border */
1348 else if (y > BY2) /* object enters playfield from the bottom */
1354 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1360 else if (dy > 0 && cut_mode == CUT_ABOVE)
1362 if (y == BY2) /* object completely above bottom border */
1368 MarkTileDirty(x, y + 1);
1369 } /* object leaves playfield to the bottom */
1370 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1372 else if (dy) /* general vertical movement */
1373 MarkTileDirty(x, y + SIGN(dy));
1377 if (!IN_SCR_FIELD(x, y))
1379 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1380 printf("DrawGraphicShifted(): This should never happen!\n");
1385 width = width * TILESIZE_VAR / TILESIZE;
1386 height = height * TILESIZE_VAR / TILESIZE;
1387 cx = cx * TILESIZE_VAR / TILESIZE;
1388 cy = cy * TILESIZE_VAR / TILESIZE;
1389 dx = dx * TILESIZE_VAR / TILESIZE;
1390 dy = dy * TILESIZE_VAR / TILESIZE;
1392 if (width > 0 && height > 0)
1394 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1399 dst_x = FX + x * TILEX_VAR + dx;
1400 dst_y = FY + y * TILEY_VAR + dy;
1402 if (mask_mode == USE_MASKING)
1403 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1406 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1409 MarkTileDirty(x, y);
1413 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1414 int graphic, int frame,
1415 int cut_mode, int mask_mode)
1420 int width = TILEX_VAR, height = TILEY_VAR;
1423 int x2 = x + SIGN(dx);
1424 int y2 = y + SIGN(dy);
1426 /* movement with two-tile animations must be sync'ed with movement position,
1427 not with current GfxFrame (which can be higher when using slow movement) */
1428 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1429 int anim_frames = graphic_info[graphic].anim_frames;
1431 /* (we also need anim_delay here for movement animations with less frames) */
1432 int anim_delay = graphic_info[graphic].anim_delay;
1433 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1435 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1436 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1438 /* re-calculate animation frame for two-tile movement animation */
1439 frame = getGraphicAnimationFrame(graphic, sync_frame);
1441 /* check if movement start graphic inside screen area and should be drawn */
1442 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1444 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1446 dst_x = FX + x1 * TILEX_VAR;
1447 dst_y = FY + y1 * TILEY_VAR;
1449 if (mask_mode == USE_MASKING)
1450 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1453 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1456 MarkTileDirty(x1, y1);
1459 /* check if movement end graphic inside screen area and should be drawn */
1460 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1462 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1464 dst_x = FX + x2 * TILEX_VAR;
1465 dst_y = FY + y2 * TILEY_VAR;
1467 if (mask_mode == USE_MASKING)
1468 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1471 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1474 MarkTileDirty(x2, y2);
1478 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1479 int graphic, int frame,
1480 int cut_mode, int mask_mode)
1484 DrawGraphic(x, y, graphic, frame);
1489 if (graphic_info[graphic].double_movement) /* EM style movement images */
1490 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1492 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1495 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1496 int frame, int cut_mode)
1498 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1501 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1502 int cut_mode, int mask_mode)
1504 int lx = LEVELX(x), ly = LEVELY(y);
1508 if (IN_LEV_FIELD(lx, ly))
1510 SetRandomAnimationValue(lx, ly);
1512 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1513 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1515 /* do not use double (EM style) movement graphic when not moving */
1516 if (graphic_info[graphic].double_movement && !dx && !dy)
1518 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1519 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1522 else /* border element */
1524 graphic = el2img(element);
1525 frame = getGraphicAnimationFrame(graphic, -1);
1528 if (element == EL_EXPANDABLE_WALL)
1530 boolean left_stopped = FALSE, right_stopped = FALSE;
1532 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1533 left_stopped = TRUE;
1534 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1535 right_stopped = TRUE;
1537 if (left_stopped && right_stopped)
1539 else if (left_stopped)
1541 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1542 frame = graphic_info[graphic].anim_frames - 1;
1544 else if (right_stopped)
1546 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1547 frame = graphic_info[graphic].anim_frames - 1;
1552 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1553 else if (mask_mode == USE_MASKING)
1554 DrawGraphicThruMask(x, y, graphic, frame);
1556 DrawGraphic(x, y, graphic, frame);
1559 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1560 int cut_mode, int mask_mode)
1562 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1563 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1564 cut_mode, mask_mode);
1567 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1570 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1573 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1576 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1579 void DrawLevelElementThruMask(int x, int y, int element)
1581 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1584 void DrawLevelFieldThruMask(int x, int y)
1586 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1589 /* !!! implementation of quicksand is totally broken !!! */
1590 #define IS_CRUMBLED_TILE(x, y, e) \
1591 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1592 !IS_MOVING(x, y) || \
1593 (e) == EL_QUICKSAND_EMPTYING || \
1594 (e) == EL_QUICKSAND_FAST_EMPTYING))
1596 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1601 int width, height, cx, cy;
1602 int sx = SCREENX(x), sy = SCREENY(y);
1603 int crumbled_border_size = graphic_info[graphic].border_size;
1606 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1608 for (i = 1; i < 4; i++)
1610 int dxx = (i & 1 ? dx : 0);
1611 int dyy = (i & 2 ? dy : 0);
1614 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1617 /* check if neighbour field is of same crumble type */
1618 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1619 graphic_info[graphic].class ==
1620 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1622 /* return if check prevents inner corner */
1623 if (same == (dxx == dx && dyy == dy))
1627 /* if we reach this point, we have an inner corner */
1629 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1631 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1632 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1633 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1634 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1636 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1637 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1640 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1645 int width, height, bx, by, cx, cy;
1646 int sx = SCREENX(x), sy = SCREENY(y);
1647 int crumbled_border_size = graphic_info[graphic].border_size;
1648 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1649 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1652 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1654 /* draw simple, sloppy, non-corner-accurate crumbled border */
1656 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1657 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1658 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1659 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1661 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1662 FX + sx * TILEX_VAR + cx,
1663 FY + sy * TILEY_VAR + cy);
1665 /* (remaining middle border part must be at least as big as corner part) */
1666 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1667 crumbled_border_size >= TILESIZE / 3)
1670 /* correct corners of crumbled border, if needed */
1672 for (i = -1; i <= 1; i += 2)
1674 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1675 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1676 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1679 /* check if neighbour field is of same crumble type */
1680 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1681 graphic_info[graphic].class ==
1682 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1684 /* no crumbled corner, but continued crumbled border */
1686 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1687 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1688 int b1 = (i == 1 ? crumbled_border_size_var :
1689 TILESIZE_VAR - 2 * crumbled_border_size_var);
1691 width = crumbled_border_size_var;
1692 height = crumbled_border_size_var;
1694 if (dir == 1 || dir == 2)
1709 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1711 FX + sx * TILEX_VAR + cx,
1712 FY + sy * TILEY_VAR + cy);
1717 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1719 int sx = SCREENX(x), sy = SCREENY(y);
1722 static int xy[4][2] =
1730 if (!IN_LEV_FIELD(x, y))
1733 element = TILE_GFX_ELEMENT(x, y);
1735 /* crumble field itself */
1736 if (IS_CRUMBLED_TILE(x, y, element))
1738 if (!IN_SCR_FIELD(sx, sy))
1741 for (i = 0; i < 4; i++)
1743 int xx = x + xy[i][0];
1744 int yy = y + xy[i][1];
1746 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1749 /* check if neighbour field is of same crumble type */
1750 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1751 graphic_info[graphic].class ==
1752 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1755 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1758 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1759 graphic_info[graphic].anim_frames == 2)
1761 for (i = 0; i < 4; i++)
1763 int dx = (i & 1 ? +1 : -1);
1764 int dy = (i & 2 ? +1 : -1);
1766 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1770 MarkTileDirty(sx, sy);
1772 else /* center field not crumbled -- crumble neighbour fields */
1774 for (i = 0; i < 4; i++)
1776 int xx = x + xy[i][0];
1777 int yy = y + xy[i][1];
1778 int sxx = sx + xy[i][0];
1779 int syy = sy + xy[i][1];
1781 if (!IN_LEV_FIELD(xx, yy) ||
1782 !IN_SCR_FIELD(sxx, syy))
1785 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1788 element = TILE_GFX_ELEMENT(xx, yy);
1790 if (!IS_CRUMBLED_TILE(xx, yy, element))
1793 graphic = el_act2crm(element, ACTION_DEFAULT);
1795 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1797 MarkTileDirty(sxx, syy);
1802 void DrawLevelFieldCrumbled(int x, int y)
1806 if (!IN_LEV_FIELD(x, y))
1809 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1810 GfxElement[x][y] != EL_UNDEFINED &&
1811 GFX_CRUMBLED(GfxElement[x][y]))
1813 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1818 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1820 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1823 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1826 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1827 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1828 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1829 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1830 int sx = SCREENX(x), sy = SCREENY(y);
1832 DrawGraphic(sx, sy, graphic1, frame1);
1833 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1836 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1838 int sx = SCREENX(x), sy = SCREENY(y);
1839 static int xy[4][2] =
1848 for (i = 0; i < 4; i++)
1850 int xx = x + xy[i][0];
1851 int yy = y + xy[i][1];
1852 int sxx = sx + xy[i][0];
1853 int syy = sy + xy[i][1];
1855 if (!IN_LEV_FIELD(xx, yy) ||
1856 !IN_SCR_FIELD(sxx, syy) ||
1857 !GFX_CRUMBLED(Feld[xx][yy]) ||
1861 DrawLevelField(xx, yy);
1865 static int getBorderElement(int x, int y)
1869 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1870 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1871 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1872 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1873 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1874 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1875 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1877 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1878 int steel_position = (x == -1 && y == -1 ? 0 :
1879 x == lev_fieldx && y == -1 ? 1 :
1880 x == -1 && y == lev_fieldy ? 2 :
1881 x == lev_fieldx && y == lev_fieldy ? 3 :
1882 x == -1 || x == lev_fieldx ? 4 :
1883 y == -1 || y == lev_fieldy ? 5 : 6);
1885 return border[steel_position][steel_type];
1888 void DrawScreenElement(int x, int y, int element)
1890 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1891 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1894 void DrawLevelElement(int x, int y, int element)
1896 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1897 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1900 void DrawScreenField(int x, int y)
1902 int lx = LEVELX(x), ly = LEVELY(y);
1903 int element, content;
1905 if (!IN_LEV_FIELD(lx, ly))
1907 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1910 element = getBorderElement(lx, ly);
1912 DrawScreenElement(x, y, element);
1917 element = Feld[lx][ly];
1918 content = Store[lx][ly];
1920 if (IS_MOVING(lx, ly))
1922 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1923 boolean cut_mode = NO_CUTTING;
1925 if (element == EL_QUICKSAND_EMPTYING ||
1926 element == EL_QUICKSAND_FAST_EMPTYING ||
1927 element == EL_MAGIC_WALL_EMPTYING ||
1928 element == EL_BD_MAGIC_WALL_EMPTYING ||
1929 element == EL_DC_MAGIC_WALL_EMPTYING ||
1930 element == EL_AMOEBA_DROPPING)
1931 cut_mode = CUT_ABOVE;
1932 else if (element == EL_QUICKSAND_FILLING ||
1933 element == EL_QUICKSAND_FAST_FILLING ||
1934 element == EL_MAGIC_WALL_FILLING ||
1935 element == EL_BD_MAGIC_WALL_FILLING ||
1936 element == EL_DC_MAGIC_WALL_FILLING)
1937 cut_mode = CUT_BELOW;
1939 if (cut_mode == CUT_ABOVE)
1940 DrawScreenElement(x, y, element);
1942 DrawScreenElement(x, y, EL_EMPTY);
1945 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1946 else if (cut_mode == NO_CUTTING)
1947 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1950 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1952 if (cut_mode == CUT_BELOW &&
1953 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1954 DrawLevelElement(lx, ly + 1, element);
1957 if (content == EL_ACID)
1959 int dir = MovDir[lx][ly];
1960 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1961 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1963 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1966 else if (IS_BLOCKED(lx, ly))
1971 boolean cut_mode = NO_CUTTING;
1972 int element_old, content_old;
1974 Blocked2Moving(lx, ly, &oldx, &oldy);
1977 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1978 MovDir[oldx][oldy] == MV_RIGHT);
1980 element_old = Feld[oldx][oldy];
1981 content_old = Store[oldx][oldy];
1983 if (element_old == EL_QUICKSAND_EMPTYING ||
1984 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1985 element_old == EL_MAGIC_WALL_EMPTYING ||
1986 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1987 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1988 element_old == EL_AMOEBA_DROPPING)
1989 cut_mode = CUT_ABOVE;
1991 DrawScreenElement(x, y, EL_EMPTY);
1994 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1996 else if (cut_mode == NO_CUTTING)
1997 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2000 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2003 else if (IS_DRAWABLE(element))
2004 DrawScreenElement(x, y, element);
2006 DrawScreenElement(x, y, EL_EMPTY);
2009 void DrawLevelField(int x, int y)
2011 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2012 DrawScreenField(SCREENX(x), SCREENY(y));
2013 else if (IS_MOVING(x, y))
2017 Moving2Blocked(x, y, &newx, &newy);
2018 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2019 DrawScreenField(SCREENX(newx), SCREENY(newy));
2021 else if (IS_BLOCKED(x, y))
2025 Blocked2Moving(x, y, &oldx, &oldy);
2026 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2027 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2031 void DrawSizedElement(int x, int y, int element, int tilesize)
2035 graphic = el2edimg(element);
2036 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2039 void DrawMiniElement(int x, int y, int element)
2043 graphic = el2edimg(element);
2044 DrawMiniGraphic(x, y, graphic);
2047 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2050 int x = sx + scroll_x, y = sy + scroll_y;
2052 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2053 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2054 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2055 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2057 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2060 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2062 int x = sx + scroll_x, y = sy + scroll_y;
2064 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2065 DrawMiniElement(sx, sy, EL_EMPTY);
2066 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2067 DrawMiniElement(sx, sy, Feld[x][y]);
2069 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2072 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2073 int x, int y, int xsize, int ysize,
2074 int tile_width, int tile_height)
2078 int dst_x = startx + x * tile_width;
2079 int dst_y = starty + y * tile_height;
2080 int width = graphic_info[graphic].width;
2081 int height = graphic_info[graphic].height;
2082 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2083 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2084 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2085 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2086 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2087 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2088 boolean draw_masked = graphic_info[graphic].draw_masked;
2090 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2092 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2094 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2098 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2099 inner_sx + (x - 1) * tile_width % inner_width);
2100 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2101 inner_sy + (y - 1) * tile_height % inner_height);
2104 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2107 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2111 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2112 int x, int y, int xsize, int ysize, int font_nr)
2114 int font_width = getFontWidth(font_nr);
2115 int font_height = getFontHeight(font_nr);
2117 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2118 font_width, font_height);
2121 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2123 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2124 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2125 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2126 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2127 boolean no_delay = (tape.warp_forward);
2128 unsigned int anim_delay = 0;
2129 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2130 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2131 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2132 int font_width = getFontWidth(font_nr);
2133 int font_height = getFontHeight(font_nr);
2134 int max_xsize = level.envelope[envelope_nr].xsize;
2135 int max_ysize = level.envelope[envelope_nr].ysize;
2136 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2137 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2138 int xend = max_xsize;
2139 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2140 int xstep = (xstart < xend ? 1 : 0);
2141 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2143 int end = MAX(xend - xstart, yend - ystart);
2146 for (i = start; i <= end; i++)
2148 int last_frame = end; // last frame of this "for" loop
2149 int x = xstart + i * xstep;
2150 int y = ystart + i * ystep;
2151 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2152 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2153 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2154 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2157 SetDrawtoField(DRAW_FIELDBUFFER);
2159 BlitScreenToBitmap(backbuffer);
2161 SetDrawtoField(DRAW_BACKBUFFER);
2163 for (yy = 0; yy < ysize; yy++)
2164 for (xx = 0; xx < xsize; xx++)
2165 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2167 DrawTextBuffer(sx + font_width, sy + font_height,
2168 level.envelope[envelope_nr].text, font_nr, max_xsize,
2169 xsize - 2, ysize - 2, 0, mask_mode,
2170 level.envelope[envelope_nr].autowrap,
2171 level.envelope[envelope_nr].centered, FALSE);
2173 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2176 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2180 void ShowEnvelope(int envelope_nr)
2182 int element = EL_ENVELOPE_1 + envelope_nr;
2183 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2184 int sound_opening = element_info[element].sound[ACTION_OPENING];
2185 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2186 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2187 boolean no_delay = (tape.warp_forward);
2188 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2189 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2190 int anim_mode = graphic_info[graphic].anim_mode;
2191 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2192 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2194 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2196 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2198 if (anim_mode == ANIM_DEFAULT)
2199 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2201 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2204 Delay(wait_delay_value);
2206 WaitForEventToContinue();
2208 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2210 if (anim_mode != ANIM_NONE)
2211 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2213 if (anim_mode == ANIM_DEFAULT)
2214 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2216 game.envelope_active = FALSE;
2218 SetDrawtoField(DRAW_FIELDBUFFER);
2220 redraw_mask |= REDRAW_FIELD;
2224 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2226 int border_size = request.border_size;
2227 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2228 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2229 int sx = sx_center - request.width / 2;
2230 int sy = sy_center - request.height / 2;
2232 if (add_border_size)
2242 void DrawEnvelopeRequest(char *text)
2244 char *text_final = text;
2245 char *text_door_style = NULL;
2246 int graphic = IMG_BACKGROUND_REQUEST;
2247 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2248 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2249 int font_nr = FONT_REQUEST;
2250 int font_width = getFontWidth(font_nr);
2251 int font_height = getFontHeight(font_nr);
2252 int border_size = request.border_size;
2253 int line_spacing = request.line_spacing;
2254 int line_height = font_height + line_spacing;
2255 int text_width = request.width - 2 * border_size;
2256 int text_height = request.height - 2 * border_size;
2257 int line_length = text_width / font_width;
2258 int max_lines = text_height / line_height;
2259 int width = request.width;
2260 int height = request.height;
2261 int tile_size = request.step_offset;
2262 int x_steps = width / tile_size;
2263 int y_steps = height / tile_size;
2267 if (request.wrap_single_words)
2269 char *src_text_ptr, *dst_text_ptr;
2271 text_door_style = checked_malloc(2 * strlen(text) + 1);
2273 src_text_ptr = text;
2274 dst_text_ptr = text_door_style;
2276 while (*src_text_ptr)
2278 if (*src_text_ptr == ' ' ||
2279 *src_text_ptr == '?' ||
2280 *src_text_ptr == '!')
2281 *dst_text_ptr++ = '\n';
2283 if (*src_text_ptr != ' ')
2284 *dst_text_ptr++ = *src_text_ptr;
2289 *dst_text_ptr = '\0';
2291 text_final = text_door_style;
2294 setRequestPosition(&sx, &sy, FALSE);
2296 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2298 for (y = 0; y < y_steps; y++)
2299 for (x = 0; x < x_steps; x++)
2300 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2301 x, y, x_steps, y_steps,
2302 tile_size, tile_size);
2304 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2305 line_length, -1, max_lines, line_spacing, mask_mode,
2306 request.autowrap, request.centered, FALSE);
2308 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2309 RedrawGadget(tool_gadget[i]);
2311 // store readily prepared envelope request for later use when animating
2312 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2314 if (text_door_style)
2315 free(text_door_style);
2318 void AnimateEnvelopeRequest(int anim_mode, int action)
2320 int graphic = IMG_BACKGROUND_REQUEST;
2321 boolean draw_masked = graphic_info[graphic].draw_masked;
2322 int delay_value_normal = request.step_delay;
2323 int delay_value_fast = delay_value_normal / 2;
2324 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2325 boolean no_delay = (tape.warp_forward);
2326 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2327 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2328 unsigned int anim_delay = 0;
2330 int width = request.width;
2331 int height = request.height;
2332 int tile_size = request.step_offset;
2333 int max_xsize = width / tile_size;
2334 int max_ysize = height / tile_size;
2335 int max_xsize_inner = max_xsize - 2;
2336 int max_ysize_inner = max_ysize - 2;
2338 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2339 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2340 int xend = max_xsize_inner;
2341 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2342 int xstep = (xstart < xend ? 1 : 0);
2343 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2345 int end = MAX(xend - xstart, yend - ystart);
2348 if (setup.quick_doors)
2356 if (action == ACTION_OPENING)
2357 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2358 else if (action == ACTION_CLOSING)
2359 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2362 for (i = start; i <= end; i++)
2364 int last_frame = end; // last frame of this "for" loop
2365 int x = xstart + i * xstep;
2366 int y = ystart + i * ystep;
2367 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2368 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2369 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2370 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2371 int src_x = sx_center - width / 2;
2372 int src_y = sy_center - height / 2;
2373 int dst_x = sx_center - xsize * tile_size / 2;
2374 int dst_y = sy_center - ysize * tile_size / 2;
2375 int xsize_size_left = (xsize - 1) * tile_size;
2376 int ysize_size_top = (ysize - 1) * tile_size;
2377 int max_xsize_pos = (max_xsize - 1) * tile_size;
2378 int max_ysize_pos = (max_ysize - 1) * tile_size;
2381 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2383 for (yy = 0; yy < 2; yy++)
2385 for (xx = 0; xx < 2; xx++)
2387 int src_xx = src_x + xx * max_xsize_pos;
2388 int src_yy = src_y + yy * max_ysize_pos;
2389 int dst_xx = dst_x + xx * xsize_size_left;
2390 int dst_yy = dst_y + yy * ysize_size_top;
2391 int xx_size = (xx ? tile_size : xsize_size_left);
2392 int yy_size = (yy ? tile_size : ysize_size_top);
2395 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2396 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2398 BlitBitmap(bitmap_db_cross, backbuffer,
2399 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2403 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2408 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2413 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2415 int last_game_status = game_status; /* save current game status */
2416 int graphic = IMG_BACKGROUND_REQUEST;
2417 int sound_opening = SND_REQUEST_OPENING;
2418 int sound_closing = SND_REQUEST_CLOSING;
2419 int anim_mode = graphic_info[graphic].anim_mode;
2420 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2421 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2423 if (game_status == GAME_MODE_PLAYING)
2424 BlitScreenToBitmap(backbuffer);
2426 SetDrawtoField(DRAW_BACKBUFFER);
2428 // SetDrawBackgroundMask(REDRAW_NONE);
2430 if (action == ACTION_OPENING)
2432 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2434 if (req_state & REQ_ASK)
2436 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2437 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2439 else if (req_state & REQ_CONFIRM)
2441 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2443 else if (req_state & REQ_PLAYER)
2445 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2446 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2447 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2448 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2451 DrawEnvelopeRequest(text);
2453 if (game_status != GAME_MODE_MAIN)
2457 /* force DOOR font inside door area */
2458 game_status = GAME_MODE_PSEUDO_DOOR;
2460 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2462 if (action == ACTION_OPENING)
2464 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2466 if (anim_mode == ANIM_DEFAULT)
2467 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2469 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2473 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2475 if (anim_mode != ANIM_NONE)
2476 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2478 if (anim_mode == ANIM_DEFAULT)
2479 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2482 game.envelope_active = FALSE;
2484 game_status = last_game_status; /* restore current game status */
2486 if (action == ACTION_CLOSING)
2488 if (game_status != GAME_MODE_MAIN)
2491 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2494 // SetDrawBackgroundMask(last_draw_background_mask);
2496 redraw_mask |= REDRAW_FIELD;
2498 if (game_status == GAME_MODE_MAIN)
2503 if (action == ACTION_CLOSING &&
2504 game_status == GAME_MODE_PLAYING &&
2505 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2506 SetDrawtoField(DRAW_FIELDBUFFER);
2509 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2513 int graphic = el2preimg(element);
2515 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2516 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2519 void DrawLevel(int draw_background_mask)
2523 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2524 SetDrawBackgroundMask(draw_background_mask);
2528 for (x = BX1; x <= BX2; x++)
2529 for (y = BY1; y <= BY2; y++)
2530 DrawScreenField(x, y);
2532 redraw_mask |= REDRAW_FIELD;
2535 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2540 for (x = 0; x < size_x; x++)
2541 for (y = 0; y < size_y; y++)
2542 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2544 redraw_mask |= REDRAW_FIELD;
2547 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2551 for (x = 0; x < size_x; x++)
2552 for (y = 0; y < size_y; y++)
2553 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2555 redraw_mask |= REDRAW_FIELD;
2558 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2560 boolean show_level_border = (BorderElement != EL_EMPTY);
2561 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2562 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2563 int tile_size = preview.tile_size;
2564 int preview_width = preview.xsize * tile_size;
2565 int preview_height = preview.ysize * tile_size;
2566 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2567 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2568 int real_preview_width = real_preview_xsize * tile_size;
2569 int real_preview_height = real_preview_ysize * tile_size;
2570 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2571 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2574 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2577 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2579 dst_x += (preview_width - real_preview_width) / 2;
2580 dst_y += (preview_height - real_preview_height) / 2;
2582 for (x = 0; x < real_preview_xsize; x++)
2584 for (y = 0; y < real_preview_ysize; y++)
2586 int lx = from_x + x + (show_level_border ? -1 : 0);
2587 int ly = from_y + y + (show_level_border ? -1 : 0);
2588 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2589 getBorderElement(lx, ly));
2591 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2592 element, tile_size);
2596 redraw_mask |= REDRAW_MICROLEVEL;
2599 #define MICROLABEL_EMPTY 0
2600 #define MICROLABEL_LEVEL_NAME 1
2601 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2602 #define MICROLABEL_LEVEL_AUTHOR 3
2603 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2604 #define MICROLABEL_IMPORTED_FROM 5
2605 #define MICROLABEL_IMPORTED_BY_HEAD 6
2606 #define MICROLABEL_IMPORTED_BY 7
2608 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2610 int max_text_width = SXSIZE;
2611 int font_width = getFontWidth(font_nr);
2613 if (pos->align == ALIGN_CENTER)
2614 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2615 else if (pos->align == ALIGN_RIGHT)
2616 max_text_width = pos->x;
2618 max_text_width = SXSIZE - pos->x;
2620 return max_text_width / font_width;
2623 static void DrawPreviewLevelLabelExt(int mode)
2625 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2626 char label_text[MAX_OUTPUT_LINESIZE + 1];
2627 int max_len_label_text;
2628 int font_nr = pos->font;
2631 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2634 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2635 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2636 mode == MICROLABEL_IMPORTED_BY_HEAD)
2637 font_nr = pos->font_alt;
2639 max_len_label_text = getMaxTextLength(pos, font_nr);
2641 if (pos->size != -1)
2642 max_len_label_text = pos->size;
2644 for (i = 0; i < max_len_label_text; i++)
2645 label_text[i] = ' ';
2646 label_text[max_len_label_text] = '\0';
2648 if (strlen(label_text) > 0)
2649 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2652 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2653 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2654 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2655 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2656 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2657 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2658 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2659 max_len_label_text);
2660 label_text[max_len_label_text] = '\0';
2662 if (strlen(label_text) > 0)
2663 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2665 redraw_mask |= REDRAW_MICROLEVEL;
2668 static void DrawPreviewLevelExt(boolean restart)
2670 static unsigned int scroll_delay = 0;
2671 static unsigned int label_delay = 0;
2672 static int from_x, from_y, scroll_direction;
2673 static int label_state, label_counter;
2674 unsigned int scroll_delay_value = preview.step_delay;
2675 boolean show_level_border = (BorderElement != EL_EMPTY);
2676 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2677 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2678 int last_game_status = game_status; /* save current game status */
2685 if (preview.anim_mode == ANIM_CENTERED)
2687 if (level_xsize > preview.xsize)
2688 from_x = (level_xsize - preview.xsize) / 2;
2689 if (level_ysize > preview.ysize)
2690 from_y = (level_ysize - preview.ysize) / 2;
2693 from_x += preview.xoffset;
2694 from_y += preview.yoffset;
2696 scroll_direction = MV_RIGHT;
2700 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2701 DrawPreviewLevelLabelExt(label_state);
2703 /* initialize delay counters */
2704 DelayReached(&scroll_delay, 0);
2705 DelayReached(&label_delay, 0);
2707 if (leveldir_current->name)
2709 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2710 char label_text[MAX_OUTPUT_LINESIZE + 1];
2711 int font_nr = pos->font;
2712 int max_len_label_text = getMaxTextLength(pos, font_nr);
2714 if (pos->size != -1)
2715 max_len_label_text = pos->size;
2717 strncpy(label_text, leveldir_current->name, max_len_label_text);
2718 label_text[max_len_label_text] = '\0';
2720 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2721 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2724 game_status = last_game_status; /* restore current game status */
2729 /* scroll preview level, if needed */
2730 if (preview.anim_mode != ANIM_NONE &&
2731 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2732 DelayReached(&scroll_delay, scroll_delay_value))
2734 switch (scroll_direction)
2739 from_x -= preview.step_offset;
2740 from_x = (from_x < 0 ? 0 : from_x);
2743 scroll_direction = MV_UP;
2747 if (from_x < level_xsize - preview.xsize)
2749 from_x += preview.step_offset;
2750 from_x = (from_x > level_xsize - preview.xsize ?
2751 level_xsize - preview.xsize : from_x);
2754 scroll_direction = MV_DOWN;
2760 from_y -= preview.step_offset;
2761 from_y = (from_y < 0 ? 0 : from_y);
2764 scroll_direction = MV_RIGHT;
2768 if (from_y < level_ysize - preview.ysize)
2770 from_y += preview.step_offset;
2771 from_y = (from_y > level_ysize - preview.ysize ?
2772 level_ysize - preview.ysize : from_y);
2775 scroll_direction = MV_LEFT;
2782 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2785 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2786 /* redraw micro level label, if needed */
2787 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2788 !strEqual(level.author, ANONYMOUS_NAME) &&
2789 !strEqual(level.author, leveldir_current->name) &&
2790 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2792 int max_label_counter = 23;
2794 if (leveldir_current->imported_from != NULL &&
2795 strlen(leveldir_current->imported_from) > 0)
2796 max_label_counter += 14;
2797 if (leveldir_current->imported_by != NULL &&
2798 strlen(leveldir_current->imported_by) > 0)
2799 max_label_counter += 14;
2801 label_counter = (label_counter + 1) % max_label_counter;
2802 label_state = (label_counter >= 0 && label_counter <= 7 ?
2803 MICROLABEL_LEVEL_NAME :
2804 label_counter >= 9 && label_counter <= 12 ?
2805 MICROLABEL_LEVEL_AUTHOR_HEAD :
2806 label_counter >= 14 && label_counter <= 21 ?
2807 MICROLABEL_LEVEL_AUTHOR :
2808 label_counter >= 23 && label_counter <= 26 ?
2809 MICROLABEL_IMPORTED_FROM_HEAD :
2810 label_counter >= 28 && label_counter <= 35 ?
2811 MICROLABEL_IMPORTED_FROM :
2812 label_counter >= 37 && label_counter <= 40 ?
2813 MICROLABEL_IMPORTED_BY_HEAD :
2814 label_counter >= 42 && label_counter <= 49 ?
2815 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2817 if (leveldir_current->imported_from == NULL &&
2818 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2819 label_state == MICROLABEL_IMPORTED_FROM))
2820 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2821 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2823 DrawPreviewLevelLabelExt(label_state);
2826 game_status = last_game_status; /* restore current game status */
2829 void DrawPreviewLevelInitial()
2831 DrawPreviewLevelExt(TRUE);
2834 void DrawPreviewLevelAnimation()
2836 DrawPreviewLevelExt(FALSE);
2839 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2840 int graphic, int sync_frame, int mask_mode)
2842 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2844 if (mask_mode == USE_MASKING)
2845 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2847 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2850 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2851 int graphic, int sync_frame,
2854 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2856 if (mask_mode == USE_MASKING)
2857 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2859 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2862 inline void DrawGraphicAnimation(int x, int y, int graphic)
2864 int lx = LEVELX(x), ly = LEVELY(y);
2866 if (!IN_SCR_FIELD(x, y))
2869 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2870 graphic, GfxFrame[lx][ly], NO_MASKING);
2872 MarkTileDirty(x, y);
2875 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2877 int lx = LEVELX(x), ly = LEVELY(y);
2879 if (!IN_SCR_FIELD(x, y))
2882 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2883 graphic, GfxFrame[lx][ly], NO_MASKING);
2884 MarkTileDirty(x, y);
2887 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2889 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2892 void DrawLevelElementAnimation(int x, int y, int element)
2894 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2896 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2899 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2901 int sx = SCREENX(x), sy = SCREENY(y);
2903 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2906 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2909 DrawGraphicAnimation(sx, sy, graphic);
2912 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2913 DrawLevelFieldCrumbled(x, y);
2915 if (GFX_CRUMBLED(Feld[x][y]))
2916 DrawLevelFieldCrumbled(x, y);
2920 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2922 int sx = SCREENX(x), sy = SCREENY(y);
2925 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2928 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2930 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2933 DrawGraphicAnimation(sx, sy, graphic);
2935 if (GFX_CRUMBLED(element))
2936 DrawLevelFieldCrumbled(x, y);
2939 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2941 if (player->use_murphy)
2943 /* this works only because currently only one player can be "murphy" ... */
2944 static int last_horizontal_dir = MV_LEFT;
2945 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2947 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2948 last_horizontal_dir = move_dir;
2950 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2952 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2954 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2960 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2963 static boolean equalGraphics(int graphic1, int graphic2)
2965 struct GraphicInfo *g1 = &graphic_info[graphic1];
2966 struct GraphicInfo *g2 = &graphic_info[graphic2];
2968 return (g1->bitmap == g2->bitmap &&
2969 g1->src_x == g2->src_x &&
2970 g1->src_y == g2->src_y &&
2971 g1->anim_frames == g2->anim_frames &&
2972 g1->anim_delay == g2->anim_delay &&
2973 g1->anim_mode == g2->anim_mode);
2976 void DrawAllPlayers()
2980 for (i = 0; i < MAX_PLAYERS; i++)
2981 if (stored_player[i].active)
2982 DrawPlayer(&stored_player[i]);
2985 void DrawPlayerField(int x, int y)
2987 if (!IS_PLAYER(x, y))
2990 DrawPlayer(PLAYERINFO(x, y));
2993 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
2995 void DrawPlayer(struct PlayerInfo *player)
2997 int jx = player->jx;
2998 int jy = player->jy;
2999 int move_dir = player->MovDir;
3000 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3001 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3002 int last_jx = (player->is_moving ? jx - dx : jx);
3003 int last_jy = (player->is_moving ? jy - dy : jy);
3004 int next_jx = jx + dx;
3005 int next_jy = jy + dy;
3006 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3007 boolean player_is_opaque = FALSE;
3008 int sx = SCREENX(jx), sy = SCREENY(jy);
3009 int sxx = 0, syy = 0;
3010 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3012 int action = ACTION_DEFAULT;
3013 int last_player_graphic = getPlayerGraphic(player, move_dir);
3014 int last_player_frame = player->Frame;
3017 /* GfxElement[][] is set to the element the player is digging or collecting;
3018 remove also for off-screen player if the player is not moving anymore */
3019 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3020 GfxElement[jx][jy] = EL_UNDEFINED;
3022 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3026 if (!IN_LEV_FIELD(jx, jy))
3028 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3029 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3030 printf("DrawPlayerField(): This should never happen!\n");
3035 if (element == EL_EXPLOSION)
3038 action = (player->is_pushing ? ACTION_PUSHING :
3039 player->is_digging ? ACTION_DIGGING :
3040 player->is_collecting ? ACTION_COLLECTING :
3041 player->is_moving ? ACTION_MOVING :
3042 player->is_snapping ? ACTION_SNAPPING :
3043 player->is_dropping ? ACTION_DROPPING :
3044 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3046 if (player->is_waiting)
3047 move_dir = player->dir_waiting;
3049 InitPlayerGfxAnimation(player, action, move_dir);
3051 /* ----------------------------------------------------------------------- */
3052 /* draw things in the field the player is leaving, if needed */
3053 /* ----------------------------------------------------------------------- */
3055 if (player->is_moving)
3057 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3059 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3061 if (last_element == EL_DYNAMITE_ACTIVE ||
3062 last_element == EL_EM_DYNAMITE_ACTIVE ||
3063 last_element == EL_SP_DISK_RED_ACTIVE)
3064 DrawDynamite(last_jx, last_jy);
3066 DrawLevelFieldThruMask(last_jx, last_jy);
3068 else if (last_element == EL_DYNAMITE_ACTIVE ||
3069 last_element == EL_EM_DYNAMITE_ACTIVE ||
3070 last_element == EL_SP_DISK_RED_ACTIVE)
3071 DrawDynamite(last_jx, last_jy);
3073 /* !!! this is not enough to prevent flickering of players which are
3074 moving next to each others without a free tile between them -- this
3075 can only be solved by drawing all players layer by layer (first the
3076 background, then the foreground etc.) !!! => TODO */
3077 else if (!IS_PLAYER(last_jx, last_jy))
3078 DrawLevelField(last_jx, last_jy);
3081 DrawLevelField(last_jx, last_jy);
3084 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3085 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3088 if (!IN_SCR_FIELD(sx, sy))
3091 /* ----------------------------------------------------------------------- */
3092 /* draw things behind the player, if needed */
3093 /* ----------------------------------------------------------------------- */
3096 DrawLevelElement(jx, jy, Back[jx][jy]);
3097 else if (IS_ACTIVE_BOMB(element))
3098 DrawLevelElement(jx, jy, EL_EMPTY);
3101 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3103 int old_element = GfxElement[jx][jy];
3104 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3105 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3107 if (GFX_CRUMBLED(old_element))
3108 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3110 DrawGraphic(sx, sy, old_graphic, frame);
3112 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3113 player_is_opaque = TRUE;
3117 GfxElement[jx][jy] = EL_UNDEFINED;
3119 /* make sure that pushed elements are drawn with correct frame rate */
3120 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3122 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3123 GfxFrame[jx][jy] = player->StepFrame;
3125 DrawLevelField(jx, jy);
3129 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3130 /* ----------------------------------------------------------------------- */
3131 /* draw player himself */
3132 /* ----------------------------------------------------------------------- */
3134 graphic = getPlayerGraphic(player, move_dir);
3136 /* in the case of changed player action or direction, prevent the current
3137 animation frame from being restarted for identical animations */
3138 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3139 player->Frame = last_player_frame;
3141 frame = getGraphicAnimationFrame(graphic, player->Frame);
3145 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3146 sxx = player->GfxPos;
3148 syy = player->GfxPos;
3151 if (player_is_opaque)
3152 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3154 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3156 if (SHIELD_ON(player))
3158 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3159 IMG_SHIELD_NORMAL_ACTIVE);
3160 int frame = getGraphicAnimationFrame(graphic, -1);
3162 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3166 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3169 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3170 sxx = player->GfxPos;
3172 syy = player->GfxPos;
3176 /* ----------------------------------------------------------------------- */
3177 /* draw things the player is pushing, if needed */
3178 /* ----------------------------------------------------------------------- */
3180 if (player->is_pushing && player->is_moving)
3182 int px = SCREENX(jx), py = SCREENY(jy);
3183 int pxx = (TILEX - ABS(sxx)) * dx;
3184 int pyy = (TILEY - ABS(syy)) * dy;
3185 int gfx_frame = GfxFrame[jx][jy];
3191 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3193 element = Feld[next_jx][next_jy];
3194 gfx_frame = GfxFrame[next_jx][next_jy];
3197 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3199 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3200 frame = getGraphicAnimationFrame(graphic, sync_frame);
3202 /* draw background element under pushed element (like the Sokoban field) */
3203 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3205 /* this allows transparent pushing animation over non-black background */
3208 DrawLevelElement(jx, jy, Back[jx][jy]);
3210 DrawLevelElement(jx, jy, EL_EMPTY);
3212 if (Back[next_jx][next_jy])
3213 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3215 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3217 else if (Back[next_jx][next_jy])
3218 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3221 /* do not draw (EM style) pushing animation when pushing is finished */
3222 /* (two-tile animations usually do not contain start and end frame) */
3223 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3224 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3226 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3228 /* masked drawing is needed for EMC style (double) movement graphics */
3229 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3230 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3234 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3235 /* ----------------------------------------------------------------------- */
3236 /* draw player himself */
3237 /* ----------------------------------------------------------------------- */
3239 graphic = getPlayerGraphic(player, move_dir);
3241 /* in the case of changed player action or direction, prevent the current
3242 animation frame from being restarted for identical animations */
3243 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3244 player->Frame = last_player_frame;
3246 frame = getGraphicAnimationFrame(graphic, player->Frame);
3250 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3251 sxx = player->GfxPos;
3253 syy = player->GfxPos;
3256 if (player_is_opaque)
3257 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3259 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3261 if (SHIELD_ON(player))
3263 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3264 IMG_SHIELD_NORMAL_ACTIVE);
3265 int frame = getGraphicAnimationFrame(graphic, -1);
3267 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3271 /* ----------------------------------------------------------------------- */
3272 /* draw things in front of player (active dynamite or dynabombs) */
3273 /* ----------------------------------------------------------------------- */
3275 if (IS_ACTIVE_BOMB(element))
3277 graphic = el2img(element);
3278 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3280 if (game.emulation == EMU_SUPAPLEX)
3281 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3283 DrawGraphicThruMask(sx, sy, graphic, frame);
3286 if (player_is_moving && last_element == EL_EXPLOSION)
3288 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3289 GfxElement[last_jx][last_jy] : EL_EMPTY);
3290 int graphic = el_act2img(element, ACTION_EXPLODING);
3291 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3292 int phase = ExplodePhase[last_jx][last_jy] - 1;
3293 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3296 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3299 /* ----------------------------------------------------------------------- */
3300 /* draw elements the player is just walking/passing through/under */
3301 /* ----------------------------------------------------------------------- */
3303 if (player_is_moving)
3305 /* handle the field the player is leaving ... */
3306 if (IS_ACCESSIBLE_INSIDE(last_element))
3307 DrawLevelField(last_jx, last_jy);
3308 else if (IS_ACCESSIBLE_UNDER(last_element))
3309 DrawLevelFieldThruMask(last_jx, last_jy);
3312 /* do not redraw accessible elements if the player is just pushing them */
3313 if (!player_is_moving || !player->is_pushing)
3315 /* ... and the field the player is entering */
3316 if (IS_ACCESSIBLE_INSIDE(element))
3317 DrawLevelField(jx, jy);
3318 else if (IS_ACCESSIBLE_UNDER(element))
3319 DrawLevelFieldThruMask(jx, jy);
3322 MarkTileDirty(sx, sy);
3325 /* ------------------------------------------------------------------------- */
3327 void WaitForEventToContinue()
3329 boolean still_wait = TRUE;
3331 /* simulate releasing mouse button over last gadget, if still pressed */
3333 HandleGadgets(-1, -1, 0);
3335 button_status = MB_RELEASED;
3349 case EVENT_BUTTONPRESS:
3350 case EVENT_KEYPRESS:
3354 case EVENT_KEYRELEASE:
3355 ClearPlayerAction();
3359 HandleOtherEvents(&event);
3363 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3370 /* don't eat all CPU time */
3375 #define MAX_REQUEST_LINES 13
3376 #define MAX_REQUEST_LINE_FONT1_LEN 7
3377 #define MAX_REQUEST_LINE_FONT2_LEN 10
3379 static int RequestHandleEvents(unsigned int req_state)
3381 int last_game_status = game_status; /* save current game status */
3385 button_status = MB_RELEASED;
3387 request_gadget_id = -1;
3396 while (NextValidEvent(&event))
3400 case EVENT_BUTTONPRESS:
3401 case EVENT_BUTTONRELEASE:
3402 case EVENT_MOTIONNOTIFY:
3404 if (event.type == EVENT_MOTIONNOTIFY)
3406 if (!PointerInWindow(window))
3407 continue; /* window and pointer on different screens */
3412 motion_status = TRUE;
3413 mx = ((MotionEvent *) &event)->x;
3414 my = ((MotionEvent *) &event)->y;
3418 motion_status = FALSE;
3419 mx = ((ButtonEvent *) &event)->x;
3420 my = ((ButtonEvent *) &event)->y;
3421 if (event.type == EVENT_BUTTONPRESS)
3422 button_status = ((ButtonEvent *) &event)->button;
3424 button_status = MB_RELEASED;
3427 /* this sets 'request_gadget_id' */
3428 HandleGadgets(mx, my, button_status);
3430 switch (request_gadget_id)
3432 case TOOL_CTRL_ID_YES:
3435 case TOOL_CTRL_ID_NO:
3438 case TOOL_CTRL_ID_CONFIRM:
3439 result = TRUE | FALSE;
3442 case TOOL_CTRL_ID_PLAYER_1:
3445 case TOOL_CTRL_ID_PLAYER_2:
3448 case TOOL_CTRL_ID_PLAYER_3:
3451 case TOOL_CTRL_ID_PLAYER_4:
3462 case EVENT_KEYPRESS:
3463 switch (GetEventKey((KeyEvent *)&event, TRUE))
3466 if (req_state & REQ_CONFIRM)
3471 #if defined(TARGET_SDL2)
3478 #if defined(TARGET_SDL2)
3488 if (req_state & REQ_PLAYER)
3492 case EVENT_KEYRELEASE:
3493 ClearPlayerAction();
3497 HandleOtherEvents(&event);
3502 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3504 int joy = AnyJoystick();
3506 if (joy & JOY_BUTTON_1)
3508 else if (joy & JOY_BUTTON_2)
3512 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3514 HandleGameActions();
3520 if (!PendingEvent()) /* delay only if no pending events */
3524 game_status = GAME_MODE_PSEUDO_DOOR;
3528 game_status = last_game_status; /* restore current game status */
3534 static boolean RequestDoor(char *text, unsigned int req_state)
3536 unsigned int old_door_state;
3537 int last_game_status = game_status; /* save current game status */
3538 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3539 int font_nr = FONT_TEXT_2;
3544 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3546 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3547 font_nr = FONT_TEXT_1;
3550 if (game_status == GAME_MODE_PLAYING)
3551 BlitScreenToBitmap(backbuffer);
3553 /* disable deactivated drawing when quick-loading level tape recording */
3554 if (tape.playing && tape.deactivate_display)
3555 TapeDeactivateDisplayOff(TRUE);
3557 SetMouseCursor(CURSOR_DEFAULT);
3559 #if defined(NETWORK_AVALIABLE)
3560 /* pause network game while waiting for request to answer */
3561 if (options.network &&
3562 game_status == GAME_MODE_PLAYING &&
3563 req_state & REQUEST_WAIT_FOR_INPUT)
3564 SendToServer_PausePlaying();
3567 old_door_state = GetDoorState();
3569 /* simulate releasing mouse button over last gadget, if still pressed */
3571 HandleGadgets(-1, -1, 0);
3575 /* draw released gadget before proceeding */
3578 if (old_door_state & DOOR_OPEN_1)
3580 CloseDoor(DOOR_CLOSE_1);
3582 /* save old door content */
3583 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3584 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3587 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3588 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3590 /* clear door drawing field */
3591 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3593 /* force DOOR font inside door area */
3594 game_status = GAME_MODE_PSEUDO_DOOR;
3596 /* write text for request */
3597 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3599 char text_line[max_request_line_len + 1];
3605 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3607 tc = *(text_ptr + tx);
3608 // if (!tc || tc == ' ')
3609 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3613 if ((tc == '?' || tc == '!') && tl == 0)
3623 strncpy(text_line, text_ptr, tl);
3626 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3627 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3628 text_line, font_nr);
3630 text_ptr += tl + (tc == ' ' ? 1 : 0);
3631 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3634 game_status = last_game_status; /* restore current game status */
3636 if (req_state & REQ_ASK)
3638 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3639 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3641 else if (req_state & REQ_CONFIRM)
3643 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3645 else if (req_state & REQ_PLAYER)
3647 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3648 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3649 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3650 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3653 /* copy request gadgets to door backbuffer */
3654 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3656 OpenDoor(DOOR_OPEN_1);
3658 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3660 if (game_status == GAME_MODE_PLAYING)
3662 SetPanelBackground();
3663 SetDrawBackgroundMask(REDRAW_DOOR_1);
3667 SetDrawBackgroundMask(REDRAW_FIELD);
3673 if (game_status != GAME_MODE_MAIN)
3676 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3678 // ---------- handle request buttons ----------
3679 result = RequestHandleEvents(req_state);
3681 if (game_status != GAME_MODE_MAIN)
3686 if (!(req_state & REQ_STAY_OPEN))
3688 CloseDoor(DOOR_CLOSE_1);
3690 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3691 (req_state & REQ_REOPEN))
3692 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3697 if (game_status == GAME_MODE_PLAYING)
3699 SetPanelBackground();
3700 SetDrawBackgroundMask(REDRAW_DOOR_1);
3704 SetDrawBackgroundMask(REDRAW_FIELD);
3707 #if defined(NETWORK_AVALIABLE)
3708 /* continue network game after request */
3709 if (options.network &&
3710 game_status == GAME_MODE_PLAYING &&
3711 req_state & REQUEST_WAIT_FOR_INPUT)
3712 SendToServer_ContinuePlaying();
3715 /* restore deactivated drawing when quick-loading level tape recording */
3716 if (tape.playing && tape.deactivate_display)
3717 TapeDeactivateDisplayOn();
3722 static boolean RequestEnvelope(char *text, unsigned int req_state)
3726 if (game_status == GAME_MODE_PLAYING)
3727 BlitScreenToBitmap(backbuffer);
3729 /* disable deactivated drawing when quick-loading level tape recording */
3730 if (tape.playing && tape.deactivate_display)
3731 TapeDeactivateDisplayOff(TRUE);
3733 SetMouseCursor(CURSOR_DEFAULT);
3735 #if defined(NETWORK_AVALIABLE)
3736 /* pause network game while waiting for request to answer */
3737 if (options.network &&
3738 game_status == GAME_MODE_PLAYING &&
3739 req_state & REQUEST_WAIT_FOR_INPUT)
3740 SendToServer_PausePlaying();
3743 /* simulate releasing mouse button over last gadget, if still pressed */
3745 HandleGadgets(-1, -1, 0);
3749 // (replace with setting corresponding request background)
3750 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3751 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3753 /* clear door drawing field */
3754 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3756 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3758 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3760 if (game_status == GAME_MODE_PLAYING)
3762 SetPanelBackground();
3763 SetDrawBackgroundMask(REDRAW_DOOR_1);
3767 SetDrawBackgroundMask(REDRAW_FIELD);
3773 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3775 // ---------- handle request buttons ----------
3776 result = RequestHandleEvents(req_state);
3778 if (game_status != GAME_MODE_MAIN)
3783 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3787 if (game_status == GAME_MODE_PLAYING)
3789 SetPanelBackground();
3790 SetDrawBackgroundMask(REDRAW_DOOR_1);
3794 SetDrawBackgroundMask(REDRAW_FIELD);
3797 #if defined(NETWORK_AVALIABLE)
3798 /* continue network game after request */
3799 if (options.network &&
3800 game_status == GAME_MODE_PLAYING &&
3801 req_state & REQUEST_WAIT_FOR_INPUT)
3802 SendToServer_ContinuePlaying();
3805 /* restore deactivated drawing when quick-loading level tape recording */
3806 if (tape.playing && tape.deactivate_display)
3807 TapeDeactivateDisplayOn();
3812 boolean Request(char *text, unsigned int req_state)
3814 if (global.use_envelope_request)
3815 return RequestEnvelope(text, req_state);
3817 return RequestDoor(text, req_state);
3820 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3822 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3823 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3826 if (dpo1->sort_priority != dpo2->sort_priority)
3827 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3829 compare_result = dpo1->nr - dpo2->nr;
3831 return compare_result;
3834 void InitGraphicCompatibilityInfo_Doors()
3840 struct DoorInfo *door;
3844 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3845 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3847 { -1, -1, -1, NULL }
3849 struct Rect door_rect_list[] =
3851 { DX, DY, DXSIZE, DYSIZE },
3852 { VX, VY, VXSIZE, VYSIZE }
3856 for (i = 0; doors[i].door_token != -1; i++)
3858 int door_token = doors[i].door_token;
3859 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3860 int part_1 = doors[i].part_1;
3861 int part_8 = doors[i].part_8;
3862 int part_2 = part_1 + 1;
3863 int part_3 = part_1 + 2;
3864 struct DoorInfo *door = doors[i].door;
3865 struct Rect *door_rect = &door_rect_list[door_index];
3866 boolean door_gfx_redefined = FALSE;
3868 /* check if any door part graphic definitions have been redefined */
3870 for (j = 0; door_part_controls[j].door_token != -1; j++)
3872 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3873 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3875 if (dpc->door_token == door_token && fi->redefined)
3876 door_gfx_redefined = TRUE;
3879 /* check for old-style door graphic/animation modifications */
3881 if (!door_gfx_redefined)
3883 if (door->anim_mode & ANIM_STATIC_PANEL)
3885 door->panel.step_xoffset = 0;
3886 door->panel.step_yoffset = 0;
3889 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3891 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3892 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3893 int num_door_steps, num_panel_steps;
3895 /* remove door part graphics other than the two default wings */
3897 for (j = 0; door_part_controls[j].door_token != -1; j++)
3899 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3900 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3902 if (dpc->graphic >= part_3 &&
3903 dpc->graphic <= part_8)
3907 /* set graphics and screen positions of the default wings */
3909 g_part_1->width = door_rect->width;
3910 g_part_1->height = door_rect->height;
3911 g_part_2->width = door_rect->width;
3912 g_part_2->height = door_rect->height;
3913 g_part_2->src_x = door_rect->width;
3914 g_part_2->src_y = g_part_1->src_y;
3916 door->part_2.x = door->part_1.x;
3917 door->part_2.y = door->part_1.y;
3919 if (door->width != -1)
3921 g_part_1->width = door->width;
3922 g_part_2->width = door->width;
3924 // special treatment for graphics and screen position of right wing
3925 g_part_2->src_x += door_rect->width - door->width;
3926 door->part_2.x += door_rect->width - door->width;
3929 if (door->height != -1)
3931 g_part_1->height = door->height;
3932 g_part_2->height = door->height;
3934 // special treatment for graphics and screen position of bottom wing
3935 g_part_2->src_y += door_rect->height - door->height;
3936 door->part_2.y += door_rect->height - door->height;
3939 /* set animation delays for the default wings and panels */
3941 door->part_1.step_delay = door->step_delay;
3942 door->part_2.step_delay = door->step_delay;
3943 door->panel.step_delay = door->step_delay;
3945 /* set animation draw order for the default wings */
3947 door->part_1.sort_priority = 2; /* draw left wing over ... */
3948 door->part_2.sort_priority = 1; /* ... right wing */
3950 /* set animation draw offset for the default wings */
3952 if (door->anim_mode & ANIM_HORIZONTAL)
3954 door->part_1.step_xoffset = door->step_offset;
3955 door->part_1.step_yoffset = 0;
3956 door->part_2.step_xoffset = door->step_offset * -1;
3957 door->part_2.step_yoffset = 0;
3959 num_door_steps = g_part_1->width / door->step_offset;
3961 else // ANIM_VERTICAL
3963 door->part_1.step_xoffset = 0;
3964 door->part_1.step_yoffset = door->step_offset;
3965 door->part_2.step_xoffset = 0;
3966 door->part_2.step_yoffset = door->step_offset * -1;
3968 num_door_steps = g_part_1->height / door->step_offset;
3971 /* set animation draw offset for the default panels */
3973 if (door->step_offset > 1)
3975 num_panel_steps = 2 * door_rect->height / door->step_offset;
3976 door->panel.start_step = num_panel_steps - num_door_steps;
3977 door->panel.start_step_closing = door->panel.start_step;
3981 num_panel_steps = door_rect->height / door->step_offset;
3982 door->panel.start_step = num_panel_steps - num_door_steps / 2;
3983 door->panel.start_step_closing = door->panel.start_step;
3984 door->panel.step_delay *= 2;
3995 for (i = 0; door_part_controls[i].door_token != -1; i++)
3997 struct DoorPartControlInfo *dpc = &door_part_controls[i];
3998 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4000 /* initialize "start_step_opening" and "start_step_closing", if needed */
4001 if (dpc->pos->start_step_opening == 0 &&
4002 dpc->pos->start_step_closing == 0)
4004 // dpc->pos->start_step_opening = dpc->pos->start_step;
4005 dpc->pos->start_step_closing = dpc->pos->start_step;
4008 /* fill structure for door part draw order (sorted below) */
4010 dpo->sort_priority = dpc->pos->sort_priority;
4013 /* sort door part controls according to sort_priority and graphic number */
4014 qsort(door_part_order, MAX_DOOR_PARTS,
4015 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4018 unsigned int OpenDoor(unsigned int door_state)
4020 if (door_state & DOOR_COPY_BACK)
4022 if (door_state & DOOR_OPEN_1)
4023 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4024 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4026 if (door_state & DOOR_OPEN_2)
4027 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4028 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4030 door_state &= ~DOOR_COPY_BACK;
4033 return MoveDoor(door_state);
4036 unsigned int CloseDoor(unsigned int door_state)
4038 unsigned int old_door_state = GetDoorState();
4040 if (!(door_state & DOOR_NO_COPY_BACK))
4042 if (old_door_state & DOOR_OPEN_1)
4043 BlitBitmap(backbuffer, bitmap_db_door_1,
4044 DX, DY, DXSIZE, DYSIZE, 0, 0);
4046 if (old_door_state & DOOR_OPEN_2)
4047 BlitBitmap(backbuffer, bitmap_db_door_2,
4048 VX, VY, VXSIZE, VYSIZE, 0, 0);
4050 door_state &= ~DOOR_NO_COPY_BACK;
4053 return MoveDoor(door_state);
4056 unsigned int GetDoorState()
4058 return MoveDoor(DOOR_GET_STATE);
4061 unsigned int SetDoorState(unsigned int door_state)
4063 return MoveDoor(door_state | DOOR_SET_STATE);
4066 int euclid(int a, int b)
4068 return (b ? euclid(b, a % b) : a);
4071 unsigned int MoveDoor(unsigned int door_state)
4073 struct Rect door_rect_list[] =
4075 { DX, DY, DXSIZE, DYSIZE },
4076 { VX, VY, VXSIZE, VYSIZE }
4078 static int door1 = DOOR_OPEN_1;
4079 static int door2 = DOOR_CLOSE_2;
4080 unsigned int door_delay = 0;
4081 unsigned int door_delay_value;
4084 if (door_state == DOOR_GET_STATE)
4085 return (door1 | door2);
4087 if (door_state & DOOR_SET_STATE)
4089 if (door_state & DOOR_ACTION_1)
4090 door1 = door_state & DOOR_ACTION_1;
4091 if (door_state & DOOR_ACTION_2)
4092 door2 = door_state & DOOR_ACTION_2;
4094 return (door1 | door2);
4097 if (!(door_state & DOOR_FORCE_REDRAW))
4099 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4100 door_state &= ~DOOR_OPEN_1;
4101 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4102 door_state &= ~DOOR_CLOSE_1;
4103 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4104 door_state &= ~DOOR_OPEN_2;
4105 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4106 door_state &= ~DOOR_CLOSE_2;
4109 if (global.autoplay_leveldir)
4111 door_state |= DOOR_NO_DELAY;
4112 door_state &= ~DOOR_CLOSE_ALL;
4115 if (game_status == GAME_MODE_EDITOR)
4116 door_state |= DOOR_NO_DELAY;
4118 if (door_state & DOOR_ACTION)
4120 boolean door_panel_drawn[NUM_DOORS];
4121 boolean panel_has_doors[NUM_DOORS];
4122 boolean door_part_skip[MAX_DOOR_PARTS];
4123 boolean door_part_done[MAX_DOOR_PARTS];
4124 boolean door_part_done_all;
4125 int num_steps[MAX_DOOR_PARTS];
4126 int max_move_delay = 0; // delay for complete animations of all doors
4127 int max_step_delay = 0; // delay (ms) between two animation frames
4128 int num_move_steps = 0; // number of animation steps for all doors
4129 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4130 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4131 int current_move_delay = 0;
4135 for (i = 0; i < NUM_DOORS; i++)
4136 panel_has_doors[i] = FALSE;
4138 for (i = 0; i < MAX_DOOR_PARTS; i++)
4140 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4141 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4142 int door_token = dpc->door_token;
4144 door_part_done[i] = FALSE;
4145 door_part_skip[i] = (!(door_state & door_token) ||
4149 for (i = 0; i < MAX_DOOR_PARTS; i++)
4151 int nr = door_part_order[i].nr;
4152 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4153 struct DoorPartPosInfo *pos = dpc->pos;
4154 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4155 int door_token = dpc->door_token;
4156 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4157 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4158 int step_xoffset = ABS(pos->step_xoffset);
4159 int step_yoffset = ABS(pos->step_yoffset);
4160 int step_delay = pos->step_delay;
4161 int current_door_state = door_state & door_token;
4162 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4163 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4164 boolean part_opening = (is_panel ? door_closing : door_opening);
4165 int start_step = (part_opening ? pos->start_step_opening :
4166 pos->start_step_closing);
4167 float move_xsize = (step_xoffset ? g->width : 0);
4168 float move_ysize = (step_yoffset ? g->height : 0);
4169 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4170 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4171 int move_steps = (move_xsteps && move_ysteps ?
4172 MIN(move_xsteps, move_ysteps) :
4173 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4174 int move_delay = move_steps * step_delay;
4176 if (door_part_skip[nr])
4179 max_move_delay = MAX(max_move_delay, move_delay);
4180 max_step_delay = (max_step_delay == 0 ? step_delay :
4181 euclid(max_step_delay, step_delay));
4182 num_steps[nr] = move_steps;
4186 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4188 panel_has_doors[door_index] = TRUE;
4192 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4194 num_move_steps = max_move_delay / max_step_delay;
4195 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4197 door_delay_value = max_step_delay;
4199 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4201 start = num_move_steps - 1;
4205 /* opening door sound has priority over simultaneously closing door */
4206 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4207 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4208 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4209 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4212 for (k = start; k < num_move_steps; k++)
4214 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4216 door_part_done_all = TRUE;
4218 for (i = 0; i < NUM_DOORS; i++)
4219 door_panel_drawn[i] = FALSE;
4221 for (i = 0; i < MAX_DOOR_PARTS; i++)
4223 int nr = door_part_order[i].nr;
4224 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4225 struct DoorPartPosInfo *pos = dpc->pos;
4226 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4227 int door_token = dpc->door_token;
4228 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4229 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4230 boolean is_panel_and_door_has_closed = FALSE;
4231 struct Rect *door_rect = &door_rect_list[door_index];
4232 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4234 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4235 int current_door_state = door_state & door_token;
4236 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4237 boolean door_closing = !door_opening;
4238 boolean part_opening = (is_panel ? door_closing : door_opening);
4239 boolean part_closing = !part_opening;
4240 int start_step = (part_opening ? pos->start_step_opening :
4241 pos->start_step_closing);
4242 int step_delay = pos->step_delay;
4243 int step_factor = step_delay / max_step_delay;
4244 int k1 = (step_factor ? k / step_factor + 1 : k);
4245 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4246 int kk = MAX(0, k2);
4249 int src_x, src_y, src_xx, src_yy;
4250 int dst_x, dst_y, dst_xx, dst_yy;
4253 if (door_part_skip[nr])
4256 if (!(door_state & door_token))
4264 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4265 int kk_door = MAX(0, k2_door);
4266 int sync_frame = kk_door * door_delay_value;
4267 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4269 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4274 if (!door_panel_drawn[door_index])
4276 ClearRectangle(drawto, door_rect->x, door_rect->y,
4277 door_rect->width, door_rect->height);
4279 door_panel_drawn[door_index] = TRUE;
4282 // draw opening or closing door parts
4284 if (pos->step_xoffset < 0) // door part on right side
4287 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4290 if (dst_xx + width > door_rect->width)
4291 width = door_rect->width - dst_xx;
4293 else // door part on left side
4296 dst_xx = pos->x - kk * pos->step_xoffset;
4300 src_xx = ABS(dst_xx);
4304 width = g->width - src_xx;
4306 // printf("::: k == %d [%d] \n", k, start_step);
4309 if (pos->step_yoffset < 0) // door part on bottom side
4312 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4315 if (dst_yy + height > door_rect->height)
4316 height = door_rect->height - dst_yy;
4318 else // door part on top side
4321 dst_yy = pos->y - kk * pos->step_yoffset;
4325 src_yy = ABS(dst_yy);
4329 height = g->height - src_yy;
4332 src_x = g_src_x + src_xx;
4333 src_y = g_src_y + src_yy;
4335 dst_x = door_rect->x + dst_xx;
4336 dst_y = door_rect->y + dst_yy;
4338 is_panel_and_door_has_closed =
4341 panel_has_doors[door_index] &&
4342 k >= num_move_steps_doors_only - 1);
4344 if (width >= 0 && width <= g->width &&
4345 height >= 0 && height <= g->height &&
4346 !is_panel_and_door_has_closed)
4348 if (is_panel || !pos->draw_masked)
4349 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4352 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4356 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4358 if ((part_opening && (width < 0 || height < 0)) ||
4359 (part_closing && (width >= g->width && height >= g->height)))
4360 door_part_done[nr] = TRUE;
4362 // continue door part animations, but not panel after door has closed
4363 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4364 door_part_done_all = FALSE;
4367 if (!(door_state & DOOR_NO_DELAY))
4371 if (game_status == GAME_MODE_MAIN)
4374 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4376 current_move_delay += max_step_delay;
4379 if (door_part_done_all)
4384 if (door_state & DOOR_ACTION_1)
4385 door1 = door_state & DOOR_ACTION_1;
4386 if (door_state & DOOR_ACTION_2)
4387 door2 = door_state & DOOR_ACTION_2;
4389 return (door1 | door2);
4392 void DrawSpecialEditorDoor()
4394 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4395 int top_border_width = gfx1->width;
4396 int top_border_height = gfx1->height;
4397 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4398 int ex = EX - outer_border;
4399 int ey = EY - outer_border;
4400 int vy = VY - outer_border;
4401 int exsize = EXSIZE + 2 * outer_border;
4403 /* draw bigger level editor toolbox window */
4404 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4405 top_border_width, top_border_height, ex, ey - top_border_height);
4406 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4407 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4409 redraw_mask |= REDRAW_ALL;
4412 void UndrawSpecialEditorDoor()
4414 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4415 int top_border_width = gfx1->width;
4416 int top_border_height = gfx1->height;
4417 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4418 int ex = EX - outer_border;
4419 int ey = EY - outer_border;
4420 int ey_top = ey - top_border_height;
4421 int exsize = EXSIZE + 2 * outer_border;
4422 int eysize = EYSIZE + 2 * outer_border;
4424 /* draw normal tape recorder window */
4425 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4427 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4428 ex, ey_top, top_border_width, top_border_height,
4430 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4431 ex, ey, exsize, eysize, ex, ey);
4435 // if screen background is set to "[NONE]", clear editor toolbox window
4436 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4437 ClearRectangle(drawto, ex, ey, exsize, eysize);
4440 redraw_mask |= REDRAW_ALL;
4444 /* ---------- new tool button stuff ---------------------------------------- */
4449 struct TextPosInfo *pos;
4452 } toolbutton_info[NUM_TOOL_BUTTONS] =
4455 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4456 TOOL_CTRL_ID_YES, "yes"
4459 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4460 TOOL_CTRL_ID_NO, "no"
4463 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4464 TOOL_CTRL_ID_CONFIRM, "confirm"
4467 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4468 TOOL_CTRL_ID_PLAYER_1, "player 1"
4471 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4472 TOOL_CTRL_ID_PLAYER_2, "player 2"
4475 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4476 TOOL_CTRL_ID_PLAYER_3, "player 3"
4479 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4480 TOOL_CTRL_ID_PLAYER_4, "player 4"
4484 void CreateToolButtons()
4488 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4490 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4491 struct TextPosInfo *pos = toolbutton_info[i].pos;
4492 struct GadgetInfo *gi;
4493 Bitmap *deco_bitmap = None;
4494 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4495 unsigned int event_mask = GD_EVENT_RELEASED;
4498 int gd_x = gfx->src_x;
4499 int gd_y = gfx->src_y;
4500 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4501 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4504 if (global.use_envelope_request)
4505 setRequestPosition(&dx, &dy, TRUE);
4507 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4509 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4511 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4512 pos->size, &deco_bitmap, &deco_x, &deco_y);
4513 deco_xpos = (gfx->width - pos->size) / 2;
4514 deco_ypos = (gfx->height - pos->size) / 2;
4517 gi = CreateGadget(GDI_CUSTOM_ID, id,
4518 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4519 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4520 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4521 GDI_WIDTH, gfx->width,
4522 GDI_HEIGHT, gfx->height,
4523 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4524 GDI_STATE, GD_BUTTON_UNPRESSED,
4525 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4526 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4527 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4528 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4529 GDI_DECORATION_SIZE, pos->size, pos->size,
4530 GDI_DECORATION_SHIFTING, 1, 1,
4531 GDI_DIRECT_DRAW, FALSE,
4532 GDI_EVENT_MASK, event_mask,
4533 GDI_CALLBACK_ACTION, HandleToolButtons,
4537 Error(ERR_EXIT, "cannot create gadget");
4539 tool_gadget[id] = gi;
4543 void FreeToolButtons()
4547 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4548 FreeGadget(tool_gadget[i]);
4551 static void UnmapToolButtons()
4555 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4556 UnmapGadget(tool_gadget[i]);
4559 static void HandleToolButtons(struct GadgetInfo *gi)
4561 request_gadget_id = gi->custom_id;
4564 static struct Mapping_EM_to_RND_object
4567 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4568 boolean is_backside; /* backside of moving element */
4574 em_object_mapping_list[] =
4577 Xblank, TRUE, FALSE,
4581 Yacid_splash_eB, FALSE, FALSE,
4582 EL_ACID_SPLASH_RIGHT, -1, -1
4585 Yacid_splash_wB, FALSE, FALSE,
4586 EL_ACID_SPLASH_LEFT, -1, -1
4589 #ifdef EM_ENGINE_BAD_ROLL
4591 Xstone_force_e, FALSE, FALSE,
4592 EL_ROCK, -1, MV_BIT_RIGHT
4595 Xstone_force_w, FALSE, FALSE,
4596 EL_ROCK, -1, MV_BIT_LEFT
4599 Xnut_force_e, FALSE, FALSE,
4600 EL_NUT, -1, MV_BIT_RIGHT
4603 Xnut_force_w, FALSE, FALSE,
4604 EL_NUT, -1, MV_BIT_LEFT
4607 Xspring_force_e, FALSE, FALSE,
4608 EL_SPRING, -1, MV_BIT_RIGHT
4611 Xspring_force_w, FALSE, FALSE,
4612 EL_SPRING, -1, MV_BIT_LEFT
4615 Xemerald_force_e, FALSE, FALSE,
4616 EL_EMERALD, -1, MV_BIT_RIGHT
4619 Xemerald_force_w, FALSE, FALSE,
4620 EL_EMERALD, -1, MV_BIT_LEFT
4623 Xdiamond_force_e, FALSE, FALSE,
4624 EL_DIAMOND, -1, MV_BIT_RIGHT
4627 Xdiamond_force_w, FALSE, FALSE,
4628 EL_DIAMOND, -1, MV_BIT_LEFT
4631 Xbomb_force_e, FALSE, FALSE,
4632 EL_BOMB, -1, MV_BIT_RIGHT
4635 Xbomb_force_w, FALSE, FALSE,
4636 EL_BOMB, -1, MV_BIT_LEFT
4638 #endif /* EM_ENGINE_BAD_ROLL */
4641 Xstone, TRUE, FALSE,
4645 Xstone_pause, FALSE, FALSE,
4649 Xstone_fall, FALSE, FALSE,
4653 Ystone_s, FALSE, FALSE,
4654 EL_ROCK, ACTION_FALLING, -1
4657 Ystone_sB, FALSE, TRUE,
4658 EL_ROCK, ACTION_FALLING, -1
4661 Ystone_e, FALSE, FALSE,
4662 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4665 Ystone_eB, FALSE, TRUE,
4666 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4669 Ystone_w, FALSE, FALSE,
4670 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4673 Ystone_wB, FALSE, TRUE,
4674 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4681 Xnut_pause, FALSE, FALSE,
4685 Xnut_fall, FALSE, FALSE,
4689 Ynut_s, FALSE, FALSE,
4690 EL_NUT, ACTION_FALLING, -1
4693 Ynut_sB, FALSE, TRUE,
4694 EL_NUT, ACTION_FALLING, -1
4697 Ynut_e, FALSE, FALSE,
4698 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4701 Ynut_eB, FALSE, TRUE,
4702 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4705 Ynut_w, FALSE, FALSE,
4706 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4709 Ynut_wB, FALSE, TRUE,
4710 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4713 Xbug_n, TRUE, FALSE,
4717 Xbug_e, TRUE, FALSE,
4718 EL_BUG_RIGHT, -1, -1
4721 Xbug_s, TRUE, FALSE,
4725 Xbug_w, TRUE, FALSE,
4729 Xbug_gon, FALSE, FALSE,
4733 Xbug_goe, FALSE, FALSE,
4734 EL_BUG_RIGHT, -1, -1
4737 Xbug_gos, FALSE, FALSE,
4741 Xbug_gow, FALSE, FALSE,
4745 Ybug_n, FALSE, FALSE,
4746 EL_BUG, ACTION_MOVING, MV_BIT_UP
4749 Ybug_nB, FALSE, TRUE,
4750 EL_BUG, ACTION_MOVING, MV_BIT_UP
4753 Ybug_e, FALSE, FALSE,
4754 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4757 Ybug_eB, FALSE, TRUE,
4758 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4761 Ybug_s, FALSE, FALSE,
4762 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4765 Ybug_sB, FALSE, TRUE,
4766 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4769 Ybug_w, FALSE, FALSE,
4770 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4773 Ybug_wB, FALSE, TRUE,
4774 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4777 Ybug_w_n, FALSE, FALSE,
4778 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4781 Ybug_n_e, FALSE, FALSE,
4782 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4785 Ybug_e_s, FALSE, FALSE,
4786 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4789 Ybug_s_w, FALSE, FALSE,
4790 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4793 Ybug_e_n, FALSE, FALSE,
4794 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4797 Ybug_s_e, FALSE, FALSE,
4798 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4801 Ybug_w_s, FALSE, FALSE,
4802 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4805 Ybug_n_w, FALSE, FALSE,
4806 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4809 Ybug_stone, FALSE, FALSE,
4810 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4813 Ybug_spring, FALSE, FALSE,
4814 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4817 Xtank_n, TRUE, FALSE,
4818 EL_SPACESHIP_UP, -1, -1
4821 Xtank_e, TRUE, FALSE,
4822 EL_SPACESHIP_RIGHT, -1, -1
4825 Xtank_s, TRUE, FALSE,
4826 EL_SPACESHIP_DOWN, -1, -1
4829 Xtank_w, TRUE, FALSE,
4830 EL_SPACESHIP_LEFT, -1, -1
4833 Xtank_gon, FALSE, FALSE,
4834 EL_SPACESHIP_UP, -1, -1
4837 Xtank_goe, FALSE, FALSE,
4838 EL_SPACESHIP_RIGHT, -1, -1
4841 Xtank_gos, FALSE, FALSE,
4842 EL_SPACESHIP_DOWN, -1, -1
4845 Xtank_gow, FALSE, FALSE,
4846 EL_SPACESHIP_LEFT, -1, -1
4849 Ytank_n, FALSE, FALSE,
4850 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4853 Ytank_nB, FALSE, TRUE,
4854 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4857 Ytank_e, FALSE, FALSE,
4858 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4861 Ytank_eB, FALSE, TRUE,
4862 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4865 Ytank_s, FALSE, FALSE,
4866 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4869 Ytank_sB, FALSE, TRUE,
4870 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4873 Ytank_w, FALSE, FALSE,
4874 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4877 Ytank_wB, FALSE, TRUE,
4878 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4881 Ytank_w_n, FALSE, FALSE,
4882 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4885 Ytank_n_e, FALSE, FALSE,
4886 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4889 Ytank_e_s, FALSE, FALSE,
4890 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4893 Ytank_s_w, FALSE, FALSE,
4894 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4897 Ytank_e_n, FALSE, FALSE,
4898 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4901 Ytank_s_e, FALSE, FALSE,
4902 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4905 Ytank_w_s, FALSE, FALSE,
4906 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4909 Ytank_n_w, FALSE, FALSE,
4910 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4913 Ytank_stone, FALSE, FALSE,
4914 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4917 Ytank_spring, FALSE, FALSE,
4918 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4921 Xandroid, TRUE, FALSE,
4922 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4925 Xandroid_1_n, FALSE, FALSE,
4926 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4929 Xandroid_2_n, FALSE, FALSE,
4930 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4933 Xandroid_1_e, FALSE, FALSE,
4934 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4937 Xandroid_2_e, FALSE, FALSE,
4938 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4941 Xandroid_1_w, FALSE, FALSE,
4942 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4945 Xandroid_2_w, FALSE, FALSE,
4946 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4949 Xandroid_1_s, FALSE, FALSE,
4950 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4953 Xandroid_2_s, FALSE, FALSE,
4954 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4957 Yandroid_n, FALSE, FALSE,
4958 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4961 Yandroid_nB, FALSE, TRUE,
4962 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4965 Yandroid_ne, FALSE, FALSE,
4966 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4969 Yandroid_neB, FALSE, TRUE,
4970 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4973 Yandroid_e, FALSE, FALSE,
4974 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4977 Yandroid_eB, FALSE, TRUE,
4978 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4981 Yandroid_se, FALSE, FALSE,
4982 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4985 Yandroid_seB, FALSE, TRUE,
4986 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4989 Yandroid_s, FALSE, FALSE,
4990 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4993 Yandroid_sB, FALSE, TRUE,
4994 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4997 Yandroid_sw, FALSE, FALSE,
4998 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5001 Yandroid_swB, FALSE, TRUE,
5002 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5005 Yandroid_w, FALSE, FALSE,
5006 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5009 Yandroid_wB, FALSE, TRUE,
5010 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5013 Yandroid_nw, FALSE, FALSE,
5014 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5017 Yandroid_nwB, FALSE, TRUE,
5018 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5021 Xspring, TRUE, FALSE,
5025 Xspring_pause, FALSE, FALSE,
5029 Xspring_e, FALSE, FALSE,
5033 Xspring_w, FALSE, FALSE,
5037 Xspring_fall, FALSE, FALSE,
5041 Yspring_s, FALSE, FALSE,
5042 EL_SPRING, ACTION_FALLING, -1
5045 Yspring_sB, FALSE, TRUE,
5046 EL_SPRING, ACTION_FALLING, -1
5049 Yspring_e, FALSE, FALSE,
5050 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5053 Yspring_eB, FALSE, TRUE,
5054 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5057 Yspring_w, FALSE, FALSE,
5058 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5061 Yspring_wB, FALSE, TRUE,
5062 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5065 Yspring_kill_e, FALSE, FALSE,
5066 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5069 Yspring_kill_eB, FALSE, TRUE,
5070 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5073 Yspring_kill_w, FALSE, FALSE,
5074 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5077 Yspring_kill_wB, FALSE, TRUE,
5078 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5081 Xeater_n, TRUE, FALSE,
5082 EL_YAMYAM_UP, -1, -1
5085 Xeater_e, TRUE, FALSE,
5086 EL_YAMYAM_RIGHT, -1, -1
5089 Xeater_w, TRUE, FALSE,
5090 EL_YAMYAM_LEFT, -1, -1
5093 Xeater_s, TRUE, FALSE,
5094 EL_YAMYAM_DOWN, -1, -1
5097 Yeater_n, FALSE, FALSE,
5098 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5101 Yeater_nB, FALSE, TRUE,
5102 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5105 Yeater_e, FALSE, FALSE,
5106 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5109 Yeater_eB, FALSE, TRUE,
5110 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5113 Yeater_s, FALSE, FALSE,
5114 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5117 Yeater_sB, FALSE, TRUE,
5118 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5121 Yeater_w, FALSE, FALSE,
5122 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5125 Yeater_wB, FALSE, TRUE,
5126 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5129 Yeater_stone, FALSE, FALSE,
5130 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5133 Yeater_spring, FALSE, FALSE,
5134 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5137 Xalien, TRUE, FALSE,
5141 Xalien_pause, FALSE, FALSE,
5145 Yalien_n, FALSE, FALSE,
5146 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5149 Yalien_nB, FALSE, TRUE,
5150 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5153 Yalien_e, FALSE, FALSE,
5154 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5157 Yalien_eB, FALSE, TRUE,
5158 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5161 Yalien_s, FALSE, FALSE,
5162 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5165 Yalien_sB, FALSE, TRUE,
5166 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5169 Yalien_w, FALSE, FALSE,
5170 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5173 Yalien_wB, FALSE, TRUE,
5174 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5177 Yalien_stone, FALSE, FALSE,
5178 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5181 Yalien_spring, FALSE, FALSE,
5182 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5185 Xemerald, TRUE, FALSE,
5189 Xemerald_pause, FALSE, FALSE,
5193 Xemerald_fall, FALSE, FALSE,
5197 Xemerald_shine, FALSE, FALSE,
5198 EL_EMERALD, ACTION_TWINKLING, -1
5201 Yemerald_s, FALSE, FALSE,
5202 EL_EMERALD, ACTION_FALLING, -1
5205 Yemerald_sB, FALSE, TRUE,
5206 EL_EMERALD, ACTION_FALLING, -1
5209 Yemerald_e, FALSE, FALSE,
5210 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5213 Yemerald_eB, FALSE, TRUE,
5214 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5217 Yemerald_w, FALSE, FALSE,
5218 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5221 Yemerald_wB, FALSE, TRUE,
5222 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5225 Yemerald_eat, FALSE, FALSE,
5226 EL_EMERALD, ACTION_COLLECTING, -1
5229 Yemerald_stone, FALSE, FALSE,
5230 EL_NUT, ACTION_BREAKING, -1
5233 Xdiamond, TRUE, FALSE,
5237 Xdiamond_pause, FALSE, FALSE,
5241 Xdiamond_fall, FALSE, FALSE,
5245 Xdiamond_shine, FALSE, FALSE,
5246 EL_DIAMOND, ACTION_TWINKLING, -1
5249 Ydiamond_s, FALSE, FALSE,
5250 EL_DIAMOND, ACTION_FALLING, -1
5253 Ydiamond_sB, FALSE, TRUE,
5254 EL_DIAMOND, ACTION_FALLING, -1
5257 Ydiamond_e, FALSE, FALSE,
5258 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5261 Ydiamond_eB, FALSE, TRUE,
5262 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5265 Ydiamond_w, FALSE, FALSE,
5266 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5269 Ydiamond_wB, FALSE, TRUE,
5270 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5273 Ydiamond_eat, FALSE, FALSE,
5274 EL_DIAMOND, ACTION_COLLECTING, -1
5277 Ydiamond_stone, FALSE, FALSE,
5278 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5281 Xdrip_fall, TRUE, FALSE,
5282 EL_AMOEBA_DROP, -1, -1
5285 Xdrip_stretch, FALSE, FALSE,
5286 EL_AMOEBA_DROP, ACTION_FALLING, -1
5289 Xdrip_stretchB, FALSE, TRUE,
5290 EL_AMOEBA_DROP, ACTION_FALLING, -1
5293 Xdrip_eat, FALSE, FALSE,
5294 EL_AMOEBA_DROP, ACTION_GROWING, -1
5297 Ydrip_s1, FALSE, FALSE,
5298 EL_AMOEBA_DROP, ACTION_FALLING, -1
5301 Ydrip_s1B, FALSE, TRUE,
5302 EL_AMOEBA_DROP, ACTION_FALLING, -1
5305 Ydrip_s2, FALSE, FALSE,
5306 EL_AMOEBA_DROP, ACTION_FALLING, -1
5309 Ydrip_s2B, FALSE, TRUE,
5310 EL_AMOEBA_DROP, ACTION_FALLING, -1
5317 Xbomb_pause, FALSE, FALSE,
5321 Xbomb_fall, FALSE, FALSE,
5325 Ybomb_s, FALSE, FALSE,
5326 EL_BOMB, ACTION_FALLING, -1
5329 Ybomb_sB, FALSE, TRUE,
5330 EL_BOMB, ACTION_FALLING, -1
5333 Ybomb_e, FALSE, FALSE,
5334 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5337 Ybomb_eB, FALSE, TRUE,
5338 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5341 Ybomb_w, FALSE, FALSE,
5342 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5345 Ybomb_wB, FALSE, TRUE,
5346 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5349 Ybomb_eat, FALSE, FALSE,
5350 EL_BOMB, ACTION_ACTIVATING, -1
5353 Xballoon, TRUE, FALSE,
5357 Yballoon_n, FALSE, FALSE,
5358 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5361 Yballoon_nB, FALSE, TRUE,
5362 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5365 Yballoon_e, FALSE, FALSE,
5366 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5369 Yballoon_eB, FALSE, TRUE,
5370 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5373 Yballoon_s, FALSE, FALSE,
5374 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5377 Yballoon_sB, FALSE, TRUE,
5378 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5381 Yballoon_w, FALSE, FALSE,
5382 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5385 Yballoon_wB, FALSE, TRUE,
5386 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5389 Xgrass, TRUE, FALSE,
5390 EL_EMC_GRASS, -1, -1
5393 Ygrass_nB, FALSE, FALSE,
5394 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5397 Ygrass_eB, FALSE, FALSE,
5398 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5401 Ygrass_sB, FALSE, FALSE,
5402 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5405 Ygrass_wB, FALSE, FALSE,
5406 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5413 Ydirt_nB, FALSE, FALSE,
5414 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5417 Ydirt_eB, FALSE, FALSE,
5418 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5421 Ydirt_sB, FALSE, FALSE,
5422 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5425 Ydirt_wB, FALSE, FALSE,
5426 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5429 Xacid_ne, TRUE, FALSE,
5430 EL_ACID_POOL_TOPRIGHT, -1, -1
5433 Xacid_se, TRUE, FALSE,
5434 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5437 Xacid_s, TRUE, FALSE,
5438 EL_ACID_POOL_BOTTOM, -1, -1
5441 Xacid_sw, TRUE, FALSE,
5442 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5445 Xacid_nw, TRUE, FALSE,
5446 EL_ACID_POOL_TOPLEFT, -1, -1
5449 Xacid_1, TRUE, FALSE,
5453 Xacid_2, FALSE, FALSE,
5457 Xacid_3, FALSE, FALSE,
5461 Xacid_4, FALSE, FALSE,
5465 Xacid_5, FALSE, FALSE,
5469 Xacid_6, FALSE, FALSE,
5473 Xacid_7, FALSE, FALSE,
5477 Xacid_8, FALSE, FALSE,
5481 Xball_1, TRUE, FALSE,
5482 EL_EMC_MAGIC_BALL, -1, -1
5485 Xball_1B, FALSE, FALSE,
5486 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5489 Xball_2, FALSE, FALSE,
5490 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5493 Xball_2B, FALSE, FALSE,
5494 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5497 Yball_eat, FALSE, FALSE,
5498 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5501 Ykey_1_eat, FALSE, FALSE,
5502 EL_EM_KEY_1, ACTION_COLLECTING, -1
5505 Ykey_2_eat, FALSE, FALSE,
5506 EL_EM_KEY_2, ACTION_COLLECTING, -1
5509 Ykey_3_eat, FALSE, FALSE,
5510 EL_EM_KEY_3, ACTION_COLLECTING, -1
5513 Ykey_4_eat, FALSE, FALSE,
5514 EL_EM_KEY_4, ACTION_COLLECTING, -1
5517 Ykey_5_eat, FALSE, FALSE,
5518 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5521 Ykey_6_eat, FALSE, FALSE,
5522 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5525 Ykey_7_eat, FALSE, FALSE,
5526 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5529 Ykey_8_eat, FALSE, FALSE,
5530 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5533 Ylenses_eat, FALSE, FALSE,
5534 EL_EMC_LENSES, ACTION_COLLECTING, -1
5537 Ymagnify_eat, FALSE, FALSE,
5538 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5541 Ygrass_eat, FALSE, FALSE,
5542 EL_EMC_GRASS, ACTION_SNAPPING, -1
5545 Ydirt_eat, FALSE, FALSE,
5546 EL_SAND, ACTION_SNAPPING, -1
5549 Xgrow_ns, TRUE, FALSE,
5550 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5553 Ygrow_ns_eat, FALSE, FALSE,
5554 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5557 Xgrow_ew, TRUE, FALSE,
5558 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5561 Ygrow_ew_eat, FALSE, FALSE,
5562 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5565 Xwonderwall, TRUE, FALSE,
5566 EL_MAGIC_WALL, -1, -1
5569 XwonderwallB, FALSE, FALSE,
5570 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5573 Xamoeba_1, TRUE, FALSE,
5574 EL_AMOEBA_DRY, ACTION_OTHER, -1
5577 Xamoeba_2, FALSE, FALSE,
5578 EL_AMOEBA_DRY, ACTION_OTHER, -1
5581 Xamoeba_3, FALSE, FALSE,
5582 EL_AMOEBA_DRY, ACTION_OTHER, -1
5585 Xamoeba_4, FALSE, FALSE,
5586 EL_AMOEBA_DRY, ACTION_OTHER, -1
5589 Xamoeba_5, TRUE, FALSE,
5590 EL_AMOEBA_WET, ACTION_OTHER, -1
5593 Xamoeba_6, FALSE, FALSE,
5594 EL_AMOEBA_WET, ACTION_OTHER, -1
5597 Xamoeba_7, FALSE, FALSE,
5598 EL_AMOEBA_WET, ACTION_OTHER, -1
5601 Xamoeba_8, FALSE, FALSE,
5602 EL_AMOEBA_WET, ACTION_OTHER, -1
5605 Xdoor_1, TRUE, FALSE,
5606 EL_EM_GATE_1, -1, -1
5609 Xdoor_2, TRUE, FALSE,
5610 EL_EM_GATE_2, -1, -1
5613 Xdoor_3, TRUE, FALSE,
5614 EL_EM_GATE_3, -1, -1
5617 Xdoor_4, TRUE, FALSE,
5618 EL_EM_GATE_4, -1, -1
5621 Xdoor_5, TRUE, FALSE,
5622 EL_EMC_GATE_5, -1, -1
5625 Xdoor_6, TRUE, FALSE,
5626 EL_EMC_GATE_6, -1, -1
5629 Xdoor_7, TRUE, FALSE,
5630 EL_EMC_GATE_7, -1, -1
5633 Xdoor_8, TRUE, FALSE,
5634 EL_EMC_GATE_8, -1, -1
5637 Xkey_1, TRUE, FALSE,
5641 Xkey_2, TRUE, FALSE,
5645 Xkey_3, TRUE, FALSE,
5649 Xkey_4, TRUE, FALSE,
5653 Xkey_5, TRUE, FALSE,
5654 EL_EMC_KEY_5, -1, -1
5657 Xkey_6, TRUE, FALSE,
5658 EL_EMC_KEY_6, -1, -1
5661 Xkey_7, TRUE, FALSE,
5662 EL_EMC_KEY_7, -1, -1
5665 Xkey_8, TRUE, FALSE,
5666 EL_EMC_KEY_8, -1, -1
5669 Xwind_n, TRUE, FALSE,
5670 EL_BALLOON_SWITCH_UP, -1, -1
5673 Xwind_e, TRUE, FALSE,
5674 EL_BALLOON_SWITCH_RIGHT, -1, -1
5677 Xwind_s, TRUE, FALSE,
5678 EL_BALLOON_SWITCH_DOWN, -1, -1
5681 Xwind_w, TRUE, FALSE,
5682 EL_BALLOON_SWITCH_LEFT, -1, -1
5685 Xwind_nesw, TRUE, FALSE,
5686 EL_BALLOON_SWITCH_ANY, -1, -1
5689 Xwind_stop, TRUE, FALSE,
5690 EL_BALLOON_SWITCH_NONE, -1, -1
5694 EL_EM_EXIT_CLOSED, -1, -1
5697 Xexit_1, TRUE, FALSE,
5698 EL_EM_EXIT_OPEN, -1, -1
5701 Xexit_2, FALSE, FALSE,
5702 EL_EM_EXIT_OPEN, -1, -1
5705 Xexit_3, FALSE, FALSE,
5706 EL_EM_EXIT_OPEN, -1, -1
5709 Xdynamite, TRUE, FALSE,
5710 EL_EM_DYNAMITE, -1, -1
5713 Ydynamite_eat, FALSE, FALSE,
5714 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5717 Xdynamite_1, TRUE, FALSE,
5718 EL_EM_DYNAMITE_ACTIVE, -1, -1
5721 Xdynamite_2, FALSE, FALSE,
5722 EL_EM_DYNAMITE_ACTIVE, -1, -1
5725 Xdynamite_3, FALSE, FALSE,
5726 EL_EM_DYNAMITE_ACTIVE, -1, -1
5729 Xdynamite_4, FALSE, FALSE,
5730 EL_EM_DYNAMITE_ACTIVE, -1, -1
5733 Xbumper, TRUE, FALSE,
5734 EL_EMC_SPRING_BUMPER, -1, -1
5737 XbumperB, FALSE, FALSE,
5738 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5741 Xwheel, TRUE, FALSE,
5742 EL_ROBOT_WHEEL, -1, -1
5745 XwheelB, FALSE, FALSE,
5746 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5749 Xswitch, TRUE, FALSE,
5750 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5753 XswitchB, FALSE, FALSE,
5754 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5758 EL_QUICKSAND_EMPTY, -1, -1
5761 Xsand_stone, TRUE, FALSE,
5762 EL_QUICKSAND_FULL, -1, -1
5765 Xsand_stonein_1, FALSE, TRUE,
5766 EL_ROCK, ACTION_FILLING, -1
5769 Xsand_stonein_2, FALSE, TRUE,
5770 EL_ROCK, ACTION_FILLING, -1
5773 Xsand_stonein_3, FALSE, TRUE,
5774 EL_ROCK, ACTION_FILLING, -1
5777 Xsand_stonein_4, FALSE, TRUE,
5778 EL_ROCK, ACTION_FILLING, -1
5781 Xsand_stonesand_1, FALSE, FALSE,
5782 EL_QUICKSAND_EMPTYING, -1, -1
5785 Xsand_stonesand_2, FALSE, FALSE,
5786 EL_QUICKSAND_EMPTYING, -1, -1
5789 Xsand_stonesand_3, FALSE, FALSE,
5790 EL_QUICKSAND_EMPTYING, -1, -1
5793 Xsand_stonesand_4, FALSE, FALSE,
5794 EL_QUICKSAND_EMPTYING, -1, -1
5797 Xsand_stonesand_quickout_1, FALSE, FALSE,
5798 EL_QUICKSAND_EMPTYING, -1, -1
5801 Xsand_stonesand_quickout_2, FALSE, FALSE,
5802 EL_QUICKSAND_EMPTYING, -1, -1
5805 Xsand_stoneout_1, FALSE, FALSE,
5806 EL_ROCK, ACTION_EMPTYING, -1
5809 Xsand_stoneout_2, FALSE, FALSE,
5810 EL_ROCK, ACTION_EMPTYING, -1
5813 Xsand_sandstone_1, FALSE, FALSE,
5814 EL_QUICKSAND_FILLING, -1, -1
5817 Xsand_sandstone_2, FALSE, FALSE,
5818 EL_QUICKSAND_FILLING, -1, -1
5821 Xsand_sandstone_3, FALSE, FALSE,
5822 EL_QUICKSAND_FILLING, -1, -1
5825 Xsand_sandstone_4, FALSE, FALSE,
5826 EL_QUICKSAND_FILLING, -1, -1
5829 Xplant, TRUE, FALSE,
5830 EL_EMC_PLANT, -1, -1
5833 Yplant, FALSE, FALSE,
5834 EL_EMC_PLANT, -1, -1
5837 Xlenses, TRUE, FALSE,
5838 EL_EMC_LENSES, -1, -1
5841 Xmagnify, TRUE, FALSE,
5842 EL_EMC_MAGNIFIER, -1, -1
5845 Xdripper, TRUE, FALSE,
5846 EL_EMC_DRIPPER, -1, -1
5849 XdripperB, FALSE, FALSE,
5850 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5853 Xfake_blank, TRUE, FALSE,
5854 EL_INVISIBLE_WALL, -1, -1
5857 Xfake_blankB, FALSE, FALSE,
5858 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5861 Xfake_grass, TRUE, FALSE,
5862 EL_EMC_FAKE_GRASS, -1, -1
5865 Xfake_grassB, FALSE, FALSE,
5866 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5869 Xfake_door_1, TRUE, FALSE,
5870 EL_EM_GATE_1_GRAY, -1, -1
5873 Xfake_door_2, TRUE, FALSE,
5874 EL_EM_GATE_2_GRAY, -1, -1
5877 Xfake_door_3, TRUE, FALSE,
5878 EL_EM_GATE_3_GRAY, -1, -1
5881 Xfake_door_4, TRUE, FALSE,
5882 EL_EM_GATE_4_GRAY, -1, -1
5885 Xfake_door_5, TRUE, FALSE,
5886 EL_EMC_GATE_5_GRAY, -1, -1
5889 Xfake_door_6, TRUE, FALSE,
5890 EL_EMC_GATE_6_GRAY, -1, -1
5893 Xfake_door_7, TRUE, FALSE,
5894 EL_EMC_GATE_7_GRAY, -1, -1
5897 Xfake_door_8, TRUE, FALSE,
5898 EL_EMC_GATE_8_GRAY, -1, -1
5901 Xfake_acid_1, TRUE, FALSE,
5902 EL_EMC_FAKE_ACID, -1, -1
5905 Xfake_acid_2, FALSE, FALSE,
5906 EL_EMC_FAKE_ACID, -1, -1
5909 Xfake_acid_3, FALSE, FALSE,
5910 EL_EMC_FAKE_ACID, -1, -1
5913 Xfake_acid_4, FALSE, FALSE,
5914 EL_EMC_FAKE_ACID, -1, -1
5917 Xfake_acid_5, FALSE, FALSE,
5918 EL_EMC_FAKE_ACID, -1, -1
5921 Xfake_acid_6, FALSE, FALSE,
5922 EL_EMC_FAKE_ACID, -1, -1
5925 Xfake_acid_7, FALSE, FALSE,
5926 EL_EMC_FAKE_ACID, -1, -1
5929 Xfake_acid_8, FALSE, FALSE,
5930 EL_EMC_FAKE_ACID, -1, -1
5933 Xsteel_1, TRUE, FALSE,
5934 EL_STEELWALL, -1, -1
5937 Xsteel_2, TRUE, FALSE,
5938 EL_EMC_STEELWALL_2, -1, -1
5941 Xsteel_3, TRUE, FALSE,
5942 EL_EMC_STEELWALL_3, -1, -1
5945 Xsteel_4, TRUE, FALSE,
5946 EL_EMC_STEELWALL_4, -1, -1
5949 Xwall_1, TRUE, FALSE,
5953 Xwall_2, TRUE, FALSE,
5954 EL_EMC_WALL_14, -1, -1
5957 Xwall_3, TRUE, FALSE,
5958 EL_EMC_WALL_15, -1, -1
5961 Xwall_4, TRUE, FALSE,
5962 EL_EMC_WALL_16, -1, -1
5965 Xround_wall_1, TRUE, FALSE,
5966 EL_WALL_SLIPPERY, -1, -1
5969 Xround_wall_2, TRUE, FALSE,
5970 EL_EMC_WALL_SLIPPERY_2, -1, -1
5973 Xround_wall_3, TRUE, FALSE,
5974 EL_EMC_WALL_SLIPPERY_3, -1, -1
5977 Xround_wall_4, TRUE, FALSE,
5978 EL_EMC_WALL_SLIPPERY_4, -1, -1
5981 Xdecor_1, TRUE, FALSE,
5982 EL_EMC_WALL_8, -1, -1
5985 Xdecor_2, TRUE, FALSE,
5986 EL_EMC_WALL_6, -1, -1
5989 Xdecor_3, TRUE, FALSE,
5990 EL_EMC_WALL_4, -1, -1
5993 Xdecor_4, TRUE, FALSE,
5994 EL_EMC_WALL_7, -1, -1
5997 Xdecor_5, TRUE, FALSE,
5998 EL_EMC_WALL_5, -1, -1
6001 Xdecor_6, TRUE, FALSE,
6002 EL_EMC_WALL_9, -1, -1
6005 Xdecor_7, TRUE, FALSE,
6006 EL_EMC_WALL_10, -1, -1
6009 Xdecor_8, TRUE, FALSE,
6010 EL_EMC_WALL_1, -1, -1
6013 Xdecor_9, TRUE, FALSE,
6014 EL_EMC_WALL_2, -1, -1
6017 Xdecor_10, TRUE, FALSE,
6018 EL_EMC_WALL_3, -1, -1
6021 Xdecor_11, TRUE, FALSE,
6022 EL_EMC_WALL_11, -1, -1
6025 Xdecor_12, TRUE, FALSE,
6026 EL_EMC_WALL_12, -1, -1
6029 Xalpha_0, TRUE, FALSE,
6030 EL_CHAR('0'), -1, -1
6033 Xalpha_1, TRUE, FALSE,
6034 EL_CHAR('1'), -1, -1
6037 Xalpha_2, TRUE, FALSE,
6038 EL_CHAR('2'), -1, -1
6041 Xalpha_3, TRUE, FALSE,
6042 EL_CHAR('3'), -1, -1
6045 Xalpha_4, TRUE, FALSE,
6046 EL_CHAR('4'), -1, -1
6049 Xalpha_5, TRUE, FALSE,
6050 EL_CHAR('5'), -1, -1
6053 Xalpha_6, TRUE, FALSE,
6054 EL_CHAR('6'), -1, -1
6057 Xalpha_7, TRUE, FALSE,
6058 EL_CHAR('7'), -1, -1
6061 Xalpha_8, TRUE, FALSE,
6062 EL_CHAR('8'), -1, -1
6065 Xalpha_9, TRUE, FALSE,
6066 EL_CHAR('9'), -1, -1
6069 Xalpha_excla, TRUE, FALSE,
6070 EL_CHAR('!'), -1, -1
6073 Xalpha_quote, TRUE, FALSE,
6074 EL_CHAR('"'), -1, -1
6077 Xalpha_comma, TRUE, FALSE,
6078 EL_CHAR(','), -1, -1
6081 Xalpha_minus, TRUE, FALSE,
6082 EL_CHAR('-'), -1, -1
6085 Xalpha_perio, TRUE, FALSE,
6086 EL_CHAR('.'), -1, -1
6089 Xalpha_colon, TRUE, FALSE,
6090 EL_CHAR(':'), -1, -1
6093 Xalpha_quest, TRUE, FALSE,
6094 EL_CHAR('?'), -1, -1
6097 Xalpha_a, TRUE, FALSE,
6098 EL_CHAR('A'), -1, -1
6101 Xalpha_b, TRUE, FALSE,
6102 EL_CHAR('B'), -1, -1
6105 Xalpha_c, TRUE, FALSE,
6106 EL_CHAR('C'), -1, -1
6109 Xalpha_d, TRUE, FALSE,
6110 EL_CHAR('D'), -1, -1
6113 Xalpha_e, TRUE, FALSE,
6114 EL_CHAR('E'), -1, -1
6117 Xalpha_f, TRUE, FALSE,
6118 EL_CHAR('F'), -1, -1
6121 Xalpha_g, TRUE, FALSE,
6122 EL_CHAR('G'), -1, -1
6125 Xalpha_h, TRUE, FALSE,
6126 EL_CHAR('H'), -1, -1
6129 Xalpha_i, TRUE, FALSE,
6130 EL_CHAR('I'), -1, -1
6133 Xalpha_j, TRUE, FALSE,
6134 EL_CHAR('J'), -1, -1
6137 Xalpha_k, TRUE, FALSE,
6138 EL_CHAR('K'), -1, -1
6141 Xalpha_l, TRUE, FALSE,
6142 EL_CHAR('L'), -1, -1
6145 Xalpha_m, TRUE, FALSE,
6146 EL_CHAR('M'), -1, -1
6149 Xalpha_n, TRUE, FALSE,
6150 EL_CHAR('N'), -1, -1
6153 Xalpha_o, TRUE, FALSE,
6154 EL_CHAR('O'), -1, -1
6157 Xalpha_p, TRUE, FALSE,
6158 EL_CHAR('P'), -1, -1
6161 Xalpha_q, TRUE, FALSE,
6162 EL_CHAR('Q'), -1, -1
6165 Xalpha_r, TRUE, FALSE,
6166 EL_CHAR('R'), -1, -1
6169 Xalpha_s, TRUE, FALSE,
6170 EL_CHAR('S'), -1, -1
6173 Xalpha_t, TRUE, FALSE,
6174 EL_CHAR('T'), -1, -1
6177 Xalpha_u, TRUE, FALSE,
6178 EL_CHAR('U'), -1, -1
6181 Xalpha_v, TRUE, FALSE,
6182 EL_CHAR('V'), -1, -1
6185 Xalpha_w, TRUE, FALSE,
6186 EL_CHAR('W'), -1, -1
6189 Xalpha_x, TRUE, FALSE,
6190 EL_CHAR('X'), -1, -1
6193 Xalpha_y, TRUE, FALSE,
6194 EL_CHAR('Y'), -1, -1
6197 Xalpha_z, TRUE, FALSE,
6198 EL_CHAR('Z'), -1, -1
6201 Xalpha_arrow_e, TRUE, FALSE,
6202 EL_CHAR('>'), -1, -1
6205 Xalpha_arrow_w, TRUE, FALSE,
6206 EL_CHAR('<'), -1, -1
6209 Xalpha_copyr, TRUE, FALSE,
6210 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6214 Xboom_bug, FALSE, FALSE,
6215 EL_BUG, ACTION_EXPLODING, -1
6218 Xboom_bomb, FALSE, FALSE,
6219 EL_BOMB, ACTION_EXPLODING, -1
6222 Xboom_android, FALSE, FALSE,
6223 EL_EMC_ANDROID, ACTION_OTHER, -1
6226 Xboom_1, FALSE, FALSE,
6227 EL_DEFAULT, ACTION_EXPLODING, -1
6230 Xboom_2, FALSE, FALSE,
6231 EL_DEFAULT, ACTION_EXPLODING, -1
6234 Znormal, FALSE, FALSE,
6238 Zdynamite, FALSE, FALSE,
6242 Zplayer, FALSE, FALSE,
6246 ZBORDER, FALSE, FALSE,
6256 static struct Mapping_EM_to_RND_player
6265 em_player_mapping_list[] =
6269 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6273 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6277 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6281 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6285 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6289 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6293 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6297 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6301 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6305 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6309 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6313 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6317 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6321 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6325 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6329 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6333 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6337 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6341 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6345 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6349 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6353 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6357 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6361 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6365 EL_PLAYER_1, ACTION_DEFAULT, -1,
6369 EL_PLAYER_2, ACTION_DEFAULT, -1,
6373 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6377 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6381 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6385 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6389 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6393 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6397 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6401 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6405 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6409 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6413 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6417 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6421 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6425 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6429 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6433 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6437 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6441 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6445 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6449 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6453 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6457 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6461 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6465 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6469 EL_PLAYER_3, ACTION_DEFAULT, -1,
6473 EL_PLAYER_4, ACTION_DEFAULT, -1,
6482 int map_element_RND_to_EM(int element_rnd)
6484 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6485 static boolean mapping_initialized = FALSE;
6487 if (!mapping_initialized)
6491 /* return "Xalpha_quest" for all undefined elements in mapping array */
6492 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6493 mapping_RND_to_EM[i] = Xalpha_quest;
6495 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6496 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6497 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6498 em_object_mapping_list[i].element_em;
6500 mapping_initialized = TRUE;
6503 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6504 return mapping_RND_to_EM[element_rnd];
6506 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6511 int map_element_EM_to_RND(int element_em)
6513 static unsigned short mapping_EM_to_RND[TILE_MAX];
6514 static boolean mapping_initialized = FALSE;
6516 if (!mapping_initialized)
6520 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6521 for (i = 0; i < TILE_MAX; i++)
6522 mapping_EM_to_RND[i] = EL_UNKNOWN;
6524 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6525 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6526 em_object_mapping_list[i].element_rnd;
6528 mapping_initialized = TRUE;
6531 if (element_em >= 0 && element_em < TILE_MAX)
6532 return mapping_EM_to_RND[element_em];
6534 Error(ERR_WARN, "invalid EM level element %d", element_em);
6539 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6541 struct LevelInfo_EM *level_em = level->native_em_level;
6542 struct LEVEL *lev = level_em->lev;
6545 for (i = 0; i < TILE_MAX; i++)
6546 lev->android_array[i] = Xblank;
6548 for (i = 0; i < level->num_android_clone_elements; i++)
6550 int element_rnd = level->android_clone_element[i];
6551 int element_em = map_element_RND_to_EM(element_rnd);
6553 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6554 if (em_object_mapping_list[j].element_rnd == element_rnd)
6555 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6559 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6561 struct LevelInfo_EM *level_em = level->native_em_level;
6562 struct LEVEL *lev = level_em->lev;
6565 level->num_android_clone_elements = 0;
6567 for (i = 0; i < TILE_MAX; i++)
6569 int element_em = lev->android_array[i];
6571 boolean element_found = FALSE;
6573 if (element_em == Xblank)
6576 element_rnd = map_element_EM_to_RND(element_em);
6578 for (j = 0; j < level->num_android_clone_elements; j++)
6579 if (level->android_clone_element[j] == element_rnd)
6580 element_found = TRUE;
6584 level->android_clone_element[level->num_android_clone_elements++] =
6587 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6592 if (level->num_android_clone_elements == 0)
6594 level->num_android_clone_elements = 1;
6595 level->android_clone_element[0] = EL_EMPTY;
6599 int map_direction_RND_to_EM(int direction)
6601 return (direction == MV_UP ? 0 :
6602 direction == MV_RIGHT ? 1 :
6603 direction == MV_DOWN ? 2 :
6604 direction == MV_LEFT ? 3 :
6608 int map_direction_EM_to_RND(int direction)
6610 return (direction == 0 ? MV_UP :
6611 direction == 1 ? MV_RIGHT :
6612 direction == 2 ? MV_DOWN :
6613 direction == 3 ? MV_LEFT :
6617 int map_element_RND_to_SP(int element_rnd)
6619 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6621 if (element_rnd >= EL_SP_START &&
6622 element_rnd <= EL_SP_END)
6623 element_sp = element_rnd - EL_SP_START;
6624 else if (element_rnd == EL_EMPTY_SPACE)
6626 else if (element_rnd == EL_INVISIBLE_WALL)
6632 int map_element_SP_to_RND(int element_sp)
6634 int element_rnd = EL_UNKNOWN;
6636 if (element_sp >= 0x00 &&
6638 element_rnd = EL_SP_START + element_sp;
6639 else if (element_sp == 0x28)
6640 element_rnd = EL_INVISIBLE_WALL;
6645 int map_action_SP_to_RND(int action_sp)
6649 case actActive: return ACTION_ACTIVE;
6650 case actImpact: return ACTION_IMPACT;
6651 case actExploding: return ACTION_EXPLODING;
6652 case actDigging: return ACTION_DIGGING;
6653 case actSnapping: return ACTION_SNAPPING;
6654 case actCollecting: return ACTION_COLLECTING;
6655 case actPassing: return ACTION_PASSING;
6656 case actPushing: return ACTION_PUSHING;
6657 case actDropping: return ACTION_DROPPING;
6659 default: return ACTION_DEFAULT;
6663 int get_next_element(int element)
6667 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6668 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6669 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6670 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6671 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6672 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6673 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6674 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6675 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6676 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6677 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6679 default: return element;
6683 int el_act_dir2img(int element, int action, int direction)
6685 element = GFX_ELEMENT(element);
6686 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6688 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6689 return element_info[element].direction_graphic[action][direction];
6692 static int el_act_dir2crm(int element, int action, int direction)
6694 element = GFX_ELEMENT(element);
6695 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6697 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6698 return element_info[element].direction_crumbled[action][direction];
6701 int el_act2img(int element, int action)
6703 element = GFX_ELEMENT(element);
6705 return element_info[element].graphic[action];
6708 int el_act2crm(int element, int action)
6710 element = GFX_ELEMENT(element);
6712 return element_info[element].crumbled[action];
6715 int el_dir2img(int element, int direction)
6717 element = GFX_ELEMENT(element);
6719 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6722 int el2baseimg(int element)
6724 return element_info[element].graphic[ACTION_DEFAULT];
6727 int el2img(int element)
6729 element = GFX_ELEMENT(element);
6731 return element_info[element].graphic[ACTION_DEFAULT];
6734 int el2edimg(int element)
6736 element = GFX_ELEMENT(element);
6738 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6741 int el2preimg(int element)
6743 element = GFX_ELEMENT(element);
6745 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6748 int el2panelimg(int element)
6750 element = GFX_ELEMENT(element);
6752 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6755 int font2baseimg(int font_nr)
6757 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6760 int getBeltNrFromBeltElement(int element)
6762 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6763 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6764 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6767 int getBeltNrFromBeltActiveElement(int element)
6769 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6770 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6771 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6774 int getBeltNrFromBeltSwitchElement(int element)
6776 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6777 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6778 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6781 int getBeltDirNrFromBeltElement(int element)
6783 static int belt_base_element[4] =
6785 EL_CONVEYOR_BELT_1_LEFT,
6786 EL_CONVEYOR_BELT_2_LEFT,
6787 EL_CONVEYOR_BELT_3_LEFT,
6788 EL_CONVEYOR_BELT_4_LEFT
6791 int belt_nr = getBeltNrFromBeltElement(element);
6792 int belt_dir_nr = element - belt_base_element[belt_nr];
6794 return (belt_dir_nr % 3);
6797 int getBeltDirNrFromBeltSwitchElement(int element)
6799 static int belt_base_element[4] =
6801 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6802 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6803 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6804 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6807 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6808 int belt_dir_nr = element - belt_base_element[belt_nr];
6810 return (belt_dir_nr % 3);
6813 int getBeltDirFromBeltElement(int element)
6815 static int belt_move_dir[3] =
6822 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6824 return belt_move_dir[belt_dir_nr];
6827 int getBeltDirFromBeltSwitchElement(int element)
6829 static int belt_move_dir[3] =
6836 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6838 return belt_move_dir[belt_dir_nr];
6841 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6843 static int belt_base_element[4] =
6845 EL_CONVEYOR_BELT_1_LEFT,
6846 EL_CONVEYOR_BELT_2_LEFT,
6847 EL_CONVEYOR_BELT_3_LEFT,
6848 EL_CONVEYOR_BELT_4_LEFT
6851 return belt_base_element[belt_nr] + belt_dir_nr;
6854 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6856 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6858 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6861 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6863 static int belt_base_element[4] =
6865 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6866 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6867 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6868 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6871 return belt_base_element[belt_nr] + belt_dir_nr;
6874 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6876 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6878 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6881 boolean getTeamMode_EM()
6883 return game.team_mode;
6886 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6888 int game_frame_delay_value;
6890 game_frame_delay_value =
6891 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6892 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6895 if (tape.playing && tape.warp_forward && !tape.pausing)
6896 game_frame_delay_value = 0;
6898 return game_frame_delay_value;
6901 unsigned int InitRND(int seed)
6903 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6904 return InitEngineRandom_EM(seed);
6905 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6906 return InitEngineRandom_SP(seed);
6908 return InitEngineRandom_RND(seed);
6911 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6912 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6914 inline static int get_effective_element_EM(int tile, int frame_em)
6916 int element = object_mapping[tile].element_rnd;
6917 int action = object_mapping[tile].action;
6918 boolean is_backside = object_mapping[tile].is_backside;
6919 boolean action_removing = (action == ACTION_DIGGING ||
6920 action == ACTION_SNAPPING ||
6921 action == ACTION_COLLECTING);
6927 case Yacid_splash_eB:
6928 case Yacid_splash_wB:
6929 return (frame_em > 5 ? EL_EMPTY : element);
6935 else /* frame_em == 7 */
6939 case Yacid_splash_eB:
6940 case Yacid_splash_wB:
6943 case Yemerald_stone:
6946 case Ydiamond_stone:
6950 case Xdrip_stretchB:
6969 case Xsand_stonein_1:
6970 case Xsand_stonein_2:
6971 case Xsand_stonein_3:
6972 case Xsand_stonein_4:
6976 return (is_backside || action_removing ? EL_EMPTY : element);
6981 inline static boolean check_linear_animation_EM(int tile)
6985 case Xsand_stonesand_1:
6986 case Xsand_stonesand_quickout_1:
6987 case Xsand_sandstone_1:
6988 case Xsand_stonein_1:
6989 case Xsand_stoneout_1:
7008 case Yacid_splash_eB:
7009 case Yacid_splash_wB:
7010 case Yemerald_stone:
7017 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7018 boolean has_crumbled_graphics,
7019 int crumbled, int sync_frame)
7021 /* if element can be crumbled, but certain action graphics are just empty
7022 space (like instantly snapping sand to empty space in 1 frame), do not
7023 treat these empty space graphics as crumbled graphics in EMC engine */
7024 if (crumbled == IMG_EMPTY_SPACE)
7025 has_crumbled_graphics = FALSE;
7027 if (has_crumbled_graphics)
7029 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7030 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7031 g_crumbled->anim_delay,
7032 g_crumbled->anim_mode,
7033 g_crumbled->anim_start_frame,
7036 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7037 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7039 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7041 g_em->has_crumbled_graphics = TRUE;
7045 g_em->crumbled_bitmap = NULL;
7046 g_em->crumbled_src_x = 0;
7047 g_em->crumbled_src_y = 0;
7048 g_em->crumbled_border_size = 0;
7050 g_em->has_crumbled_graphics = FALSE;
7054 void ResetGfxAnimation_EM(int x, int y, int tile)
7059 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7060 int tile, int frame_em, int x, int y)
7062 int action = object_mapping[tile].action;
7063 int direction = object_mapping[tile].direction;
7064 int effective_element = get_effective_element_EM(tile, frame_em);
7065 int graphic = (direction == MV_NONE ?
7066 el_act2img(effective_element, action) :
7067 el_act_dir2img(effective_element, action, direction));
7068 struct GraphicInfo *g = &graphic_info[graphic];
7070 boolean action_removing = (action == ACTION_DIGGING ||
7071 action == ACTION_SNAPPING ||
7072 action == ACTION_COLLECTING);
7073 boolean action_moving = (action == ACTION_FALLING ||
7074 action == ACTION_MOVING ||
7075 action == ACTION_PUSHING ||
7076 action == ACTION_EATING ||
7077 action == ACTION_FILLING ||
7078 action == ACTION_EMPTYING);
7079 boolean action_falling = (action == ACTION_FALLING ||
7080 action == ACTION_FILLING ||
7081 action == ACTION_EMPTYING);
7083 /* special case: graphic uses "2nd movement tile" and has defined
7084 7 frames for movement animation (or less) => use default graphic
7085 for last (8th) frame which ends the movement animation */
7086 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7088 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7089 graphic = (direction == MV_NONE ?
7090 el_act2img(effective_element, action) :
7091 el_act_dir2img(effective_element, action, direction));
7093 g = &graphic_info[graphic];
7096 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7100 else if (action_moving)
7102 boolean is_backside = object_mapping[tile].is_backside;
7106 int direction = object_mapping[tile].direction;
7107 int move_dir = (action_falling ? MV_DOWN : direction);
7112 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7113 if (g->double_movement && frame_em == 0)
7117 if (move_dir == MV_LEFT)
7118 GfxFrame[x - 1][y] = GfxFrame[x][y];
7119 else if (move_dir == MV_RIGHT)
7120 GfxFrame[x + 1][y] = GfxFrame[x][y];
7121 else if (move_dir == MV_UP)
7122 GfxFrame[x][y - 1] = GfxFrame[x][y];
7123 else if (move_dir == MV_DOWN)
7124 GfxFrame[x][y + 1] = GfxFrame[x][y];
7131 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7132 if (tile == Xsand_stonesand_quickout_1 ||
7133 tile == Xsand_stonesand_quickout_2)
7137 if (graphic_info[graphic].anim_global_sync)
7138 sync_frame = FrameCounter;
7139 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7140 sync_frame = GfxFrame[x][y];
7142 sync_frame = 0; /* playfield border (pseudo steel) */
7144 SetRandomAnimationValue(x, y);
7146 int frame = getAnimationFrame(g->anim_frames,
7149 g->anim_start_frame,
7152 g_em->unique_identifier =
7153 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7156 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7157 int tile, int frame_em, int x, int y)
7159 int action = object_mapping[tile].action;
7160 int direction = object_mapping[tile].direction;
7161 boolean is_backside = object_mapping[tile].is_backside;
7162 int effective_element = get_effective_element_EM(tile, frame_em);
7163 int effective_action = action;
7164 int graphic = (direction == MV_NONE ?
7165 el_act2img(effective_element, effective_action) :
7166 el_act_dir2img(effective_element, effective_action,
7168 int crumbled = (direction == MV_NONE ?
7169 el_act2crm(effective_element, effective_action) :
7170 el_act_dir2crm(effective_element, effective_action,
7172 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7173 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7174 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7175 struct GraphicInfo *g = &graphic_info[graphic];
7178 /* special case: graphic uses "2nd movement tile" and has defined
7179 7 frames for movement animation (or less) => use default graphic
7180 for last (8th) frame which ends the movement animation */
7181 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7183 effective_action = ACTION_DEFAULT;
7184 graphic = (direction == MV_NONE ?
7185 el_act2img(effective_element, effective_action) :
7186 el_act_dir2img(effective_element, effective_action,
7188 crumbled = (direction == MV_NONE ?
7189 el_act2crm(effective_element, effective_action) :
7190 el_act_dir2crm(effective_element, effective_action,
7193 g = &graphic_info[graphic];
7196 if (graphic_info[graphic].anim_global_sync)
7197 sync_frame = FrameCounter;
7198 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7199 sync_frame = GfxFrame[x][y];
7201 sync_frame = 0; /* playfield border (pseudo steel) */
7203 SetRandomAnimationValue(x, y);
7205 int frame = getAnimationFrame(g->anim_frames,
7208 g->anim_start_frame,
7211 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7212 g->double_movement && is_backside);
7214 /* (updating the "crumbled" graphic definitions is probably not really needed,
7215 as animations for crumbled graphics can't be longer than one EMC cycle) */
7216 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7220 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7221 int player_nr, int anim, int frame_em)
7223 int element = player_mapping[player_nr][anim].element_rnd;
7224 int action = player_mapping[player_nr][anim].action;
7225 int direction = player_mapping[player_nr][anim].direction;
7226 int graphic = (direction == MV_NONE ?
7227 el_act2img(element, action) :
7228 el_act_dir2img(element, action, direction));
7229 struct GraphicInfo *g = &graphic_info[graphic];
7232 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7234 stored_player[player_nr].StepFrame = frame_em;
7236 sync_frame = stored_player[player_nr].Frame;
7238 int frame = getAnimationFrame(g->anim_frames,
7241 g->anim_start_frame,
7244 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7245 &g_em->src_x, &g_em->src_y, FALSE);
7248 void InitGraphicInfo_EM(void)
7253 int num_em_gfx_errors = 0;
7255 if (graphic_info_em_object[0][0].bitmap == NULL)
7257 /* EM graphics not yet initialized in em_open_all() */
7262 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7265 /* always start with reliable default values */
7266 for (i = 0; i < TILE_MAX; i++)
7268 object_mapping[i].element_rnd = EL_UNKNOWN;
7269 object_mapping[i].is_backside = FALSE;
7270 object_mapping[i].action = ACTION_DEFAULT;
7271 object_mapping[i].direction = MV_NONE;
7274 /* always start with reliable default values */
7275 for (p = 0; p < MAX_PLAYERS; p++)
7277 for (i = 0; i < SPR_MAX; i++)
7279 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7280 player_mapping[p][i].action = ACTION_DEFAULT;
7281 player_mapping[p][i].direction = MV_NONE;
7285 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7287 int e = em_object_mapping_list[i].element_em;
7289 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7290 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7292 if (em_object_mapping_list[i].action != -1)
7293 object_mapping[e].action = em_object_mapping_list[i].action;
7295 if (em_object_mapping_list[i].direction != -1)
7296 object_mapping[e].direction =
7297 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7300 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7302 int a = em_player_mapping_list[i].action_em;
7303 int p = em_player_mapping_list[i].player_nr;
7305 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7307 if (em_player_mapping_list[i].action != -1)
7308 player_mapping[p][a].action = em_player_mapping_list[i].action;
7310 if (em_player_mapping_list[i].direction != -1)
7311 player_mapping[p][a].direction =
7312 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7315 for (i = 0; i < TILE_MAX; i++)
7317 int element = object_mapping[i].element_rnd;
7318 int action = object_mapping[i].action;
7319 int direction = object_mapping[i].direction;
7320 boolean is_backside = object_mapping[i].is_backside;
7321 boolean action_exploding = ((action == ACTION_EXPLODING ||
7322 action == ACTION_SMASHED_BY_ROCK ||
7323 action == ACTION_SMASHED_BY_SPRING) &&
7324 element != EL_DIAMOND);
7325 boolean action_active = (action == ACTION_ACTIVE);
7326 boolean action_other = (action == ACTION_OTHER);
7328 for (j = 0; j < 8; j++)
7330 int effective_element = get_effective_element_EM(i, j);
7331 int effective_action = (j < 7 ? action :
7332 i == Xdrip_stretch ? action :
7333 i == Xdrip_stretchB ? action :
7334 i == Ydrip_s1 ? action :
7335 i == Ydrip_s1B ? action :
7336 i == Xball_1B ? action :
7337 i == Xball_2 ? action :
7338 i == Xball_2B ? action :
7339 i == Yball_eat ? action :
7340 i == Ykey_1_eat ? action :
7341 i == Ykey_2_eat ? action :
7342 i == Ykey_3_eat ? action :
7343 i == Ykey_4_eat ? action :
7344 i == Ykey_5_eat ? action :
7345 i == Ykey_6_eat ? action :
7346 i == Ykey_7_eat ? action :
7347 i == Ykey_8_eat ? action :
7348 i == Ylenses_eat ? action :
7349 i == Ymagnify_eat ? action :
7350 i == Ygrass_eat ? action :
7351 i == Ydirt_eat ? action :
7352 i == Xsand_stonein_1 ? action :
7353 i == Xsand_stonein_2 ? action :
7354 i == Xsand_stonein_3 ? action :
7355 i == Xsand_stonein_4 ? action :
7356 i == Xsand_stoneout_1 ? action :
7357 i == Xsand_stoneout_2 ? action :
7358 i == Xboom_android ? ACTION_EXPLODING :
7359 action_exploding ? ACTION_EXPLODING :
7360 action_active ? action :
7361 action_other ? action :
7363 int graphic = (el_act_dir2img(effective_element, effective_action,
7365 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7367 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7368 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7369 boolean has_action_graphics = (graphic != base_graphic);
7370 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7371 struct GraphicInfo *g = &graphic_info[graphic];
7372 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7375 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7376 boolean special_animation = (action != ACTION_DEFAULT &&
7377 g->anim_frames == 3 &&
7378 g->anim_delay == 2 &&
7379 g->anim_mode & ANIM_LINEAR);
7380 int sync_frame = (i == Xdrip_stretch ? 7 :
7381 i == Xdrip_stretchB ? 7 :
7382 i == Ydrip_s2 ? j + 8 :
7383 i == Ydrip_s2B ? j + 8 :
7392 i == Xfake_acid_1 ? 0 :
7393 i == Xfake_acid_2 ? 10 :
7394 i == Xfake_acid_3 ? 20 :
7395 i == Xfake_acid_4 ? 30 :
7396 i == Xfake_acid_5 ? 40 :
7397 i == Xfake_acid_6 ? 50 :
7398 i == Xfake_acid_7 ? 60 :
7399 i == Xfake_acid_8 ? 70 :
7401 i == Xball_2B ? j + 8 :
7402 i == Yball_eat ? j + 1 :
7403 i == Ykey_1_eat ? j + 1 :
7404 i == Ykey_2_eat ? j + 1 :
7405 i == Ykey_3_eat ? j + 1 :
7406 i == Ykey_4_eat ? j + 1 :
7407 i == Ykey_5_eat ? j + 1 :
7408 i == Ykey_6_eat ? j + 1 :
7409 i == Ykey_7_eat ? j + 1 :
7410 i == Ykey_8_eat ? j + 1 :
7411 i == Ylenses_eat ? j + 1 :
7412 i == Ymagnify_eat ? j + 1 :
7413 i == Ygrass_eat ? j + 1 :
7414 i == Ydirt_eat ? j + 1 :
7415 i == Xamoeba_1 ? 0 :
7416 i == Xamoeba_2 ? 1 :
7417 i == Xamoeba_3 ? 2 :
7418 i == Xamoeba_4 ? 3 :
7419 i == Xamoeba_5 ? 0 :
7420 i == Xamoeba_6 ? 1 :
7421 i == Xamoeba_7 ? 2 :
7422 i == Xamoeba_8 ? 3 :
7423 i == Xexit_2 ? j + 8 :
7424 i == Xexit_3 ? j + 16 :
7425 i == Xdynamite_1 ? 0 :
7426 i == Xdynamite_2 ? 8 :
7427 i == Xdynamite_3 ? 16 :
7428 i == Xdynamite_4 ? 24 :
7429 i == Xsand_stonein_1 ? j + 1 :
7430 i == Xsand_stonein_2 ? j + 9 :
7431 i == Xsand_stonein_3 ? j + 17 :
7432 i == Xsand_stonein_4 ? j + 25 :
7433 i == Xsand_stoneout_1 && j == 0 ? 0 :
7434 i == Xsand_stoneout_1 && j == 1 ? 0 :
7435 i == Xsand_stoneout_1 && j == 2 ? 1 :
7436 i == Xsand_stoneout_1 && j == 3 ? 2 :
7437 i == Xsand_stoneout_1 && j == 4 ? 2 :
7438 i == Xsand_stoneout_1 && j == 5 ? 3 :
7439 i == Xsand_stoneout_1 && j == 6 ? 4 :
7440 i == Xsand_stoneout_1 && j == 7 ? 4 :
7441 i == Xsand_stoneout_2 && j == 0 ? 5 :
7442 i == Xsand_stoneout_2 && j == 1 ? 6 :
7443 i == Xsand_stoneout_2 && j == 2 ? 7 :
7444 i == Xsand_stoneout_2 && j == 3 ? 8 :
7445 i == Xsand_stoneout_2 && j == 4 ? 9 :
7446 i == Xsand_stoneout_2 && j == 5 ? 11 :
7447 i == Xsand_stoneout_2 && j == 6 ? 13 :
7448 i == Xsand_stoneout_2 && j == 7 ? 15 :
7449 i == Xboom_bug && j == 1 ? 2 :
7450 i == Xboom_bug && j == 2 ? 2 :
7451 i == Xboom_bug && j == 3 ? 4 :
7452 i == Xboom_bug && j == 4 ? 4 :
7453 i == Xboom_bug && j == 5 ? 2 :
7454 i == Xboom_bug && j == 6 ? 2 :
7455 i == Xboom_bug && j == 7 ? 0 :
7456 i == Xboom_bomb && j == 1 ? 2 :
7457 i == Xboom_bomb && j == 2 ? 2 :
7458 i == Xboom_bomb && j == 3 ? 4 :
7459 i == Xboom_bomb && j == 4 ? 4 :
7460 i == Xboom_bomb && j == 5 ? 2 :
7461 i == Xboom_bomb && j == 6 ? 2 :
7462 i == Xboom_bomb && j == 7 ? 0 :
7463 i == Xboom_android && j == 7 ? 6 :
7464 i == Xboom_1 && j == 1 ? 2 :
7465 i == Xboom_1 && j == 2 ? 2 :
7466 i == Xboom_1 && j == 3 ? 4 :
7467 i == Xboom_1 && j == 4 ? 4 :
7468 i == Xboom_1 && j == 5 ? 6 :
7469 i == Xboom_1 && j == 6 ? 6 :
7470 i == Xboom_1 && j == 7 ? 8 :
7471 i == Xboom_2 && j == 0 ? 8 :
7472 i == Xboom_2 && j == 1 ? 8 :
7473 i == Xboom_2 && j == 2 ? 10 :
7474 i == Xboom_2 && j == 3 ? 10 :
7475 i == Xboom_2 && j == 4 ? 10 :
7476 i == Xboom_2 && j == 5 ? 12 :
7477 i == Xboom_2 && j == 6 ? 12 :
7478 i == Xboom_2 && j == 7 ? 12 :
7479 special_animation && j == 4 ? 3 :
7480 effective_action != action ? 0 :
7484 Bitmap *debug_bitmap = g_em->bitmap;
7485 int debug_src_x = g_em->src_x;
7486 int debug_src_y = g_em->src_y;
7489 int frame = getAnimationFrame(g->anim_frames,
7492 g->anim_start_frame,
7495 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7496 g->double_movement && is_backside);
7498 g_em->bitmap = src_bitmap;
7499 g_em->src_x = src_x;
7500 g_em->src_y = src_y;
7501 g_em->src_offset_x = 0;
7502 g_em->src_offset_y = 0;
7503 g_em->dst_offset_x = 0;
7504 g_em->dst_offset_y = 0;
7505 g_em->width = TILEX;
7506 g_em->height = TILEY;
7508 g_em->preserve_background = FALSE;
7510 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7513 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7514 effective_action == ACTION_MOVING ||
7515 effective_action == ACTION_PUSHING ||
7516 effective_action == ACTION_EATING)) ||
7517 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7518 effective_action == ACTION_EMPTYING)))
7521 (effective_action == ACTION_FALLING ||
7522 effective_action == ACTION_FILLING ||
7523 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7524 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7525 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7526 int num_steps = (i == Ydrip_s1 ? 16 :
7527 i == Ydrip_s1B ? 16 :
7528 i == Ydrip_s2 ? 16 :
7529 i == Ydrip_s2B ? 16 :
7530 i == Xsand_stonein_1 ? 32 :
7531 i == Xsand_stonein_2 ? 32 :
7532 i == Xsand_stonein_3 ? 32 :
7533 i == Xsand_stonein_4 ? 32 :
7534 i == Xsand_stoneout_1 ? 16 :
7535 i == Xsand_stoneout_2 ? 16 : 8);
7536 int cx = ABS(dx) * (TILEX / num_steps);
7537 int cy = ABS(dy) * (TILEY / num_steps);
7538 int step_frame = (i == Ydrip_s2 ? j + 8 :
7539 i == Ydrip_s2B ? j + 8 :
7540 i == Xsand_stonein_2 ? j + 8 :
7541 i == Xsand_stonein_3 ? j + 16 :
7542 i == Xsand_stonein_4 ? j + 24 :
7543 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7544 int step = (is_backside ? step_frame : num_steps - step_frame);
7546 if (is_backside) /* tile where movement starts */
7548 if (dx < 0 || dy < 0)
7550 g_em->src_offset_x = cx * step;
7551 g_em->src_offset_y = cy * step;
7555 g_em->dst_offset_x = cx * step;
7556 g_em->dst_offset_y = cy * step;
7559 else /* tile where movement ends */
7561 if (dx < 0 || dy < 0)
7563 g_em->dst_offset_x = cx * step;
7564 g_em->dst_offset_y = cy * step;
7568 g_em->src_offset_x = cx * step;
7569 g_em->src_offset_y = cy * step;
7573 g_em->width = TILEX - cx * step;
7574 g_em->height = TILEY - cy * step;
7577 /* create unique graphic identifier to decide if tile must be redrawn */
7578 /* bit 31 - 16 (16 bit): EM style graphic
7579 bit 15 - 12 ( 4 bit): EM style frame
7580 bit 11 - 6 ( 6 bit): graphic width
7581 bit 5 - 0 ( 6 bit): graphic height */
7582 g_em->unique_identifier =
7583 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7587 /* skip check for EMC elements not contained in original EMC artwork */
7588 if (element == EL_EMC_FAKE_ACID)
7591 if (g_em->bitmap != debug_bitmap ||
7592 g_em->src_x != debug_src_x ||
7593 g_em->src_y != debug_src_y ||
7594 g_em->src_offset_x != 0 ||
7595 g_em->src_offset_y != 0 ||
7596 g_em->dst_offset_x != 0 ||
7597 g_em->dst_offset_y != 0 ||
7598 g_em->width != TILEX ||
7599 g_em->height != TILEY)
7601 static int last_i = -1;
7609 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7610 i, element, element_info[element].token_name,
7611 element_action_info[effective_action].suffix, direction);
7613 if (element != effective_element)
7614 printf(" [%d ('%s')]",
7616 element_info[effective_element].token_name);
7620 if (g_em->bitmap != debug_bitmap)
7621 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7622 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7624 if (g_em->src_x != debug_src_x ||
7625 g_em->src_y != debug_src_y)
7626 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7627 j, (is_backside ? 'B' : 'F'),
7628 g_em->src_x, g_em->src_y,
7629 g_em->src_x / 32, g_em->src_y / 32,
7630 debug_src_x, debug_src_y,
7631 debug_src_x / 32, debug_src_y / 32);
7633 if (g_em->src_offset_x != 0 ||
7634 g_em->src_offset_y != 0 ||
7635 g_em->dst_offset_x != 0 ||
7636 g_em->dst_offset_y != 0)
7637 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7639 g_em->src_offset_x, g_em->src_offset_y,
7640 g_em->dst_offset_x, g_em->dst_offset_y);
7642 if (g_em->width != TILEX ||
7643 g_em->height != TILEY)
7644 printf(" %d (%d): size %d,%d should be %d,%d\n",
7646 g_em->width, g_em->height, TILEX, TILEY);
7648 num_em_gfx_errors++;
7655 for (i = 0; i < TILE_MAX; i++)
7657 for (j = 0; j < 8; j++)
7659 int element = object_mapping[i].element_rnd;
7660 int action = object_mapping[i].action;
7661 int direction = object_mapping[i].direction;
7662 boolean is_backside = object_mapping[i].is_backside;
7663 int graphic_action = el_act_dir2img(element, action, direction);
7664 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7666 if ((action == ACTION_SMASHED_BY_ROCK ||
7667 action == ACTION_SMASHED_BY_SPRING ||
7668 action == ACTION_EATING) &&
7669 graphic_action == graphic_default)
7671 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7672 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7673 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7674 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7677 /* no separate animation for "smashed by rock" -- use rock instead */
7678 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7679 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7681 g_em->bitmap = g_xx->bitmap;
7682 g_em->src_x = g_xx->src_x;
7683 g_em->src_y = g_xx->src_y;
7684 g_em->src_offset_x = g_xx->src_offset_x;
7685 g_em->src_offset_y = g_xx->src_offset_y;
7686 g_em->dst_offset_x = g_xx->dst_offset_x;
7687 g_em->dst_offset_y = g_xx->dst_offset_y;
7688 g_em->width = g_xx->width;
7689 g_em->height = g_xx->height;
7690 g_em->unique_identifier = g_xx->unique_identifier;
7693 g_em->preserve_background = TRUE;
7698 for (p = 0; p < MAX_PLAYERS; p++)
7700 for (i = 0; i < SPR_MAX; i++)
7702 int element = player_mapping[p][i].element_rnd;
7703 int action = player_mapping[p][i].action;
7704 int direction = player_mapping[p][i].direction;
7706 for (j = 0; j < 8; j++)
7708 int effective_element = element;
7709 int effective_action = action;
7710 int graphic = (direction == MV_NONE ?
7711 el_act2img(effective_element, effective_action) :
7712 el_act_dir2img(effective_element, effective_action,
7714 struct GraphicInfo *g = &graphic_info[graphic];
7715 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7721 Bitmap *debug_bitmap = g_em->bitmap;
7722 int debug_src_x = g_em->src_x;
7723 int debug_src_y = g_em->src_y;
7726 int frame = getAnimationFrame(g->anim_frames,
7729 g->anim_start_frame,
7732 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7734 g_em->bitmap = src_bitmap;
7735 g_em->src_x = src_x;
7736 g_em->src_y = src_y;
7737 g_em->src_offset_x = 0;
7738 g_em->src_offset_y = 0;
7739 g_em->dst_offset_x = 0;
7740 g_em->dst_offset_y = 0;
7741 g_em->width = TILEX;
7742 g_em->height = TILEY;
7746 /* skip check for EMC elements not contained in original EMC artwork */
7747 if (element == EL_PLAYER_3 ||
7748 element == EL_PLAYER_4)
7751 if (g_em->bitmap != debug_bitmap ||
7752 g_em->src_x != debug_src_x ||
7753 g_em->src_y != debug_src_y)
7755 static int last_i = -1;
7763 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7764 p, i, element, element_info[element].token_name,
7765 element_action_info[effective_action].suffix, direction);
7767 if (element != effective_element)
7768 printf(" [%d ('%s')]",
7770 element_info[effective_element].token_name);
7774 if (g_em->bitmap != debug_bitmap)
7775 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7776 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7778 if (g_em->src_x != debug_src_x ||
7779 g_em->src_y != debug_src_y)
7780 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7782 g_em->src_x, g_em->src_y,
7783 g_em->src_x / 32, g_em->src_y / 32,
7784 debug_src_x, debug_src_y,
7785 debug_src_x / 32, debug_src_y / 32);
7787 num_em_gfx_errors++;
7797 printf("::: [%d errors found]\n", num_em_gfx_errors);
7803 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7804 boolean any_player_moving,
7805 boolean any_player_snapping,
7806 boolean any_player_dropping)
7808 static boolean player_was_waiting = TRUE;
7810 if (frame == 0 && !any_player_dropping)
7812 if (!player_was_waiting)
7814 if (!SaveEngineSnapshotToList())
7817 player_was_waiting = TRUE;
7820 else if (any_player_moving || any_player_snapping || any_player_dropping)
7822 player_was_waiting = FALSE;
7826 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7827 boolean murphy_is_dropping)
7829 static boolean player_was_waiting = TRUE;
7831 if (murphy_is_waiting)
7833 if (!player_was_waiting)
7835 if (!SaveEngineSnapshotToList())
7838 player_was_waiting = TRUE;
7843 player_was_waiting = FALSE;
7847 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7848 boolean any_player_moving,
7849 boolean any_player_snapping,
7850 boolean any_player_dropping)
7852 if (tape.single_step && tape.recording && !tape.pausing)
7853 if (frame == 0 && !any_player_dropping)
7854 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7856 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7857 any_player_snapping, any_player_dropping);
7860 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7861 boolean murphy_is_dropping)
7863 if (tape.single_step && tape.recording && !tape.pausing)
7864 if (murphy_is_waiting)
7865 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7867 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7870 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7871 int graphic, int sync_frame, int x, int y)
7873 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7875 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7878 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7880 return (IS_NEXT_FRAME(sync_frame, graphic));
7883 int getGraphicInfo_Delay(int graphic)
7885 return graphic_info[graphic].anim_delay;
7888 void PlayMenuSoundExt(int sound)
7890 if (sound == SND_UNDEFINED)
7893 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7894 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7897 if (IS_LOOP_SOUND(sound))
7898 PlaySoundLoop(sound);
7903 void PlayMenuSound()
7905 PlayMenuSoundExt(menu.sound[game_status]);
7908 void PlayMenuSoundStereo(int sound, int stereo_position)
7910 if (sound == SND_UNDEFINED)
7913 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7914 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7917 if (IS_LOOP_SOUND(sound))
7918 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7920 PlaySoundStereo(sound, stereo_position);
7923 void PlayMenuSoundIfLoopExt(int sound)
7925 if (sound == SND_UNDEFINED)
7928 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7929 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7932 if (IS_LOOP_SOUND(sound))
7933 PlaySoundLoop(sound);
7936 void PlayMenuSoundIfLoop()
7938 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7941 void PlayMenuMusicExt(int music)
7943 if (music == MUS_UNDEFINED)
7946 if (!setup.sound_music)
7952 void PlayMenuMusic()
7954 PlayMenuMusicExt(menu.music[game_status]);
7957 void PlaySoundActivating()
7960 PlaySound(SND_MENU_ITEM_ACTIVATING);
7964 void PlaySoundSelecting()
7967 PlaySound(SND_MENU_ITEM_SELECTING);
7971 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7973 boolean change_fullscreen = (setup.fullscreen !=
7974 video.fullscreen_enabled);
7975 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7976 !strEqual(setup.fullscreen_mode,
7977 video.fullscreen_mode_current));
7978 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7979 setup.window_scaling_percent !=
7980 video.window_scaling_percent);
7982 if (change_window_scaling_percent && video.fullscreen_enabled)
7985 if (!change_window_scaling_percent && !video.fullscreen_available)
7988 #if defined(TARGET_SDL2)
7989 if (change_window_scaling_percent)
7991 SDLSetWindowScaling(setup.window_scaling_percent);
7995 else if (change_fullscreen)
7997 SDLSetWindowFullscreen(setup.fullscreen);
7999 /* set setup value according to successfully changed fullscreen mode */
8000 setup.fullscreen = video.fullscreen_enabled;
8006 if (change_fullscreen ||
8007 change_fullscreen_mode ||
8008 change_window_scaling_percent)
8010 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8012 /* save backbuffer content which gets lost when toggling fullscreen mode */
8013 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8015 if (change_fullscreen_mode)
8017 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8018 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8021 if (change_window_scaling_percent)
8023 /* keep window mode, but change window scaling */
8024 video.fullscreen_enabled = TRUE; /* force new window scaling */
8027 /* toggle fullscreen */
8028 ChangeVideoModeIfNeeded(setup.fullscreen);
8030 /* set setup value according to successfully changed fullscreen mode */
8031 setup.fullscreen = video.fullscreen_enabled;
8033 /* restore backbuffer content from temporary backbuffer backup bitmap */
8034 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8036 FreeBitmap(tmp_backbuffer);
8038 /* update visible window/screen */
8039 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8043 void ChangeViewportPropertiesIfNeeded()
8045 int gfx_game_mode = game_status;
8046 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8048 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8049 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8050 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8051 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8052 int border_size = vp_playfield->border_size;
8053 int new_sx = vp_playfield->x + border_size;
8054 int new_sy = vp_playfield->y + border_size;
8055 int new_sxsize = vp_playfield->width - 2 * border_size;
8056 int new_sysize = vp_playfield->height - 2 * border_size;
8057 int new_real_sx = vp_playfield->x;
8058 int new_real_sy = vp_playfield->y;
8059 int new_full_sxsize = vp_playfield->width;
8060 int new_full_sysize = vp_playfield->height;
8061 int new_dx = vp_door_1->x;
8062 int new_dy = vp_door_1->y;
8063 int new_dxsize = vp_door_1->width;
8064 int new_dysize = vp_door_1->height;
8065 int new_vx = vp_door_2->x;
8066 int new_vy = vp_door_2->y;
8067 int new_vxsize = vp_door_2->width;
8068 int new_vysize = vp_door_2->height;
8069 int new_ex = vp_door_3->x;
8070 int new_ey = vp_door_3->y;
8071 int new_exsize = vp_door_3->width;
8072 int new_eysize = vp_door_3->height;
8073 int new_tilesize_var =
8074 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8076 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8077 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8078 int new_scr_fieldx = new_sxsize / tilesize;
8079 int new_scr_fieldy = new_sysize / tilesize;
8080 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8081 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8082 boolean init_gfx_buffers = FALSE;
8083 boolean init_video_buffer = FALSE;
8084 boolean init_gadgets_and_toons = FALSE;
8085 boolean init_em_graphics = FALSE;
8086 boolean drawing_area_changed = FALSE;
8088 if (viewport.window.width != WIN_XSIZE ||
8089 viewport.window.height != WIN_YSIZE)
8091 WIN_XSIZE = viewport.window.width;
8092 WIN_YSIZE = viewport.window.height;
8094 init_video_buffer = TRUE;
8095 init_gfx_buffers = TRUE;
8097 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8100 if (new_scr_fieldx != SCR_FIELDX ||
8101 new_scr_fieldy != SCR_FIELDY)
8103 /* this always toggles between MAIN and GAME when using small tile size */
8105 SCR_FIELDX = new_scr_fieldx;
8106 SCR_FIELDY = new_scr_fieldy;
8108 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8119 new_sxsize != SXSIZE ||
8120 new_sysize != SYSIZE ||
8121 new_dxsize != DXSIZE ||
8122 new_dysize != DYSIZE ||
8123 new_vxsize != VXSIZE ||
8124 new_vysize != VYSIZE ||
8125 new_exsize != EXSIZE ||
8126 new_eysize != EYSIZE ||
8127 new_real_sx != REAL_SX ||
8128 new_real_sy != REAL_SY ||
8129 new_full_sxsize != FULL_SXSIZE ||
8130 new_full_sysize != FULL_SYSIZE ||
8131 new_tilesize_var != TILESIZE_VAR
8134 if (new_tilesize_var != TILESIZE_VAR)
8136 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8138 // changing tile size invalidates scroll values of engine snapshots
8139 FreeEngineSnapshotSingle();
8141 // changing tile size requires update of graphic mapping for EM engine
8142 init_em_graphics = TRUE;
8147 new_sxsize != SXSIZE ||
8148 new_sysize != SYSIZE ||
8149 new_real_sx != REAL_SX ||
8150 new_real_sy != REAL_SY ||
8151 new_full_sxsize != FULL_SXSIZE ||
8152 new_full_sysize != FULL_SYSIZE)
8154 if (!init_video_buffer)
8155 drawing_area_changed = TRUE;
8166 SXSIZE = new_sxsize;
8167 SYSIZE = new_sysize;
8168 DXSIZE = new_dxsize;
8169 DYSIZE = new_dysize;
8170 VXSIZE = new_vxsize;
8171 VYSIZE = new_vysize;
8172 EXSIZE = new_exsize;
8173 EYSIZE = new_eysize;
8174 REAL_SX = new_real_sx;
8175 REAL_SY = new_real_sy;
8176 FULL_SXSIZE = new_full_sxsize;
8177 FULL_SYSIZE = new_full_sysize;
8178 TILESIZE_VAR = new_tilesize_var;
8180 init_gfx_buffers = TRUE;
8181 init_gadgets_and_toons = TRUE;
8183 // printf("::: viewports: init_gfx_buffers\n");
8184 // printf("::: viewports: init_gadgets_and_toons\n");
8187 if (init_gfx_buffers)
8189 // printf("::: init_gfx_buffers\n");
8191 SCR_FIELDX = new_scr_fieldx_buffers;
8192 SCR_FIELDY = new_scr_fieldy_buffers;
8196 SCR_FIELDX = new_scr_fieldx;
8197 SCR_FIELDY = new_scr_fieldy;
8199 gfx.drawing_area_changed = drawing_area_changed;
8201 SetDrawDeactivationMask(REDRAW_NONE);
8202 SetDrawBackgroundMask(REDRAW_FIELD);
8205 if (init_video_buffer)
8207 // printf("::: init_video_buffer\n");
8209 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8212 if (init_gadgets_and_toons)
8214 // printf("::: init_gadgets_and_toons\n");
8220 if (init_em_graphics)
8222 InitGraphicInfo_EM();