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 && setup.soft_scrolling)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
247 drawto_field = fieldbuffer;
249 else /* DRAW_BACKBUFFER */
255 BX2 = SCR_FIELDX - 1;
256 BY2 = SCR_FIELDY - 1;
260 drawto_field = backbuffer;
264 static void RedrawPlayfield_RND()
266 if (game.envelope_active)
269 DrawLevel(REDRAW_ALL);
273 void RedrawPlayfield()
275 if (game_status != GAME_MODE_PLAYING)
278 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
279 RedrawPlayfield_EM(TRUE);
280 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
281 RedrawPlayfield_SP(TRUE);
282 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
283 RedrawPlayfield_RND();
285 BlitScreenToBitmap(backbuffer);
287 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
291 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
293 Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
295 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
298 void DrawMaskedBorder_FIELD()
300 if (global.border_status >= GAME_MODE_TITLE &&
301 global.border_status <= GAME_MODE_PLAYING &&
302 border.draw_masked[global.border_status])
303 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
306 void DrawMaskedBorder_DOOR_1()
308 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
309 (global.border_status != GAME_MODE_EDITOR ||
310 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
311 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
314 void DrawMaskedBorder_DOOR_2()
316 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
317 global.border_status != GAME_MODE_EDITOR)
318 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
321 void DrawMaskedBorder_DOOR_3()
323 /* currently not available */
326 void DrawMaskedBorder_ALL()
328 DrawMaskedBorder_FIELD();
329 DrawMaskedBorder_DOOR_1();
330 DrawMaskedBorder_DOOR_2();
331 DrawMaskedBorder_DOOR_3();
334 void DrawMaskedBorder(int redraw_mask)
336 /* never draw masked screen borders on borderless screens */
337 if (effectiveGameStatus() == GAME_MODE_LOADING ||
338 effectiveGameStatus() == GAME_MODE_TITLE)
341 /* never draw masked screen borders when displaying request outside door */
342 if (effectiveGameStatus() == GAME_MODE_PSEUDO_DOOR &&
343 global.use_envelope_request)
346 if (redraw_mask & REDRAW_ALL)
347 DrawMaskedBorder_ALL();
350 if (redraw_mask & REDRAW_FIELD)
351 DrawMaskedBorder_FIELD();
352 if (redraw_mask & REDRAW_DOOR_1)
353 DrawMaskedBorder_DOOR_1();
354 if (redraw_mask & REDRAW_DOOR_2)
355 DrawMaskedBorder_DOOR_2();
356 if (redraw_mask & REDRAW_DOOR_3)
357 DrawMaskedBorder_DOOR_3();
361 static void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
363 DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
364 int fx = FX, fy = FY;
365 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
366 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
368 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
369 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
370 int dx_var = dx * TILESIZE_VAR / TILESIZE;
371 int dy_var = dy * TILESIZE_VAR / TILESIZE;
374 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
375 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
377 if (EVEN(SCR_FIELDX))
379 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
380 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
382 fx += (dx_var > 0 ? TILEX_VAR : 0);
389 if (EVEN(SCR_FIELDY))
391 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
392 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
394 fy += (dy_var > 0 ? TILEY_VAR : 0);
401 if (full_lev_fieldx <= SCR_FIELDX)
403 if (EVEN(SCR_FIELDX))
404 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
406 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
409 if (full_lev_fieldy <= SCR_FIELDY)
411 if (EVEN(SCR_FIELDY))
412 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
414 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
417 if (border.draw_masked[GAME_MODE_PLAYING])
419 if (buffer != backbuffer)
421 /* copy playfield buffer to backbuffer to add masked border */
422 BlitBitmap(buffer, backbuffer, fx, fy, SXSIZE, SYSIZE, SX, SY);
423 DrawMaskedBorder(REDRAW_FIELD);
426 BlitBitmap(backbuffer, target_bitmap,
427 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
432 BlitBitmap(buffer, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
436 void BlitScreenToBitmap(Bitmap *target_bitmap)
438 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
439 BlitScreenToBitmap_EM(target_bitmap);
440 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
441 BlitScreenToBitmap_SP(target_bitmap);
442 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
443 BlitScreenToBitmap_RND(target_bitmap);
449 DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
451 if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
452 redraw_mask |= REDRAW_FIELD;
455 // never redraw single tiles, always redraw the whole field
456 // (redrawing single tiles up to a certain threshold was faster on old,
457 // now legacy graphics, but slows things down on modern graphics now)
458 // UPDATE: this is now globally defined by value of REDRAWTILES_THRESHOLD
459 if (redraw_mask & REDRAW_TILES)
460 redraw_mask |= REDRAW_FIELD;
464 /* !!! TEST ONLY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
465 /* (force full redraw) */
466 if (game_status == GAME_MODE_PLAYING)
467 redraw_mask |= REDRAW_FIELD;
470 if (redraw_mask & REDRAW_FIELD)
471 redraw_mask &= ~REDRAW_TILES;
473 if (redraw_mask == REDRAW_NONE)
478 if (redraw_mask & REDRAW_ALL)
479 printf("[REDRAW_ALL]");
480 if (redraw_mask & REDRAW_FIELD)
481 printf("[REDRAW_FIELD]");
482 if (redraw_mask & REDRAW_TILES)
483 printf("[REDRAW_TILES]");
484 if (redraw_mask & REDRAW_DOOR_1)
485 printf("[REDRAW_DOOR_1]");
486 if (redraw_mask & REDRAW_DOOR_2)
487 printf("[REDRAW_DOOR_2]");
488 if (redraw_mask & REDRAW_FROM_BACKBUFFER)
489 printf("[REDRAW_FROM_BACKBUFFER]");
490 printf(" [%d]\n", FrameCounter);
493 if (redraw_mask & REDRAW_TILES &&
494 game_status == GAME_MODE_PLAYING &&
495 border.draw_masked[GAME_MODE_PLAYING])
496 redraw_mask |= REDRAW_FIELD;
498 if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
500 static boolean last_frame_skipped = FALSE;
501 boolean skip_even_when_not_scrolling = TRUE;
502 boolean just_scrolling = (ScreenMovDir != 0);
503 boolean verbose = FALSE;
505 if (global.fps_slowdown_factor > 1 &&
506 (FrameCounter % global.fps_slowdown_factor) &&
507 (just_scrolling || skip_even_when_not_scrolling))
509 redraw_mask &= ~REDRAW_MAIN;
511 last_frame_skipped = TRUE;
514 printf("FRAME SKIPPED\n");
518 if (last_frame_skipped)
519 redraw_mask |= REDRAW_FIELD;
521 last_frame_skipped = FALSE;
524 printf("frame not skipped\n");
528 /* synchronize X11 graphics at this point; if we would synchronize the
529 display immediately after the buffer switching (after the XFlush),
530 this could mean that we have to wait for the graphics to complete,
531 although we could go on doing calculations for the next frame */
535 /* never draw masked border to backbuffer when using playfield buffer */
536 if (game_status != GAME_MODE_PLAYING ||
537 redraw_mask & REDRAW_FROM_BACKBUFFER ||
538 buffer == backbuffer)
539 DrawMaskedBorder(redraw_mask);
541 DrawMaskedBorder(redraw_mask & REDRAW_DOORS);
543 if (redraw_mask & REDRAW_ALL)
545 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
547 redraw_mask = REDRAW_NONE;
550 if (redraw_mask & REDRAW_FIELD)
552 if (game_status != GAME_MODE_PLAYING ||
553 redraw_mask & REDRAW_FROM_BACKBUFFER)
555 BlitBitmap(backbuffer, window,
556 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
560 BlitScreenToBitmap_RND(window);
563 redraw_mask &= ~REDRAW_MAIN;
566 if (redraw_mask & REDRAW_DOORS)
568 if (redraw_mask & REDRAW_DOOR_1)
569 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
571 if (redraw_mask & REDRAW_DOOR_2)
572 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
574 if (redraw_mask & REDRAW_DOOR_3)
575 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
577 redraw_mask &= ~REDRAW_DOORS;
580 if (redraw_mask & REDRAW_MICROLEVEL)
582 BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
583 SX, SY + 10 * TILEY);
585 redraw_mask &= ~REDRAW_MICROLEVEL;
588 if (redraw_mask & REDRAW_TILES)
594 int dx_var = dx * TILESIZE_VAR / TILESIZE;
595 int dy_var = dy * TILESIZE_VAR / TILESIZE;
597 int fx = FX, fy = FY;
599 int scr_fieldx = SCR_FIELDX + (EVEN(SCR_FIELDX) ? 2 : 0);
600 int scr_fieldy = SCR_FIELDY + (EVEN(SCR_FIELDY) ? 2 : 0);
602 InitGfxClipRegion(TRUE, SX, SY, SXSIZE, SYSIZE);
604 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
605 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
607 if (EVEN(SCR_FIELDX))
609 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
611 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
620 fx += (dx_var > 0 ? TILEX_VAR : 0);
624 if (EVEN(SCR_FIELDY))
626 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
628 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
637 fy += (dy_var > 0 ? TILEY_VAR : 0);
641 for (x = 0; x < scr_fieldx; x++)
642 for (y = 0 ; y < scr_fieldy; y++)
643 if (redraw[redraw_x1 + x][redraw_y1 + y])
644 BlitBitmap(buffer, window,
645 FX + x * TILEX_VAR, FY + y * TILEY_VAR,
646 TILEX_VAR, TILEY_VAR,
647 sx + x * TILEX_VAR, sy + y * TILEY_VAR);
649 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
652 if (redraw_mask & REDRAW_FPS) /* display frames per second */
657 sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
658 if (!global.fps_slowdown)
661 sprintf(text, "%04.1f fps%s", global.frames_per_second, info1);
663 DrawTextExt(window, SX + SXSIZE + SX, 0, text, FONT_TEXT_2, BLIT_OPAQUE);
666 for (x = 0; x < MAX_BUF_XSIZE; x++)
667 for (y = 0; y < MAX_BUF_YSIZE; y++)
670 redraw_mask = REDRAW_NONE;
673 static void FadeCrossSaveBackbuffer()
675 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
678 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
680 static int fade_type_skip = FADE_TYPE_NONE;
681 void (*draw_border_function)(void) = NULL;
682 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
683 int x, y, width, height;
684 int fade_delay, post_delay;
686 if (fade_type == FADE_TYPE_FADE_OUT)
688 if (fade_type_skip != FADE_TYPE_NONE)
690 /* skip all fade operations until specified fade operation */
691 if (fade_type & fade_type_skip)
692 fade_type_skip = FADE_TYPE_NONE;
697 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
699 FadeCrossSaveBackbuffer();
705 redraw_mask |= fade_mask;
707 if (fade_type == FADE_TYPE_SKIP)
709 fade_type_skip = fade_mode;
714 fade_delay = fading.fade_delay;
715 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
717 if (fade_type_skip != FADE_TYPE_NONE)
719 /* skip all fade operations until specified fade operation */
720 if (fade_type & fade_type_skip)
721 fade_type_skip = FADE_TYPE_NONE;
726 if (global.autoplay_leveldir)
731 if (fade_mask == REDRAW_FIELD)
736 height = FULL_SYSIZE;
738 if (border.draw_masked_when_fading)
739 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
741 DrawMaskedBorder_FIELD(); /* draw once */
743 else /* REDRAW_ALL */
751 if (!setup.fade_screens ||
753 fading.fade_mode == FADE_MODE_NONE)
755 if (fade_mode == FADE_MODE_FADE_OUT)
758 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
760 redraw_mask &= ~fade_mask;
765 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
766 draw_border_function);
768 redraw_mask &= ~fade_mask;
771 void FadeIn(int fade_mask)
773 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
774 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
776 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
779 void FadeOut(int fade_mask)
781 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
782 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
784 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
786 global.border_status = game_status;
789 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
791 static struct TitleFadingInfo fading_leave_stored;
794 fading_leave_stored = fading_leave;
796 fading = fading_leave_stored;
799 void FadeSetEnterMenu()
801 fading = menu.enter_menu;
803 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
806 void FadeSetLeaveMenu()
808 fading = menu.leave_menu;
810 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
813 void FadeSetEnterScreen()
815 fading = menu.enter_screen[game_status];
817 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
820 void FadeSetNextScreen()
822 fading = menu.next_screen;
824 // (do not overwrite fade mode set by FadeSetEnterScreen)
825 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
828 void FadeSetLeaveScreen()
830 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
833 void FadeSetFromType(int type)
835 if (type & TYPE_ENTER_SCREEN)
836 FadeSetEnterScreen();
837 else if (type & TYPE_ENTER)
839 else if (type & TYPE_LEAVE)
843 void FadeSetDisabled()
845 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
847 fading = fading_none;
850 void FadeSkipNextFadeIn()
852 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
855 void FadeSkipNextFadeOut()
857 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
860 void SetWindowBackgroundImageIfDefined(int graphic)
862 if (graphic_info[graphic].bitmap)
863 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
866 void SetMainBackgroundImageIfDefined(int graphic)
868 if (graphic_info[graphic].bitmap)
869 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
872 void SetDoorBackgroundImageIfDefined(int graphic)
874 if (graphic_info[graphic].bitmap)
875 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
878 void SetWindowBackgroundImage(int graphic)
880 SetWindowBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
881 graphic_info[graphic].bitmap ?
882 graphic_info[graphic].bitmap :
883 graphic_info[IMG_BACKGROUND].bitmap);
886 void SetMainBackgroundImage(int graphic)
888 SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
889 graphic_info[graphic].bitmap ?
890 graphic_info[graphic].bitmap :
891 graphic_info[IMG_BACKGROUND].bitmap);
894 void SetDoorBackgroundImage(int graphic)
896 SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
897 graphic_info[graphic].bitmap ?
898 graphic_info[graphic].bitmap :
899 graphic_info[IMG_BACKGROUND].bitmap);
902 void SetPanelBackground()
904 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
906 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
907 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
909 SetDoorBackgroundBitmap(bitmap_db_panel);
912 void DrawBackground(int x, int y, int width, int height)
914 /* "drawto" might still point to playfield buffer here (hall of fame) */
915 ClearRectangleOnBackground(backbuffer, x, y, width, height);
917 if (IN_GFX_FIELD_FULL(x, y))
918 redraw_mask |= REDRAW_FIELD;
919 else if (IN_GFX_DOOR_1(x, y))
920 redraw_mask |= REDRAW_DOOR_1;
921 else if (IN_GFX_DOOR_2(x, y))
922 redraw_mask |= REDRAW_DOOR_2;
923 else if (IN_GFX_DOOR_3(x, y))
924 redraw_mask |= REDRAW_DOOR_3;
927 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
929 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
931 if (font->bitmap == NULL)
934 DrawBackground(x, y, width, height);
937 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
939 struct GraphicInfo *g = &graphic_info[graphic];
941 if (g->bitmap == NULL)
944 DrawBackground(x, y, width, height);
949 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
950 /* (when entering hall of fame after playing) */
951 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
953 /* !!! maybe this should be done before clearing the background !!! */
954 if (setup.soft_scrolling && game_status == GAME_MODE_PLAYING)
956 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
957 SetDrawtoField(DRAW_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 (!setup.soft_scrolling && ScreenMovPos)
3154 if (player_is_opaque)
3155 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3157 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3159 if (SHIELD_ON(player))
3161 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3162 IMG_SHIELD_NORMAL_ACTIVE);
3163 int frame = getGraphicAnimationFrame(graphic, -1);
3165 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3169 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3172 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3173 sxx = player->GfxPos;
3175 syy = player->GfxPos;
3179 /* ----------------------------------------------------------------------- */
3180 /* draw things the player is pushing, if needed */
3181 /* ----------------------------------------------------------------------- */
3183 if (player->is_pushing && player->is_moving)
3185 int px = SCREENX(jx), py = SCREENY(jy);
3186 int pxx = (TILEX - ABS(sxx)) * dx;
3187 int pyy = (TILEY - ABS(syy)) * dy;
3188 int gfx_frame = GfxFrame[jx][jy];
3194 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3196 element = Feld[next_jx][next_jy];
3197 gfx_frame = GfxFrame[next_jx][next_jy];
3200 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3202 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3203 frame = getGraphicAnimationFrame(graphic, sync_frame);
3205 /* draw background element under pushed element (like the Sokoban field) */
3206 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3208 /* this allows transparent pushing animation over non-black background */
3211 DrawLevelElement(jx, jy, Back[jx][jy]);
3213 DrawLevelElement(jx, jy, EL_EMPTY);
3215 if (Back[next_jx][next_jy])
3216 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3218 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3220 else if (Back[next_jx][next_jy])
3221 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3224 /* do not draw (EM style) pushing animation when pushing is finished */
3225 /* (two-tile animations usually do not contain start and end frame) */
3226 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3227 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3229 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3231 /* masked drawing is needed for EMC style (double) movement graphics */
3232 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3233 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3237 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3238 /* ----------------------------------------------------------------------- */
3239 /* draw player himself */
3240 /* ----------------------------------------------------------------------- */
3242 graphic = getPlayerGraphic(player, move_dir);
3244 /* in the case of changed player action or direction, prevent the current
3245 animation frame from being restarted for identical animations */
3246 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3247 player->Frame = last_player_frame;
3249 frame = getGraphicAnimationFrame(graphic, player->Frame);
3253 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3254 sxx = player->GfxPos;
3256 syy = player->GfxPos;
3259 if (!setup.soft_scrolling && ScreenMovPos)
3262 if (player_is_opaque)
3263 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3265 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3267 if (SHIELD_ON(player))
3269 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3270 IMG_SHIELD_NORMAL_ACTIVE);
3271 int frame = getGraphicAnimationFrame(graphic, -1);
3273 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3277 /* ----------------------------------------------------------------------- */
3278 /* draw things in front of player (active dynamite or dynabombs) */
3279 /* ----------------------------------------------------------------------- */
3281 if (IS_ACTIVE_BOMB(element))
3283 graphic = el2img(element);
3284 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3286 if (game.emulation == EMU_SUPAPLEX)
3287 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3289 DrawGraphicThruMask(sx, sy, graphic, frame);
3292 if (player_is_moving && last_element == EL_EXPLOSION)
3294 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3295 GfxElement[last_jx][last_jy] : EL_EMPTY);
3296 int graphic = el_act2img(element, ACTION_EXPLODING);
3297 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3298 int phase = ExplodePhase[last_jx][last_jy] - 1;
3299 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3302 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3305 /* ----------------------------------------------------------------------- */
3306 /* draw elements the player is just walking/passing through/under */
3307 /* ----------------------------------------------------------------------- */
3309 if (player_is_moving)
3311 /* handle the field the player is leaving ... */
3312 if (IS_ACCESSIBLE_INSIDE(last_element))
3313 DrawLevelField(last_jx, last_jy);
3314 else if (IS_ACCESSIBLE_UNDER(last_element))
3315 DrawLevelFieldThruMask(last_jx, last_jy);
3318 /* do not redraw accessible elements if the player is just pushing them */
3319 if (!player_is_moving || !player->is_pushing)
3321 /* ... and the field the player is entering */
3322 if (IS_ACCESSIBLE_INSIDE(element))
3323 DrawLevelField(jx, jy);
3324 else if (IS_ACCESSIBLE_UNDER(element))
3325 DrawLevelFieldThruMask(jx, jy);
3328 MarkTileDirty(sx, sy);
3331 /* ------------------------------------------------------------------------- */
3333 void WaitForEventToContinue()
3335 boolean still_wait = TRUE;
3337 /* simulate releasing mouse button over last gadget, if still pressed */
3339 HandleGadgets(-1, -1, 0);
3341 button_status = MB_RELEASED;
3355 case EVENT_BUTTONPRESS:
3356 case EVENT_KEYPRESS:
3360 case EVENT_KEYRELEASE:
3361 ClearPlayerAction();
3365 HandleOtherEvents(&event);
3369 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3376 /* don't eat all CPU time */
3381 #define MAX_REQUEST_LINES 13
3382 #define MAX_REQUEST_LINE_FONT1_LEN 7
3383 #define MAX_REQUEST_LINE_FONT2_LEN 10
3385 static int RequestHandleEvents(unsigned int req_state)
3387 int last_game_status = game_status; /* save current game status */
3391 button_status = MB_RELEASED;
3393 request_gadget_id = -1;
3402 while (NextValidEvent(&event))
3406 case EVENT_BUTTONPRESS:
3407 case EVENT_BUTTONRELEASE:
3408 case EVENT_MOTIONNOTIFY:
3410 if (event.type == EVENT_MOTIONNOTIFY)
3412 if (!PointerInWindow(window))
3413 continue; /* window and pointer on different screens */
3418 motion_status = TRUE;
3419 mx = ((MotionEvent *) &event)->x;
3420 my = ((MotionEvent *) &event)->y;
3424 motion_status = FALSE;
3425 mx = ((ButtonEvent *) &event)->x;
3426 my = ((ButtonEvent *) &event)->y;
3427 if (event.type == EVENT_BUTTONPRESS)
3428 button_status = ((ButtonEvent *) &event)->button;
3430 button_status = MB_RELEASED;
3433 /* this sets 'request_gadget_id' */
3434 HandleGadgets(mx, my, button_status);
3436 switch (request_gadget_id)
3438 case TOOL_CTRL_ID_YES:
3441 case TOOL_CTRL_ID_NO:
3444 case TOOL_CTRL_ID_CONFIRM:
3445 result = TRUE | FALSE;
3448 case TOOL_CTRL_ID_PLAYER_1:
3451 case TOOL_CTRL_ID_PLAYER_2:
3454 case TOOL_CTRL_ID_PLAYER_3:
3457 case TOOL_CTRL_ID_PLAYER_4:
3468 case EVENT_KEYPRESS:
3469 switch (GetEventKey((KeyEvent *)&event, TRUE))
3472 if (req_state & REQ_CONFIRM)
3477 #if defined(TARGET_SDL2)
3484 #if defined(TARGET_SDL2)
3494 if (req_state & REQ_PLAYER)
3498 case EVENT_KEYRELEASE:
3499 ClearPlayerAction();
3503 HandleOtherEvents(&event);
3508 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3510 int joy = AnyJoystick();
3512 if (joy & JOY_BUTTON_1)
3514 else if (joy & JOY_BUTTON_2)
3518 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3520 HandleGameActions();
3526 if (!PendingEvent()) /* delay only if no pending events */
3530 game_status = GAME_MODE_PSEUDO_DOOR;
3534 game_status = last_game_status; /* restore current game status */
3540 static boolean RequestDoor(char *text, unsigned int req_state)
3542 unsigned int old_door_state;
3543 int last_game_status = game_status; /* save current game status */
3544 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3545 int font_nr = FONT_TEXT_2;
3550 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3552 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3553 font_nr = FONT_TEXT_1;
3556 if (game_status == GAME_MODE_PLAYING)
3557 BlitScreenToBitmap(backbuffer);
3559 /* disable deactivated drawing when quick-loading level tape recording */
3560 if (tape.playing && tape.deactivate_display)
3561 TapeDeactivateDisplayOff(TRUE);
3563 SetMouseCursor(CURSOR_DEFAULT);
3565 #if defined(NETWORK_AVALIABLE)
3566 /* pause network game while waiting for request to answer */
3567 if (options.network &&
3568 game_status == GAME_MODE_PLAYING &&
3569 req_state & REQUEST_WAIT_FOR_INPUT)
3570 SendToServer_PausePlaying();
3573 old_door_state = GetDoorState();
3575 /* simulate releasing mouse button over last gadget, if still pressed */
3577 HandleGadgets(-1, -1, 0);
3581 /* draw released gadget before proceeding */
3584 if (old_door_state & DOOR_OPEN_1)
3586 CloseDoor(DOOR_CLOSE_1);
3588 /* save old door content */
3589 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3590 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3593 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3594 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3596 /* clear door drawing field */
3597 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3599 /* force DOOR font inside door area */
3600 game_status = GAME_MODE_PSEUDO_DOOR;
3602 /* write text for request */
3603 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3605 char text_line[max_request_line_len + 1];
3611 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3613 tc = *(text_ptr + tx);
3614 // if (!tc || tc == ' ')
3615 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3619 if ((tc == '?' || tc == '!') && tl == 0)
3629 strncpy(text_line, text_ptr, tl);
3632 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3633 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3634 text_line, font_nr);
3636 text_ptr += tl + (tc == ' ' ? 1 : 0);
3637 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3640 game_status = last_game_status; /* restore current game status */
3642 if (req_state & REQ_ASK)
3644 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3645 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3647 else if (req_state & REQ_CONFIRM)
3649 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3651 else if (req_state & REQ_PLAYER)
3653 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3654 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3655 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3656 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3659 /* copy request gadgets to door backbuffer */
3660 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3662 OpenDoor(DOOR_OPEN_1);
3664 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3666 if (game_status == GAME_MODE_PLAYING)
3668 SetPanelBackground();
3669 SetDrawBackgroundMask(REDRAW_DOOR_1);
3673 SetDrawBackgroundMask(REDRAW_FIELD);
3679 if (game_status != GAME_MODE_MAIN)
3682 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3684 // ---------- handle request buttons ----------
3685 result = RequestHandleEvents(req_state);
3687 if (game_status != GAME_MODE_MAIN)
3692 if (!(req_state & REQ_STAY_OPEN))
3694 CloseDoor(DOOR_CLOSE_1);
3696 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3697 (req_state & REQ_REOPEN))
3698 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3703 if (game_status == GAME_MODE_PLAYING)
3705 SetPanelBackground();
3706 SetDrawBackgroundMask(REDRAW_DOOR_1);
3710 SetDrawBackgroundMask(REDRAW_FIELD);
3713 #if defined(NETWORK_AVALIABLE)
3714 /* continue network game after request */
3715 if (options.network &&
3716 game_status == GAME_MODE_PLAYING &&
3717 req_state & REQUEST_WAIT_FOR_INPUT)
3718 SendToServer_ContinuePlaying();
3721 /* restore deactivated drawing when quick-loading level tape recording */
3722 if (tape.playing && tape.deactivate_display)
3723 TapeDeactivateDisplayOn();
3728 static boolean RequestEnvelope(char *text, unsigned int req_state)
3732 if (game_status == GAME_MODE_PLAYING)
3733 BlitScreenToBitmap(backbuffer);
3735 /* disable deactivated drawing when quick-loading level tape recording */
3736 if (tape.playing && tape.deactivate_display)
3737 TapeDeactivateDisplayOff(TRUE);
3739 SetMouseCursor(CURSOR_DEFAULT);
3741 #if defined(NETWORK_AVALIABLE)
3742 /* pause network game while waiting for request to answer */
3743 if (options.network &&
3744 game_status == GAME_MODE_PLAYING &&
3745 req_state & REQUEST_WAIT_FOR_INPUT)
3746 SendToServer_PausePlaying();
3749 /* simulate releasing mouse button over last gadget, if still pressed */
3751 HandleGadgets(-1, -1, 0);
3755 // (replace with setting corresponding request background)
3756 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3757 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3759 /* clear door drawing field */
3760 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3762 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3764 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3766 if (game_status == GAME_MODE_PLAYING)
3768 SetPanelBackground();
3769 SetDrawBackgroundMask(REDRAW_DOOR_1);
3773 SetDrawBackgroundMask(REDRAW_FIELD);
3779 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3781 // ---------- handle request buttons ----------
3782 result = RequestHandleEvents(req_state);
3784 if (game_status != GAME_MODE_MAIN)
3789 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3793 if (game_status == GAME_MODE_PLAYING)
3795 SetPanelBackground();
3796 SetDrawBackgroundMask(REDRAW_DOOR_1);
3800 SetDrawBackgroundMask(REDRAW_FIELD);
3803 #if defined(NETWORK_AVALIABLE)
3804 /* continue network game after request */
3805 if (options.network &&
3806 game_status == GAME_MODE_PLAYING &&
3807 req_state & REQUEST_WAIT_FOR_INPUT)
3808 SendToServer_ContinuePlaying();
3811 /* restore deactivated drawing when quick-loading level tape recording */
3812 if (tape.playing && tape.deactivate_display)
3813 TapeDeactivateDisplayOn();
3818 boolean Request(char *text, unsigned int req_state)
3820 if (global.use_envelope_request)
3821 return RequestEnvelope(text, req_state);
3823 return RequestDoor(text, req_state);
3826 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3828 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3829 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3832 if (dpo1->sort_priority != dpo2->sort_priority)
3833 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3835 compare_result = dpo1->nr - dpo2->nr;
3837 return compare_result;
3840 void InitGraphicCompatibilityInfo_Doors()
3846 struct DoorInfo *door;
3850 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3851 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3853 { -1, -1, -1, NULL }
3855 struct Rect door_rect_list[] =
3857 { DX, DY, DXSIZE, DYSIZE },
3858 { VX, VY, VXSIZE, VYSIZE }
3862 for (i = 0; doors[i].door_token != -1; i++)
3864 int door_token = doors[i].door_token;
3865 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3866 int part_1 = doors[i].part_1;
3867 int part_8 = doors[i].part_8;
3868 int part_2 = part_1 + 1;
3869 int part_3 = part_1 + 2;
3870 struct DoorInfo *door = doors[i].door;
3871 struct Rect *door_rect = &door_rect_list[door_index];
3872 boolean door_gfx_redefined = FALSE;
3874 /* check if any door part graphic definitions have been redefined */
3876 for (j = 0; door_part_controls[j].door_token != -1; j++)
3878 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3879 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3881 if (dpc->door_token == door_token && fi->redefined)
3882 door_gfx_redefined = TRUE;
3885 /* check for old-style door graphic/animation modifications */
3887 if (!door_gfx_redefined)
3889 if (door->anim_mode & ANIM_STATIC_PANEL)
3891 door->panel.step_xoffset = 0;
3892 door->panel.step_yoffset = 0;
3895 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3897 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3898 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3899 int num_door_steps, num_panel_steps;
3901 /* remove door part graphics other than the two default wings */
3903 for (j = 0; door_part_controls[j].door_token != -1; j++)
3905 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3906 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3908 if (dpc->graphic >= part_3 &&
3909 dpc->graphic <= part_8)
3913 /* set graphics and screen positions of the default wings */
3915 g_part_1->width = door_rect->width;
3916 g_part_1->height = door_rect->height;
3917 g_part_2->width = door_rect->width;
3918 g_part_2->height = door_rect->height;
3919 g_part_2->src_x = door_rect->width;
3920 g_part_2->src_y = g_part_1->src_y;
3922 door->part_2.x = door->part_1.x;
3923 door->part_2.y = door->part_1.y;
3925 if (door->width != -1)
3927 g_part_1->width = door->width;
3928 g_part_2->width = door->width;
3930 // special treatment for graphics and screen position of right wing
3931 g_part_2->src_x += door_rect->width - door->width;
3932 door->part_2.x += door_rect->width - door->width;
3935 if (door->height != -1)
3937 g_part_1->height = door->height;
3938 g_part_2->height = door->height;
3940 // special treatment for graphics and screen position of bottom wing
3941 g_part_2->src_y += door_rect->height - door->height;
3942 door->part_2.y += door_rect->height - door->height;
3945 /* set animation delays for the default wings and panels */
3947 door->part_1.step_delay = door->step_delay;
3948 door->part_2.step_delay = door->step_delay;
3949 door->panel.step_delay = door->step_delay;
3951 /* set animation draw order for the default wings */
3953 door->part_1.sort_priority = 2; /* draw left wing over ... */
3954 door->part_2.sort_priority = 1; /* ... right wing */
3956 /* set animation draw offset for the default wings */
3958 if (door->anim_mode & ANIM_HORIZONTAL)
3960 door->part_1.step_xoffset = door->step_offset;
3961 door->part_1.step_yoffset = 0;
3962 door->part_2.step_xoffset = door->step_offset * -1;
3963 door->part_2.step_yoffset = 0;
3965 num_door_steps = g_part_1->width / door->step_offset;
3967 else // ANIM_VERTICAL
3969 door->part_1.step_xoffset = 0;
3970 door->part_1.step_yoffset = door->step_offset;
3971 door->part_2.step_xoffset = 0;
3972 door->part_2.step_yoffset = door->step_offset * -1;
3974 num_door_steps = g_part_1->height / door->step_offset;
3977 /* set animation draw offset for the default panels */
3979 if (door->step_offset > 1)
3981 num_panel_steps = 2 * door_rect->height / door->step_offset;
3982 door->panel.start_step = num_panel_steps - num_door_steps;
3983 door->panel.start_step_closing = door->panel.start_step;
3987 num_panel_steps = door_rect->height / door->step_offset;
3988 door->panel.start_step = num_panel_steps - num_door_steps / 2;
3989 door->panel.start_step_closing = door->panel.start_step;
3990 door->panel.step_delay *= 2;
4001 for (i = 0; door_part_controls[i].door_token != -1; i++)
4003 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4004 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4006 /* initialize "start_step_opening" and "start_step_closing", if needed */
4007 if (dpc->pos->start_step_opening == 0 &&
4008 dpc->pos->start_step_closing == 0)
4010 // dpc->pos->start_step_opening = dpc->pos->start_step;
4011 dpc->pos->start_step_closing = dpc->pos->start_step;
4014 /* fill structure for door part draw order (sorted below) */
4016 dpo->sort_priority = dpc->pos->sort_priority;
4019 /* sort door part controls according to sort_priority and graphic number */
4020 qsort(door_part_order, MAX_DOOR_PARTS,
4021 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4024 unsigned int OpenDoor(unsigned int door_state)
4026 if (door_state & DOOR_COPY_BACK)
4028 if (door_state & DOOR_OPEN_1)
4029 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4030 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4032 if (door_state & DOOR_OPEN_2)
4033 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4034 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4036 door_state &= ~DOOR_COPY_BACK;
4039 return MoveDoor(door_state);
4042 unsigned int CloseDoor(unsigned int door_state)
4044 unsigned int old_door_state = GetDoorState();
4046 if (!(door_state & DOOR_NO_COPY_BACK))
4048 if (old_door_state & DOOR_OPEN_1)
4049 BlitBitmap(backbuffer, bitmap_db_door_1,
4050 DX, DY, DXSIZE, DYSIZE, 0, 0);
4052 if (old_door_state & DOOR_OPEN_2)
4053 BlitBitmap(backbuffer, bitmap_db_door_2,
4054 VX, VY, VXSIZE, VYSIZE, 0, 0);
4056 door_state &= ~DOOR_NO_COPY_BACK;
4059 return MoveDoor(door_state);
4062 unsigned int GetDoorState()
4064 return MoveDoor(DOOR_GET_STATE);
4067 unsigned int SetDoorState(unsigned int door_state)
4069 return MoveDoor(door_state | DOOR_SET_STATE);
4072 int euclid(int a, int b)
4074 return (b ? euclid(b, a % b) : a);
4077 unsigned int MoveDoor(unsigned int door_state)
4079 struct Rect door_rect_list[] =
4081 { DX, DY, DXSIZE, DYSIZE },
4082 { VX, VY, VXSIZE, VYSIZE }
4084 static int door1 = DOOR_OPEN_1;
4085 static int door2 = DOOR_CLOSE_2;
4086 unsigned int door_delay = 0;
4087 unsigned int door_delay_value;
4090 if (door_state == DOOR_GET_STATE)
4091 return (door1 | door2);
4093 if (door_state & DOOR_SET_STATE)
4095 if (door_state & DOOR_ACTION_1)
4096 door1 = door_state & DOOR_ACTION_1;
4097 if (door_state & DOOR_ACTION_2)
4098 door2 = door_state & DOOR_ACTION_2;
4100 return (door1 | door2);
4103 if (!(door_state & DOOR_FORCE_REDRAW))
4105 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4106 door_state &= ~DOOR_OPEN_1;
4107 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4108 door_state &= ~DOOR_CLOSE_1;
4109 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4110 door_state &= ~DOOR_OPEN_2;
4111 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4112 door_state &= ~DOOR_CLOSE_2;
4115 if (global.autoplay_leveldir)
4117 door_state |= DOOR_NO_DELAY;
4118 door_state &= ~DOOR_CLOSE_ALL;
4121 if (game_status == GAME_MODE_EDITOR)
4122 door_state |= DOOR_NO_DELAY;
4124 if (door_state & DOOR_ACTION)
4126 boolean door_panel_drawn[NUM_DOORS];
4127 boolean panel_has_doors[NUM_DOORS];
4128 boolean door_part_skip[MAX_DOOR_PARTS];
4129 boolean door_part_done[MAX_DOOR_PARTS];
4130 boolean door_part_done_all;
4131 int num_steps[MAX_DOOR_PARTS];
4132 int max_move_delay = 0; // delay for complete animations of all doors
4133 int max_step_delay = 0; // delay (ms) between two animation frames
4134 int num_move_steps = 0; // number of animation steps for all doors
4135 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4136 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4137 int current_move_delay = 0;
4141 for (i = 0; i < NUM_DOORS; i++)
4142 panel_has_doors[i] = FALSE;
4144 for (i = 0; i < MAX_DOOR_PARTS; i++)
4146 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4147 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4148 int door_token = dpc->door_token;
4150 door_part_done[i] = FALSE;
4151 door_part_skip[i] = (!(door_state & door_token) ||
4155 for (i = 0; i < MAX_DOOR_PARTS; i++)
4157 int nr = door_part_order[i].nr;
4158 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4159 struct DoorPartPosInfo *pos = dpc->pos;
4160 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4161 int door_token = dpc->door_token;
4162 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4163 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4164 int step_xoffset = ABS(pos->step_xoffset);
4165 int step_yoffset = ABS(pos->step_yoffset);
4166 int step_delay = pos->step_delay;
4167 int current_door_state = door_state & door_token;
4168 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4169 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4170 boolean part_opening = (is_panel ? door_closing : door_opening);
4171 int start_step = (part_opening ? pos->start_step_opening :
4172 pos->start_step_closing);
4173 float move_xsize = (step_xoffset ? g->width : 0);
4174 float move_ysize = (step_yoffset ? g->height : 0);
4175 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4176 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4177 int move_steps = (move_xsteps && move_ysteps ?
4178 MIN(move_xsteps, move_ysteps) :
4179 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4180 int move_delay = move_steps * step_delay;
4182 if (door_part_skip[nr])
4185 max_move_delay = MAX(max_move_delay, move_delay);
4186 max_step_delay = (max_step_delay == 0 ? step_delay :
4187 euclid(max_step_delay, step_delay));
4188 num_steps[nr] = move_steps;
4192 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4194 panel_has_doors[door_index] = TRUE;
4198 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4200 num_move_steps = max_move_delay / max_step_delay;
4201 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4203 door_delay_value = max_step_delay;
4205 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4207 start = num_move_steps - 1;
4211 /* opening door sound has priority over simultaneously closing door */
4212 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4213 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4214 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4215 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4218 for (k = start; k < num_move_steps; k++)
4220 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4222 door_part_done_all = TRUE;
4224 for (i = 0; i < NUM_DOORS; i++)
4225 door_panel_drawn[i] = FALSE;
4227 for (i = 0; i < MAX_DOOR_PARTS; i++)
4229 int nr = door_part_order[i].nr;
4230 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4231 struct DoorPartPosInfo *pos = dpc->pos;
4232 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4233 int door_token = dpc->door_token;
4234 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4235 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4236 boolean is_panel_and_door_has_closed = FALSE;
4237 struct Rect *door_rect = &door_rect_list[door_index];
4238 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4240 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4241 int current_door_state = door_state & door_token;
4242 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4243 boolean door_closing = !door_opening;
4244 boolean part_opening = (is_panel ? door_closing : door_opening);
4245 boolean part_closing = !part_opening;
4246 int start_step = (part_opening ? pos->start_step_opening :
4247 pos->start_step_closing);
4248 int step_delay = pos->step_delay;
4249 int step_factor = step_delay / max_step_delay;
4250 int k1 = (step_factor ? k / step_factor + 1 : k);
4251 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4252 int kk = MAX(0, k2);
4255 int src_x, src_y, src_xx, src_yy;
4256 int dst_x, dst_y, dst_xx, dst_yy;
4259 if (door_part_skip[nr])
4262 if (!(door_state & door_token))
4270 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4271 int kk_door = MAX(0, k2_door);
4272 int sync_frame = kk_door * door_delay_value;
4273 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4275 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4280 if (!door_panel_drawn[door_index])
4282 ClearRectangle(drawto, door_rect->x, door_rect->y,
4283 door_rect->width, door_rect->height);
4285 door_panel_drawn[door_index] = TRUE;
4288 // draw opening or closing door parts
4290 if (pos->step_xoffset < 0) // door part on right side
4293 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4296 if (dst_xx + width > door_rect->width)
4297 width = door_rect->width - dst_xx;
4299 else // door part on left side
4302 dst_xx = pos->x - kk * pos->step_xoffset;
4306 src_xx = ABS(dst_xx);
4310 width = g->width - src_xx;
4312 // printf("::: k == %d [%d] \n", k, start_step);
4315 if (pos->step_yoffset < 0) // door part on bottom side
4318 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4321 if (dst_yy + height > door_rect->height)
4322 height = door_rect->height - dst_yy;
4324 else // door part on top side
4327 dst_yy = pos->y - kk * pos->step_yoffset;
4331 src_yy = ABS(dst_yy);
4335 height = g->height - src_yy;
4338 src_x = g_src_x + src_xx;
4339 src_y = g_src_y + src_yy;
4341 dst_x = door_rect->x + dst_xx;
4342 dst_y = door_rect->y + dst_yy;
4344 is_panel_and_door_has_closed =
4347 panel_has_doors[door_index] &&
4348 k >= num_move_steps_doors_only - 1);
4350 if (width >= 0 && width <= g->width &&
4351 height >= 0 && height <= g->height &&
4352 !is_panel_and_door_has_closed)
4354 if (is_panel || !pos->draw_masked)
4355 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4358 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4362 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4364 if ((part_opening && (width < 0 || height < 0)) ||
4365 (part_closing && (width >= g->width && height >= g->height)))
4366 door_part_done[nr] = TRUE;
4368 // continue door part animations, but not panel after door has closed
4369 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4370 door_part_done_all = FALSE;
4373 if (!(door_state & DOOR_NO_DELAY))
4377 if (game_status == GAME_MODE_MAIN)
4380 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4382 current_move_delay += max_step_delay;
4385 if (door_part_done_all)
4390 if (door_state & DOOR_ACTION_1)
4391 door1 = door_state & DOOR_ACTION_1;
4392 if (door_state & DOOR_ACTION_2)
4393 door2 = door_state & DOOR_ACTION_2;
4395 return (door1 | door2);
4398 void DrawSpecialEditorDoor()
4400 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4401 int top_border_width = gfx1->width;
4402 int top_border_height = gfx1->height;
4403 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4404 int ex = EX - outer_border;
4405 int ey = EY - outer_border;
4406 int vy = VY - outer_border;
4407 int exsize = EXSIZE + 2 * outer_border;
4409 /* draw bigger level editor toolbox window */
4410 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4411 top_border_width, top_border_height, ex, ey - top_border_height);
4412 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4413 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4415 redraw_mask |= REDRAW_ALL;
4418 void UndrawSpecialEditorDoor()
4420 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4421 int top_border_width = gfx1->width;
4422 int top_border_height = gfx1->height;
4423 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4424 int ex = EX - outer_border;
4425 int ey = EY - outer_border;
4426 int ey_top = ey - top_border_height;
4427 int exsize = EXSIZE + 2 * outer_border;
4428 int eysize = EYSIZE + 2 * outer_border;
4430 /* draw normal tape recorder window */
4431 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4433 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4434 ex, ey_top, top_border_width, top_border_height,
4436 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4437 ex, ey, exsize, eysize, ex, ey);
4441 // if screen background is set to "[NONE]", clear editor toolbox window
4442 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4443 ClearRectangle(drawto, ex, ey, exsize, eysize);
4446 redraw_mask |= REDRAW_ALL;
4450 /* ---------- new tool button stuff ---------------------------------------- */
4455 struct TextPosInfo *pos;
4458 } toolbutton_info[NUM_TOOL_BUTTONS] =
4461 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4462 TOOL_CTRL_ID_YES, "yes"
4465 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4466 TOOL_CTRL_ID_NO, "no"
4469 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4470 TOOL_CTRL_ID_CONFIRM, "confirm"
4473 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4474 TOOL_CTRL_ID_PLAYER_1, "player 1"
4477 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4478 TOOL_CTRL_ID_PLAYER_2, "player 2"
4481 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4482 TOOL_CTRL_ID_PLAYER_3, "player 3"
4485 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4486 TOOL_CTRL_ID_PLAYER_4, "player 4"
4490 void CreateToolButtons()
4494 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4496 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4497 struct TextPosInfo *pos = toolbutton_info[i].pos;
4498 struct GadgetInfo *gi;
4499 Bitmap *deco_bitmap = None;
4500 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4501 unsigned int event_mask = GD_EVENT_RELEASED;
4504 int gd_x = gfx->src_x;
4505 int gd_y = gfx->src_y;
4506 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4507 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4510 if (global.use_envelope_request)
4511 setRequestPosition(&dx, &dy, TRUE);
4513 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4515 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4517 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4518 pos->size, &deco_bitmap, &deco_x, &deco_y);
4519 deco_xpos = (gfx->width - pos->size) / 2;
4520 deco_ypos = (gfx->height - pos->size) / 2;
4523 gi = CreateGadget(GDI_CUSTOM_ID, id,
4524 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4525 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4526 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4527 GDI_WIDTH, gfx->width,
4528 GDI_HEIGHT, gfx->height,
4529 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4530 GDI_STATE, GD_BUTTON_UNPRESSED,
4531 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4532 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4533 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4534 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4535 GDI_DECORATION_SIZE, pos->size, pos->size,
4536 GDI_DECORATION_SHIFTING, 1, 1,
4537 GDI_DIRECT_DRAW, FALSE,
4538 GDI_EVENT_MASK, event_mask,
4539 GDI_CALLBACK_ACTION, HandleToolButtons,
4543 Error(ERR_EXIT, "cannot create gadget");
4545 tool_gadget[id] = gi;
4549 void FreeToolButtons()
4553 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4554 FreeGadget(tool_gadget[i]);
4557 static void UnmapToolButtons()
4561 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4562 UnmapGadget(tool_gadget[i]);
4565 static void HandleToolButtons(struct GadgetInfo *gi)
4567 request_gadget_id = gi->custom_id;
4570 static struct Mapping_EM_to_RND_object
4573 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4574 boolean is_backside; /* backside of moving element */
4580 em_object_mapping_list[] =
4583 Xblank, TRUE, FALSE,
4587 Yacid_splash_eB, FALSE, FALSE,
4588 EL_ACID_SPLASH_RIGHT, -1, -1
4591 Yacid_splash_wB, FALSE, FALSE,
4592 EL_ACID_SPLASH_LEFT, -1, -1
4595 #ifdef EM_ENGINE_BAD_ROLL
4597 Xstone_force_e, FALSE, FALSE,
4598 EL_ROCK, -1, MV_BIT_RIGHT
4601 Xstone_force_w, FALSE, FALSE,
4602 EL_ROCK, -1, MV_BIT_LEFT
4605 Xnut_force_e, FALSE, FALSE,
4606 EL_NUT, -1, MV_BIT_RIGHT
4609 Xnut_force_w, FALSE, FALSE,
4610 EL_NUT, -1, MV_BIT_LEFT
4613 Xspring_force_e, FALSE, FALSE,
4614 EL_SPRING, -1, MV_BIT_RIGHT
4617 Xspring_force_w, FALSE, FALSE,
4618 EL_SPRING, -1, MV_BIT_LEFT
4621 Xemerald_force_e, FALSE, FALSE,
4622 EL_EMERALD, -1, MV_BIT_RIGHT
4625 Xemerald_force_w, FALSE, FALSE,
4626 EL_EMERALD, -1, MV_BIT_LEFT
4629 Xdiamond_force_e, FALSE, FALSE,
4630 EL_DIAMOND, -1, MV_BIT_RIGHT
4633 Xdiamond_force_w, FALSE, FALSE,
4634 EL_DIAMOND, -1, MV_BIT_LEFT
4637 Xbomb_force_e, FALSE, FALSE,
4638 EL_BOMB, -1, MV_BIT_RIGHT
4641 Xbomb_force_w, FALSE, FALSE,
4642 EL_BOMB, -1, MV_BIT_LEFT
4644 #endif /* EM_ENGINE_BAD_ROLL */
4647 Xstone, TRUE, FALSE,
4651 Xstone_pause, FALSE, FALSE,
4655 Xstone_fall, FALSE, FALSE,
4659 Ystone_s, FALSE, FALSE,
4660 EL_ROCK, ACTION_FALLING, -1
4663 Ystone_sB, FALSE, TRUE,
4664 EL_ROCK, ACTION_FALLING, -1
4667 Ystone_e, FALSE, FALSE,
4668 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4671 Ystone_eB, FALSE, TRUE,
4672 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4675 Ystone_w, FALSE, FALSE,
4676 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4679 Ystone_wB, FALSE, TRUE,
4680 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4687 Xnut_pause, FALSE, FALSE,
4691 Xnut_fall, FALSE, FALSE,
4695 Ynut_s, FALSE, FALSE,
4696 EL_NUT, ACTION_FALLING, -1
4699 Ynut_sB, FALSE, TRUE,
4700 EL_NUT, ACTION_FALLING, -1
4703 Ynut_e, FALSE, FALSE,
4704 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4707 Ynut_eB, FALSE, TRUE,
4708 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4711 Ynut_w, FALSE, FALSE,
4712 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4715 Ynut_wB, FALSE, TRUE,
4716 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4719 Xbug_n, TRUE, FALSE,
4723 Xbug_e, TRUE, FALSE,
4724 EL_BUG_RIGHT, -1, -1
4727 Xbug_s, TRUE, FALSE,
4731 Xbug_w, TRUE, FALSE,
4735 Xbug_gon, FALSE, FALSE,
4739 Xbug_goe, FALSE, FALSE,
4740 EL_BUG_RIGHT, -1, -1
4743 Xbug_gos, FALSE, FALSE,
4747 Xbug_gow, FALSE, FALSE,
4751 Ybug_n, FALSE, FALSE,
4752 EL_BUG, ACTION_MOVING, MV_BIT_UP
4755 Ybug_nB, FALSE, TRUE,
4756 EL_BUG, ACTION_MOVING, MV_BIT_UP
4759 Ybug_e, FALSE, FALSE,
4760 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4763 Ybug_eB, FALSE, TRUE,
4764 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4767 Ybug_s, FALSE, FALSE,
4768 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4771 Ybug_sB, FALSE, TRUE,
4772 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4775 Ybug_w, FALSE, FALSE,
4776 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4779 Ybug_wB, FALSE, TRUE,
4780 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4783 Ybug_w_n, FALSE, FALSE,
4784 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4787 Ybug_n_e, FALSE, FALSE,
4788 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4791 Ybug_e_s, FALSE, FALSE,
4792 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4795 Ybug_s_w, FALSE, FALSE,
4796 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4799 Ybug_e_n, FALSE, FALSE,
4800 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4803 Ybug_s_e, FALSE, FALSE,
4804 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4807 Ybug_w_s, FALSE, FALSE,
4808 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4811 Ybug_n_w, FALSE, FALSE,
4812 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4815 Ybug_stone, FALSE, FALSE,
4816 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4819 Ybug_spring, FALSE, FALSE,
4820 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4823 Xtank_n, TRUE, FALSE,
4824 EL_SPACESHIP_UP, -1, -1
4827 Xtank_e, TRUE, FALSE,
4828 EL_SPACESHIP_RIGHT, -1, -1
4831 Xtank_s, TRUE, FALSE,
4832 EL_SPACESHIP_DOWN, -1, -1
4835 Xtank_w, TRUE, FALSE,
4836 EL_SPACESHIP_LEFT, -1, -1
4839 Xtank_gon, FALSE, FALSE,
4840 EL_SPACESHIP_UP, -1, -1
4843 Xtank_goe, FALSE, FALSE,
4844 EL_SPACESHIP_RIGHT, -1, -1
4847 Xtank_gos, FALSE, FALSE,
4848 EL_SPACESHIP_DOWN, -1, -1
4851 Xtank_gow, FALSE, FALSE,
4852 EL_SPACESHIP_LEFT, -1, -1
4855 Ytank_n, FALSE, FALSE,
4856 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4859 Ytank_nB, FALSE, TRUE,
4860 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4863 Ytank_e, FALSE, FALSE,
4864 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4867 Ytank_eB, FALSE, TRUE,
4868 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4871 Ytank_s, FALSE, FALSE,
4872 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4875 Ytank_sB, FALSE, TRUE,
4876 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4879 Ytank_w, FALSE, FALSE,
4880 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4883 Ytank_wB, FALSE, TRUE,
4884 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4887 Ytank_w_n, FALSE, FALSE,
4888 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4891 Ytank_n_e, FALSE, FALSE,
4892 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4895 Ytank_e_s, FALSE, FALSE,
4896 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4899 Ytank_s_w, FALSE, FALSE,
4900 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4903 Ytank_e_n, FALSE, FALSE,
4904 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4907 Ytank_s_e, FALSE, FALSE,
4908 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4911 Ytank_w_s, FALSE, FALSE,
4912 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4915 Ytank_n_w, FALSE, FALSE,
4916 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4919 Ytank_stone, FALSE, FALSE,
4920 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4923 Ytank_spring, FALSE, FALSE,
4924 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4927 Xandroid, TRUE, FALSE,
4928 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4931 Xandroid_1_n, FALSE, FALSE,
4932 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4935 Xandroid_2_n, FALSE, FALSE,
4936 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4939 Xandroid_1_e, FALSE, FALSE,
4940 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4943 Xandroid_2_e, FALSE, FALSE,
4944 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4947 Xandroid_1_w, FALSE, FALSE,
4948 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4951 Xandroid_2_w, FALSE, FALSE,
4952 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4955 Xandroid_1_s, FALSE, FALSE,
4956 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4959 Xandroid_2_s, FALSE, FALSE,
4960 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4963 Yandroid_n, FALSE, FALSE,
4964 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4967 Yandroid_nB, FALSE, TRUE,
4968 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4971 Yandroid_ne, FALSE, FALSE,
4972 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4975 Yandroid_neB, FALSE, TRUE,
4976 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4979 Yandroid_e, FALSE, FALSE,
4980 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4983 Yandroid_eB, FALSE, TRUE,
4984 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4987 Yandroid_se, FALSE, FALSE,
4988 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4991 Yandroid_seB, FALSE, TRUE,
4992 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4995 Yandroid_s, FALSE, FALSE,
4996 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4999 Yandroid_sB, FALSE, TRUE,
5000 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5003 Yandroid_sw, FALSE, FALSE,
5004 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5007 Yandroid_swB, FALSE, TRUE,
5008 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5011 Yandroid_w, FALSE, FALSE,
5012 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5015 Yandroid_wB, FALSE, TRUE,
5016 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5019 Yandroid_nw, FALSE, FALSE,
5020 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5023 Yandroid_nwB, FALSE, TRUE,
5024 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5027 Xspring, TRUE, FALSE,
5031 Xspring_pause, FALSE, FALSE,
5035 Xspring_e, FALSE, FALSE,
5039 Xspring_w, FALSE, FALSE,
5043 Xspring_fall, FALSE, FALSE,
5047 Yspring_s, FALSE, FALSE,
5048 EL_SPRING, ACTION_FALLING, -1
5051 Yspring_sB, FALSE, TRUE,
5052 EL_SPRING, ACTION_FALLING, -1
5055 Yspring_e, FALSE, FALSE,
5056 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5059 Yspring_eB, FALSE, TRUE,
5060 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5063 Yspring_w, FALSE, FALSE,
5064 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5067 Yspring_wB, FALSE, TRUE,
5068 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5071 Yspring_kill_e, FALSE, FALSE,
5072 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5075 Yspring_kill_eB, FALSE, TRUE,
5076 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5079 Yspring_kill_w, FALSE, FALSE,
5080 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5083 Yspring_kill_wB, FALSE, TRUE,
5084 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5087 Xeater_n, TRUE, FALSE,
5088 EL_YAMYAM_UP, -1, -1
5091 Xeater_e, TRUE, FALSE,
5092 EL_YAMYAM_RIGHT, -1, -1
5095 Xeater_w, TRUE, FALSE,
5096 EL_YAMYAM_LEFT, -1, -1
5099 Xeater_s, TRUE, FALSE,
5100 EL_YAMYAM_DOWN, -1, -1
5103 Yeater_n, FALSE, FALSE,
5104 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5107 Yeater_nB, FALSE, TRUE,
5108 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5111 Yeater_e, FALSE, FALSE,
5112 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5115 Yeater_eB, FALSE, TRUE,
5116 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5119 Yeater_s, FALSE, FALSE,
5120 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5123 Yeater_sB, FALSE, TRUE,
5124 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5127 Yeater_w, FALSE, FALSE,
5128 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5131 Yeater_wB, FALSE, TRUE,
5132 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5135 Yeater_stone, FALSE, FALSE,
5136 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5139 Yeater_spring, FALSE, FALSE,
5140 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5143 Xalien, TRUE, FALSE,
5147 Xalien_pause, FALSE, FALSE,
5151 Yalien_n, FALSE, FALSE,
5152 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5155 Yalien_nB, FALSE, TRUE,
5156 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5159 Yalien_e, FALSE, FALSE,
5160 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5163 Yalien_eB, FALSE, TRUE,
5164 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5167 Yalien_s, FALSE, FALSE,
5168 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5171 Yalien_sB, FALSE, TRUE,
5172 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5175 Yalien_w, FALSE, FALSE,
5176 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5179 Yalien_wB, FALSE, TRUE,
5180 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5183 Yalien_stone, FALSE, FALSE,
5184 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5187 Yalien_spring, FALSE, FALSE,
5188 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5191 Xemerald, TRUE, FALSE,
5195 Xemerald_pause, FALSE, FALSE,
5199 Xemerald_fall, FALSE, FALSE,
5203 Xemerald_shine, FALSE, FALSE,
5204 EL_EMERALD, ACTION_TWINKLING, -1
5207 Yemerald_s, FALSE, FALSE,
5208 EL_EMERALD, ACTION_FALLING, -1
5211 Yemerald_sB, FALSE, TRUE,
5212 EL_EMERALD, ACTION_FALLING, -1
5215 Yemerald_e, FALSE, FALSE,
5216 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5219 Yemerald_eB, FALSE, TRUE,
5220 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5223 Yemerald_w, FALSE, FALSE,
5224 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5227 Yemerald_wB, FALSE, TRUE,
5228 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5231 Yemerald_eat, FALSE, FALSE,
5232 EL_EMERALD, ACTION_COLLECTING, -1
5235 Yemerald_stone, FALSE, FALSE,
5236 EL_NUT, ACTION_BREAKING, -1
5239 Xdiamond, TRUE, FALSE,
5243 Xdiamond_pause, FALSE, FALSE,
5247 Xdiamond_fall, FALSE, FALSE,
5251 Xdiamond_shine, FALSE, FALSE,
5252 EL_DIAMOND, ACTION_TWINKLING, -1
5255 Ydiamond_s, FALSE, FALSE,
5256 EL_DIAMOND, ACTION_FALLING, -1
5259 Ydiamond_sB, FALSE, TRUE,
5260 EL_DIAMOND, ACTION_FALLING, -1
5263 Ydiamond_e, FALSE, FALSE,
5264 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5267 Ydiamond_eB, FALSE, TRUE,
5268 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5271 Ydiamond_w, FALSE, FALSE,
5272 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5275 Ydiamond_wB, FALSE, TRUE,
5276 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5279 Ydiamond_eat, FALSE, FALSE,
5280 EL_DIAMOND, ACTION_COLLECTING, -1
5283 Ydiamond_stone, FALSE, FALSE,
5284 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5287 Xdrip_fall, TRUE, FALSE,
5288 EL_AMOEBA_DROP, -1, -1
5291 Xdrip_stretch, FALSE, FALSE,
5292 EL_AMOEBA_DROP, ACTION_FALLING, -1
5295 Xdrip_stretchB, FALSE, TRUE,
5296 EL_AMOEBA_DROP, ACTION_FALLING, -1
5299 Xdrip_eat, FALSE, FALSE,
5300 EL_AMOEBA_DROP, ACTION_GROWING, -1
5303 Ydrip_s1, FALSE, FALSE,
5304 EL_AMOEBA_DROP, ACTION_FALLING, -1
5307 Ydrip_s1B, FALSE, TRUE,
5308 EL_AMOEBA_DROP, ACTION_FALLING, -1
5311 Ydrip_s2, FALSE, FALSE,
5312 EL_AMOEBA_DROP, ACTION_FALLING, -1
5315 Ydrip_s2B, FALSE, TRUE,
5316 EL_AMOEBA_DROP, ACTION_FALLING, -1
5323 Xbomb_pause, FALSE, FALSE,
5327 Xbomb_fall, FALSE, FALSE,
5331 Ybomb_s, FALSE, FALSE,
5332 EL_BOMB, ACTION_FALLING, -1
5335 Ybomb_sB, FALSE, TRUE,
5336 EL_BOMB, ACTION_FALLING, -1
5339 Ybomb_e, FALSE, FALSE,
5340 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5343 Ybomb_eB, FALSE, TRUE,
5344 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5347 Ybomb_w, FALSE, FALSE,
5348 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5351 Ybomb_wB, FALSE, TRUE,
5352 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5355 Ybomb_eat, FALSE, FALSE,
5356 EL_BOMB, ACTION_ACTIVATING, -1
5359 Xballoon, TRUE, FALSE,
5363 Yballoon_n, FALSE, FALSE,
5364 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5367 Yballoon_nB, FALSE, TRUE,
5368 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5371 Yballoon_e, FALSE, FALSE,
5372 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5375 Yballoon_eB, FALSE, TRUE,
5376 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5379 Yballoon_s, FALSE, FALSE,
5380 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5383 Yballoon_sB, FALSE, TRUE,
5384 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5387 Yballoon_w, FALSE, FALSE,
5388 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5391 Yballoon_wB, FALSE, TRUE,
5392 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5395 Xgrass, TRUE, FALSE,
5396 EL_EMC_GRASS, -1, -1
5399 Ygrass_nB, FALSE, FALSE,
5400 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5403 Ygrass_eB, FALSE, FALSE,
5404 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5407 Ygrass_sB, FALSE, FALSE,
5408 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5411 Ygrass_wB, FALSE, FALSE,
5412 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5419 Ydirt_nB, FALSE, FALSE,
5420 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5423 Ydirt_eB, FALSE, FALSE,
5424 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5427 Ydirt_sB, FALSE, FALSE,
5428 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5431 Ydirt_wB, FALSE, FALSE,
5432 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5435 Xacid_ne, TRUE, FALSE,
5436 EL_ACID_POOL_TOPRIGHT, -1, -1
5439 Xacid_se, TRUE, FALSE,
5440 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5443 Xacid_s, TRUE, FALSE,
5444 EL_ACID_POOL_BOTTOM, -1, -1
5447 Xacid_sw, TRUE, FALSE,
5448 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5451 Xacid_nw, TRUE, FALSE,
5452 EL_ACID_POOL_TOPLEFT, -1, -1
5455 Xacid_1, TRUE, FALSE,
5459 Xacid_2, FALSE, FALSE,
5463 Xacid_3, FALSE, FALSE,
5467 Xacid_4, FALSE, FALSE,
5471 Xacid_5, FALSE, FALSE,
5475 Xacid_6, FALSE, FALSE,
5479 Xacid_7, FALSE, FALSE,
5483 Xacid_8, FALSE, FALSE,
5487 Xball_1, TRUE, FALSE,
5488 EL_EMC_MAGIC_BALL, -1, -1
5491 Xball_1B, FALSE, FALSE,
5492 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5495 Xball_2, FALSE, FALSE,
5496 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5499 Xball_2B, FALSE, FALSE,
5500 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5503 Yball_eat, FALSE, FALSE,
5504 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5507 Ykey_1_eat, FALSE, FALSE,
5508 EL_EM_KEY_1, ACTION_COLLECTING, -1
5511 Ykey_2_eat, FALSE, FALSE,
5512 EL_EM_KEY_2, ACTION_COLLECTING, -1
5515 Ykey_3_eat, FALSE, FALSE,
5516 EL_EM_KEY_3, ACTION_COLLECTING, -1
5519 Ykey_4_eat, FALSE, FALSE,
5520 EL_EM_KEY_4, ACTION_COLLECTING, -1
5523 Ykey_5_eat, FALSE, FALSE,
5524 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5527 Ykey_6_eat, FALSE, FALSE,
5528 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5531 Ykey_7_eat, FALSE, FALSE,
5532 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5535 Ykey_8_eat, FALSE, FALSE,
5536 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5539 Ylenses_eat, FALSE, FALSE,
5540 EL_EMC_LENSES, ACTION_COLLECTING, -1
5543 Ymagnify_eat, FALSE, FALSE,
5544 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5547 Ygrass_eat, FALSE, FALSE,
5548 EL_EMC_GRASS, ACTION_SNAPPING, -1
5551 Ydirt_eat, FALSE, FALSE,
5552 EL_SAND, ACTION_SNAPPING, -1
5555 Xgrow_ns, TRUE, FALSE,
5556 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5559 Ygrow_ns_eat, FALSE, FALSE,
5560 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5563 Xgrow_ew, TRUE, FALSE,
5564 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5567 Ygrow_ew_eat, FALSE, FALSE,
5568 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5571 Xwonderwall, TRUE, FALSE,
5572 EL_MAGIC_WALL, -1, -1
5575 XwonderwallB, FALSE, FALSE,
5576 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5579 Xamoeba_1, TRUE, FALSE,
5580 EL_AMOEBA_DRY, ACTION_OTHER, -1
5583 Xamoeba_2, FALSE, FALSE,
5584 EL_AMOEBA_DRY, ACTION_OTHER, -1
5587 Xamoeba_3, FALSE, FALSE,
5588 EL_AMOEBA_DRY, ACTION_OTHER, -1
5591 Xamoeba_4, FALSE, FALSE,
5592 EL_AMOEBA_DRY, ACTION_OTHER, -1
5595 Xamoeba_5, TRUE, FALSE,
5596 EL_AMOEBA_WET, ACTION_OTHER, -1
5599 Xamoeba_6, FALSE, FALSE,
5600 EL_AMOEBA_WET, ACTION_OTHER, -1
5603 Xamoeba_7, FALSE, FALSE,
5604 EL_AMOEBA_WET, ACTION_OTHER, -1
5607 Xamoeba_8, FALSE, FALSE,
5608 EL_AMOEBA_WET, ACTION_OTHER, -1
5611 Xdoor_1, TRUE, FALSE,
5612 EL_EM_GATE_1, -1, -1
5615 Xdoor_2, TRUE, FALSE,
5616 EL_EM_GATE_2, -1, -1
5619 Xdoor_3, TRUE, FALSE,
5620 EL_EM_GATE_3, -1, -1
5623 Xdoor_4, TRUE, FALSE,
5624 EL_EM_GATE_4, -1, -1
5627 Xdoor_5, TRUE, FALSE,
5628 EL_EMC_GATE_5, -1, -1
5631 Xdoor_6, TRUE, FALSE,
5632 EL_EMC_GATE_6, -1, -1
5635 Xdoor_7, TRUE, FALSE,
5636 EL_EMC_GATE_7, -1, -1
5639 Xdoor_8, TRUE, FALSE,
5640 EL_EMC_GATE_8, -1, -1
5643 Xkey_1, TRUE, FALSE,
5647 Xkey_2, TRUE, FALSE,
5651 Xkey_3, TRUE, FALSE,
5655 Xkey_4, TRUE, FALSE,
5659 Xkey_5, TRUE, FALSE,
5660 EL_EMC_KEY_5, -1, -1
5663 Xkey_6, TRUE, FALSE,
5664 EL_EMC_KEY_6, -1, -1
5667 Xkey_7, TRUE, FALSE,
5668 EL_EMC_KEY_7, -1, -1
5671 Xkey_8, TRUE, FALSE,
5672 EL_EMC_KEY_8, -1, -1
5675 Xwind_n, TRUE, FALSE,
5676 EL_BALLOON_SWITCH_UP, -1, -1
5679 Xwind_e, TRUE, FALSE,
5680 EL_BALLOON_SWITCH_RIGHT, -1, -1
5683 Xwind_s, TRUE, FALSE,
5684 EL_BALLOON_SWITCH_DOWN, -1, -1
5687 Xwind_w, TRUE, FALSE,
5688 EL_BALLOON_SWITCH_LEFT, -1, -1
5691 Xwind_nesw, TRUE, FALSE,
5692 EL_BALLOON_SWITCH_ANY, -1, -1
5695 Xwind_stop, TRUE, FALSE,
5696 EL_BALLOON_SWITCH_NONE, -1, -1
5700 EL_EM_EXIT_CLOSED, -1, -1
5703 Xexit_1, TRUE, FALSE,
5704 EL_EM_EXIT_OPEN, -1, -1
5707 Xexit_2, FALSE, FALSE,
5708 EL_EM_EXIT_OPEN, -1, -1
5711 Xexit_3, FALSE, FALSE,
5712 EL_EM_EXIT_OPEN, -1, -1
5715 Xdynamite, TRUE, FALSE,
5716 EL_EM_DYNAMITE, -1, -1
5719 Ydynamite_eat, FALSE, FALSE,
5720 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5723 Xdynamite_1, TRUE, FALSE,
5724 EL_EM_DYNAMITE_ACTIVE, -1, -1
5727 Xdynamite_2, FALSE, FALSE,
5728 EL_EM_DYNAMITE_ACTIVE, -1, -1
5731 Xdynamite_3, FALSE, FALSE,
5732 EL_EM_DYNAMITE_ACTIVE, -1, -1
5735 Xdynamite_4, FALSE, FALSE,
5736 EL_EM_DYNAMITE_ACTIVE, -1, -1
5739 Xbumper, TRUE, FALSE,
5740 EL_EMC_SPRING_BUMPER, -1, -1
5743 XbumperB, FALSE, FALSE,
5744 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5747 Xwheel, TRUE, FALSE,
5748 EL_ROBOT_WHEEL, -1, -1
5751 XwheelB, FALSE, FALSE,
5752 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5755 Xswitch, TRUE, FALSE,
5756 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5759 XswitchB, FALSE, FALSE,
5760 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5764 EL_QUICKSAND_EMPTY, -1, -1
5767 Xsand_stone, TRUE, FALSE,
5768 EL_QUICKSAND_FULL, -1, -1
5771 Xsand_stonein_1, FALSE, TRUE,
5772 EL_ROCK, ACTION_FILLING, -1
5775 Xsand_stonein_2, FALSE, TRUE,
5776 EL_ROCK, ACTION_FILLING, -1
5779 Xsand_stonein_3, FALSE, TRUE,
5780 EL_ROCK, ACTION_FILLING, -1
5783 Xsand_stonein_4, FALSE, TRUE,
5784 EL_ROCK, ACTION_FILLING, -1
5787 Xsand_stonesand_1, FALSE, FALSE,
5788 EL_QUICKSAND_EMPTYING, -1, -1
5791 Xsand_stonesand_2, FALSE, FALSE,
5792 EL_QUICKSAND_EMPTYING, -1, -1
5795 Xsand_stonesand_3, FALSE, FALSE,
5796 EL_QUICKSAND_EMPTYING, -1, -1
5799 Xsand_stonesand_4, FALSE, FALSE,
5800 EL_QUICKSAND_EMPTYING, -1, -1
5803 Xsand_stonesand_quickout_1, FALSE, FALSE,
5804 EL_QUICKSAND_EMPTYING, -1, -1
5807 Xsand_stonesand_quickout_2, FALSE, FALSE,
5808 EL_QUICKSAND_EMPTYING, -1, -1
5811 Xsand_stoneout_1, FALSE, FALSE,
5812 EL_ROCK, ACTION_EMPTYING, -1
5815 Xsand_stoneout_2, FALSE, FALSE,
5816 EL_ROCK, ACTION_EMPTYING, -1
5819 Xsand_sandstone_1, FALSE, FALSE,
5820 EL_QUICKSAND_FILLING, -1, -1
5823 Xsand_sandstone_2, FALSE, FALSE,
5824 EL_QUICKSAND_FILLING, -1, -1
5827 Xsand_sandstone_3, FALSE, FALSE,
5828 EL_QUICKSAND_FILLING, -1, -1
5831 Xsand_sandstone_4, FALSE, FALSE,
5832 EL_QUICKSAND_FILLING, -1, -1
5835 Xplant, TRUE, FALSE,
5836 EL_EMC_PLANT, -1, -1
5839 Yplant, FALSE, FALSE,
5840 EL_EMC_PLANT, -1, -1
5843 Xlenses, TRUE, FALSE,
5844 EL_EMC_LENSES, -1, -1
5847 Xmagnify, TRUE, FALSE,
5848 EL_EMC_MAGNIFIER, -1, -1
5851 Xdripper, TRUE, FALSE,
5852 EL_EMC_DRIPPER, -1, -1
5855 XdripperB, FALSE, FALSE,
5856 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5859 Xfake_blank, TRUE, FALSE,
5860 EL_INVISIBLE_WALL, -1, -1
5863 Xfake_blankB, FALSE, FALSE,
5864 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5867 Xfake_grass, TRUE, FALSE,
5868 EL_EMC_FAKE_GRASS, -1, -1
5871 Xfake_grassB, FALSE, FALSE,
5872 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5875 Xfake_door_1, TRUE, FALSE,
5876 EL_EM_GATE_1_GRAY, -1, -1
5879 Xfake_door_2, TRUE, FALSE,
5880 EL_EM_GATE_2_GRAY, -1, -1
5883 Xfake_door_3, TRUE, FALSE,
5884 EL_EM_GATE_3_GRAY, -1, -1
5887 Xfake_door_4, TRUE, FALSE,
5888 EL_EM_GATE_4_GRAY, -1, -1
5891 Xfake_door_5, TRUE, FALSE,
5892 EL_EMC_GATE_5_GRAY, -1, -1
5895 Xfake_door_6, TRUE, FALSE,
5896 EL_EMC_GATE_6_GRAY, -1, -1
5899 Xfake_door_7, TRUE, FALSE,
5900 EL_EMC_GATE_7_GRAY, -1, -1
5903 Xfake_door_8, TRUE, FALSE,
5904 EL_EMC_GATE_8_GRAY, -1, -1
5907 Xfake_acid_1, TRUE, FALSE,
5908 EL_EMC_FAKE_ACID, -1, -1
5911 Xfake_acid_2, FALSE, FALSE,
5912 EL_EMC_FAKE_ACID, -1, -1
5915 Xfake_acid_3, FALSE, FALSE,
5916 EL_EMC_FAKE_ACID, -1, -1
5919 Xfake_acid_4, FALSE, FALSE,
5920 EL_EMC_FAKE_ACID, -1, -1
5923 Xfake_acid_5, FALSE, FALSE,
5924 EL_EMC_FAKE_ACID, -1, -1
5927 Xfake_acid_6, FALSE, FALSE,
5928 EL_EMC_FAKE_ACID, -1, -1
5931 Xfake_acid_7, FALSE, FALSE,
5932 EL_EMC_FAKE_ACID, -1, -1
5935 Xfake_acid_8, FALSE, FALSE,
5936 EL_EMC_FAKE_ACID, -1, -1
5939 Xsteel_1, TRUE, FALSE,
5940 EL_STEELWALL, -1, -1
5943 Xsteel_2, TRUE, FALSE,
5944 EL_EMC_STEELWALL_2, -1, -1
5947 Xsteel_3, TRUE, FALSE,
5948 EL_EMC_STEELWALL_3, -1, -1
5951 Xsteel_4, TRUE, FALSE,
5952 EL_EMC_STEELWALL_4, -1, -1
5955 Xwall_1, TRUE, FALSE,
5959 Xwall_2, TRUE, FALSE,
5960 EL_EMC_WALL_14, -1, -1
5963 Xwall_3, TRUE, FALSE,
5964 EL_EMC_WALL_15, -1, -1
5967 Xwall_4, TRUE, FALSE,
5968 EL_EMC_WALL_16, -1, -1
5971 Xround_wall_1, TRUE, FALSE,
5972 EL_WALL_SLIPPERY, -1, -1
5975 Xround_wall_2, TRUE, FALSE,
5976 EL_EMC_WALL_SLIPPERY_2, -1, -1
5979 Xround_wall_3, TRUE, FALSE,
5980 EL_EMC_WALL_SLIPPERY_3, -1, -1
5983 Xround_wall_4, TRUE, FALSE,
5984 EL_EMC_WALL_SLIPPERY_4, -1, -1
5987 Xdecor_1, TRUE, FALSE,
5988 EL_EMC_WALL_8, -1, -1
5991 Xdecor_2, TRUE, FALSE,
5992 EL_EMC_WALL_6, -1, -1
5995 Xdecor_3, TRUE, FALSE,
5996 EL_EMC_WALL_4, -1, -1
5999 Xdecor_4, TRUE, FALSE,
6000 EL_EMC_WALL_7, -1, -1
6003 Xdecor_5, TRUE, FALSE,
6004 EL_EMC_WALL_5, -1, -1
6007 Xdecor_6, TRUE, FALSE,
6008 EL_EMC_WALL_9, -1, -1
6011 Xdecor_7, TRUE, FALSE,
6012 EL_EMC_WALL_10, -1, -1
6015 Xdecor_8, TRUE, FALSE,
6016 EL_EMC_WALL_1, -1, -1
6019 Xdecor_9, TRUE, FALSE,
6020 EL_EMC_WALL_2, -1, -1
6023 Xdecor_10, TRUE, FALSE,
6024 EL_EMC_WALL_3, -1, -1
6027 Xdecor_11, TRUE, FALSE,
6028 EL_EMC_WALL_11, -1, -1
6031 Xdecor_12, TRUE, FALSE,
6032 EL_EMC_WALL_12, -1, -1
6035 Xalpha_0, TRUE, FALSE,
6036 EL_CHAR('0'), -1, -1
6039 Xalpha_1, TRUE, FALSE,
6040 EL_CHAR('1'), -1, -1
6043 Xalpha_2, TRUE, FALSE,
6044 EL_CHAR('2'), -1, -1
6047 Xalpha_3, TRUE, FALSE,
6048 EL_CHAR('3'), -1, -1
6051 Xalpha_4, TRUE, FALSE,
6052 EL_CHAR('4'), -1, -1
6055 Xalpha_5, TRUE, FALSE,
6056 EL_CHAR('5'), -1, -1
6059 Xalpha_6, TRUE, FALSE,
6060 EL_CHAR('6'), -1, -1
6063 Xalpha_7, TRUE, FALSE,
6064 EL_CHAR('7'), -1, -1
6067 Xalpha_8, TRUE, FALSE,
6068 EL_CHAR('8'), -1, -1
6071 Xalpha_9, TRUE, FALSE,
6072 EL_CHAR('9'), -1, -1
6075 Xalpha_excla, TRUE, FALSE,
6076 EL_CHAR('!'), -1, -1
6079 Xalpha_quote, TRUE, FALSE,
6080 EL_CHAR('"'), -1, -1
6083 Xalpha_comma, TRUE, FALSE,
6084 EL_CHAR(','), -1, -1
6087 Xalpha_minus, TRUE, FALSE,
6088 EL_CHAR('-'), -1, -1
6091 Xalpha_perio, TRUE, FALSE,
6092 EL_CHAR('.'), -1, -1
6095 Xalpha_colon, TRUE, FALSE,
6096 EL_CHAR(':'), -1, -1
6099 Xalpha_quest, TRUE, FALSE,
6100 EL_CHAR('?'), -1, -1
6103 Xalpha_a, TRUE, FALSE,
6104 EL_CHAR('A'), -1, -1
6107 Xalpha_b, TRUE, FALSE,
6108 EL_CHAR('B'), -1, -1
6111 Xalpha_c, TRUE, FALSE,
6112 EL_CHAR('C'), -1, -1
6115 Xalpha_d, TRUE, FALSE,
6116 EL_CHAR('D'), -1, -1
6119 Xalpha_e, TRUE, FALSE,
6120 EL_CHAR('E'), -1, -1
6123 Xalpha_f, TRUE, FALSE,
6124 EL_CHAR('F'), -1, -1
6127 Xalpha_g, TRUE, FALSE,
6128 EL_CHAR('G'), -1, -1
6131 Xalpha_h, TRUE, FALSE,
6132 EL_CHAR('H'), -1, -1
6135 Xalpha_i, TRUE, FALSE,
6136 EL_CHAR('I'), -1, -1
6139 Xalpha_j, TRUE, FALSE,
6140 EL_CHAR('J'), -1, -1
6143 Xalpha_k, TRUE, FALSE,
6144 EL_CHAR('K'), -1, -1
6147 Xalpha_l, TRUE, FALSE,
6148 EL_CHAR('L'), -1, -1
6151 Xalpha_m, TRUE, FALSE,
6152 EL_CHAR('M'), -1, -1
6155 Xalpha_n, TRUE, FALSE,
6156 EL_CHAR('N'), -1, -1
6159 Xalpha_o, TRUE, FALSE,
6160 EL_CHAR('O'), -1, -1
6163 Xalpha_p, TRUE, FALSE,
6164 EL_CHAR('P'), -1, -1
6167 Xalpha_q, TRUE, FALSE,
6168 EL_CHAR('Q'), -1, -1
6171 Xalpha_r, TRUE, FALSE,
6172 EL_CHAR('R'), -1, -1
6175 Xalpha_s, TRUE, FALSE,
6176 EL_CHAR('S'), -1, -1
6179 Xalpha_t, TRUE, FALSE,
6180 EL_CHAR('T'), -1, -1
6183 Xalpha_u, TRUE, FALSE,
6184 EL_CHAR('U'), -1, -1
6187 Xalpha_v, TRUE, FALSE,
6188 EL_CHAR('V'), -1, -1
6191 Xalpha_w, TRUE, FALSE,
6192 EL_CHAR('W'), -1, -1
6195 Xalpha_x, TRUE, FALSE,
6196 EL_CHAR('X'), -1, -1
6199 Xalpha_y, TRUE, FALSE,
6200 EL_CHAR('Y'), -1, -1
6203 Xalpha_z, TRUE, FALSE,
6204 EL_CHAR('Z'), -1, -1
6207 Xalpha_arrow_e, TRUE, FALSE,
6208 EL_CHAR('>'), -1, -1
6211 Xalpha_arrow_w, TRUE, FALSE,
6212 EL_CHAR('<'), -1, -1
6215 Xalpha_copyr, TRUE, FALSE,
6216 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6220 Xboom_bug, FALSE, FALSE,
6221 EL_BUG, ACTION_EXPLODING, -1
6224 Xboom_bomb, FALSE, FALSE,
6225 EL_BOMB, ACTION_EXPLODING, -1
6228 Xboom_android, FALSE, FALSE,
6229 EL_EMC_ANDROID, ACTION_OTHER, -1
6232 Xboom_1, FALSE, FALSE,
6233 EL_DEFAULT, ACTION_EXPLODING, -1
6236 Xboom_2, FALSE, FALSE,
6237 EL_DEFAULT, ACTION_EXPLODING, -1
6240 Znormal, FALSE, FALSE,
6244 Zdynamite, FALSE, FALSE,
6248 Zplayer, FALSE, FALSE,
6252 ZBORDER, FALSE, FALSE,
6262 static struct Mapping_EM_to_RND_player
6271 em_player_mapping_list[] =
6275 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6279 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6283 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6287 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6291 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6295 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6299 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6303 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6307 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6311 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6315 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6319 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6323 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6327 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6331 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6335 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6339 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6343 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6347 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6351 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6355 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6359 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6363 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6367 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6371 EL_PLAYER_1, ACTION_DEFAULT, -1,
6375 EL_PLAYER_2, ACTION_DEFAULT, -1,
6379 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6383 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6387 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6391 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6395 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6399 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6403 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6407 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6411 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6415 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6419 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6423 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6427 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6431 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6435 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6439 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6443 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6447 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6451 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6455 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6459 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6463 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6467 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6471 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6475 EL_PLAYER_3, ACTION_DEFAULT, -1,
6479 EL_PLAYER_4, ACTION_DEFAULT, -1,
6488 int map_element_RND_to_EM(int element_rnd)
6490 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6491 static boolean mapping_initialized = FALSE;
6493 if (!mapping_initialized)
6497 /* return "Xalpha_quest" for all undefined elements in mapping array */
6498 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6499 mapping_RND_to_EM[i] = Xalpha_quest;
6501 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6502 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6503 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6504 em_object_mapping_list[i].element_em;
6506 mapping_initialized = TRUE;
6509 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6510 return mapping_RND_to_EM[element_rnd];
6512 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6517 int map_element_EM_to_RND(int element_em)
6519 static unsigned short mapping_EM_to_RND[TILE_MAX];
6520 static boolean mapping_initialized = FALSE;
6522 if (!mapping_initialized)
6526 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6527 for (i = 0; i < TILE_MAX; i++)
6528 mapping_EM_to_RND[i] = EL_UNKNOWN;
6530 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6531 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6532 em_object_mapping_list[i].element_rnd;
6534 mapping_initialized = TRUE;
6537 if (element_em >= 0 && element_em < TILE_MAX)
6538 return mapping_EM_to_RND[element_em];
6540 Error(ERR_WARN, "invalid EM level element %d", element_em);
6545 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6547 struct LevelInfo_EM *level_em = level->native_em_level;
6548 struct LEVEL *lev = level_em->lev;
6551 for (i = 0; i < TILE_MAX; i++)
6552 lev->android_array[i] = Xblank;
6554 for (i = 0; i < level->num_android_clone_elements; i++)
6556 int element_rnd = level->android_clone_element[i];
6557 int element_em = map_element_RND_to_EM(element_rnd);
6559 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6560 if (em_object_mapping_list[j].element_rnd == element_rnd)
6561 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6565 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6567 struct LevelInfo_EM *level_em = level->native_em_level;
6568 struct LEVEL *lev = level_em->lev;
6571 level->num_android_clone_elements = 0;
6573 for (i = 0; i < TILE_MAX; i++)
6575 int element_em = lev->android_array[i];
6577 boolean element_found = FALSE;
6579 if (element_em == Xblank)
6582 element_rnd = map_element_EM_to_RND(element_em);
6584 for (j = 0; j < level->num_android_clone_elements; j++)
6585 if (level->android_clone_element[j] == element_rnd)
6586 element_found = TRUE;
6590 level->android_clone_element[level->num_android_clone_elements++] =
6593 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6598 if (level->num_android_clone_elements == 0)
6600 level->num_android_clone_elements = 1;
6601 level->android_clone_element[0] = EL_EMPTY;
6605 int map_direction_RND_to_EM(int direction)
6607 return (direction == MV_UP ? 0 :
6608 direction == MV_RIGHT ? 1 :
6609 direction == MV_DOWN ? 2 :
6610 direction == MV_LEFT ? 3 :
6614 int map_direction_EM_to_RND(int direction)
6616 return (direction == 0 ? MV_UP :
6617 direction == 1 ? MV_RIGHT :
6618 direction == 2 ? MV_DOWN :
6619 direction == 3 ? MV_LEFT :
6623 int map_element_RND_to_SP(int element_rnd)
6625 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6627 if (element_rnd >= EL_SP_START &&
6628 element_rnd <= EL_SP_END)
6629 element_sp = element_rnd - EL_SP_START;
6630 else if (element_rnd == EL_EMPTY_SPACE)
6632 else if (element_rnd == EL_INVISIBLE_WALL)
6638 int map_element_SP_to_RND(int element_sp)
6640 int element_rnd = EL_UNKNOWN;
6642 if (element_sp >= 0x00 &&
6644 element_rnd = EL_SP_START + element_sp;
6645 else if (element_sp == 0x28)
6646 element_rnd = EL_INVISIBLE_WALL;
6651 int map_action_SP_to_RND(int action_sp)
6655 case actActive: return ACTION_ACTIVE;
6656 case actImpact: return ACTION_IMPACT;
6657 case actExploding: return ACTION_EXPLODING;
6658 case actDigging: return ACTION_DIGGING;
6659 case actSnapping: return ACTION_SNAPPING;
6660 case actCollecting: return ACTION_COLLECTING;
6661 case actPassing: return ACTION_PASSING;
6662 case actPushing: return ACTION_PUSHING;
6663 case actDropping: return ACTION_DROPPING;
6665 default: return ACTION_DEFAULT;
6669 int get_next_element(int element)
6673 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6674 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6675 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6676 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6677 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6678 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6679 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6680 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6681 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6682 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6683 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6685 default: return element;
6689 int el_act_dir2img(int element, int action, int direction)
6691 element = GFX_ELEMENT(element);
6692 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6694 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6695 return element_info[element].direction_graphic[action][direction];
6698 static int el_act_dir2crm(int element, int action, int direction)
6700 element = GFX_ELEMENT(element);
6701 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6703 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6704 return element_info[element].direction_crumbled[action][direction];
6707 int el_act2img(int element, int action)
6709 element = GFX_ELEMENT(element);
6711 return element_info[element].graphic[action];
6714 int el_act2crm(int element, int action)
6716 element = GFX_ELEMENT(element);
6718 return element_info[element].crumbled[action];
6721 int el_dir2img(int element, int direction)
6723 element = GFX_ELEMENT(element);
6725 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6728 int el2baseimg(int element)
6730 return element_info[element].graphic[ACTION_DEFAULT];
6733 int el2img(int element)
6735 element = GFX_ELEMENT(element);
6737 return element_info[element].graphic[ACTION_DEFAULT];
6740 int el2edimg(int element)
6742 element = GFX_ELEMENT(element);
6744 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6747 int el2preimg(int element)
6749 element = GFX_ELEMENT(element);
6751 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6754 int el2panelimg(int element)
6756 element = GFX_ELEMENT(element);
6758 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6761 int font2baseimg(int font_nr)
6763 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6766 int getBeltNrFromBeltElement(int element)
6768 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6769 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6770 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6773 int getBeltNrFromBeltActiveElement(int element)
6775 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6776 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6777 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6780 int getBeltNrFromBeltSwitchElement(int element)
6782 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6783 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6784 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6787 int getBeltDirNrFromBeltElement(int element)
6789 static int belt_base_element[4] =
6791 EL_CONVEYOR_BELT_1_LEFT,
6792 EL_CONVEYOR_BELT_2_LEFT,
6793 EL_CONVEYOR_BELT_3_LEFT,
6794 EL_CONVEYOR_BELT_4_LEFT
6797 int belt_nr = getBeltNrFromBeltElement(element);
6798 int belt_dir_nr = element - belt_base_element[belt_nr];
6800 return (belt_dir_nr % 3);
6803 int getBeltDirNrFromBeltSwitchElement(int element)
6805 static int belt_base_element[4] =
6807 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6808 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6809 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6810 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6813 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6814 int belt_dir_nr = element - belt_base_element[belt_nr];
6816 return (belt_dir_nr % 3);
6819 int getBeltDirFromBeltElement(int element)
6821 static int belt_move_dir[3] =
6828 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6830 return belt_move_dir[belt_dir_nr];
6833 int getBeltDirFromBeltSwitchElement(int element)
6835 static int belt_move_dir[3] =
6842 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6844 return belt_move_dir[belt_dir_nr];
6847 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6849 static int belt_base_element[4] =
6851 EL_CONVEYOR_BELT_1_LEFT,
6852 EL_CONVEYOR_BELT_2_LEFT,
6853 EL_CONVEYOR_BELT_3_LEFT,
6854 EL_CONVEYOR_BELT_4_LEFT
6857 return belt_base_element[belt_nr] + belt_dir_nr;
6860 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6862 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6864 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6867 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6869 static int belt_base_element[4] =
6871 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6872 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6873 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6874 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6877 return belt_base_element[belt_nr] + belt_dir_nr;
6880 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6882 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6884 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6887 boolean getTeamMode_EM()
6889 return game.team_mode;
6892 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6894 int game_frame_delay_value;
6896 game_frame_delay_value =
6897 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6898 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6901 if (tape.playing && tape.warp_forward && !tape.pausing)
6902 game_frame_delay_value = 0;
6904 return game_frame_delay_value;
6907 unsigned int InitRND(int seed)
6909 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6910 return InitEngineRandom_EM(seed);
6911 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6912 return InitEngineRandom_SP(seed);
6914 return InitEngineRandom_RND(seed);
6917 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6918 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6920 inline static int get_effective_element_EM(int tile, int frame_em)
6922 int element = object_mapping[tile].element_rnd;
6923 int action = object_mapping[tile].action;
6924 boolean is_backside = object_mapping[tile].is_backside;
6925 boolean action_removing = (action == ACTION_DIGGING ||
6926 action == ACTION_SNAPPING ||
6927 action == ACTION_COLLECTING);
6933 case Yacid_splash_eB:
6934 case Yacid_splash_wB:
6935 return (frame_em > 5 ? EL_EMPTY : element);
6941 else /* frame_em == 7 */
6945 case Yacid_splash_eB:
6946 case Yacid_splash_wB:
6949 case Yemerald_stone:
6952 case Ydiamond_stone:
6956 case Xdrip_stretchB:
6975 case Xsand_stonein_1:
6976 case Xsand_stonein_2:
6977 case Xsand_stonein_3:
6978 case Xsand_stonein_4:
6982 return (is_backside || action_removing ? EL_EMPTY : element);
6987 inline static boolean check_linear_animation_EM(int tile)
6991 case Xsand_stonesand_1:
6992 case Xsand_stonesand_quickout_1:
6993 case Xsand_sandstone_1:
6994 case Xsand_stonein_1:
6995 case Xsand_stoneout_1:
7014 case Yacid_splash_eB:
7015 case Yacid_splash_wB:
7016 case Yemerald_stone:
7023 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7024 boolean has_crumbled_graphics,
7025 int crumbled, int sync_frame)
7027 /* if element can be crumbled, but certain action graphics are just empty
7028 space (like instantly snapping sand to empty space in 1 frame), do not
7029 treat these empty space graphics as crumbled graphics in EMC engine */
7030 if (crumbled == IMG_EMPTY_SPACE)
7031 has_crumbled_graphics = FALSE;
7033 if (has_crumbled_graphics)
7035 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7036 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7037 g_crumbled->anim_delay,
7038 g_crumbled->anim_mode,
7039 g_crumbled->anim_start_frame,
7042 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7043 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7045 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7047 g_em->has_crumbled_graphics = TRUE;
7051 g_em->crumbled_bitmap = NULL;
7052 g_em->crumbled_src_x = 0;
7053 g_em->crumbled_src_y = 0;
7054 g_em->crumbled_border_size = 0;
7056 g_em->has_crumbled_graphics = FALSE;
7060 void ResetGfxAnimation_EM(int x, int y, int tile)
7065 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7066 int tile, int frame_em, int x, int y)
7068 int action = object_mapping[tile].action;
7069 int direction = object_mapping[tile].direction;
7070 int effective_element = get_effective_element_EM(tile, frame_em);
7071 int graphic = (direction == MV_NONE ?
7072 el_act2img(effective_element, action) :
7073 el_act_dir2img(effective_element, action, direction));
7074 struct GraphicInfo *g = &graphic_info[graphic];
7076 boolean action_removing = (action == ACTION_DIGGING ||
7077 action == ACTION_SNAPPING ||
7078 action == ACTION_COLLECTING);
7079 boolean action_moving = (action == ACTION_FALLING ||
7080 action == ACTION_MOVING ||
7081 action == ACTION_PUSHING ||
7082 action == ACTION_EATING ||
7083 action == ACTION_FILLING ||
7084 action == ACTION_EMPTYING);
7085 boolean action_falling = (action == ACTION_FALLING ||
7086 action == ACTION_FILLING ||
7087 action == ACTION_EMPTYING);
7089 /* special case: graphic uses "2nd movement tile" and has defined
7090 7 frames for movement animation (or less) => use default graphic
7091 for last (8th) frame which ends the movement animation */
7092 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7094 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7095 graphic = (direction == MV_NONE ?
7096 el_act2img(effective_element, action) :
7097 el_act_dir2img(effective_element, action, direction));
7099 g = &graphic_info[graphic];
7102 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7106 else if (action_moving)
7108 boolean is_backside = object_mapping[tile].is_backside;
7112 int direction = object_mapping[tile].direction;
7113 int move_dir = (action_falling ? MV_DOWN : direction);
7118 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7119 if (g->double_movement && frame_em == 0)
7123 if (move_dir == MV_LEFT)
7124 GfxFrame[x - 1][y] = GfxFrame[x][y];
7125 else if (move_dir == MV_RIGHT)
7126 GfxFrame[x + 1][y] = GfxFrame[x][y];
7127 else if (move_dir == MV_UP)
7128 GfxFrame[x][y - 1] = GfxFrame[x][y];
7129 else if (move_dir == MV_DOWN)
7130 GfxFrame[x][y + 1] = GfxFrame[x][y];
7137 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7138 if (tile == Xsand_stonesand_quickout_1 ||
7139 tile == Xsand_stonesand_quickout_2)
7143 if (graphic_info[graphic].anim_global_sync)
7144 sync_frame = FrameCounter;
7145 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7146 sync_frame = GfxFrame[x][y];
7148 sync_frame = 0; /* playfield border (pseudo steel) */
7150 SetRandomAnimationValue(x, y);
7152 int frame = getAnimationFrame(g->anim_frames,
7155 g->anim_start_frame,
7158 g_em->unique_identifier =
7159 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7162 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7163 int tile, int frame_em, int x, int y)
7165 int action = object_mapping[tile].action;
7166 int direction = object_mapping[tile].direction;
7167 boolean is_backside = object_mapping[tile].is_backside;
7168 int effective_element = get_effective_element_EM(tile, frame_em);
7169 int effective_action = action;
7170 int graphic = (direction == MV_NONE ?
7171 el_act2img(effective_element, effective_action) :
7172 el_act_dir2img(effective_element, effective_action,
7174 int crumbled = (direction == MV_NONE ?
7175 el_act2crm(effective_element, effective_action) :
7176 el_act_dir2crm(effective_element, effective_action,
7178 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7179 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7180 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7181 struct GraphicInfo *g = &graphic_info[graphic];
7184 /* special case: graphic uses "2nd movement tile" and has defined
7185 7 frames for movement animation (or less) => use default graphic
7186 for last (8th) frame which ends the movement animation */
7187 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7189 effective_action = ACTION_DEFAULT;
7190 graphic = (direction == MV_NONE ?
7191 el_act2img(effective_element, effective_action) :
7192 el_act_dir2img(effective_element, effective_action,
7194 crumbled = (direction == MV_NONE ?
7195 el_act2crm(effective_element, effective_action) :
7196 el_act_dir2crm(effective_element, effective_action,
7199 g = &graphic_info[graphic];
7202 if (graphic_info[graphic].anim_global_sync)
7203 sync_frame = FrameCounter;
7204 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7205 sync_frame = GfxFrame[x][y];
7207 sync_frame = 0; /* playfield border (pseudo steel) */
7209 SetRandomAnimationValue(x, y);
7211 int frame = getAnimationFrame(g->anim_frames,
7214 g->anim_start_frame,
7217 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7218 g->double_movement && is_backside);
7220 /* (updating the "crumbled" graphic definitions is probably not really needed,
7221 as animations for crumbled graphics can't be longer than one EMC cycle) */
7222 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7226 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7227 int player_nr, int anim, int frame_em)
7229 int element = player_mapping[player_nr][anim].element_rnd;
7230 int action = player_mapping[player_nr][anim].action;
7231 int direction = player_mapping[player_nr][anim].direction;
7232 int graphic = (direction == MV_NONE ?
7233 el_act2img(element, action) :
7234 el_act_dir2img(element, action, direction));
7235 struct GraphicInfo *g = &graphic_info[graphic];
7238 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7240 stored_player[player_nr].StepFrame = frame_em;
7242 sync_frame = stored_player[player_nr].Frame;
7244 int frame = getAnimationFrame(g->anim_frames,
7247 g->anim_start_frame,
7250 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7251 &g_em->src_x, &g_em->src_y, FALSE);
7254 void InitGraphicInfo_EM(void)
7259 int num_em_gfx_errors = 0;
7261 if (graphic_info_em_object[0][0].bitmap == NULL)
7263 /* EM graphics not yet initialized in em_open_all() */
7268 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7271 /* always start with reliable default values */
7272 for (i = 0; i < TILE_MAX; i++)
7274 object_mapping[i].element_rnd = EL_UNKNOWN;
7275 object_mapping[i].is_backside = FALSE;
7276 object_mapping[i].action = ACTION_DEFAULT;
7277 object_mapping[i].direction = MV_NONE;
7280 /* always start with reliable default values */
7281 for (p = 0; p < MAX_PLAYERS; p++)
7283 for (i = 0; i < SPR_MAX; i++)
7285 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7286 player_mapping[p][i].action = ACTION_DEFAULT;
7287 player_mapping[p][i].direction = MV_NONE;
7291 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7293 int e = em_object_mapping_list[i].element_em;
7295 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7296 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7298 if (em_object_mapping_list[i].action != -1)
7299 object_mapping[e].action = em_object_mapping_list[i].action;
7301 if (em_object_mapping_list[i].direction != -1)
7302 object_mapping[e].direction =
7303 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7306 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7308 int a = em_player_mapping_list[i].action_em;
7309 int p = em_player_mapping_list[i].player_nr;
7311 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7313 if (em_player_mapping_list[i].action != -1)
7314 player_mapping[p][a].action = em_player_mapping_list[i].action;
7316 if (em_player_mapping_list[i].direction != -1)
7317 player_mapping[p][a].direction =
7318 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7321 for (i = 0; i < TILE_MAX; i++)
7323 int element = object_mapping[i].element_rnd;
7324 int action = object_mapping[i].action;
7325 int direction = object_mapping[i].direction;
7326 boolean is_backside = object_mapping[i].is_backside;
7327 boolean action_exploding = ((action == ACTION_EXPLODING ||
7328 action == ACTION_SMASHED_BY_ROCK ||
7329 action == ACTION_SMASHED_BY_SPRING) &&
7330 element != EL_DIAMOND);
7331 boolean action_active = (action == ACTION_ACTIVE);
7332 boolean action_other = (action == ACTION_OTHER);
7334 for (j = 0; j < 8; j++)
7336 int effective_element = get_effective_element_EM(i, j);
7337 int effective_action = (j < 7 ? action :
7338 i == Xdrip_stretch ? action :
7339 i == Xdrip_stretchB ? action :
7340 i == Ydrip_s1 ? action :
7341 i == Ydrip_s1B ? action :
7342 i == Xball_1B ? action :
7343 i == Xball_2 ? action :
7344 i == Xball_2B ? action :
7345 i == Yball_eat ? action :
7346 i == Ykey_1_eat ? action :
7347 i == Ykey_2_eat ? action :
7348 i == Ykey_3_eat ? action :
7349 i == Ykey_4_eat ? action :
7350 i == Ykey_5_eat ? action :
7351 i == Ykey_6_eat ? action :
7352 i == Ykey_7_eat ? action :
7353 i == Ykey_8_eat ? action :
7354 i == Ylenses_eat ? action :
7355 i == Ymagnify_eat ? action :
7356 i == Ygrass_eat ? action :
7357 i == Ydirt_eat ? action :
7358 i == Xsand_stonein_1 ? action :
7359 i == Xsand_stonein_2 ? action :
7360 i == Xsand_stonein_3 ? action :
7361 i == Xsand_stonein_4 ? action :
7362 i == Xsand_stoneout_1 ? action :
7363 i == Xsand_stoneout_2 ? action :
7364 i == Xboom_android ? ACTION_EXPLODING :
7365 action_exploding ? ACTION_EXPLODING :
7366 action_active ? action :
7367 action_other ? action :
7369 int graphic = (el_act_dir2img(effective_element, effective_action,
7371 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7373 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7374 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7375 boolean has_action_graphics = (graphic != base_graphic);
7376 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7377 struct GraphicInfo *g = &graphic_info[graphic];
7378 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7381 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7382 boolean special_animation = (action != ACTION_DEFAULT &&
7383 g->anim_frames == 3 &&
7384 g->anim_delay == 2 &&
7385 g->anim_mode & ANIM_LINEAR);
7386 int sync_frame = (i == Xdrip_stretch ? 7 :
7387 i == Xdrip_stretchB ? 7 :
7388 i == Ydrip_s2 ? j + 8 :
7389 i == Ydrip_s2B ? j + 8 :
7398 i == Xfake_acid_1 ? 0 :
7399 i == Xfake_acid_2 ? 10 :
7400 i == Xfake_acid_3 ? 20 :
7401 i == Xfake_acid_4 ? 30 :
7402 i == Xfake_acid_5 ? 40 :
7403 i == Xfake_acid_6 ? 50 :
7404 i == Xfake_acid_7 ? 60 :
7405 i == Xfake_acid_8 ? 70 :
7407 i == Xball_2B ? j + 8 :
7408 i == Yball_eat ? j + 1 :
7409 i == Ykey_1_eat ? j + 1 :
7410 i == Ykey_2_eat ? j + 1 :
7411 i == Ykey_3_eat ? j + 1 :
7412 i == Ykey_4_eat ? j + 1 :
7413 i == Ykey_5_eat ? j + 1 :
7414 i == Ykey_6_eat ? j + 1 :
7415 i == Ykey_7_eat ? j + 1 :
7416 i == Ykey_8_eat ? j + 1 :
7417 i == Ylenses_eat ? j + 1 :
7418 i == Ymagnify_eat ? j + 1 :
7419 i == Ygrass_eat ? j + 1 :
7420 i == Ydirt_eat ? j + 1 :
7421 i == Xamoeba_1 ? 0 :
7422 i == Xamoeba_2 ? 1 :
7423 i == Xamoeba_3 ? 2 :
7424 i == Xamoeba_4 ? 3 :
7425 i == Xamoeba_5 ? 0 :
7426 i == Xamoeba_6 ? 1 :
7427 i == Xamoeba_7 ? 2 :
7428 i == Xamoeba_8 ? 3 :
7429 i == Xexit_2 ? j + 8 :
7430 i == Xexit_3 ? j + 16 :
7431 i == Xdynamite_1 ? 0 :
7432 i == Xdynamite_2 ? 8 :
7433 i == Xdynamite_3 ? 16 :
7434 i == Xdynamite_4 ? 24 :
7435 i == Xsand_stonein_1 ? j + 1 :
7436 i == Xsand_stonein_2 ? j + 9 :
7437 i == Xsand_stonein_3 ? j + 17 :
7438 i == Xsand_stonein_4 ? j + 25 :
7439 i == Xsand_stoneout_1 && j == 0 ? 0 :
7440 i == Xsand_stoneout_1 && j == 1 ? 0 :
7441 i == Xsand_stoneout_1 && j == 2 ? 1 :
7442 i == Xsand_stoneout_1 && j == 3 ? 2 :
7443 i == Xsand_stoneout_1 && j == 4 ? 2 :
7444 i == Xsand_stoneout_1 && j == 5 ? 3 :
7445 i == Xsand_stoneout_1 && j == 6 ? 4 :
7446 i == Xsand_stoneout_1 && j == 7 ? 4 :
7447 i == Xsand_stoneout_2 && j == 0 ? 5 :
7448 i == Xsand_stoneout_2 && j == 1 ? 6 :
7449 i == Xsand_stoneout_2 && j == 2 ? 7 :
7450 i == Xsand_stoneout_2 && j == 3 ? 8 :
7451 i == Xsand_stoneout_2 && j == 4 ? 9 :
7452 i == Xsand_stoneout_2 && j == 5 ? 11 :
7453 i == Xsand_stoneout_2 && j == 6 ? 13 :
7454 i == Xsand_stoneout_2 && j == 7 ? 15 :
7455 i == Xboom_bug && j == 1 ? 2 :
7456 i == Xboom_bug && j == 2 ? 2 :
7457 i == Xboom_bug && j == 3 ? 4 :
7458 i == Xboom_bug && j == 4 ? 4 :
7459 i == Xboom_bug && j == 5 ? 2 :
7460 i == Xboom_bug && j == 6 ? 2 :
7461 i == Xboom_bug && j == 7 ? 0 :
7462 i == Xboom_bomb && j == 1 ? 2 :
7463 i == Xboom_bomb && j == 2 ? 2 :
7464 i == Xboom_bomb && j == 3 ? 4 :
7465 i == Xboom_bomb && j == 4 ? 4 :
7466 i == Xboom_bomb && j == 5 ? 2 :
7467 i == Xboom_bomb && j == 6 ? 2 :
7468 i == Xboom_bomb && j == 7 ? 0 :
7469 i == Xboom_android && j == 7 ? 6 :
7470 i == Xboom_1 && j == 1 ? 2 :
7471 i == Xboom_1 && j == 2 ? 2 :
7472 i == Xboom_1 && j == 3 ? 4 :
7473 i == Xboom_1 && j == 4 ? 4 :
7474 i == Xboom_1 && j == 5 ? 6 :
7475 i == Xboom_1 && j == 6 ? 6 :
7476 i == Xboom_1 && j == 7 ? 8 :
7477 i == Xboom_2 && j == 0 ? 8 :
7478 i == Xboom_2 && j == 1 ? 8 :
7479 i == Xboom_2 && j == 2 ? 10 :
7480 i == Xboom_2 && j == 3 ? 10 :
7481 i == Xboom_2 && j == 4 ? 10 :
7482 i == Xboom_2 && j == 5 ? 12 :
7483 i == Xboom_2 && j == 6 ? 12 :
7484 i == Xboom_2 && j == 7 ? 12 :
7485 special_animation && j == 4 ? 3 :
7486 effective_action != action ? 0 :
7490 Bitmap *debug_bitmap = g_em->bitmap;
7491 int debug_src_x = g_em->src_x;
7492 int debug_src_y = g_em->src_y;
7495 int frame = getAnimationFrame(g->anim_frames,
7498 g->anim_start_frame,
7501 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7502 g->double_movement && is_backside);
7504 g_em->bitmap = src_bitmap;
7505 g_em->src_x = src_x;
7506 g_em->src_y = src_y;
7507 g_em->src_offset_x = 0;
7508 g_em->src_offset_y = 0;
7509 g_em->dst_offset_x = 0;
7510 g_em->dst_offset_y = 0;
7511 g_em->width = TILEX;
7512 g_em->height = TILEY;
7514 g_em->preserve_background = FALSE;
7516 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7519 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7520 effective_action == ACTION_MOVING ||
7521 effective_action == ACTION_PUSHING ||
7522 effective_action == ACTION_EATING)) ||
7523 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7524 effective_action == ACTION_EMPTYING)))
7527 (effective_action == ACTION_FALLING ||
7528 effective_action == ACTION_FILLING ||
7529 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7530 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7531 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7532 int num_steps = (i == Ydrip_s1 ? 16 :
7533 i == Ydrip_s1B ? 16 :
7534 i == Ydrip_s2 ? 16 :
7535 i == Ydrip_s2B ? 16 :
7536 i == Xsand_stonein_1 ? 32 :
7537 i == Xsand_stonein_2 ? 32 :
7538 i == Xsand_stonein_3 ? 32 :
7539 i == Xsand_stonein_4 ? 32 :
7540 i == Xsand_stoneout_1 ? 16 :
7541 i == Xsand_stoneout_2 ? 16 : 8);
7542 int cx = ABS(dx) * (TILEX / num_steps);
7543 int cy = ABS(dy) * (TILEY / num_steps);
7544 int step_frame = (i == Ydrip_s2 ? j + 8 :
7545 i == Ydrip_s2B ? j + 8 :
7546 i == Xsand_stonein_2 ? j + 8 :
7547 i == Xsand_stonein_3 ? j + 16 :
7548 i == Xsand_stonein_4 ? j + 24 :
7549 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7550 int step = (is_backside ? step_frame : num_steps - step_frame);
7552 if (is_backside) /* tile where movement starts */
7554 if (dx < 0 || dy < 0)
7556 g_em->src_offset_x = cx * step;
7557 g_em->src_offset_y = cy * step;
7561 g_em->dst_offset_x = cx * step;
7562 g_em->dst_offset_y = cy * step;
7565 else /* tile where movement ends */
7567 if (dx < 0 || dy < 0)
7569 g_em->dst_offset_x = cx * step;
7570 g_em->dst_offset_y = cy * step;
7574 g_em->src_offset_x = cx * step;
7575 g_em->src_offset_y = cy * step;
7579 g_em->width = TILEX - cx * step;
7580 g_em->height = TILEY - cy * step;
7583 /* create unique graphic identifier to decide if tile must be redrawn */
7584 /* bit 31 - 16 (16 bit): EM style graphic
7585 bit 15 - 12 ( 4 bit): EM style frame
7586 bit 11 - 6 ( 6 bit): graphic width
7587 bit 5 - 0 ( 6 bit): graphic height */
7588 g_em->unique_identifier =
7589 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7593 /* skip check for EMC elements not contained in original EMC artwork */
7594 if (element == EL_EMC_FAKE_ACID)
7597 if (g_em->bitmap != debug_bitmap ||
7598 g_em->src_x != debug_src_x ||
7599 g_em->src_y != debug_src_y ||
7600 g_em->src_offset_x != 0 ||
7601 g_em->src_offset_y != 0 ||
7602 g_em->dst_offset_x != 0 ||
7603 g_em->dst_offset_y != 0 ||
7604 g_em->width != TILEX ||
7605 g_em->height != TILEY)
7607 static int last_i = -1;
7615 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7616 i, element, element_info[element].token_name,
7617 element_action_info[effective_action].suffix, direction);
7619 if (element != effective_element)
7620 printf(" [%d ('%s')]",
7622 element_info[effective_element].token_name);
7626 if (g_em->bitmap != debug_bitmap)
7627 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7628 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7630 if (g_em->src_x != debug_src_x ||
7631 g_em->src_y != debug_src_y)
7632 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7633 j, (is_backside ? 'B' : 'F'),
7634 g_em->src_x, g_em->src_y,
7635 g_em->src_x / 32, g_em->src_y / 32,
7636 debug_src_x, debug_src_y,
7637 debug_src_x / 32, debug_src_y / 32);
7639 if (g_em->src_offset_x != 0 ||
7640 g_em->src_offset_y != 0 ||
7641 g_em->dst_offset_x != 0 ||
7642 g_em->dst_offset_y != 0)
7643 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7645 g_em->src_offset_x, g_em->src_offset_y,
7646 g_em->dst_offset_x, g_em->dst_offset_y);
7648 if (g_em->width != TILEX ||
7649 g_em->height != TILEY)
7650 printf(" %d (%d): size %d,%d should be %d,%d\n",
7652 g_em->width, g_em->height, TILEX, TILEY);
7654 num_em_gfx_errors++;
7661 for (i = 0; i < TILE_MAX; i++)
7663 for (j = 0; j < 8; j++)
7665 int element = object_mapping[i].element_rnd;
7666 int action = object_mapping[i].action;
7667 int direction = object_mapping[i].direction;
7668 boolean is_backside = object_mapping[i].is_backside;
7669 int graphic_action = el_act_dir2img(element, action, direction);
7670 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7672 if ((action == ACTION_SMASHED_BY_ROCK ||
7673 action == ACTION_SMASHED_BY_SPRING ||
7674 action == ACTION_EATING) &&
7675 graphic_action == graphic_default)
7677 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7678 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7679 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7680 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7683 /* no separate animation for "smashed by rock" -- use rock instead */
7684 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7685 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7687 g_em->bitmap = g_xx->bitmap;
7688 g_em->src_x = g_xx->src_x;
7689 g_em->src_y = g_xx->src_y;
7690 g_em->src_offset_x = g_xx->src_offset_x;
7691 g_em->src_offset_y = g_xx->src_offset_y;
7692 g_em->dst_offset_x = g_xx->dst_offset_x;
7693 g_em->dst_offset_y = g_xx->dst_offset_y;
7694 g_em->width = g_xx->width;
7695 g_em->height = g_xx->height;
7696 g_em->unique_identifier = g_xx->unique_identifier;
7699 g_em->preserve_background = TRUE;
7704 for (p = 0; p < MAX_PLAYERS; p++)
7706 for (i = 0; i < SPR_MAX; i++)
7708 int element = player_mapping[p][i].element_rnd;
7709 int action = player_mapping[p][i].action;
7710 int direction = player_mapping[p][i].direction;
7712 for (j = 0; j < 8; j++)
7714 int effective_element = element;
7715 int effective_action = action;
7716 int graphic = (direction == MV_NONE ?
7717 el_act2img(effective_element, effective_action) :
7718 el_act_dir2img(effective_element, effective_action,
7720 struct GraphicInfo *g = &graphic_info[graphic];
7721 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7727 Bitmap *debug_bitmap = g_em->bitmap;
7728 int debug_src_x = g_em->src_x;
7729 int debug_src_y = g_em->src_y;
7732 int frame = getAnimationFrame(g->anim_frames,
7735 g->anim_start_frame,
7738 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7740 g_em->bitmap = src_bitmap;
7741 g_em->src_x = src_x;
7742 g_em->src_y = src_y;
7743 g_em->src_offset_x = 0;
7744 g_em->src_offset_y = 0;
7745 g_em->dst_offset_x = 0;
7746 g_em->dst_offset_y = 0;
7747 g_em->width = TILEX;
7748 g_em->height = TILEY;
7752 /* skip check for EMC elements not contained in original EMC artwork */
7753 if (element == EL_PLAYER_3 ||
7754 element == EL_PLAYER_4)
7757 if (g_em->bitmap != debug_bitmap ||
7758 g_em->src_x != debug_src_x ||
7759 g_em->src_y != debug_src_y)
7761 static int last_i = -1;
7769 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7770 p, i, element, element_info[element].token_name,
7771 element_action_info[effective_action].suffix, direction);
7773 if (element != effective_element)
7774 printf(" [%d ('%s')]",
7776 element_info[effective_element].token_name);
7780 if (g_em->bitmap != debug_bitmap)
7781 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7782 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7784 if (g_em->src_x != debug_src_x ||
7785 g_em->src_y != debug_src_y)
7786 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7788 g_em->src_x, g_em->src_y,
7789 g_em->src_x / 32, g_em->src_y / 32,
7790 debug_src_x, debug_src_y,
7791 debug_src_x / 32, debug_src_y / 32);
7793 num_em_gfx_errors++;
7803 printf("::: [%d errors found]\n", num_em_gfx_errors);
7809 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7810 boolean any_player_moving,
7811 boolean any_player_snapping,
7812 boolean any_player_dropping)
7814 static boolean player_was_waiting = TRUE;
7816 if (frame == 0 && !any_player_dropping)
7818 if (!player_was_waiting)
7820 if (!SaveEngineSnapshotToList())
7823 player_was_waiting = TRUE;
7826 else if (any_player_moving || any_player_snapping || any_player_dropping)
7828 player_was_waiting = FALSE;
7832 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7833 boolean murphy_is_dropping)
7835 static boolean player_was_waiting = TRUE;
7837 if (murphy_is_waiting)
7839 if (!player_was_waiting)
7841 if (!SaveEngineSnapshotToList())
7844 player_was_waiting = TRUE;
7849 player_was_waiting = FALSE;
7853 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7854 boolean any_player_moving,
7855 boolean any_player_snapping,
7856 boolean any_player_dropping)
7858 if (tape.single_step && tape.recording && !tape.pausing)
7859 if (frame == 0 && !any_player_dropping)
7860 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7862 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7863 any_player_snapping, any_player_dropping);
7866 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7867 boolean murphy_is_dropping)
7869 if (tape.single_step && tape.recording && !tape.pausing)
7870 if (murphy_is_waiting)
7871 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7873 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7876 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7877 int graphic, int sync_frame, int x, int y)
7879 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7881 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7884 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7886 return (IS_NEXT_FRAME(sync_frame, graphic));
7889 int getGraphicInfo_Delay(int graphic)
7891 return graphic_info[graphic].anim_delay;
7894 void PlayMenuSoundExt(int sound)
7896 if (sound == SND_UNDEFINED)
7899 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7900 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7903 if (IS_LOOP_SOUND(sound))
7904 PlaySoundLoop(sound);
7909 void PlayMenuSound()
7911 PlayMenuSoundExt(menu.sound[game_status]);
7914 void PlayMenuSoundStereo(int sound, int stereo_position)
7916 if (sound == SND_UNDEFINED)
7919 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7920 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7923 if (IS_LOOP_SOUND(sound))
7924 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7926 PlaySoundStereo(sound, stereo_position);
7929 void PlayMenuSoundIfLoopExt(int sound)
7931 if (sound == SND_UNDEFINED)
7934 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7935 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7938 if (IS_LOOP_SOUND(sound))
7939 PlaySoundLoop(sound);
7942 void PlayMenuSoundIfLoop()
7944 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7947 void PlayMenuMusicExt(int music)
7949 if (music == MUS_UNDEFINED)
7952 if (!setup.sound_music)
7958 void PlayMenuMusic()
7960 PlayMenuMusicExt(menu.music[game_status]);
7963 void PlaySoundActivating()
7966 PlaySound(SND_MENU_ITEM_ACTIVATING);
7970 void PlaySoundSelecting()
7973 PlaySound(SND_MENU_ITEM_SELECTING);
7977 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7979 boolean change_fullscreen = (setup.fullscreen !=
7980 video.fullscreen_enabled);
7981 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7982 !strEqual(setup.fullscreen_mode,
7983 video.fullscreen_mode_current));
7984 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7985 setup.window_scaling_percent !=
7986 video.window_scaling_percent);
7988 if (change_window_scaling_percent && video.fullscreen_enabled)
7991 if (!change_window_scaling_percent && !video.fullscreen_available)
7994 #if defined(TARGET_SDL2)
7995 if (change_window_scaling_percent)
7997 SDLSetWindowScaling(setup.window_scaling_percent);
8001 else if (change_fullscreen)
8003 SDLSetWindowFullscreen(setup.fullscreen);
8005 /* set setup value according to successfully changed fullscreen mode */
8006 setup.fullscreen = video.fullscreen_enabled;
8012 if (change_fullscreen ||
8013 change_fullscreen_mode ||
8014 change_window_scaling_percent)
8016 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8018 /* save backbuffer content which gets lost when toggling fullscreen mode */
8019 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8021 if (change_fullscreen_mode)
8023 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8024 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8027 if (change_window_scaling_percent)
8029 /* keep window mode, but change window scaling */
8030 video.fullscreen_enabled = TRUE; /* force new window scaling */
8033 /* toggle fullscreen */
8034 ChangeVideoModeIfNeeded(setup.fullscreen);
8036 /* set setup value according to successfully changed fullscreen mode */
8037 setup.fullscreen = video.fullscreen_enabled;
8039 /* restore backbuffer content from temporary backbuffer backup bitmap */
8040 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8042 FreeBitmap(tmp_backbuffer);
8044 /* update visible window/screen */
8045 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8049 void ChangeViewportPropertiesIfNeeded()
8051 int gfx_game_mode = game_status;
8052 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8054 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8055 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8056 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8057 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8058 int border_size = vp_playfield->border_size;
8059 int new_sx = vp_playfield->x + border_size;
8060 int new_sy = vp_playfield->y + border_size;
8061 int new_sxsize = vp_playfield->width - 2 * border_size;
8062 int new_sysize = vp_playfield->height - 2 * border_size;
8063 int new_real_sx = vp_playfield->x;
8064 int new_real_sy = vp_playfield->y;
8065 int new_full_sxsize = vp_playfield->width;
8066 int new_full_sysize = vp_playfield->height;
8067 int new_dx = vp_door_1->x;
8068 int new_dy = vp_door_1->y;
8069 int new_dxsize = vp_door_1->width;
8070 int new_dysize = vp_door_1->height;
8071 int new_vx = vp_door_2->x;
8072 int new_vy = vp_door_2->y;
8073 int new_vxsize = vp_door_2->width;
8074 int new_vysize = vp_door_2->height;
8075 int new_ex = vp_door_3->x;
8076 int new_ey = vp_door_3->y;
8077 int new_exsize = vp_door_3->width;
8078 int new_eysize = vp_door_3->height;
8079 int new_tilesize_var =
8080 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8082 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8083 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8084 int new_scr_fieldx = new_sxsize / tilesize;
8085 int new_scr_fieldy = new_sysize / tilesize;
8086 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8087 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8088 boolean init_gfx_buffers = FALSE;
8089 boolean init_video_buffer = FALSE;
8090 boolean init_gadgets_and_toons = FALSE;
8091 boolean init_em_graphics = FALSE;
8092 boolean drawing_area_changed = FALSE;
8094 if (viewport.window.width != WIN_XSIZE ||
8095 viewport.window.height != WIN_YSIZE)
8097 WIN_XSIZE = viewport.window.width;
8098 WIN_YSIZE = viewport.window.height;
8100 init_video_buffer = TRUE;
8101 init_gfx_buffers = TRUE;
8103 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8106 if (new_scr_fieldx != SCR_FIELDX ||
8107 new_scr_fieldy != SCR_FIELDY)
8109 /* this always toggles between MAIN and GAME when using small tile size */
8111 SCR_FIELDX = new_scr_fieldx;
8112 SCR_FIELDY = new_scr_fieldy;
8114 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8125 new_sxsize != SXSIZE ||
8126 new_sysize != SYSIZE ||
8127 new_dxsize != DXSIZE ||
8128 new_dysize != DYSIZE ||
8129 new_vxsize != VXSIZE ||
8130 new_vysize != VYSIZE ||
8131 new_exsize != EXSIZE ||
8132 new_eysize != EYSIZE ||
8133 new_real_sx != REAL_SX ||
8134 new_real_sy != REAL_SY ||
8135 new_full_sxsize != FULL_SXSIZE ||
8136 new_full_sysize != FULL_SYSIZE ||
8137 new_tilesize_var != TILESIZE_VAR
8140 if (new_tilesize_var != TILESIZE_VAR)
8142 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8144 // changing tile size invalidates scroll values of engine snapshots
8145 FreeEngineSnapshotSingle();
8147 // changing tile size requires update of graphic mapping for EM engine
8148 init_em_graphics = TRUE;
8153 new_sxsize != SXSIZE ||
8154 new_sysize != SYSIZE ||
8155 new_real_sx != REAL_SX ||
8156 new_real_sy != REAL_SY ||
8157 new_full_sxsize != FULL_SXSIZE ||
8158 new_full_sysize != FULL_SYSIZE)
8160 if (!init_video_buffer)
8161 drawing_area_changed = TRUE;
8172 SXSIZE = new_sxsize;
8173 SYSIZE = new_sysize;
8174 DXSIZE = new_dxsize;
8175 DYSIZE = new_dysize;
8176 VXSIZE = new_vxsize;
8177 VYSIZE = new_vysize;
8178 EXSIZE = new_exsize;
8179 EYSIZE = new_eysize;
8180 REAL_SX = new_real_sx;
8181 REAL_SY = new_real_sy;
8182 FULL_SXSIZE = new_full_sxsize;
8183 FULL_SYSIZE = new_full_sysize;
8184 TILESIZE_VAR = new_tilesize_var;
8186 init_gfx_buffers = TRUE;
8187 init_gadgets_and_toons = TRUE;
8189 // printf("::: viewports: init_gfx_buffers\n");
8190 // printf("::: viewports: init_gadgets_and_toons\n");
8193 if (init_gfx_buffers)
8195 // printf("::: init_gfx_buffers\n");
8197 SCR_FIELDX = new_scr_fieldx_buffers;
8198 SCR_FIELDY = new_scr_fieldy_buffers;
8202 SCR_FIELDX = new_scr_fieldx;
8203 SCR_FIELDY = new_scr_fieldy;
8205 gfx.drawing_area_changed = drawing_area_changed;
8207 SetDrawDeactivationMask(REDRAW_NONE);
8208 SetDrawBackgroundMask(REDRAW_FIELD);
8211 if (init_video_buffer)
8213 // printf("::: init_video_buffer\n");
8215 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8218 if (init_gadgets_and_toons)
8220 // printf("::: init_gadgets_and_toons\n");
8226 if (init_em_graphics)
8228 InitGraphicInfo_EM();