1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static char *print_if_not_empty(int element)
178 static char *s = NULL;
179 char *token_name = element_info[element].token_name;
184 s = checked_malloc(strlen(token_name) + 10 + 1);
186 if (element != EL_EMPTY)
187 sprintf(s, "%d\t['%s']", element, token_name);
189 sprintf(s, "%d", element);
194 void DumpTile(int x, int y)
199 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
205 printf_line("-", 79);
206 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
207 printf_line("-", 79);
209 if (!IN_LEV_FIELD(x, y))
211 printf("(not in level field)\n");
217 printf(" Feld: %d\t['%s']\n", Feld[x][y],
218 element_info[Feld[x][y]].token_name);
219 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
220 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
221 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
222 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
223 printf(" MovPos: %d\n", MovPos[x][y]);
224 printf(" MovDir: %d\n", MovDir[x][y]);
225 printf(" MovDelay: %d\n", MovDelay[x][y]);
226 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
227 printf(" CustomValue: %d\n", CustomValue[x][y]);
228 printf(" GfxElement: %d\n", GfxElement[x][y]);
229 printf(" GfxAction: %d\n", GfxAction[x][y]);
230 printf(" GfxFrame: %d\n", GfxFrame[x][y]);
234 void SetDrawtoField(int mode)
236 if (mode == DRAW_BUFFERED && setup.soft_scrolling)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
247 drawto_field = fieldbuffer;
249 else /* DRAW_BACKBUFFER */
255 BX2 = SCR_FIELDX - 1;
256 BY2 = SCR_FIELDY - 1;
260 drawto_field = backbuffer;
264 static void RedrawPlayfield_RND()
266 if (game.envelope_active)
269 DrawLevel(REDRAW_ALL);
273 void RedrawPlayfield()
275 if (game_status != GAME_MODE_PLAYING)
278 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
279 RedrawPlayfield_EM(TRUE);
280 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
281 RedrawPlayfield_SP(TRUE);
282 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
283 RedrawPlayfield_RND();
285 BlitScreenToBitmap(backbuffer);
287 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
291 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
293 Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
295 SetClipOrigin(bitmap, bitmap->stored_clip_gc, 0, 0);
296 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
299 void DrawMaskedBorder_FIELD()
301 if (global.border_status >= GAME_MODE_TITLE &&
302 global.border_status <= GAME_MODE_PLAYING &&
303 border.draw_masked[global.border_status])
304 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
307 void DrawMaskedBorder_DOOR_1()
309 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
310 (global.border_status != GAME_MODE_EDITOR ||
311 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
312 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
315 void DrawMaskedBorder_DOOR_2()
317 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
318 global.border_status != GAME_MODE_EDITOR)
319 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
322 void DrawMaskedBorder_DOOR_3()
324 /* currently not available */
327 void DrawMaskedBorder_ALL()
329 DrawMaskedBorder_FIELD();
330 DrawMaskedBorder_DOOR_1();
331 DrawMaskedBorder_DOOR_2();
332 DrawMaskedBorder_DOOR_3();
335 void DrawMaskedBorder(int redraw_mask)
337 /* never draw masked screen borders on borderless screens */
338 if (effectiveGameStatus() == GAME_MODE_LOADING ||
339 effectiveGameStatus() == GAME_MODE_TITLE)
342 /* never draw masked screen borders when displaying request outside door */
343 if (effectiveGameStatus() == GAME_MODE_PSEUDO_DOOR &&
344 global.use_envelope_request)
347 if (redraw_mask & REDRAW_ALL)
348 DrawMaskedBorder_ALL();
351 if (redraw_mask & REDRAW_FIELD)
352 DrawMaskedBorder_FIELD();
353 if (redraw_mask & REDRAW_DOOR_1)
354 DrawMaskedBorder_DOOR_1();
355 if (redraw_mask & REDRAW_DOOR_2)
356 DrawMaskedBorder_DOOR_2();
357 if (redraw_mask & REDRAW_DOOR_3)
358 DrawMaskedBorder_DOOR_3();
362 static void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
364 DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
365 int fx = FX, fy = FY;
366 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
367 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
369 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
370 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
371 int dx_var = dx * TILESIZE_VAR / TILESIZE;
372 int dy_var = dy * TILESIZE_VAR / TILESIZE;
375 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
376 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
378 if (EVEN(SCR_FIELDX))
380 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
381 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
383 fx += (dx_var > 0 ? TILEX_VAR : 0);
390 if (EVEN(SCR_FIELDY))
392 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
393 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
395 fy += (dy_var > 0 ? TILEY_VAR : 0);
402 if (full_lev_fieldx <= SCR_FIELDX)
404 if (EVEN(SCR_FIELDX))
405 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
407 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
410 if (full_lev_fieldy <= SCR_FIELDY)
412 if (EVEN(SCR_FIELDY))
413 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
415 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
418 if (border.draw_masked[GAME_MODE_PLAYING])
420 if (buffer != backbuffer)
422 /* copy playfield buffer to backbuffer to add masked border */
423 BlitBitmap(buffer, backbuffer, fx, fy, SXSIZE, SYSIZE, SX, SY);
424 DrawMaskedBorder(REDRAW_FIELD);
427 BlitBitmap(backbuffer, target_bitmap,
428 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
433 BlitBitmap(buffer, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
437 void BlitScreenToBitmap(Bitmap *target_bitmap)
439 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
440 BlitScreenToBitmap_EM(target_bitmap);
441 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
442 BlitScreenToBitmap_SP(target_bitmap);
443 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
444 BlitScreenToBitmap_RND(target_bitmap);
450 DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
452 if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
453 redraw_mask |= REDRAW_FIELD;
456 // never redraw single tiles, always redraw the whole field
457 // (redrawing single tiles up to a certain threshold was faster on old,
458 // now legacy graphics, but slows things down on modern graphics now)
459 // UPDATE: this is now globally defined by value of REDRAWTILES_THRESHOLD
460 if (redraw_mask & REDRAW_TILES)
461 redraw_mask |= REDRAW_FIELD;
465 /* !!! TEST ONLY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
466 /* (force full redraw) */
467 if (game_status == GAME_MODE_PLAYING)
468 redraw_mask |= REDRAW_FIELD;
471 if (redraw_mask & REDRAW_FIELD)
472 redraw_mask &= ~REDRAW_TILES;
474 if (redraw_mask == REDRAW_NONE)
479 if (redraw_mask & REDRAW_ALL)
480 printf("[REDRAW_ALL]");
481 if (redraw_mask & REDRAW_FIELD)
482 printf("[REDRAW_FIELD]");
483 if (redraw_mask & REDRAW_TILES)
484 printf("[REDRAW_TILES]");
485 if (redraw_mask & REDRAW_DOOR_1)
486 printf("[REDRAW_DOOR_1]");
487 if (redraw_mask & REDRAW_DOOR_2)
488 printf("[REDRAW_DOOR_2]");
489 if (redraw_mask & REDRAW_FROM_BACKBUFFER)
490 printf("[REDRAW_FROM_BACKBUFFER]");
491 printf(" [%d]\n", FrameCounter);
494 if (redraw_mask & REDRAW_TILES &&
495 game_status == GAME_MODE_PLAYING &&
496 border.draw_masked[GAME_MODE_PLAYING])
497 redraw_mask |= REDRAW_FIELD;
499 if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
501 static boolean last_frame_skipped = FALSE;
502 boolean skip_even_when_not_scrolling = TRUE;
503 boolean just_scrolling = (ScreenMovDir != 0);
504 boolean verbose = FALSE;
506 if (global.fps_slowdown_factor > 1 &&
507 (FrameCounter % global.fps_slowdown_factor) &&
508 (just_scrolling || skip_even_when_not_scrolling))
510 redraw_mask &= ~REDRAW_MAIN;
512 last_frame_skipped = TRUE;
515 printf("FRAME SKIPPED\n");
519 if (last_frame_skipped)
520 redraw_mask |= REDRAW_FIELD;
522 last_frame_skipped = FALSE;
525 printf("frame not skipped\n");
529 /* synchronize X11 graphics at this point; if we would synchronize the
530 display immediately after the buffer switching (after the XFlush),
531 this could mean that we have to wait for the graphics to complete,
532 although we could go on doing calculations for the next frame */
536 /* never draw masked border to backbuffer when using playfield buffer */
537 if (game_status != GAME_MODE_PLAYING ||
538 redraw_mask & REDRAW_FROM_BACKBUFFER ||
539 buffer == backbuffer)
540 DrawMaskedBorder(redraw_mask);
542 DrawMaskedBorder(redraw_mask & REDRAW_DOORS);
544 if (redraw_mask & REDRAW_ALL)
546 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
548 redraw_mask = REDRAW_NONE;
551 if (redraw_mask & REDRAW_FIELD)
553 if (game_status != GAME_MODE_PLAYING ||
554 redraw_mask & REDRAW_FROM_BACKBUFFER)
556 BlitBitmap(backbuffer, window,
557 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
561 BlitScreenToBitmap_RND(window);
564 redraw_mask &= ~REDRAW_MAIN;
567 if (redraw_mask & REDRAW_DOORS)
569 if (redraw_mask & REDRAW_DOOR_1)
570 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
572 if (redraw_mask & REDRAW_DOOR_2)
573 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
575 if (redraw_mask & REDRAW_DOOR_3)
576 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
578 redraw_mask &= ~REDRAW_DOORS;
581 if (redraw_mask & REDRAW_MICROLEVEL)
583 BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
584 SX, SY + 10 * TILEY);
586 redraw_mask &= ~REDRAW_MICROLEVEL;
589 if (redraw_mask & REDRAW_TILES)
595 int dx_var = dx * TILESIZE_VAR / TILESIZE;
596 int dy_var = dy * TILESIZE_VAR / TILESIZE;
598 int fx = FX, fy = FY;
600 int scr_fieldx = SCR_FIELDX + (EVEN(SCR_FIELDX) ? 2 : 0);
601 int scr_fieldy = SCR_FIELDY + (EVEN(SCR_FIELDY) ? 2 : 0);
603 InitGfxClipRegion(TRUE, SX, SY, SXSIZE, SYSIZE);
605 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
606 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
608 if (EVEN(SCR_FIELDX))
610 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
612 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
621 fx += (dx_var > 0 ? TILEX_VAR : 0);
625 if (EVEN(SCR_FIELDY))
627 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
629 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
638 fy += (dy_var > 0 ? TILEY_VAR : 0);
642 for (x = 0; x < scr_fieldx; x++)
643 for (y = 0 ; y < scr_fieldy; y++)
644 if (redraw[redraw_x1 + x][redraw_y1 + y])
645 BlitBitmap(buffer, window,
646 FX + x * TILEX_VAR, FY + y * TILEY_VAR,
647 TILEX_VAR, TILEY_VAR,
648 sx + x * TILEX_VAR, sy + y * TILEY_VAR);
650 InitGfxClipRegion(FALSE, -1, -1, -1, -1);
653 if (redraw_mask & REDRAW_FPS) /* display frames per second */
658 sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
659 if (!global.fps_slowdown)
662 sprintf(text, "%04.1f fps%s", global.frames_per_second, info1);
664 DrawTextExt(window, SX + SXSIZE + SX, 0, text, FONT_TEXT_2, BLIT_OPAQUE);
669 for (x = 0; x < MAX_BUF_XSIZE; x++)
670 for (y = 0; y < MAX_BUF_YSIZE; y++)
673 redraw_mask = REDRAW_NONE;
676 static void FadeCrossSaveBackbuffer()
678 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
681 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
683 static int fade_type_skip = FADE_TYPE_NONE;
684 void (*draw_border_function)(void) = NULL;
685 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
686 int x, y, width, height;
687 int fade_delay, post_delay;
689 if (fade_type == FADE_TYPE_FADE_OUT)
691 if (fade_type_skip != FADE_TYPE_NONE)
693 /* skip all fade operations until specified fade operation */
694 if (fade_type & fade_type_skip)
695 fade_type_skip = FADE_TYPE_NONE;
700 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
702 FadeCrossSaveBackbuffer();
708 redraw_mask |= fade_mask;
710 if (fade_type == FADE_TYPE_SKIP)
712 fade_type_skip = fade_mode;
717 fade_delay = fading.fade_delay;
718 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
720 if (fade_type_skip != FADE_TYPE_NONE)
722 /* skip all fade operations until specified fade operation */
723 if (fade_type & fade_type_skip)
724 fade_type_skip = FADE_TYPE_NONE;
729 if (global.autoplay_leveldir)
734 if (fade_mask == REDRAW_FIELD)
739 height = FULL_SYSIZE;
741 if (border.draw_masked_when_fading)
742 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
744 DrawMaskedBorder_FIELD(); /* draw once */
746 else /* REDRAW_ALL */
754 if (!setup.fade_screens ||
756 fading.fade_mode == FADE_MODE_NONE)
758 if (fade_mode == FADE_MODE_FADE_OUT)
761 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
763 redraw_mask &= ~fade_mask;
768 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
769 draw_border_function);
771 redraw_mask &= ~fade_mask;
774 void FadeIn(int fade_mask)
776 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
777 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
779 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
782 void FadeOut(int fade_mask)
784 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
785 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
787 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
789 global.border_status = game_status;
792 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
794 static struct TitleFadingInfo fading_leave_stored;
797 fading_leave_stored = fading_leave;
799 fading = fading_leave_stored;
802 void FadeSetEnterMenu()
804 fading = menu.enter_menu;
806 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
809 void FadeSetLeaveMenu()
811 fading = menu.leave_menu;
813 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
816 void FadeSetEnterScreen()
818 fading = menu.enter_screen[game_status];
820 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
823 void FadeSetNextScreen()
825 fading = menu.next_screen;
827 // (do not overwrite fade mode set by FadeSetEnterScreen)
828 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
831 void FadeSetLeaveScreen()
833 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
836 void FadeSetFromType(int type)
838 if (type & TYPE_ENTER_SCREEN)
839 FadeSetEnterScreen();
840 else if (type & TYPE_ENTER)
842 else if (type & TYPE_LEAVE)
846 void FadeSetDisabled()
848 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
850 fading = fading_none;
853 void FadeSkipNextFadeIn()
855 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
858 void FadeSkipNextFadeOut()
860 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
863 void SetWindowBackgroundImageIfDefined(int graphic)
865 if (graphic_info[graphic].bitmap)
866 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
869 void SetMainBackgroundImageIfDefined(int graphic)
871 if (graphic_info[graphic].bitmap)
872 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
875 void SetDoorBackgroundImageIfDefined(int graphic)
877 if (graphic_info[graphic].bitmap)
878 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
881 void SetWindowBackgroundImage(int graphic)
883 SetWindowBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
884 graphic_info[graphic].bitmap ?
885 graphic_info[graphic].bitmap :
886 graphic_info[IMG_BACKGROUND].bitmap);
889 void SetMainBackgroundImage(int graphic)
891 SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
892 graphic_info[graphic].bitmap ?
893 graphic_info[graphic].bitmap :
894 graphic_info[IMG_BACKGROUND].bitmap);
897 void SetDoorBackgroundImage(int graphic)
899 SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
900 graphic_info[graphic].bitmap ?
901 graphic_info[graphic].bitmap :
902 graphic_info[IMG_BACKGROUND].bitmap);
905 void SetPanelBackground()
907 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
909 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
910 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
912 SetDoorBackgroundBitmap(bitmap_db_panel);
915 void DrawBackground(int x, int y, int width, int height)
917 /* "drawto" might still point to playfield buffer here (hall of fame) */
918 ClearRectangleOnBackground(backbuffer, x, y, width, height);
920 if (IN_GFX_FIELD_FULL(x, y))
921 redraw_mask |= REDRAW_FIELD;
922 else if (IN_GFX_DOOR_1(x, y))
923 redraw_mask |= REDRAW_DOOR_1;
924 else if (IN_GFX_DOOR_2(x, y))
925 redraw_mask |= REDRAW_DOOR_2;
926 else if (IN_GFX_DOOR_3(x, y))
927 redraw_mask |= REDRAW_DOOR_3;
930 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
932 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
934 if (font->bitmap == NULL)
937 DrawBackground(x, y, width, height);
940 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
942 struct GraphicInfo *g = &graphic_info[graphic];
944 if (g->bitmap == NULL)
947 DrawBackground(x, y, width, height);
952 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
953 /* (when entering hall of fame after playing) */
954 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
956 /* !!! maybe this should be done before clearing the background !!! */
957 if (setup.soft_scrolling && game_status == GAME_MODE_PLAYING)
959 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
960 SetDrawtoField(DRAW_BUFFERED);
963 SetDrawtoField(DRAW_BACKBUFFER);
966 void MarkTileDirty(int x, int y)
968 int xx = redraw_x1 + x;
969 int yy = redraw_y1 + y;
974 redraw[xx][yy] = TRUE;
975 redraw_mask |= REDRAW_TILES;
978 void SetBorderElement()
982 BorderElement = EL_EMPTY;
984 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
986 for (x = 0; x < lev_fieldx; x++)
988 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
989 BorderElement = EL_STEELWALL;
991 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
997 void FloodFillLevel(int from_x, int from_y, int fill_element,
998 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
999 int max_fieldx, int max_fieldy)
1003 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1004 static int safety = 0;
1006 /* check if starting field still has the desired content */
1007 if (field[from_x][from_y] == fill_element)
1012 if (safety > max_fieldx * max_fieldy)
1013 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1015 old_element = field[from_x][from_y];
1016 field[from_x][from_y] = fill_element;
1018 for (i = 0; i < 4; i++)
1020 x = from_x + check[i][0];
1021 y = from_y + check[i][1];
1023 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1024 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1030 void SetRandomAnimationValue(int x, int y)
1032 gfx.anim_random_frame = GfxRandom[x][y];
1035 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
1037 /* animation synchronized with global frame counter, not move position */
1038 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1039 sync_frame = FrameCounter;
1041 return getAnimationFrame(graphic_info[graphic].anim_frames,
1042 graphic_info[graphic].anim_delay,
1043 graphic_info[graphic].anim_mode,
1044 graphic_info[graphic].anim_start_frame,
1048 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize_raw,
1049 Bitmap **bitmap, int *x, int *y,
1050 boolean get_backside)
1054 int width_mult, width_div;
1055 int height_mult, height_div;
1059 { 15, 16, 2, 3 }, /* 1 x 1 */
1060 { 7, 8, 2, 3 }, /* 2 x 2 */
1061 { 3, 4, 2, 3 }, /* 4 x 4 */
1062 { 1, 2, 2, 3 }, /* 8 x 8 */
1063 { 0, 1, 2, 3 }, /* 16 x 16 */
1064 { 0, 1, 0, 1 }, /* 32 x 32 */
1066 struct GraphicInfo *g = &graphic_info[graphic];
1067 Bitmap *src_bitmap = g->bitmap;
1068 int tilesize = MIN(MAX(1, tilesize_raw), TILESIZE);
1069 int offset_calc_pos = log_2(tilesize);
1070 int bitmap_width = src_bitmap->width;
1071 int bitmap_height = src_bitmap->height;
1072 int width_mult = offset_calc[offset_calc_pos].width_mult;
1073 int width_div = offset_calc[offset_calc_pos].width_div;
1074 int height_mult = offset_calc[offset_calc_pos].height_mult;
1075 int height_div = offset_calc[offset_calc_pos].height_div;
1076 int startx = bitmap_width * width_mult / width_div;
1077 int starty = bitmap_height * height_mult / height_div;
1078 int src_x = (g->src_x + (get_backside ? g->offset2_x : 0)) *
1079 tilesize_raw / TILESIZE;
1080 int src_y = (g->src_y + (get_backside ? g->offset2_y : 0)) *
1081 tilesize_raw / TILESIZE;
1082 int width = g->width * tilesize_raw / TILESIZE;
1083 int height = g->height * tilesize_raw / TILESIZE;
1084 int offset_x = g->offset_x * tilesize_raw / TILESIZE;
1085 int offset_y = g->offset_y * tilesize_raw / TILESIZE;
1087 if (game.tile_size != TILESIZE)
1089 int bitmap_width_std =
1090 bitmap_width * TILESIZE / (TILESIZE + game.tile_size);
1091 int bitmap_height_std =
1092 bitmap_height * TILESIZE / game.tile_size * 3 / 2;
1094 if (tilesize_raw == game.tile_size)
1096 startx = bitmap_width_std;
1101 bitmap_width = bitmap_width_std;
1103 if (game.tile_size > TILESIZE * 3 / 2)
1104 bitmap_height = bitmap_height_std;
1106 startx = bitmap_width * width_mult / width_div;
1107 starty = bitmap_height * height_mult / height_div;
1111 if (g->offset_y == 0) /* frames are ordered horizontally */
1113 int max_width = g->anim_frames_per_line * width;
1114 int pos = (src_y / height) * max_width + src_x + frame * offset_x;
1116 src_x = pos % max_width;
1117 src_y = src_y % height + pos / max_width * height;
1119 else if (g->offset_x == 0) /* frames are ordered vertically */
1121 int max_height = g->anim_frames_per_line * height;
1122 int pos = (src_x / width) * max_height + src_y + frame * offset_y;
1124 src_x = src_x % width + pos / max_height * width;
1125 src_y = pos % max_height;
1127 else /* frames are ordered diagonally */
1129 src_x = src_x + frame * offset_x;
1130 src_y = src_y + frame * offset_y;
1133 *bitmap = src_bitmap;
1134 *x = startx + src_x;
1135 *y = starty + src_y;
1138 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1139 int *x, int *y, boolean get_backside)
1141 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1145 void getSizedGraphicSource(int graphic, int frame, int tilesize_raw,
1146 Bitmap **bitmap, int *x, int *y)
1148 getSizedGraphicSourceExt(graphic, frame, tilesize_raw, bitmap, x, y, FALSE);
1151 void getFixedGraphicSource(int graphic, int frame,
1152 Bitmap **bitmap, int *x, int *y)
1154 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1157 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1159 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1162 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1163 int *x, int *y, boolean get_backside)
1165 struct GraphicInfo *g = &graphic_info[graphic];
1166 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1167 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1169 if (TILESIZE_VAR != TILESIZE)
1170 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1173 *bitmap = g->bitmap;
1175 if (g->offset_y == 0) /* frames are ordered horizontally */
1177 int max_width = g->anim_frames_per_line * g->width;
1178 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1180 *x = pos % max_width;
1181 *y = src_y % g->height + pos / max_width * g->height;
1183 else if (g->offset_x == 0) /* frames are ordered vertically */
1185 int max_height = g->anim_frames_per_line * g->height;
1186 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1188 *x = src_x % g->width + pos / max_height * g->width;
1189 *y = pos % max_height;
1191 else /* frames are ordered diagonally */
1193 *x = src_x + frame * g->offset_x;
1194 *y = src_y + frame * g->offset_y;
1198 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1200 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1203 void DrawGraphic(int x, int y, int graphic, int frame)
1206 if (!IN_SCR_FIELD(x, y))
1208 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1209 printf("DrawGraphic(): This should never happen!\n");
1214 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1217 MarkTileDirty(x, y);
1220 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1223 if (!IN_SCR_FIELD(x, y))
1225 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1226 printf("DrawGraphic(): This should never happen!\n");
1231 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1233 MarkTileDirty(x, y);
1236 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1242 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1244 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1247 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1253 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1254 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1257 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1260 if (!IN_SCR_FIELD(x, y))
1262 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1263 printf("DrawGraphicThruMask(): This should never happen!\n");
1268 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1271 MarkTileDirty(x, y);
1274 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1277 if (!IN_SCR_FIELD(x, y))
1279 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1280 printf("DrawGraphicThruMask(): This should never happen!\n");
1285 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1287 MarkTileDirty(x, y);
1290 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1296 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1298 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1299 dst_x - src_x, dst_y - src_y);
1301 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1305 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1306 int graphic, int frame)
1311 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1313 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1314 dst_x - src_x, dst_y - src_y);
1315 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dst_x, dst_y);
1318 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1320 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1322 MarkTileDirty(x / tilesize, y / tilesize);
1325 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1331 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1332 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1335 void DrawMiniGraphic(int x, int y, int graphic)
1337 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1338 MarkTileDirty(x / 2, y / 2);
1341 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1346 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1347 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1350 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1351 int graphic, int frame,
1352 int cut_mode, int mask_mode)
1357 int width = TILEX, height = TILEY;
1360 if (dx || dy) /* shifted graphic */
1362 if (x < BX1) /* object enters playfield from the left */
1369 else if (x > BX2) /* object enters playfield from the right */
1375 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1381 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1383 else if (dx) /* general horizontal movement */
1384 MarkTileDirty(x + SIGN(dx), y);
1386 if (y < BY1) /* object enters playfield from the top */
1388 if (cut_mode==CUT_BELOW) /* object completely above top border */
1396 else if (y > BY2) /* object enters playfield from the bottom */
1402 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1408 else if (dy > 0 && cut_mode == CUT_ABOVE)
1410 if (y == BY2) /* object completely above bottom border */
1416 MarkTileDirty(x, y + 1);
1417 } /* object leaves playfield to the bottom */
1418 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1420 else if (dy) /* general vertical movement */
1421 MarkTileDirty(x, y + SIGN(dy));
1425 if (!IN_SCR_FIELD(x, y))
1427 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1428 printf("DrawGraphicShifted(): This should never happen!\n");
1433 width = width * TILESIZE_VAR / TILESIZE;
1434 height = height * TILESIZE_VAR / TILESIZE;
1435 cx = cx * TILESIZE_VAR / TILESIZE;
1436 cy = cy * TILESIZE_VAR / TILESIZE;
1437 dx = dx * TILESIZE_VAR / TILESIZE;
1438 dy = dy * TILESIZE_VAR / TILESIZE;
1440 if (width > 0 && height > 0)
1442 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1447 dst_x = FX + x * TILEX_VAR + dx;
1448 dst_y = FY + y * TILEY_VAR + dy;
1450 if (mask_mode == USE_MASKING)
1452 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1453 dst_x - src_x, dst_y - src_y);
1454 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1458 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1461 MarkTileDirty(x, y);
1465 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1466 int graphic, int frame,
1467 int cut_mode, int mask_mode)
1472 int width = TILEX_VAR, height = TILEY_VAR;
1475 int x2 = x + SIGN(dx);
1476 int y2 = y + SIGN(dy);
1478 /* movement with two-tile animations must be sync'ed with movement position,
1479 not with current GfxFrame (which can be higher when using slow movement) */
1480 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1481 int anim_frames = graphic_info[graphic].anim_frames;
1483 /* (we also need anim_delay here for movement animations with less frames) */
1484 int anim_delay = graphic_info[graphic].anim_delay;
1485 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1487 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1488 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1490 /* re-calculate animation frame for two-tile movement animation */
1491 frame = getGraphicAnimationFrame(graphic, sync_frame);
1493 /* check if movement start graphic inside screen area and should be drawn */
1494 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1496 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1498 dst_x = FX + x1 * TILEX_VAR;
1499 dst_y = FY + y1 * TILEY_VAR;
1501 if (mask_mode == USE_MASKING)
1503 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1504 dst_x - src_x, dst_y - src_y);
1505 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1509 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1512 MarkTileDirty(x1, y1);
1515 /* check if movement end graphic inside screen area and should be drawn */
1516 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1518 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1520 dst_x = FX + x2 * TILEX_VAR;
1521 dst_y = FY + y2 * TILEY_VAR;
1523 if (mask_mode == USE_MASKING)
1525 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
1526 dst_x - src_x, dst_y - src_y);
1527 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1531 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1534 MarkTileDirty(x2, y2);
1538 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1539 int graphic, int frame,
1540 int cut_mode, int mask_mode)
1544 DrawGraphic(x, y, graphic, frame);
1549 if (graphic_info[graphic].double_movement) /* EM style movement images */
1550 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1552 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1555 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1556 int frame, int cut_mode)
1558 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1561 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1562 int cut_mode, int mask_mode)
1564 int lx = LEVELX(x), ly = LEVELY(y);
1568 if (IN_LEV_FIELD(lx, ly))
1570 SetRandomAnimationValue(lx, ly);
1572 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1573 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1575 /* do not use double (EM style) movement graphic when not moving */
1576 if (graphic_info[graphic].double_movement && !dx && !dy)
1578 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1579 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1582 else /* border element */
1584 graphic = el2img(element);
1585 frame = getGraphicAnimationFrame(graphic, -1);
1588 if (element == EL_EXPANDABLE_WALL)
1590 boolean left_stopped = FALSE, right_stopped = FALSE;
1592 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1593 left_stopped = TRUE;
1594 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1595 right_stopped = TRUE;
1597 if (left_stopped && right_stopped)
1599 else if (left_stopped)
1601 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1602 frame = graphic_info[graphic].anim_frames - 1;
1604 else if (right_stopped)
1606 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1607 frame = graphic_info[graphic].anim_frames - 1;
1612 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1613 else if (mask_mode == USE_MASKING)
1614 DrawGraphicThruMask(x, y, graphic, frame);
1616 DrawGraphic(x, y, graphic, frame);
1619 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1620 int cut_mode, int mask_mode)
1622 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1623 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1624 cut_mode, mask_mode);
1627 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1630 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1633 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1636 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1639 void DrawLevelElementThruMask(int x, int y, int element)
1641 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1644 void DrawLevelFieldThruMask(int x, int y)
1646 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1649 /* !!! implementation of quicksand is totally broken !!! */
1650 #define IS_CRUMBLED_TILE(x, y, e) \
1651 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1652 !IS_MOVING(x, y) || \
1653 (e) == EL_QUICKSAND_EMPTYING || \
1654 (e) == EL_QUICKSAND_FAST_EMPTYING))
1656 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1661 int width, height, cx, cy;
1662 int sx = SCREENX(x), sy = SCREENY(y);
1663 int crumbled_border_size = graphic_info[graphic].border_size;
1666 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1668 for (i = 1; i < 4; i++)
1670 int dxx = (i & 1 ? dx : 0);
1671 int dyy = (i & 2 ? dy : 0);
1674 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1677 /* check if neighbour field is of same crumble type */
1678 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1679 graphic_info[graphic].class ==
1680 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1682 /* return if check prevents inner corner */
1683 if (same == (dxx == dx && dyy == dy))
1687 /* if we reach this point, we have an inner corner */
1689 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1691 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1692 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1693 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1694 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1696 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1697 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1700 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1705 int width, height, bx, by, cx, cy;
1706 int sx = SCREENX(x), sy = SCREENY(y);
1707 int crumbled_border_size = graphic_info[graphic].border_size;
1708 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1709 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1712 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1714 /* draw simple, sloppy, non-corner-accurate crumbled border */
1716 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1717 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1718 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1719 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1721 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1722 FX + sx * TILEX_VAR + cx,
1723 FY + sy * TILEY_VAR + cy);
1725 /* (remaining middle border part must be at least as big as corner part) */
1726 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1727 crumbled_border_size >= TILESIZE / 3)
1730 /* correct corners of crumbled border, if needed */
1732 for (i = -1; i <= 1; i += 2)
1734 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1735 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1736 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1739 /* check if neighbour field is of same crumble type */
1740 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1741 graphic_info[graphic].class ==
1742 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1744 /* no crumbled corner, but continued crumbled border */
1746 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1747 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1748 int b1 = (i == 1 ? crumbled_border_size_var :
1749 TILESIZE_VAR - 2 * crumbled_border_size_var);
1751 width = crumbled_border_size_var;
1752 height = crumbled_border_size_var;
1754 if (dir == 1 || dir == 2)
1769 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1771 FX + sx * TILEX_VAR + cx,
1772 FY + sy * TILEY_VAR + cy);
1777 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1779 int sx = SCREENX(x), sy = SCREENY(y);
1782 static int xy[4][2] =
1790 if (!IN_LEV_FIELD(x, y))
1793 element = TILE_GFX_ELEMENT(x, y);
1795 /* crumble field itself */
1796 if (IS_CRUMBLED_TILE(x, y, element))
1798 if (!IN_SCR_FIELD(sx, sy))
1801 for (i = 0; i < 4; i++)
1803 int xx = x + xy[i][0];
1804 int yy = y + xy[i][1];
1806 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1809 /* check if neighbour field is of same crumble type */
1810 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1811 graphic_info[graphic].class ==
1812 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1815 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1818 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1819 graphic_info[graphic].anim_frames == 2)
1821 for (i = 0; i < 4; i++)
1823 int dx = (i & 1 ? +1 : -1);
1824 int dy = (i & 2 ? +1 : -1);
1826 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1830 MarkTileDirty(sx, sy);
1832 else /* center field not crumbled -- crumble neighbour fields */
1834 for (i = 0; i < 4; i++)
1836 int xx = x + xy[i][0];
1837 int yy = y + xy[i][1];
1838 int sxx = sx + xy[i][0];
1839 int syy = sy + xy[i][1];
1841 if (!IN_LEV_FIELD(xx, yy) ||
1842 !IN_SCR_FIELD(sxx, syy))
1845 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1848 element = TILE_GFX_ELEMENT(xx, yy);
1850 if (!IS_CRUMBLED_TILE(xx, yy, element))
1853 graphic = el_act2crm(element, ACTION_DEFAULT);
1855 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1857 MarkTileDirty(sxx, syy);
1862 void DrawLevelFieldCrumbled(int x, int y)
1866 if (!IN_LEV_FIELD(x, y))
1869 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1870 GfxElement[x][y] != EL_UNDEFINED &&
1871 GFX_CRUMBLED(GfxElement[x][y]))
1873 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1878 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1880 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1883 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1886 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1887 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1888 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1889 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1890 int sx = SCREENX(x), sy = SCREENY(y);
1892 DrawGraphic(sx, sy, graphic1, frame1);
1893 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1896 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1898 int sx = SCREENX(x), sy = SCREENY(y);
1899 static int xy[4][2] =
1908 for (i = 0; i < 4; i++)
1910 int xx = x + xy[i][0];
1911 int yy = y + xy[i][1];
1912 int sxx = sx + xy[i][0];
1913 int syy = sy + xy[i][1];
1915 if (!IN_LEV_FIELD(xx, yy) ||
1916 !IN_SCR_FIELD(sxx, syy) ||
1917 !GFX_CRUMBLED(Feld[xx][yy]) ||
1921 DrawLevelField(xx, yy);
1925 static int getBorderElement(int x, int y)
1929 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1930 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1931 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1932 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1933 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1934 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1935 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1937 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1938 int steel_position = (x == -1 && y == -1 ? 0 :
1939 x == lev_fieldx && y == -1 ? 1 :
1940 x == -1 && y == lev_fieldy ? 2 :
1941 x == lev_fieldx && y == lev_fieldy ? 3 :
1942 x == -1 || x == lev_fieldx ? 4 :
1943 y == -1 || y == lev_fieldy ? 5 : 6);
1945 return border[steel_position][steel_type];
1948 void DrawScreenElement(int x, int y, int element)
1950 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1951 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1954 void DrawLevelElement(int x, int y, int element)
1956 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1957 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1960 void DrawScreenField(int x, int y)
1962 int lx = LEVELX(x), ly = LEVELY(y);
1963 int element, content;
1965 if (!IN_LEV_FIELD(lx, ly))
1967 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1970 element = getBorderElement(lx, ly);
1972 DrawScreenElement(x, y, element);
1977 element = Feld[lx][ly];
1978 content = Store[lx][ly];
1980 if (IS_MOVING(lx, ly))
1982 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1983 boolean cut_mode = NO_CUTTING;
1985 if (element == EL_QUICKSAND_EMPTYING ||
1986 element == EL_QUICKSAND_FAST_EMPTYING ||
1987 element == EL_MAGIC_WALL_EMPTYING ||
1988 element == EL_BD_MAGIC_WALL_EMPTYING ||
1989 element == EL_DC_MAGIC_WALL_EMPTYING ||
1990 element == EL_AMOEBA_DROPPING)
1991 cut_mode = CUT_ABOVE;
1992 else if (element == EL_QUICKSAND_FILLING ||
1993 element == EL_QUICKSAND_FAST_FILLING ||
1994 element == EL_MAGIC_WALL_FILLING ||
1995 element == EL_BD_MAGIC_WALL_FILLING ||
1996 element == EL_DC_MAGIC_WALL_FILLING)
1997 cut_mode = CUT_BELOW;
1999 if (cut_mode == CUT_ABOVE)
2000 DrawScreenElement(x, y, element);
2002 DrawScreenElement(x, y, EL_EMPTY);
2005 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2006 else if (cut_mode == NO_CUTTING)
2007 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2010 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2012 if (cut_mode == CUT_BELOW &&
2013 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2014 DrawLevelElement(lx, ly + 1, element);
2017 if (content == EL_ACID)
2019 int dir = MovDir[lx][ly];
2020 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2021 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2023 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2026 else if (IS_BLOCKED(lx, ly))
2031 boolean cut_mode = NO_CUTTING;
2032 int element_old, content_old;
2034 Blocked2Moving(lx, ly, &oldx, &oldy);
2037 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2038 MovDir[oldx][oldy] == MV_RIGHT);
2040 element_old = Feld[oldx][oldy];
2041 content_old = Store[oldx][oldy];
2043 if (element_old == EL_QUICKSAND_EMPTYING ||
2044 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2045 element_old == EL_MAGIC_WALL_EMPTYING ||
2046 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2047 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2048 element_old == EL_AMOEBA_DROPPING)
2049 cut_mode = CUT_ABOVE;
2051 DrawScreenElement(x, y, EL_EMPTY);
2054 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2056 else if (cut_mode == NO_CUTTING)
2057 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2060 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2063 else if (IS_DRAWABLE(element))
2064 DrawScreenElement(x, y, element);
2066 DrawScreenElement(x, y, EL_EMPTY);
2069 void DrawLevelField(int x, int y)
2071 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2072 DrawScreenField(SCREENX(x), SCREENY(y));
2073 else if (IS_MOVING(x, y))
2077 Moving2Blocked(x, y, &newx, &newy);
2078 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2079 DrawScreenField(SCREENX(newx), SCREENY(newy));
2081 else if (IS_BLOCKED(x, y))
2085 Blocked2Moving(x, y, &oldx, &oldy);
2086 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2087 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2091 void DrawMiniElement(int x, int y, int element)
2095 graphic = el2edimg(element);
2096 DrawMiniGraphic(x, y, graphic);
2099 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2101 int x = sx + scroll_x, y = sy + scroll_y;
2103 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2104 DrawMiniElement(sx, sy, EL_EMPTY);
2105 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2106 DrawMiniElement(sx, sy, Feld[x][y]);
2108 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2111 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2112 int x, int y, int xsize, int ysize,
2113 int tile_width, int tile_height)
2117 int dst_x = startx + x * tile_width;
2118 int dst_y = starty + y * tile_height;
2119 int width = graphic_info[graphic].width;
2120 int height = graphic_info[graphic].height;
2121 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2122 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2123 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2124 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2125 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2126 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2127 boolean draw_masked = graphic_info[graphic].draw_masked;
2129 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2131 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2133 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2137 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2138 inner_sx + (x - 1) * tile_width % inner_width);
2139 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2140 inner_sy + (y - 1) * tile_height % inner_height);
2144 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2145 dst_x - src_x, dst_y - src_y);
2146 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2150 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2154 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2155 int x, int y, int xsize, int ysize, int font_nr)
2157 int font_width = getFontWidth(font_nr);
2158 int font_height = getFontHeight(font_nr);
2160 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2161 font_width, font_height);
2164 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2166 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2167 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2168 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2169 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2170 boolean no_delay = (tape.warp_forward);
2171 unsigned int anim_delay = 0;
2172 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2173 int anim_delay_value = (no_delay ? 0 : frame_delay_value);
2174 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2175 int font_width = getFontWidth(font_nr);
2176 int font_height = getFontHeight(font_nr);
2177 int max_xsize = level.envelope[envelope_nr].xsize;
2178 int max_ysize = level.envelope[envelope_nr].ysize;
2179 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2180 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2181 int xend = max_xsize;
2182 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2183 int xstep = (xstart < xend ? 1 : 0);
2184 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2187 for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2189 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2190 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2191 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2192 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2195 SetDrawtoField(DRAW_BUFFERED);
2197 BlitScreenToBitmap(backbuffer);
2199 SetDrawtoField(DRAW_BACKBUFFER);
2201 for (yy = 0; yy < ysize; yy++)
2202 for (xx = 0; xx < xsize; xx++)
2203 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2205 DrawTextBuffer(sx + font_width, sy + font_height,
2206 level.envelope[envelope_nr].text, font_nr, max_xsize,
2207 xsize - 2, ysize - 2, 0, mask_mode,
2208 level.envelope[envelope_nr].autowrap,
2209 level.envelope[envelope_nr].centered, FALSE);
2211 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2214 WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2218 void ShowEnvelope(int envelope_nr)
2220 int element = EL_ENVELOPE_1 + envelope_nr;
2221 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2222 int sound_opening = element_info[element].sound[ACTION_OPENING];
2223 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2224 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2225 boolean no_delay = (tape.warp_forward);
2226 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2227 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2228 int anim_mode = graphic_info[graphic].anim_mode;
2229 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2230 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2232 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2234 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2236 if (anim_mode == ANIM_DEFAULT)
2237 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2239 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2242 Delay(wait_delay_value);
2244 WaitForEventToContinue();
2246 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2248 if (anim_mode != ANIM_NONE)
2249 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2251 if (anim_mode == ANIM_DEFAULT)
2252 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2254 game.envelope_active = FALSE;
2256 SetDrawtoField(DRAW_BUFFERED);
2258 redraw_mask |= REDRAW_FIELD;
2262 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2264 int border_size = request.border_size;
2265 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2266 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2267 int sx = sx_center - request.width / 2;
2268 int sy = sy_center - request.height / 2;
2270 if (add_border_size)
2280 void DrawEnvelopeRequest(char *text)
2282 char *text_final = text;
2283 char *text_door_style = NULL;
2284 int graphic = IMG_BACKGROUND_REQUEST;
2285 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2286 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2287 int font_nr = FONT_REQUEST;
2288 int font_width = getFontWidth(font_nr);
2289 int font_height = getFontHeight(font_nr);
2290 int border_size = request.border_size;
2291 int line_spacing = request.line_spacing;
2292 int line_height = font_height + line_spacing;
2293 int text_width = request.width - 2 * border_size;
2294 int text_height = request.height - 2 * border_size;
2295 int line_length = text_width / font_width;
2296 int max_lines = text_height / line_height;
2297 int width = request.width;
2298 int height = request.height;
2299 int tile_size = request.step_offset;
2300 int x_steps = width / tile_size;
2301 int y_steps = height / tile_size;
2305 if (request.wrap_single_words)
2307 char *src_text_ptr, *dst_text_ptr;
2309 text_door_style = checked_malloc(2 * strlen(text) + 1);
2311 src_text_ptr = text;
2312 dst_text_ptr = text_door_style;
2314 while (*src_text_ptr)
2316 if (*src_text_ptr == ' ' ||
2317 *src_text_ptr == '?' ||
2318 *src_text_ptr == '!')
2319 *dst_text_ptr++ = '\n';
2321 if (*src_text_ptr != ' ')
2322 *dst_text_ptr++ = *src_text_ptr;
2327 *dst_text_ptr = '\0';
2329 text_final = text_door_style;
2332 setRequestPosition(&sx, &sy, FALSE);
2334 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2336 for (y = 0; y < y_steps; y++)
2337 for (x = 0; x < x_steps; x++)
2338 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2339 x, y, x_steps, y_steps,
2340 tile_size, tile_size);
2342 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2343 line_length, -1, max_lines, line_spacing, mask_mode,
2344 request.autowrap, request.centered, FALSE);
2346 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2347 RedrawGadget(tool_gadget[i]);
2349 // store readily prepared envelope request for later use when animating
2350 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2352 if (text_door_style)
2353 free(text_door_style);
2356 void AnimateEnvelopeRequest(int anim_mode, int action)
2358 int graphic = IMG_BACKGROUND_REQUEST;
2359 boolean draw_masked = graphic_info[graphic].draw_masked;
2360 int delay_value_normal = request.step_delay;
2361 int delay_value_fast = delay_value_normal / 2;
2362 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2363 boolean no_delay = (tape.warp_forward);
2364 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2365 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0);
2366 unsigned int anim_delay = 0;
2368 int width = request.width;
2369 int height = request.height;
2370 int tile_size = request.step_offset;
2371 int max_xsize = width / tile_size;
2372 int max_ysize = height / tile_size;
2373 int max_xsize_inner = max_xsize - 2;
2374 int max_ysize_inner = max_ysize - 2;
2376 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2377 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2378 int xend = max_xsize_inner;
2379 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2380 int xstep = (xstart < xend ? 1 : 0);
2381 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2384 for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2386 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2387 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2388 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2389 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2390 int src_x = sx_center - width / 2;
2391 int src_y = sy_center - height / 2;
2392 int dst_x = sx_center - xsize * tile_size / 2;
2393 int dst_y = sy_center - ysize * tile_size / 2;
2394 int xsize_size_left = (xsize - 1) * tile_size;
2395 int ysize_size_top = (ysize - 1) * tile_size;
2396 int max_xsize_pos = (max_xsize - 1) * tile_size;
2397 int max_ysize_pos = (max_ysize - 1) * tile_size;
2400 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2402 for (yy = 0; yy < 2; yy++)
2404 for (xx = 0; xx < 2; xx++)
2406 int src_xx = src_x + xx * max_xsize_pos;
2407 int src_yy = src_y + yy * max_ysize_pos;
2408 int dst_xx = dst_x + xx * xsize_size_left;
2409 int dst_yy = dst_y + yy * ysize_size_top;
2410 int xx_size = (xx ? tile_size : xsize_size_left);
2411 int yy_size = (yy ? tile_size : ysize_size_top);
2414 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2415 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2417 BlitBitmap(bitmap_db_cross, backbuffer,
2418 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2422 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2427 WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2432 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2434 int last_game_status = game_status; /* save current game status */
2435 int graphic = IMG_BACKGROUND_REQUEST;
2436 int sound_opening = SND_REQUEST_OPENING;
2437 int sound_closing = SND_REQUEST_CLOSING;
2438 int anim_mode = graphic_info[graphic].anim_mode;
2439 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2440 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2442 if (game_status == GAME_MODE_PLAYING)
2443 BlitScreenToBitmap(backbuffer);
2445 SetDrawtoField(DRAW_BACKBUFFER);
2447 // SetDrawBackgroundMask(REDRAW_NONE);
2449 if (action == ACTION_OPENING)
2451 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2453 if (req_state & REQ_ASK)
2455 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2456 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2458 else if (req_state & REQ_CONFIRM)
2460 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2462 else if (req_state & REQ_PLAYER)
2464 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2465 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2466 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2467 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2470 DrawEnvelopeRequest(text);
2472 if (game_status != GAME_MODE_MAIN)
2476 /* force DOOR font inside door area */
2477 game_status = GAME_MODE_PSEUDO_DOOR;
2479 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2481 if (action == ACTION_OPENING)
2483 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2485 if (anim_mode == ANIM_DEFAULT)
2486 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2488 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2493 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2495 if (anim_mode != ANIM_NONE)
2496 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2498 if (anim_mode == ANIM_DEFAULT)
2499 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2502 game.envelope_active = FALSE;
2504 game_status = last_game_status; /* restore current game status */
2506 if (action == ACTION_CLOSING)
2508 if (game_status != GAME_MODE_MAIN)
2511 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2514 // SetDrawBackgroundMask(last_draw_background_mask);
2516 redraw_mask |= REDRAW_FIELD;
2518 if (game_status == GAME_MODE_MAIN)
2523 if (action == ACTION_CLOSING &&
2524 game_status == GAME_MODE_PLAYING &&
2525 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2526 SetDrawtoField(DRAW_BUFFERED);
2529 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2533 int graphic = el2preimg(element);
2535 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2536 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2539 void DrawLevel(int draw_background_mask)
2543 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2544 SetDrawBackgroundMask(draw_background_mask);
2548 for (x = BX1; x <= BX2; x++)
2549 for (y = BY1; y <= BY2; y++)
2550 DrawScreenField(x, y);
2552 redraw_mask |= REDRAW_FIELD;
2555 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2559 for (x = 0; x < size_x; x++)
2560 for (y = 0; y < size_y; y++)
2561 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2563 redraw_mask |= REDRAW_FIELD;
2566 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2568 boolean show_level_border = (BorderElement != EL_EMPTY);
2569 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2570 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2571 int tile_size = preview.tile_size;
2572 int preview_width = preview.xsize * tile_size;
2573 int preview_height = preview.ysize * tile_size;
2574 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2575 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2576 int real_preview_width = real_preview_xsize * tile_size;
2577 int real_preview_height = real_preview_ysize * tile_size;
2578 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2579 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2582 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2585 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2587 dst_x += (preview_width - real_preview_width) / 2;
2588 dst_y += (preview_height - real_preview_height) / 2;
2590 for (x = 0; x < real_preview_xsize; x++)
2592 for (y = 0; y < real_preview_ysize; y++)
2594 int lx = from_x + x + (show_level_border ? -1 : 0);
2595 int ly = from_y + y + (show_level_border ? -1 : 0);
2596 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2597 getBorderElement(lx, ly));
2599 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2600 element, tile_size);
2604 redraw_mask |= REDRAW_MICROLEVEL;
2607 #define MICROLABEL_EMPTY 0
2608 #define MICROLABEL_LEVEL_NAME 1
2609 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2610 #define MICROLABEL_LEVEL_AUTHOR 3
2611 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2612 #define MICROLABEL_IMPORTED_FROM 5
2613 #define MICROLABEL_IMPORTED_BY_HEAD 6
2614 #define MICROLABEL_IMPORTED_BY 7
2616 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2618 int max_text_width = SXSIZE;
2619 int font_width = getFontWidth(font_nr);
2621 if (pos->align == ALIGN_CENTER)
2622 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2623 else if (pos->align == ALIGN_RIGHT)
2624 max_text_width = pos->x;
2626 max_text_width = SXSIZE - pos->x;
2628 return max_text_width / font_width;
2631 static void DrawPreviewLevelLabelExt(int mode)
2633 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2634 char label_text[MAX_OUTPUT_LINESIZE + 1];
2635 int max_len_label_text;
2636 int font_nr = pos->font;
2639 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2642 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2643 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2644 mode == MICROLABEL_IMPORTED_BY_HEAD)
2645 font_nr = pos->font_alt;
2647 max_len_label_text = getMaxTextLength(pos, font_nr);
2649 if (pos->size != -1)
2650 max_len_label_text = pos->size;
2652 for (i = 0; i < max_len_label_text; i++)
2653 label_text[i] = ' ';
2654 label_text[max_len_label_text] = '\0';
2656 if (strlen(label_text) > 0)
2657 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2660 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2661 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2662 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2663 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2664 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2665 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2666 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2667 max_len_label_text);
2668 label_text[max_len_label_text] = '\0';
2670 if (strlen(label_text) > 0)
2671 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2673 redraw_mask |= REDRAW_MICROLEVEL;
2676 static void DrawPreviewLevelExt(boolean restart)
2678 static unsigned int scroll_delay = 0;
2679 static unsigned int label_delay = 0;
2680 static int from_x, from_y, scroll_direction;
2681 static int label_state, label_counter;
2682 unsigned int scroll_delay_value = preview.step_delay;
2683 boolean show_level_border = (BorderElement != EL_EMPTY);
2684 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2685 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2686 int last_game_status = game_status; /* save current game status */
2693 if (preview.anim_mode == ANIM_CENTERED)
2695 if (level_xsize > preview.xsize)
2696 from_x = (level_xsize - preview.xsize) / 2;
2697 if (level_ysize > preview.ysize)
2698 from_y = (level_ysize - preview.ysize) / 2;
2701 from_x += preview.xoffset;
2702 from_y += preview.yoffset;
2704 scroll_direction = MV_RIGHT;
2708 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2709 DrawPreviewLevelLabelExt(label_state);
2711 /* initialize delay counters */
2712 DelayReached(&scroll_delay, 0);
2713 DelayReached(&label_delay, 0);
2715 if (leveldir_current->name)
2717 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2718 char label_text[MAX_OUTPUT_LINESIZE + 1];
2719 int font_nr = pos->font;
2720 int max_len_label_text = getMaxTextLength(pos, font_nr);
2722 if (pos->size != -1)
2723 max_len_label_text = pos->size;
2725 strncpy(label_text, leveldir_current->name, max_len_label_text);
2726 label_text[max_len_label_text] = '\0';
2728 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2729 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2732 game_status = last_game_status; /* restore current game status */
2737 /* scroll preview level, if needed */
2738 if (preview.anim_mode != ANIM_NONE &&
2739 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2740 DelayReached(&scroll_delay, scroll_delay_value))
2742 switch (scroll_direction)
2747 from_x -= preview.step_offset;
2748 from_x = (from_x < 0 ? 0 : from_x);
2751 scroll_direction = MV_UP;
2755 if (from_x < level_xsize - preview.xsize)
2757 from_x += preview.step_offset;
2758 from_x = (from_x > level_xsize - preview.xsize ?
2759 level_xsize - preview.xsize : from_x);
2762 scroll_direction = MV_DOWN;
2768 from_y -= preview.step_offset;
2769 from_y = (from_y < 0 ? 0 : from_y);
2772 scroll_direction = MV_RIGHT;
2776 if (from_y < level_ysize - preview.ysize)
2778 from_y += preview.step_offset;
2779 from_y = (from_y > level_ysize - preview.ysize ?
2780 level_ysize - preview.ysize : from_y);
2783 scroll_direction = MV_LEFT;
2790 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2793 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2794 /* redraw micro level label, if needed */
2795 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2796 !strEqual(level.author, ANONYMOUS_NAME) &&
2797 !strEqual(level.author, leveldir_current->name) &&
2798 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2800 int max_label_counter = 23;
2802 if (leveldir_current->imported_from != NULL &&
2803 strlen(leveldir_current->imported_from) > 0)
2804 max_label_counter += 14;
2805 if (leveldir_current->imported_by != NULL &&
2806 strlen(leveldir_current->imported_by) > 0)
2807 max_label_counter += 14;
2809 label_counter = (label_counter + 1) % max_label_counter;
2810 label_state = (label_counter >= 0 && label_counter <= 7 ?
2811 MICROLABEL_LEVEL_NAME :
2812 label_counter >= 9 && label_counter <= 12 ?
2813 MICROLABEL_LEVEL_AUTHOR_HEAD :
2814 label_counter >= 14 && label_counter <= 21 ?
2815 MICROLABEL_LEVEL_AUTHOR :
2816 label_counter >= 23 && label_counter <= 26 ?
2817 MICROLABEL_IMPORTED_FROM_HEAD :
2818 label_counter >= 28 && label_counter <= 35 ?
2819 MICROLABEL_IMPORTED_FROM :
2820 label_counter >= 37 && label_counter <= 40 ?
2821 MICROLABEL_IMPORTED_BY_HEAD :
2822 label_counter >= 42 && label_counter <= 49 ?
2823 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2825 if (leveldir_current->imported_from == NULL &&
2826 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2827 label_state == MICROLABEL_IMPORTED_FROM))
2828 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2829 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2831 DrawPreviewLevelLabelExt(label_state);
2834 game_status = last_game_status; /* restore current game status */
2837 void DrawPreviewLevelInitial()
2839 DrawPreviewLevelExt(TRUE);
2842 void DrawPreviewLevelAnimation()
2844 DrawPreviewLevelExt(FALSE);
2847 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2848 int graphic, int sync_frame, int mask_mode)
2850 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2852 if (mask_mode == USE_MASKING)
2853 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2855 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2858 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2859 int graphic, int sync_frame,
2862 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2864 if (mask_mode == USE_MASKING)
2865 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2867 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2870 inline void DrawGraphicAnimation(int x, int y, int graphic)
2872 int lx = LEVELX(x), ly = LEVELY(y);
2874 if (!IN_SCR_FIELD(x, y))
2877 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2878 graphic, GfxFrame[lx][ly], NO_MASKING);
2880 MarkTileDirty(x, y);
2883 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2885 int lx = LEVELX(x), ly = LEVELY(y);
2887 if (!IN_SCR_FIELD(x, y))
2890 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2891 graphic, GfxFrame[lx][ly], NO_MASKING);
2892 MarkTileDirty(x, y);
2895 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2897 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2900 void DrawLevelElementAnimation(int x, int y, int element)
2902 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2904 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2907 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2909 int sx = SCREENX(x), sy = SCREENY(y);
2911 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2914 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2917 DrawGraphicAnimation(sx, sy, graphic);
2920 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2921 DrawLevelFieldCrumbled(x, y);
2923 if (GFX_CRUMBLED(Feld[x][y]))
2924 DrawLevelFieldCrumbled(x, y);
2928 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2930 int sx = SCREENX(x), sy = SCREENY(y);
2933 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2936 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2938 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2941 DrawGraphicAnimation(sx, sy, graphic);
2943 if (GFX_CRUMBLED(element))
2944 DrawLevelFieldCrumbled(x, y);
2947 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2949 if (player->use_murphy)
2951 /* this works only because currently only one player can be "murphy" ... */
2952 static int last_horizontal_dir = MV_LEFT;
2953 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2955 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2956 last_horizontal_dir = move_dir;
2958 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2960 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2962 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2968 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2971 static boolean equalGraphics(int graphic1, int graphic2)
2973 struct GraphicInfo *g1 = &graphic_info[graphic1];
2974 struct GraphicInfo *g2 = &graphic_info[graphic2];
2976 return (g1->bitmap == g2->bitmap &&
2977 g1->src_x == g2->src_x &&
2978 g1->src_y == g2->src_y &&
2979 g1->anim_frames == g2->anim_frames &&
2980 g1->anim_delay == g2->anim_delay &&
2981 g1->anim_mode == g2->anim_mode);
2984 void DrawAllPlayers()
2988 for (i = 0; i < MAX_PLAYERS; i++)
2989 if (stored_player[i].active)
2990 DrawPlayer(&stored_player[i]);
2993 void DrawPlayerField(int x, int y)
2995 if (!IS_PLAYER(x, y))
2998 DrawPlayer(PLAYERINFO(x, y));
3001 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3003 void DrawPlayer(struct PlayerInfo *player)
3005 int jx = player->jx;
3006 int jy = player->jy;
3007 int move_dir = player->MovDir;
3008 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3009 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3010 int last_jx = (player->is_moving ? jx - dx : jx);
3011 int last_jy = (player->is_moving ? jy - dy : jy);
3012 int next_jx = jx + dx;
3013 int next_jy = jy + dy;
3014 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3015 boolean player_is_opaque = FALSE;
3016 int sx = SCREENX(jx), sy = SCREENY(jy);
3017 int sxx = 0, syy = 0;
3018 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3020 int action = ACTION_DEFAULT;
3021 int last_player_graphic = getPlayerGraphic(player, move_dir);
3022 int last_player_frame = player->Frame;
3025 /* GfxElement[][] is set to the element the player is digging or collecting;
3026 remove also for off-screen player if the player is not moving anymore */
3027 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3028 GfxElement[jx][jy] = EL_UNDEFINED;
3030 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3034 if (!IN_LEV_FIELD(jx, jy))
3036 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3037 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3038 printf("DrawPlayerField(): This should never happen!\n");
3043 if (element == EL_EXPLOSION)
3046 action = (player->is_pushing ? ACTION_PUSHING :
3047 player->is_digging ? ACTION_DIGGING :
3048 player->is_collecting ? ACTION_COLLECTING :
3049 player->is_moving ? ACTION_MOVING :
3050 player->is_snapping ? ACTION_SNAPPING :
3051 player->is_dropping ? ACTION_DROPPING :
3052 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3054 if (player->is_waiting)
3055 move_dir = player->dir_waiting;
3057 InitPlayerGfxAnimation(player, action, move_dir);
3059 /* ----------------------------------------------------------------------- */
3060 /* draw things in the field the player is leaving, if needed */
3061 /* ----------------------------------------------------------------------- */
3063 if (player->is_moving)
3065 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3067 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3069 if (last_element == EL_DYNAMITE_ACTIVE ||
3070 last_element == EL_EM_DYNAMITE_ACTIVE ||
3071 last_element == EL_SP_DISK_RED_ACTIVE)
3072 DrawDynamite(last_jx, last_jy);
3074 DrawLevelFieldThruMask(last_jx, last_jy);
3076 else if (last_element == EL_DYNAMITE_ACTIVE ||
3077 last_element == EL_EM_DYNAMITE_ACTIVE ||
3078 last_element == EL_SP_DISK_RED_ACTIVE)
3079 DrawDynamite(last_jx, last_jy);
3081 /* !!! this is not enough to prevent flickering of players which are
3082 moving next to each others without a free tile between them -- this
3083 can only be solved by drawing all players layer by layer (first the
3084 background, then the foreground etc.) !!! => TODO */
3085 else if (!IS_PLAYER(last_jx, last_jy))
3086 DrawLevelField(last_jx, last_jy);
3089 DrawLevelField(last_jx, last_jy);
3092 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3093 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3096 if (!IN_SCR_FIELD(sx, sy))
3099 /* ----------------------------------------------------------------------- */
3100 /* draw things behind the player, if needed */
3101 /* ----------------------------------------------------------------------- */
3104 DrawLevelElement(jx, jy, Back[jx][jy]);
3105 else if (IS_ACTIVE_BOMB(element))
3106 DrawLevelElement(jx, jy, EL_EMPTY);
3109 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3111 int old_element = GfxElement[jx][jy];
3112 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3113 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3115 if (GFX_CRUMBLED(old_element))
3116 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3118 DrawGraphic(sx, sy, old_graphic, frame);
3120 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3121 player_is_opaque = TRUE;
3125 GfxElement[jx][jy] = EL_UNDEFINED;
3127 /* make sure that pushed elements are drawn with correct frame rate */
3128 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3130 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3131 GfxFrame[jx][jy] = player->StepFrame;
3133 DrawLevelField(jx, jy);
3137 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3138 /* ----------------------------------------------------------------------- */
3139 /* draw player himself */
3140 /* ----------------------------------------------------------------------- */
3142 graphic = getPlayerGraphic(player, move_dir);
3144 /* in the case of changed player action or direction, prevent the current
3145 animation frame from being restarted for identical animations */
3146 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3147 player->Frame = last_player_frame;
3149 frame = getGraphicAnimationFrame(graphic, player->Frame);
3153 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3154 sxx = player->GfxPos;
3156 syy = player->GfxPos;
3159 if (!setup.soft_scrolling && ScreenMovPos)
3162 if (player_is_opaque)
3163 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3165 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3167 if (SHIELD_ON(player))
3169 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3170 IMG_SHIELD_NORMAL_ACTIVE);
3171 int frame = getGraphicAnimationFrame(graphic, -1);
3173 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3177 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3180 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3181 sxx = player->GfxPos;
3183 syy = player->GfxPos;
3187 /* ----------------------------------------------------------------------- */
3188 /* draw things the player is pushing, if needed */
3189 /* ----------------------------------------------------------------------- */
3191 if (player->is_pushing && player->is_moving)
3193 int px = SCREENX(jx), py = SCREENY(jy);
3194 int pxx = (TILEX - ABS(sxx)) * dx;
3195 int pyy = (TILEY - ABS(syy)) * dy;
3196 int gfx_frame = GfxFrame[jx][jy];
3202 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3204 element = Feld[next_jx][next_jy];
3205 gfx_frame = GfxFrame[next_jx][next_jy];
3208 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3210 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3211 frame = getGraphicAnimationFrame(graphic, sync_frame);
3213 /* draw background element under pushed element (like the Sokoban field) */
3214 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3216 /* this allows transparent pushing animation over non-black background */
3219 DrawLevelElement(jx, jy, Back[jx][jy]);
3221 DrawLevelElement(jx, jy, EL_EMPTY);
3223 if (Back[next_jx][next_jy])
3224 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3226 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3228 else if (Back[next_jx][next_jy])
3229 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3232 /* do not draw (EM style) pushing animation when pushing is finished */
3233 /* (two-tile animations usually do not contain start and end frame) */
3234 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3235 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3237 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3239 /* masked drawing is needed for EMC style (double) movement graphics */
3240 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3241 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3245 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3246 /* ----------------------------------------------------------------------- */
3247 /* draw player himself */
3248 /* ----------------------------------------------------------------------- */
3250 graphic = getPlayerGraphic(player, move_dir);
3252 /* in the case of changed player action or direction, prevent the current
3253 animation frame from being restarted for identical animations */
3254 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3255 player->Frame = last_player_frame;
3257 frame = getGraphicAnimationFrame(graphic, player->Frame);
3261 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3262 sxx = player->GfxPos;
3264 syy = player->GfxPos;
3267 if (!setup.soft_scrolling && ScreenMovPos)
3270 if (player_is_opaque)
3271 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3273 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3275 if (SHIELD_ON(player))
3277 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3278 IMG_SHIELD_NORMAL_ACTIVE);
3279 int frame = getGraphicAnimationFrame(graphic, -1);
3281 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3285 /* ----------------------------------------------------------------------- */
3286 /* draw things in front of player (active dynamite or dynabombs) */
3287 /* ----------------------------------------------------------------------- */
3289 if (IS_ACTIVE_BOMB(element))
3291 graphic = el2img(element);
3292 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3294 if (game.emulation == EMU_SUPAPLEX)
3295 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3297 DrawGraphicThruMask(sx, sy, graphic, frame);
3300 if (player_is_moving && last_element == EL_EXPLOSION)
3302 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3303 GfxElement[last_jx][last_jy] : EL_EMPTY);
3304 int graphic = el_act2img(element, ACTION_EXPLODING);
3305 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3306 int phase = ExplodePhase[last_jx][last_jy] - 1;
3307 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3310 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3313 /* ----------------------------------------------------------------------- */
3314 /* draw elements the player is just walking/passing through/under */
3315 /* ----------------------------------------------------------------------- */
3317 if (player_is_moving)
3319 /* handle the field the player is leaving ... */
3320 if (IS_ACCESSIBLE_INSIDE(last_element))
3321 DrawLevelField(last_jx, last_jy);
3322 else if (IS_ACCESSIBLE_UNDER(last_element))
3323 DrawLevelFieldThruMask(last_jx, last_jy);
3326 /* do not redraw accessible elements if the player is just pushing them */
3327 if (!player_is_moving || !player->is_pushing)
3329 /* ... and the field the player is entering */
3330 if (IS_ACCESSIBLE_INSIDE(element))
3331 DrawLevelField(jx, jy);
3332 else if (IS_ACCESSIBLE_UNDER(element))
3333 DrawLevelFieldThruMask(jx, jy);
3336 MarkTileDirty(sx, sy);
3339 /* ------------------------------------------------------------------------- */
3341 void WaitForEventToContinue()
3343 boolean still_wait = TRUE;
3345 /* simulate releasing mouse button over last gadget, if still pressed */
3347 HandleGadgets(-1, -1, 0);
3349 button_status = MB_RELEASED;
3363 case EVENT_BUTTONPRESS:
3364 case EVENT_KEYPRESS:
3368 case EVENT_KEYRELEASE:
3369 ClearPlayerAction();
3373 HandleOtherEvents(&event);
3377 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3384 /* don't eat all CPU time */
3389 #define MAX_REQUEST_LINES 13
3390 #define MAX_REQUEST_LINE_FONT1_LEN 7
3391 #define MAX_REQUEST_LINE_FONT2_LEN 10
3393 static int RequestHandleEvents(unsigned int req_state)
3395 int last_game_status = game_status; /* save current game status */
3399 button_status = MB_RELEASED;
3401 request_gadget_id = -1;
3414 case EVENT_BUTTONPRESS:
3415 case EVENT_BUTTONRELEASE:
3416 case EVENT_MOTIONNOTIFY:
3418 if (event.type == EVENT_MOTIONNOTIFY)
3420 if (!PointerInWindow(window))
3421 continue; /* window and pointer are on different screens */
3426 motion_status = TRUE;
3427 mx = ((MotionEvent *) &event)->x;
3428 my = ((MotionEvent *) &event)->y;
3432 motion_status = FALSE;
3433 mx = ((ButtonEvent *) &event)->x;
3434 my = ((ButtonEvent *) &event)->y;
3435 if (event.type == EVENT_BUTTONPRESS)
3436 button_status = ((ButtonEvent *) &event)->button;
3438 button_status = MB_RELEASED;
3441 /* this sets 'request_gadget_id' */
3442 HandleGadgets(mx, my, button_status);
3444 switch (request_gadget_id)
3446 case TOOL_CTRL_ID_YES:
3449 case TOOL_CTRL_ID_NO:
3452 case TOOL_CTRL_ID_CONFIRM:
3453 result = TRUE | FALSE;
3456 case TOOL_CTRL_ID_PLAYER_1:
3459 case TOOL_CTRL_ID_PLAYER_2:
3462 case TOOL_CTRL_ID_PLAYER_3:
3465 case TOOL_CTRL_ID_PLAYER_4:
3476 case EVENT_KEYPRESS:
3477 switch (GetEventKey((KeyEvent *)&event, TRUE))
3480 if (req_state & REQ_CONFIRM)
3485 #if defined(TARGET_SDL2)
3492 #if defined(TARGET_SDL2)
3502 if (req_state & REQ_PLAYER)
3506 case EVENT_KEYRELEASE:
3507 ClearPlayerAction();
3511 HandleOtherEvents(&event);
3515 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3517 int joy = AnyJoystick();
3519 if (joy & JOY_BUTTON_1)
3521 else if (joy & JOY_BUTTON_2)
3525 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3527 HandleGameActions();
3533 if (!PendingEvent()) /* delay only if no pending events */
3537 game_status = GAME_MODE_PSEUDO_DOOR;
3541 game_status = last_game_status; /* restore current game status */
3547 static boolean RequestDoor(char *text, unsigned int req_state)
3549 unsigned int old_door_state;
3550 int last_game_status = game_status; /* save current game status */
3551 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3552 int font_nr = FONT_TEXT_2;
3557 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3559 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3560 font_nr = FONT_TEXT_1;
3563 if (game_status == GAME_MODE_PLAYING)
3564 BlitScreenToBitmap(backbuffer);
3566 /* disable deactivated drawing when quick-loading level tape recording */
3567 if (tape.playing && tape.deactivate_display)
3568 TapeDeactivateDisplayOff(TRUE);
3570 SetMouseCursor(CURSOR_DEFAULT);
3572 #if defined(NETWORK_AVALIABLE)
3573 /* pause network game while waiting for request to answer */
3574 if (options.network &&
3575 game_status == GAME_MODE_PLAYING &&
3576 req_state & REQUEST_WAIT_FOR_INPUT)
3577 SendToServer_PausePlaying();
3580 old_door_state = GetDoorState();
3582 /* simulate releasing mouse button over last gadget, if still pressed */
3584 HandleGadgets(-1, -1, 0);
3588 /* draw released gadget before proceeding */
3591 if (old_door_state & DOOR_OPEN_1)
3593 CloseDoor(DOOR_CLOSE_1);
3595 /* save old door content */
3596 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3597 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3600 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3601 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3603 /* clear door drawing field */
3604 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3606 /* force DOOR font inside door area */
3607 game_status = GAME_MODE_PSEUDO_DOOR;
3609 /* write text for request */
3610 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3612 char text_line[max_request_line_len + 1];
3618 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3620 tc = *(text_ptr + tx);
3621 // if (!tc || tc == ' ')
3622 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3626 if ((tc == '?' || tc == '!') && tl == 0)
3636 strncpy(text_line, text_ptr, tl);
3639 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3640 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3641 text_line, font_nr);
3643 text_ptr += tl + (tc == ' ' ? 1 : 0);
3644 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3647 game_status = last_game_status; /* restore current game status */
3649 if (req_state & REQ_ASK)
3651 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3652 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3654 else if (req_state & REQ_CONFIRM)
3656 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3658 else if (req_state & REQ_PLAYER)
3660 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3661 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3662 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3663 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3666 /* copy request gadgets to door backbuffer */
3667 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3669 OpenDoor(DOOR_OPEN_1);
3671 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3673 if (game_status == GAME_MODE_PLAYING)
3675 SetPanelBackground();
3676 SetDrawBackgroundMask(REDRAW_DOOR_1);
3680 SetDrawBackgroundMask(REDRAW_FIELD);
3686 if (game_status != GAME_MODE_MAIN)
3689 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3691 // ---------- handle request buttons ----------
3692 result = RequestHandleEvents(req_state);
3694 if (game_status != GAME_MODE_MAIN)
3699 if (!(req_state & REQ_STAY_OPEN))
3701 CloseDoor(DOOR_CLOSE_1);
3703 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3704 (req_state & REQ_REOPEN))
3705 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3710 if (game_status == GAME_MODE_PLAYING)
3712 SetPanelBackground();
3713 SetDrawBackgroundMask(REDRAW_DOOR_1);
3717 SetDrawBackgroundMask(REDRAW_FIELD);
3720 #if defined(NETWORK_AVALIABLE)
3721 /* continue network game after request */
3722 if (options.network &&
3723 game_status == GAME_MODE_PLAYING &&
3724 req_state & REQUEST_WAIT_FOR_INPUT)
3725 SendToServer_ContinuePlaying();
3728 /* restore deactivated drawing when quick-loading level tape recording */
3729 if (tape.playing && tape.deactivate_display)
3730 TapeDeactivateDisplayOn();
3735 static boolean RequestEnvelope(char *text, unsigned int req_state)
3739 if (game_status == GAME_MODE_PLAYING)
3740 BlitScreenToBitmap(backbuffer);
3742 /* disable deactivated drawing when quick-loading level tape recording */
3743 if (tape.playing && tape.deactivate_display)
3744 TapeDeactivateDisplayOff(TRUE);
3746 SetMouseCursor(CURSOR_DEFAULT);
3748 #if defined(NETWORK_AVALIABLE)
3749 /* pause network game while waiting for request to answer */
3750 if (options.network &&
3751 game_status == GAME_MODE_PLAYING &&
3752 req_state & REQUEST_WAIT_FOR_INPUT)
3753 SendToServer_PausePlaying();
3756 /* simulate releasing mouse button over last gadget, if still pressed */
3758 HandleGadgets(-1, -1, 0);
3762 // (replace with setting corresponding request background)
3763 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3764 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3766 /* clear door drawing field */
3767 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3769 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3771 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3773 if (game_status == GAME_MODE_PLAYING)
3775 SetPanelBackground();
3776 SetDrawBackgroundMask(REDRAW_DOOR_1);
3780 SetDrawBackgroundMask(REDRAW_FIELD);
3786 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3788 // ---------- handle request buttons ----------
3789 result = RequestHandleEvents(req_state);
3791 if (game_status != GAME_MODE_MAIN)
3796 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3800 if (game_status == GAME_MODE_PLAYING)
3802 SetPanelBackground();
3803 SetDrawBackgroundMask(REDRAW_DOOR_1);
3807 SetDrawBackgroundMask(REDRAW_FIELD);
3810 #if defined(NETWORK_AVALIABLE)
3811 /* continue network game after request */
3812 if (options.network &&
3813 game_status == GAME_MODE_PLAYING &&
3814 req_state & REQUEST_WAIT_FOR_INPUT)
3815 SendToServer_ContinuePlaying();
3818 /* restore deactivated drawing when quick-loading level tape recording */
3819 if (tape.playing && tape.deactivate_display)
3820 TapeDeactivateDisplayOn();
3825 boolean Request(char *text, unsigned int req_state)
3827 if (global.use_envelope_request)
3828 return RequestEnvelope(text, req_state);
3830 return RequestDoor(text, req_state);
3833 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3835 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3836 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3839 if (dpo1->sort_priority != dpo2->sort_priority)
3840 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3842 compare_result = dpo1->nr - dpo2->nr;
3844 return compare_result;
3847 void InitGraphicCompatibilityInfo_Doors()
3853 struct DoorInfo *door;
3857 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3858 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3860 { -1, -1, -1, NULL }
3862 struct Rect door_rect_list[] =
3864 { DX, DY, DXSIZE, DYSIZE },
3865 { VX, VY, VXSIZE, VYSIZE }
3869 for (i = 0; doors[i].door_token != -1; i++)
3871 int door_token = doors[i].door_token;
3872 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3873 int part_1 = doors[i].part_1;
3874 int part_8 = doors[i].part_8;
3875 int part_2 = part_1 + 1;
3876 int part_3 = part_1 + 2;
3877 struct DoorInfo *door = doors[i].door;
3878 struct Rect *door_rect = &door_rect_list[door_index];
3879 boolean door_gfx_redefined = FALSE;
3881 /* check if any door part graphic definitions have been redefined */
3883 for (j = 0; door_part_controls[j].door_token != -1; j++)
3885 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3886 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3888 if (dpc->door_token == door_token && fi->redefined)
3889 door_gfx_redefined = TRUE;
3892 /* check for old-style door graphic/animation modifications */
3894 if (!door_gfx_redefined)
3896 if (door->anim_mode & ANIM_STATIC_PANEL)
3898 door->panel.step_xoffset = 0;
3899 door->panel.step_yoffset = 0;
3902 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3904 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3905 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3906 int num_door_steps, num_panel_steps;
3908 /* remove door part graphics other than the two default wings */
3910 for (j = 0; door_part_controls[j].door_token != -1; j++)
3912 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3913 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3915 if (dpc->graphic >= part_3 &&
3916 dpc->graphic <= part_8)
3920 /* set graphics and screen positions of the default wings */
3922 g_part_1->width = door_rect->width;
3923 g_part_1->height = door_rect->height;
3924 g_part_2->width = door_rect->width;
3925 g_part_2->height = door_rect->height;
3926 g_part_2->src_x = door_rect->width;
3927 g_part_2->src_y = g_part_1->src_y;
3929 door->part_2.x = door->part_1.x;
3930 door->part_2.y = door->part_1.y;
3932 if (door->width != -1)
3934 g_part_1->width = door->width;
3935 g_part_2->width = door->width;
3937 // special treatment for graphics and screen position of right wing
3938 g_part_2->src_x += door_rect->width - door->width;
3939 door->part_2.x += door_rect->width - door->width;
3942 if (door->height != -1)
3944 g_part_1->height = door->height;
3945 g_part_2->height = door->height;
3947 // special treatment for graphics and screen position of bottom wing
3948 g_part_2->src_y += door_rect->height - door->height;
3949 door->part_2.y += door_rect->height - door->height;
3952 /* set animation delays for the default wings and panels */
3954 door->part_1.step_delay = door->step_delay;
3955 door->part_2.step_delay = door->step_delay;
3956 door->panel.step_delay = door->step_delay;
3958 /* set animation draw order for the default wings */
3960 door->part_1.sort_priority = 2; /* draw left wing over ... */
3961 door->part_2.sort_priority = 1; /* ... right wing */
3963 /* set animation draw offset for the default wings */
3965 if (door->anim_mode & ANIM_HORIZONTAL)
3967 door->part_1.step_xoffset = door->step_offset;
3968 door->part_1.step_yoffset = 0;
3969 door->part_2.step_xoffset = door->step_offset * -1;
3970 door->part_2.step_yoffset = 0;
3972 num_door_steps = g_part_1->width / door->step_offset;
3974 else // ANIM_VERTICAL
3976 door->part_1.step_xoffset = 0;
3977 door->part_1.step_yoffset = door->step_offset;
3978 door->part_2.step_xoffset = 0;
3979 door->part_2.step_yoffset = door->step_offset * -1;
3981 num_door_steps = g_part_1->height / door->step_offset;
3984 /* set animation draw offset for the default panels */
3986 if (door->step_offset > 1)
3988 num_panel_steps = 2 * door_rect->height / door->step_offset;
3989 door->panel.start_step = num_panel_steps - num_door_steps;
3993 num_panel_steps = door_rect->height / door->step_offset;
3994 door->panel.start_step = num_panel_steps - num_door_steps / 2;
3995 door->panel.step_delay *= 2;
4006 for (i = 0; door_part_controls[i].door_token != -1; i++)
4008 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4009 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4011 /* initialize "start_step_opening" and "start_step_closing", if needed */
4012 if (dpc->pos->start_step_opening == 0 &&
4013 dpc->pos->start_step_closing == 0)
4015 // dpc->pos->start_step_opening = dpc->pos->start_step;
4016 dpc->pos->start_step_closing = dpc->pos->start_step;
4019 /* fill structure for door part draw order (sorted below) */
4021 dpo->sort_priority = dpc->pos->sort_priority;
4024 /* sort door part controls according to sort_priority and graphic number */
4025 qsort(door_part_order, MAX_DOOR_PARTS,
4026 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4029 unsigned int OpenDoor(unsigned int door_state)
4031 if (door_state & DOOR_COPY_BACK)
4033 if (door_state & DOOR_OPEN_1)
4034 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4035 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4037 if (door_state & DOOR_OPEN_2)
4038 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4039 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4041 door_state &= ~DOOR_COPY_BACK;
4044 return MoveDoor(door_state);
4047 unsigned int CloseDoor(unsigned int door_state)
4049 unsigned int old_door_state = GetDoorState();
4051 if (!(door_state & DOOR_NO_COPY_BACK))
4053 if (old_door_state & DOOR_OPEN_1)
4054 BlitBitmap(backbuffer, bitmap_db_door_1,
4055 DX, DY, DXSIZE, DYSIZE, 0, 0);
4057 if (old_door_state & DOOR_OPEN_2)
4058 BlitBitmap(backbuffer, bitmap_db_door_2,
4059 VX, VY, VXSIZE, VYSIZE, 0, 0);
4061 door_state &= ~DOOR_NO_COPY_BACK;
4064 return MoveDoor(door_state);
4067 unsigned int GetDoorState()
4069 return MoveDoor(DOOR_GET_STATE);
4072 unsigned int SetDoorState(unsigned int door_state)
4074 return MoveDoor(door_state | DOOR_SET_STATE);
4077 int euclid(int a, int b)
4079 return (b ? euclid(b, a % b) : a);
4082 unsigned int MoveDoor(unsigned int door_state)
4084 struct Rect door_rect_list[] =
4086 { DX, DY, DXSIZE, DYSIZE },
4087 { VX, VY, VXSIZE, VYSIZE }
4089 static int door1 = DOOR_OPEN_1;
4090 static int door2 = DOOR_CLOSE_2;
4091 unsigned int door_delay = 0;
4092 unsigned int door_delay_value;
4095 if (door_1.width < 0 || door_1.width > DXSIZE)
4096 door_1.width = DXSIZE;
4097 if (door_1.height < 0 || door_1.height > DYSIZE)
4098 door_1.height = DYSIZE;
4099 if (door_2.width < 0 || door_2.width > VXSIZE)
4100 door_2.width = VXSIZE;
4101 if (door_2.height < 0 || door_2.height > VYSIZE)
4102 door_2.height = VYSIZE;
4104 if (door_state == DOOR_GET_STATE)
4105 return (door1 | door2);
4107 if (door_state & DOOR_SET_STATE)
4109 if (door_state & DOOR_ACTION_1)
4110 door1 = door_state & DOOR_ACTION_1;
4111 if (door_state & DOOR_ACTION_2)
4112 door2 = door_state & DOOR_ACTION_2;
4114 return (door1 | door2);
4117 if (!(door_state & DOOR_FORCE_REDRAW))
4119 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4120 door_state &= ~DOOR_OPEN_1;
4121 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4122 door_state &= ~DOOR_CLOSE_1;
4123 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4124 door_state &= ~DOOR_OPEN_2;
4125 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4126 door_state &= ~DOOR_CLOSE_2;
4129 if (global.autoplay_leveldir)
4131 door_state |= DOOR_NO_DELAY;
4132 door_state &= ~DOOR_CLOSE_ALL;
4135 if (game_status == GAME_MODE_EDITOR)
4136 door_state |= DOOR_NO_DELAY;
4138 if (door_state & DOOR_ACTION)
4140 boolean door_panel_drawn[NUM_DOORS];
4141 boolean panel_has_doors[NUM_DOORS];
4142 boolean door_part_skip[MAX_DOOR_PARTS];
4143 boolean door_part_done[MAX_DOOR_PARTS];
4144 boolean door_part_done_all;
4145 int num_steps[MAX_DOOR_PARTS];
4146 int max_move_delay = 0; // delay for complete animations of all doors
4147 int max_step_delay = 0; // delay (ms) between two animation frames
4148 int num_move_steps = 0; // number of animation steps for all doors
4149 int current_move_delay = 0;
4152 for (i = 0; i < NUM_DOORS; i++)
4153 panel_has_doors[i] = FALSE;
4155 for (i = 0; i < MAX_DOOR_PARTS; i++)
4157 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4158 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4159 int door_token = dpc->door_token;
4161 door_part_done[i] = FALSE;
4162 door_part_skip[i] = (!(door_state & door_token) ||
4166 for (i = 0; i < MAX_DOOR_PARTS; i++)
4168 int nr = door_part_order[i].nr;
4169 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4170 struct DoorPartPosInfo *pos = dpc->pos;
4171 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4172 int door_token = dpc->door_token;
4173 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4174 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4175 int step_xoffset = ABS(pos->step_xoffset);
4176 int step_yoffset = ABS(pos->step_yoffset);
4177 int step_delay = pos->step_delay;
4178 int current_door_state = door_state & door_token;
4179 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4180 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4181 boolean part_opening = (is_panel ? door_closing : door_opening);
4182 int start_step = (part_opening ? pos->start_step_opening :
4183 pos->start_step_closing);
4184 float move_xsize = (step_xoffset ? g->width : 0);
4185 float move_ysize = (step_yoffset ? g->height : 0);
4186 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4187 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4188 int move_steps = (move_xsteps && move_ysteps ?
4189 MIN(move_xsteps, move_ysteps) :
4190 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4191 int move_delay = move_steps * step_delay;
4193 if (door_part_skip[nr])
4197 panel_has_doors[door_index] = TRUE;
4199 max_move_delay = MAX(max_move_delay, move_delay);
4200 max_step_delay = (max_step_delay == 0 ? step_delay :
4201 euclid(max_step_delay, step_delay));
4202 num_steps[nr] = move_steps;
4205 num_move_steps = max_move_delay / max_step_delay;
4207 door_delay_value = max_step_delay;
4209 for (k = 0; k < num_move_steps; k++)
4211 door_part_done_all = TRUE;
4213 for (i = 0; i < NUM_DOORS; i++)
4214 door_panel_drawn[i] = FALSE;
4216 for (i = 0; i < MAX_DOOR_PARTS; i++)
4218 int nr = door_part_order[i].nr;
4219 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4220 struct DoorPartPosInfo *pos = dpc->pos;
4221 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4222 int door_token = dpc->door_token;
4223 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4224 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4225 struct Rect *door_rect = &door_rect_list[door_index];
4226 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4228 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4229 int current_door_state = door_state & door_token;
4230 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4231 boolean door_closing = !door_opening;
4232 boolean part_opening = (is_panel ? door_closing : door_opening);
4233 boolean part_closing = !part_opening;
4234 int start_step = (part_opening ? pos->start_step_opening :
4235 pos->start_step_closing);
4236 int step_delay = pos->step_delay;
4237 int step_factor = step_delay / max_step_delay;
4238 int k1 = (step_factor ? k / step_factor + 1 : k);
4239 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4240 int kk = (k2 < 0 ? 0 : k2);
4241 int src_x, src_y, src_xx, src_yy;
4242 int dst_x, dst_y, dst_xx, dst_yy;
4245 if (door_part_skip[nr])
4248 if (!(door_state & door_token))
4256 if (!door_panel_drawn[door_index])
4258 ClearRectangle(drawto, door_rect->x, door_rect->y,
4259 door_rect->width, door_rect->height);
4261 door_panel_drawn[door_index] = TRUE;
4264 // draw opening or closing door parts
4266 if (pos->step_xoffset < 0) // door part on right side
4269 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4272 if (dst_xx + width > door_rect->width)
4273 width = door_rect->width - dst_xx;
4275 else // door part on left side
4278 dst_xx = pos->x - kk * pos->step_xoffset;
4282 src_xx = ABS(dst_xx);
4286 width = g->width - src_xx;
4288 // printf("::: k == %d [%d] \n", k, start_step);
4291 if (pos->step_yoffset < 0) // door part on bottom side
4294 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4297 if (dst_yy + height > door_rect->height)
4298 height = door_rect->height - dst_yy;
4300 else // door part on top side
4303 dst_yy = pos->y - kk * pos->step_yoffset;
4307 src_yy = ABS(dst_yy);
4311 height = g->height - src_yy;
4321 src_x = g->src_x + src_xx;
4322 src_y = g->src_y + src_yy;
4325 dst_x = door_rect->x + dst_xx;
4326 dst_y = door_rect->y + dst_yy;
4328 if (width >= 0 && width <= g->width &&
4329 height >= 0 && height <= g->height)
4331 if (is_panel || !pos->draw_masked)
4332 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4335 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4339 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4341 if ((part_opening && (width < 0 || height < 0)) ||
4342 (part_closing && (width >= g->width && height >= g->height)))
4343 door_part_done[nr] = TRUE;
4345 // continue door part animations, but not panel after door has closed
4346 if (!door_part_done[nr] &&
4347 !(is_panel && door_closing && panel_has_doors[door_index]))
4348 door_part_done_all = FALSE;
4351 if (!(door_state & DOOR_NO_DELAY))
4355 if (game_status == GAME_MODE_MAIN)
4358 WaitUntilDelayReached(&door_delay, door_delay_value);
4360 current_move_delay += max_step_delay;
4363 if (door_part_done_all)
4368 if (door_state & DOOR_ACTION_1)
4369 door1 = door_state & DOOR_ACTION_1;
4370 if (door_state & DOOR_ACTION_2)
4371 door2 = door_state & DOOR_ACTION_2;
4373 return (door1 | door2);
4376 void DrawSpecialEditorDoor()
4378 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4379 int top_border_width = gfx1->width;
4380 int top_border_height = gfx1->height;
4381 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4382 int ex = EX - outer_border;
4383 int ey = EY - outer_border;
4384 int vy = VY - outer_border;
4385 int exsize = EXSIZE + 2 * outer_border;
4387 CloseDoor(DOOR_CLOSE_2);
4389 /* draw bigger level editor toolbox window */
4390 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4391 top_border_width, top_border_height, ex, ey - top_border_height);
4392 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4393 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4395 redraw_mask |= REDRAW_ALL;
4398 void UndrawSpecialEditorDoor()
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 ey_top = ey - top_border_height;
4407 int exsize = EXSIZE + 2 * outer_border;
4408 int eysize = EYSIZE + 2 * outer_border;
4410 /* draw normal tape recorder window */
4411 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4413 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4414 ex, ey_top, top_border_width, top_border_height,
4416 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4417 ex, ey, exsize, eysize, ex, ey);
4421 // if screen background is set to "[NONE]", clear editor toolbox window
4422 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4423 ClearRectangle(drawto, ex, ey, exsize, eysize);
4426 redraw_mask |= REDRAW_ALL;
4430 /* ---------- new tool button stuff ---------------------------------------- */
4435 struct TextPosInfo *pos;
4438 } toolbutton_info[NUM_TOOL_BUTTONS] =
4441 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4442 TOOL_CTRL_ID_YES, "yes"
4445 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4446 TOOL_CTRL_ID_NO, "no"
4449 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4450 TOOL_CTRL_ID_CONFIRM, "confirm"
4453 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4454 TOOL_CTRL_ID_PLAYER_1, "player 1"
4457 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4458 TOOL_CTRL_ID_PLAYER_2, "player 2"
4461 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4462 TOOL_CTRL_ID_PLAYER_3, "player 3"
4465 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4466 TOOL_CTRL_ID_PLAYER_4, "player 4"
4470 void CreateToolButtons()
4474 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4476 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4477 struct TextPosInfo *pos = toolbutton_info[i].pos;
4478 struct GadgetInfo *gi;
4479 Bitmap *deco_bitmap = None;
4480 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4481 unsigned int event_mask = GD_EVENT_RELEASED;
4484 int gd_x = gfx->src_x;
4485 int gd_y = gfx->src_y;
4486 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4487 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4490 if (global.use_envelope_request)
4491 setRequestPosition(&dx, &dy, TRUE);
4493 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4495 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4497 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4498 pos->size, &deco_bitmap, &deco_x, &deco_y);
4499 deco_xpos = (gfx->width - pos->size) / 2;
4500 deco_ypos = (gfx->height - pos->size) / 2;
4503 gi = CreateGadget(GDI_CUSTOM_ID, id,
4504 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4505 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4506 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4507 GDI_WIDTH, gfx->width,
4508 GDI_HEIGHT, gfx->height,
4509 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4510 GDI_STATE, GD_BUTTON_UNPRESSED,
4511 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4512 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4513 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4514 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4515 GDI_DECORATION_SIZE, pos->size, pos->size,
4516 GDI_DECORATION_SHIFTING, 1, 1,
4517 GDI_DIRECT_DRAW, FALSE,
4518 GDI_EVENT_MASK, event_mask,
4519 GDI_CALLBACK_ACTION, HandleToolButtons,
4523 Error(ERR_EXIT, "cannot create gadget");
4525 tool_gadget[id] = gi;
4529 void FreeToolButtons()
4533 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4534 FreeGadget(tool_gadget[i]);
4537 static void UnmapToolButtons()
4541 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4542 UnmapGadget(tool_gadget[i]);
4545 static void HandleToolButtons(struct GadgetInfo *gi)
4547 request_gadget_id = gi->custom_id;
4550 static struct Mapping_EM_to_RND_object
4553 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4554 boolean is_backside; /* backside of moving element */
4560 em_object_mapping_list[] =
4563 Xblank, TRUE, FALSE,
4567 Yacid_splash_eB, FALSE, FALSE,
4568 EL_ACID_SPLASH_RIGHT, -1, -1
4571 Yacid_splash_wB, FALSE, FALSE,
4572 EL_ACID_SPLASH_LEFT, -1, -1
4575 #ifdef EM_ENGINE_BAD_ROLL
4577 Xstone_force_e, FALSE, FALSE,
4578 EL_ROCK, -1, MV_BIT_RIGHT
4581 Xstone_force_w, FALSE, FALSE,
4582 EL_ROCK, -1, MV_BIT_LEFT
4585 Xnut_force_e, FALSE, FALSE,
4586 EL_NUT, -1, MV_BIT_RIGHT
4589 Xnut_force_w, FALSE, FALSE,
4590 EL_NUT, -1, MV_BIT_LEFT
4593 Xspring_force_e, FALSE, FALSE,
4594 EL_SPRING, -1, MV_BIT_RIGHT
4597 Xspring_force_w, FALSE, FALSE,
4598 EL_SPRING, -1, MV_BIT_LEFT
4601 Xemerald_force_e, FALSE, FALSE,
4602 EL_EMERALD, -1, MV_BIT_RIGHT
4605 Xemerald_force_w, FALSE, FALSE,
4606 EL_EMERALD, -1, MV_BIT_LEFT
4609 Xdiamond_force_e, FALSE, FALSE,
4610 EL_DIAMOND, -1, MV_BIT_RIGHT
4613 Xdiamond_force_w, FALSE, FALSE,
4614 EL_DIAMOND, -1, MV_BIT_LEFT
4617 Xbomb_force_e, FALSE, FALSE,
4618 EL_BOMB, -1, MV_BIT_RIGHT
4621 Xbomb_force_w, FALSE, FALSE,
4622 EL_BOMB, -1, MV_BIT_LEFT
4624 #endif /* EM_ENGINE_BAD_ROLL */
4627 Xstone, TRUE, FALSE,
4631 Xstone_pause, FALSE, FALSE,
4635 Xstone_fall, FALSE, FALSE,
4639 Ystone_s, FALSE, FALSE,
4640 EL_ROCK, ACTION_FALLING, -1
4643 Ystone_sB, FALSE, TRUE,
4644 EL_ROCK, ACTION_FALLING, -1
4647 Ystone_e, FALSE, FALSE,
4648 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4651 Ystone_eB, FALSE, TRUE,
4652 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4655 Ystone_w, FALSE, FALSE,
4656 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4659 Ystone_wB, FALSE, TRUE,
4660 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4667 Xnut_pause, FALSE, FALSE,
4671 Xnut_fall, FALSE, FALSE,
4675 Ynut_s, FALSE, FALSE,
4676 EL_NUT, ACTION_FALLING, -1
4679 Ynut_sB, FALSE, TRUE,
4680 EL_NUT, ACTION_FALLING, -1
4683 Ynut_e, FALSE, FALSE,
4684 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4687 Ynut_eB, FALSE, TRUE,
4688 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4691 Ynut_w, FALSE, FALSE,
4692 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4695 Ynut_wB, FALSE, TRUE,
4696 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4699 Xbug_n, TRUE, FALSE,
4703 Xbug_e, TRUE, FALSE,
4704 EL_BUG_RIGHT, -1, -1
4707 Xbug_s, TRUE, FALSE,
4711 Xbug_w, TRUE, FALSE,
4715 Xbug_gon, FALSE, FALSE,
4719 Xbug_goe, FALSE, FALSE,
4720 EL_BUG_RIGHT, -1, -1
4723 Xbug_gos, FALSE, FALSE,
4727 Xbug_gow, FALSE, FALSE,
4731 Ybug_n, FALSE, FALSE,
4732 EL_BUG, ACTION_MOVING, MV_BIT_UP
4735 Ybug_nB, FALSE, TRUE,
4736 EL_BUG, ACTION_MOVING, MV_BIT_UP
4739 Ybug_e, FALSE, FALSE,
4740 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4743 Ybug_eB, FALSE, TRUE,
4744 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4747 Ybug_s, FALSE, FALSE,
4748 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4751 Ybug_sB, FALSE, TRUE,
4752 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4755 Ybug_w, FALSE, FALSE,
4756 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4759 Ybug_wB, FALSE, TRUE,
4760 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4763 Ybug_w_n, FALSE, FALSE,
4764 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4767 Ybug_n_e, FALSE, FALSE,
4768 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4771 Ybug_e_s, FALSE, FALSE,
4772 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4775 Ybug_s_w, FALSE, FALSE,
4776 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4779 Ybug_e_n, FALSE, FALSE,
4780 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4783 Ybug_s_e, FALSE, FALSE,
4784 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4787 Ybug_w_s, FALSE, FALSE,
4788 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4791 Ybug_n_w, FALSE, FALSE,
4792 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4795 Ybug_stone, FALSE, FALSE,
4796 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4799 Ybug_spring, FALSE, FALSE,
4800 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4803 Xtank_n, TRUE, FALSE,
4804 EL_SPACESHIP_UP, -1, -1
4807 Xtank_e, TRUE, FALSE,
4808 EL_SPACESHIP_RIGHT, -1, -1
4811 Xtank_s, TRUE, FALSE,
4812 EL_SPACESHIP_DOWN, -1, -1
4815 Xtank_w, TRUE, FALSE,
4816 EL_SPACESHIP_LEFT, -1, -1
4819 Xtank_gon, FALSE, FALSE,
4820 EL_SPACESHIP_UP, -1, -1
4823 Xtank_goe, FALSE, FALSE,
4824 EL_SPACESHIP_RIGHT, -1, -1
4827 Xtank_gos, FALSE, FALSE,
4828 EL_SPACESHIP_DOWN, -1, -1
4831 Xtank_gow, FALSE, FALSE,
4832 EL_SPACESHIP_LEFT, -1, -1
4835 Ytank_n, FALSE, FALSE,
4836 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4839 Ytank_nB, FALSE, TRUE,
4840 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4843 Ytank_e, FALSE, FALSE,
4844 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4847 Ytank_eB, FALSE, TRUE,
4848 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4851 Ytank_s, FALSE, FALSE,
4852 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4855 Ytank_sB, FALSE, TRUE,
4856 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4859 Ytank_w, FALSE, FALSE,
4860 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4863 Ytank_wB, FALSE, TRUE,
4864 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4867 Ytank_w_n, FALSE, FALSE,
4868 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4871 Ytank_n_e, FALSE, FALSE,
4872 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4875 Ytank_e_s, FALSE, FALSE,
4876 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4879 Ytank_s_w, FALSE, FALSE,
4880 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4883 Ytank_e_n, FALSE, FALSE,
4884 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4887 Ytank_s_e, FALSE, FALSE,
4888 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4891 Ytank_w_s, FALSE, FALSE,
4892 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4895 Ytank_n_w, FALSE, FALSE,
4896 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4899 Ytank_stone, FALSE, FALSE,
4900 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4903 Ytank_spring, FALSE, FALSE,
4904 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4907 Xandroid, TRUE, FALSE,
4908 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4911 Xandroid_1_n, FALSE, FALSE,
4912 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4915 Xandroid_2_n, FALSE, FALSE,
4916 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4919 Xandroid_1_e, FALSE, FALSE,
4920 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4923 Xandroid_2_e, FALSE, FALSE,
4924 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4927 Xandroid_1_w, FALSE, FALSE,
4928 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4931 Xandroid_2_w, FALSE, FALSE,
4932 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4935 Xandroid_1_s, FALSE, FALSE,
4936 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4939 Xandroid_2_s, FALSE, FALSE,
4940 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4943 Yandroid_n, FALSE, FALSE,
4944 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4947 Yandroid_nB, FALSE, TRUE,
4948 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4951 Yandroid_ne, FALSE, FALSE,
4952 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4955 Yandroid_neB, FALSE, TRUE,
4956 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4959 Yandroid_e, FALSE, FALSE,
4960 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4963 Yandroid_eB, FALSE, TRUE,
4964 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4967 Yandroid_se, FALSE, FALSE,
4968 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4971 Yandroid_seB, FALSE, TRUE,
4972 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4975 Yandroid_s, FALSE, FALSE,
4976 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4979 Yandroid_sB, FALSE, TRUE,
4980 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4983 Yandroid_sw, FALSE, FALSE,
4984 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
4987 Yandroid_swB, FALSE, TRUE,
4988 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
4991 Yandroid_w, FALSE, FALSE,
4992 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
4995 Yandroid_wB, FALSE, TRUE,
4996 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
4999 Yandroid_nw, FALSE, FALSE,
5000 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5003 Yandroid_nwB, FALSE, TRUE,
5004 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5007 Xspring, TRUE, FALSE,
5011 Xspring_pause, FALSE, FALSE,
5015 Xspring_e, FALSE, FALSE,
5019 Xspring_w, FALSE, FALSE,
5023 Xspring_fall, FALSE, FALSE,
5027 Yspring_s, FALSE, FALSE,
5028 EL_SPRING, ACTION_FALLING, -1
5031 Yspring_sB, FALSE, TRUE,
5032 EL_SPRING, ACTION_FALLING, -1
5035 Yspring_e, FALSE, FALSE,
5036 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5039 Yspring_eB, FALSE, TRUE,
5040 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5043 Yspring_w, FALSE, FALSE,
5044 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5047 Yspring_wB, FALSE, TRUE,
5048 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5051 Yspring_kill_e, FALSE, FALSE,
5052 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5055 Yspring_kill_eB, FALSE, TRUE,
5056 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5059 Yspring_kill_w, FALSE, FALSE,
5060 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5063 Yspring_kill_wB, FALSE, TRUE,
5064 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5067 Xeater_n, TRUE, FALSE,
5068 EL_YAMYAM_UP, -1, -1
5071 Xeater_e, TRUE, FALSE,
5072 EL_YAMYAM_RIGHT, -1, -1
5075 Xeater_w, TRUE, FALSE,
5076 EL_YAMYAM_LEFT, -1, -1
5079 Xeater_s, TRUE, FALSE,
5080 EL_YAMYAM_DOWN, -1, -1
5083 Yeater_n, FALSE, FALSE,
5084 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5087 Yeater_nB, FALSE, TRUE,
5088 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5091 Yeater_e, FALSE, FALSE,
5092 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5095 Yeater_eB, FALSE, TRUE,
5096 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5099 Yeater_s, FALSE, FALSE,
5100 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5103 Yeater_sB, FALSE, TRUE,
5104 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5107 Yeater_w, FALSE, FALSE,
5108 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5111 Yeater_wB, FALSE, TRUE,
5112 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5115 Yeater_stone, FALSE, FALSE,
5116 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5119 Yeater_spring, FALSE, FALSE,
5120 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5123 Xalien, TRUE, FALSE,
5127 Xalien_pause, FALSE, FALSE,
5131 Yalien_n, FALSE, FALSE,
5132 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5135 Yalien_nB, FALSE, TRUE,
5136 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5139 Yalien_e, FALSE, FALSE,
5140 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5143 Yalien_eB, FALSE, TRUE,
5144 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5147 Yalien_s, FALSE, FALSE,
5148 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5151 Yalien_sB, FALSE, TRUE,
5152 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5155 Yalien_w, FALSE, FALSE,
5156 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5159 Yalien_wB, FALSE, TRUE,
5160 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5163 Yalien_stone, FALSE, FALSE,
5164 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5167 Yalien_spring, FALSE, FALSE,
5168 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5171 Xemerald, TRUE, FALSE,
5175 Xemerald_pause, FALSE, FALSE,
5179 Xemerald_fall, FALSE, FALSE,
5183 Xemerald_shine, FALSE, FALSE,
5184 EL_EMERALD, ACTION_TWINKLING, -1
5187 Yemerald_s, FALSE, FALSE,
5188 EL_EMERALD, ACTION_FALLING, -1
5191 Yemerald_sB, FALSE, TRUE,
5192 EL_EMERALD, ACTION_FALLING, -1
5195 Yemerald_e, FALSE, FALSE,
5196 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5199 Yemerald_eB, FALSE, TRUE,
5200 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5203 Yemerald_w, FALSE, FALSE,
5204 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5207 Yemerald_wB, FALSE, TRUE,
5208 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5211 Yemerald_eat, FALSE, FALSE,
5212 EL_EMERALD, ACTION_COLLECTING, -1
5215 Yemerald_stone, FALSE, FALSE,
5216 EL_NUT, ACTION_BREAKING, -1
5219 Xdiamond, TRUE, FALSE,
5223 Xdiamond_pause, FALSE, FALSE,
5227 Xdiamond_fall, FALSE, FALSE,
5231 Xdiamond_shine, FALSE, FALSE,
5232 EL_DIAMOND, ACTION_TWINKLING, -1
5235 Ydiamond_s, FALSE, FALSE,
5236 EL_DIAMOND, ACTION_FALLING, -1
5239 Ydiamond_sB, FALSE, TRUE,
5240 EL_DIAMOND, ACTION_FALLING, -1
5243 Ydiamond_e, FALSE, FALSE,
5244 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5247 Ydiamond_eB, FALSE, TRUE,
5248 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5251 Ydiamond_w, FALSE, FALSE,
5252 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5255 Ydiamond_wB, FALSE, TRUE,
5256 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5259 Ydiamond_eat, FALSE, FALSE,
5260 EL_DIAMOND, ACTION_COLLECTING, -1
5263 Ydiamond_stone, FALSE, FALSE,
5264 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5267 Xdrip_fall, TRUE, FALSE,
5268 EL_AMOEBA_DROP, -1, -1
5271 Xdrip_stretch, FALSE, FALSE,
5272 EL_AMOEBA_DROP, ACTION_FALLING, -1
5275 Xdrip_stretchB, FALSE, TRUE,
5276 EL_AMOEBA_DROP, ACTION_FALLING, -1
5279 Xdrip_eat, FALSE, FALSE,
5280 EL_AMOEBA_DROP, ACTION_GROWING, -1
5283 Ydrip_s1, FALSE, FALSE,
5284 EL_AMOEBA_DROP, ACTION_FALLING, -1
5287 Ydrip_s1B, FALSE, TRUE,
5288 EL_AMOEBA_DROP, ACTION_FALLING, -1
5291 Ydrip_s2, FALSE, FALSE,
5292 EL_AMOEBA_DROP, ACTION_FALLING, -1
5295 Ydrip_s2B, FALSE, TRUE,
5296 EL_AMOEBA_DROP, ACTION_FALLING, -1
5303 Xbomb_pause, FALSE, FALSE,
5307 Xbomb_fall, FALSE, FALSE,
5311 Ybomb_s, FALSE, FALSE,
5312 EL_BOMB, ACTION_FALLING, -1
5315 Ybomb_sB, FALSE, TRUE,
5316 EL_BOMB, ACTION_FALLING, -1
5319 Ybomb_e, FALSE, FALSE,
5320 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5323 Ybomb_eB, FALSE, TRUE,
5324 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5327 Ybomb_w, FALSE, FALSE,
5328 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5331 Ybomb_wB, FALSE, TRUE,
5332 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5335 Ybomb_eat, FALSE, FALSE,
5336 EL_BOMB, ACTION_ACTIVATING, -1
5339 Xballoon, TRUE, FALSE,
5343 Yballoon_n, FALSE, FALSE,
5344 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5347 Yballoon_nB, FALSE, TRUE,
5348 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5351 Yballoon_e, FALSE, FALSE,
5352 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5355 Yballoon_eB, FALSE, TRUE,
5356 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5359 Yballoon_s, FALSE, FALSE,
5360 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5363 Yballoon_sB, FALSE, TRUE,
5364 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5367 Yballoon_w, FALSE, FALSE,
5368 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5371 Yballoon_wB, FALSE, TRUE,
5372 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5375 Xgrass, TRUE, FALSE,
5376 EL_EMC_GRASS, -1, -1
5379 Ygrass_nB, FALSE, FALSE,
5380 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5383 Ygrass_eB, FALSE, FALSE,
5384 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5387 Ygrass_sB, FALSE, FALSE,
5388 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5391 Ygrass_wB, FALSE, FALSE,
5392 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5399 Ydirt_nB, FALSE, FALSE,
5400 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5403 Ydirt_eB, FALSE, FALSE,
5404 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5407 Ydirt_sB, FALSE, FALSE,
5408 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5411 Ydirt_wB, FALSE, FALSE,
5412 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5415 Xacid_ne, TRUE, FALSE,
5416 EL_ACID_POOL_TOPRIGHT, -1, -1
5419 Xacid_se, TRUE, FALSE,
5420 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5423 Xacid_s, TRUE, FALSE,
5424 EL_ACID_POOL_BOTTOM, -1, -1
5427 Xacid_sw, TRUE, FALSE,
5428 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5431 Xacid_nw, TRUE, FALSE,
5432 EL_ACID_POOL_TOPLEFT, -1, -1
5435 Xacid_1, TRUE, FALSE,
5439 Xacid_2, FALSE, FALSE,
5443 Xacid_3, FALSE, FALSE,
5447 Xacid_4, FALSE, FALSE,
5451 Xacid_5, FALSE, FALSE,
5455 Xacid_6, FALSE, FALSE,
5459 Xacid_7, FALSE, FALSE,
5463 Xacid_8, FALSE, FALSE,
5467 Xball_1, TRUE, FALSE,
5468 EL_EMC_MAGIC_BALL, -1, -1
5471 Xball_1B, FALSE, FALSE,
5472 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5475 Xball_2, FALSE, FALSE,
5476 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5479 Xball_2B, FALSE, FALSE,
5480 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5483 Yball_eat, FALSE, FALSE,
5484 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5487 Ykey_1_eat, FALSE, FALSE,
5488 EL_EM_KEY_1, ACTION_COLLECTING, -1
5491 Ykey_2_eat, FALSE, FALSE,
5492 EL_EM_KEY_2, ACTION_COLLECTING, -1
5495 Ykey_3_eat, FALSE, FALSE,
5496 EL_EM_KEY_3, ACTION_COLLECTING, -1
5499 Ykey_4_eat, FALSE, FALSE,
5500 EL_EM_KEY_4, ACTION_COLLECTING, -1
5503 Ykey_5_eat, FALSE, FALSE,
5504 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5507 Ykey_6_eat, FALSE, FALSE,
5508 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5511 Ykey_7_eat, FALSE, FALSE,
5512 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5515 Ykey_8_eat, FALSE, FALSE,
5516 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5519 Ylenses_eat, FALSE, FALSE,
5520 EL_EMC_LENSES, ACTION_COLLECTING, -1
5523 Ymagnify_eat, FALSE, FALSE,
5524 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5527 Ygrass_eat, FALSE, FALSE,
5528 EL_EMC_GRASS, ACTION_SNAPPING, -1
5531 Ydirt_eat, FALSE, FALSE,
5532 EL_SAND, ACTION_SNAPPING, -1
5535 Xgrow_ns, TRUE, FALSE,
5536 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5539 Ygrow_ns_eat, FALSE, FALSE,
5540 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5543 Xgrow_ew, TRUE, FALSE,
5544 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5547 Ygrow_ew_eat, FALSE, FALSE,
5548 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5551 Xwonderwall, TRUE, FALSE,
5552 EL_MAGIC_WALL, -1, -1
5555 XwonderwallB, FALSE, FALSE,
5556 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5559 Xamoeba_1, TRUE, FALSE,
5560 EL_AMOEBA_DRY, ACTION_OTHER, -1
5563 Xamoeba_2, FALSE, FALSE,
5564 EL_AMOEBA_DRY, ACTION_OTHER, -1
5567 Xamoeba_3, FALSE, FALSE,
5568 EL_AMOEBA_DRY, ACTION_OTHER, -1
5571 Xamoeba_4, FALSE, FALSE,
5572 EL_AMOEBA_DRY, ACTION_OTHER, -1
5575 Xamoeba_5, TRUE, FALSE,
5576 EL_AMOEBA_WET, ACTION_OTHER, -1
5579 Xamoeba_6, FALSE, FALSE,
5580 EL_AMOEBA_WET, ACTION_OTHER, -1
5583 Xamoeba_7, FALSE, FALSE,
5584 EL_AMOEBA_WET, ACTION_OTHER, -1
5587 Xamoeba_8, FALSE, FALSE,
5588 EL_AMOEBA_WET, ACTION_OTHER, -1
5591 Xdoor_1, TRUE, FALSE,
5592 EL_EM_GATE_1, -1, -1
5595 Xdoor_2, TRUE, FALSE,
5596 EL_EM_GATE_2, -1, -1
5599 Xdoor_3, TRUE, FALSE,
5600 EL_EM_GATE_3, -1, -1
5603 Xdoor_4, TRUE, FALSE,
5604 EL_EM_GATE_4, -1, -1
5607 Xdoor_5, TRUE, FALSE,
5608 EL_EMC_GATE_5, -1, -1
5611 Xdoor_6, TRUE, FALSE,
5612 EL_EMC_GATE_6, -1, -1
5615 Xdoor_7, TRUE, FALSE,
5616 EL_EMC_GATE_7, -1, -1
5619 Xdoor_8, TRUE, FALSE,
5620 EL_EMC_GATE_8, -1, -1
5623 Xkey_1, TRUE, FALSE,
5627 Xkey_2, TRUE, FALSE,
5631 Xkey_3, TRUE, FALSE,
5635 Xkey_4, TRUE, FALSE,
5639 Xkey_5, TRUE, FALSE,
5640 EL_EMC_KEY_5, -1, -1
5643 Xkey_6, TRUE, FALSE,
5644 EL_EMC_KEY_6, -1, -1
5647 Xkey_7, TRUE, FALSE,
5648 EL_EMC_KEY_7, -1, -1
5651 Xkey_8, TRUE, FALSE,
5652 EL_EMC_KEY_8, -1, -1
5655 Xwind_n, TRUE, FALSE,
5656 EL_BALLOON_SWITCH_UP, -1, -1
5659 Xwind_e, TRUE, FALSE,
5660 EL_BALLOON_SWITCH_RIGHT, -1, -1
5663 Xwind_s, TRUE, FALSE,
5664 EL_BALLOON_SWITCH_DOWN, -1, -1
5667 Xwind_w, TRUE, FALSE,
5668 EL_BALLOON_SWITCH_LEFT, -1, -1
5671 Xwind_nesw, TRUE, FALSE,
5672 EL_BALLOON_SWITCH_ANY, -1, -1
5675 Xwind_stop, TRUE, FALSE,
5676 EL_BALLOON_SWITCH_NONE, -1, -1
5680 EL_EM_EXIT_CLOSED, -1, -1
5683 Xexit_1, TRUE, FALSE,
5684 EL_EM_EXIT_OPEN, -1, -1
5687 Xexit_2, FALSE, FALSE,
5688 EL_EM_EXIT_OPEN, -1, -1
5691 Xexit_3, FALSE, FALSE,
5692 EL_EM_EXIT_OPEN, -1, -1
5695 Xdynamite, TRUE, FALSE,
5696 EL_EM_DYNAMITE, -1, -1
5699 Ydynamite_eat, FALSE, FALSE,
5700 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5703 Xdynamite_1, TRUE, FALSE,
5704 EL_EM_DYNAMITE_ACTIVE, -1, -1
5707 Xdynamite_2, FALSE, FALSE,
5708 EL_EM_DYNAMITE_ACTIVE, -1, -1
5711 Xdynamite_3, FALSE, FALSE,
5712 EL_EM_DYNAMITE_ACTIVE, -1, -1
5715 Xdynamite_4, FALSE, FALSE,
5716 EL_EM_DYNAMITE_ACTIVE, -1, -1
5719 Xbumper, TRUE, FALSE,
5720 EL_EMC_SPRING_BUMPER, -1, -1
5723 XbumperB, FALSE, FALSE,
5724 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5727 Xwheel, TRUE, FALSE,
5728 EL_ROBOT_WHEEL, -1, -1
5731 XwheelB, FALSE, FALSE,
5732 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5735 Xswitch, TRUE, FALSE,
5736 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5739 XswitchB, FALSE, FALSE,
5740 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5744 EL_QUICKSAND_EMPTY, -1, -1
5747 Xsand_stone, TRUE, FALSE,
5748 EL_QUICKSAND_FULL, -1, -1
5751 Xsand_stonein_1, FALSE, TRUE,
5752 EL_ROCK, ACTION_FILLING, -1
5755 Xsand_stonein_2, FALSE, TRUE,
5756 EL_ROCK, ACTION_FILLING, -1
5759 Xsand_stonein_3, FALSE, TRUE,
5760 EL_ROCK, ACTION_FILLING, -1
5763 Xsand_stonein_4, FALSE, TRUE,
5764 EL_ROCK, ACTION_FILLING, -1
5767 Xsand_stonesand_1, FALSE, FALSE,
5768 EL_QUICKSAND_EMPTYING, -1, -1
5771 Xsand_stonesand_2, FALSE, FALSE,
5772 EL_QUICKSAND_EMPTYING, -1, -1
5775 Xsand_stonesand_3, FALSE, FALSE,
5776 EL_QUICKSAND_EMPTYING, -1, -1
5779 Xsand_stonesand_4, FALSE, FALSE,
5780 EL_QUICKSAND_EMPTYING, -1, -1
5783 Xsand_stonesand_quickout_1, FALSE, FALSE,
5784 EL_QUICKSAND_EMPTYING, -1, -1
5787 Xsand_stonesand_quickout_2, FALSE, FALSE,
5788 EL_QUICKSAND_EMPTYING, -1, -1
5791 Xsand_stoneout_1, FALSE, FALSE,
5792 EL_ROCK, ACTION_EMPTYING, -1
5795 Xsand_stoneout_2, FALSE, FALSE,
5796 EL_ROCK, ACTION_EMPTYING, -1
5799 Xsand_sandstone_1, FALSE, FALSE,
5800 EL_QUICKSAND_FILLING, -1, -1
5803 Xsand_sandstone_2, FALSE, FALSE,
5804 EL_QUICKSAND_FILLING, -1, -1
5807 Xsand_sandstone_3, FALSE, FALSE,
5808 EL_QUICKSAND_FILLING, -1, -1
5811 Xsand_sandstone_4, FALSE, FALSE,
5812 EL_QUICKSAND_FILLING, -1, -1
5815 Xplant, TRUE, FALSE,
5816 EL_EMC_PLANT, -1, -1
5819 Yplant, FALSE, FALSE,
5820 EL_EMC_PLANT, -1, -1
5823 Xlenses, TRUE, FALSE,
5824 EL_EMC_LENSES, -1, -1
5827 Xmagnify, TRUE, FALSE,
5828 EL_EMC_MAGNIFIER, -1, -1
5831 Xdripper, TRUE, FALSE,
5832 EL_EMC_DRIPPER, -1, -1
5835 XdripperB, FALSE, FALSE,
5836 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5839 Xfake_blank, TRUE, FALSE,
5840 EL_INVISIBLE_WALL, -1, -1
5843 Xfake_blankB, FALSE, FALSE,
5844 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5847 Xfake_grass, TRUE, FALSE,
5848 EL_EMC_FAKE_GRASS, -1, -1
5851 Xfake_grassB, FALSE, FALSE,
5852 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5855 Xfake_door_1, TRUE, FALSE,
5856 EL_EM_GATE_1_GRAY, -1, -1
5859 Xfake_door_2, TRUE, FALSE,
5860 EL_EM_GATE_2_GRAY, -1, -1
5863 Xfake_door_3, TRUE, FALSE,
5864 EL_EM_GATE_3_GRAY, -1, -1
5867 Xfake_door_4, TRUE, FALSE,
5868 EL_EM_GATE_4_GRAY, -1, -1
5871 Xfake_door_5, TRUE, FALSE,
5872 EL_EMC_GATE_5_GRAY, -1, -1
5875 Xfake_door_6, TRUE, FALSE,
5876 EL_EMC_GATE_6_GRAY, -1, -1
5879 Xfake_door_7, TRUE, FALSE,
5880 EL_EMC_GATE_7_GRAY, -1, -1
5883 Xfake_door_8, TRUE, FALSE,
5884 EL_EMC_GATE_8_GRAY, -1, -1
5887 Xfake_acid_1, TRUE, FALSE,
5888 EL_EMC_FAKE_ACID, -1, -1
5891 Xfake_acid_2, FALSE, FALSE,
5892 EL_EMC_FAKE_ACID, -1, -1
5895 Xfake_acid_3, FALSE, FALSE,
5896 EL_EMC_FAKE_ACID, -1, -1
5899 Xfake_acid_4, FALSE, FALSE,
5900 EL_EMC_FAKE_ACID, -1, -1
5903 Xfake_acid_5, FALSE, FALSE,
5904 EL_EMC_FAKE_ACID, -1, -1
5907 Xfake_acid_6, FALSE, FALSE,
5908 EL_EMC_FAKE_ACID, -1, -1
5911 Xfake_acid_7, FALSE, FALSE,
5912 EL_EMC_FAKE_ACID, -1, -1
5915 Xfake_acid_8, FALSE, FALSE,
5916 EL_EMC_FAKE_ACID, -1, -1
5919 Xsteel_1, TRUE, FALSE,
5920 EL_STEELWALL, -1, -1
5923 Xsteel_2, TRUE, FALSE,
5924 EL_EMC_STEELWALL_2, -1, -1
5927 Xsteel_3, TRUE, FALSE,
5928 EL_EMC_STEELWALL_3, -1, -1
5931 Xsteel_4, TRUE, FALSE,
5932 EL_EMC_STEELWALL_4, -1, -1
5935 Xwall_1, TRUE, FALSE,
5939 Xwall_2, TRUE, FALSE,
5940 EL_EMC_WALL_14, -1, -1
5943 Xwall_3, TRUE, FALSE,
5944 EL_EMC_WALL_15, -1, -1
5947 Xwall_4, TRUE, FALSE,
5948 EL_EMC_WALL_16, -1, -1
5951 Xround_wall_1, TRUE, FALSE,
5952 EL_WALL_SLIPPERY, -1, -1
5955 Xround_wall_2, TRUE, FALSE,
5956 EL_EMC_WALL_SLIPPERY_2, -1, -1
5959 Xround_wall_3, TRUE, FALSE,
5960 EL_EMC_WALL_SLIPPERY_3, -1, -1
5963 Xround_wall_4, TRUE, FALSE,
5964 EL_EMC_WALL_SLIPPERY_4, -1, -1
5967 Xdecor_1, TRUE, FALSE,
5968 EL_EMC_WALL_8, -1, -1
5971 Xdecor_2, TRUE, FALSE,
5972 EL_EMC_WALL_6, -1, -1
5975 Xdecor_3, TRUE, FALSE,
5976 EL_EMC_WALL_4, -1, -1
5979 Xdecor_4, TRUE, FALSE,
5980 EL_EMC_WALL_7, -1, -1
5983 Xdecor_5, TRUE, FALSE,
5984 EL_EMC_WALL_5, -1, -1
5987 Xdecor_6, TRUE, FALSE,
5988 EL_EMC_WALL_9, -1, -1
5991 Xdecor_7, TRUE, FALSE,
5992 EL_EMC_WALL_10, -1, -1
5995 Xdecor_8, TRUE, FALSE,
5996 EL_EMC_WALL_1, -1, -1
5999 Xdecor_9, TRUE, FALSE,
6000 EL_EMC_WALL_2, -1, -1
6003 Xdecor_10, TRUE, FALSE,
6004 EL_EMC_WALL_3, -1, -1
6007 Xdecor_11, TRUE, FALSE,
6008 EL_EMC_WALL_11, -1, -1
6011 Xdecor_12, TRUE, FALSE,
6012 EL_EMC_WALL_12, -1, -1
6015 Xalpha_0, TRUE, FALSE,
6016 EL_CHAR('0'), -1, -1
6019 Xalpha_1, TRUE, FALSE,
6020 EL_CHAR('1'), -1, -1
6023 Xalpha_2, TRUE, FALSE,
6024 EL_CHAR('2'), -1, -1
6027 Xalpha_3, TRUE, FALSE,
6028 EL_CHAR('3'), -1, -1
6031 Xalpha_4, TRUE, FALSE,
6032 EL_CHAR('4'), -1, -1
6035 Xalpha_5, TRUE, FALSE,
6036 EL_CHAR('5'), -1, -1
6039 Xalpha_6, TRUE, FALSE,
6040 EL_CHAR('6'), -1, -1
6043 Xalpha_7, TRUE, FALSE,
6044 EL_CHAR('7'), -1, -1
6047 Xalpha_8, TRUE, FALSE,
6048 EL_CHAR('8'), -1, -1
6051 Xalpha_9, TRUE, FALSE,
6052 EL_CHAR('9'), -1, -1
6055 Xalpha_excla, TRUE, FALSE,
6056 EL_CHAR('!'), -1, -1
6059 Xalpha_quote, TRUE, FALSE,
6060 EL_CHAR('"'), -1, -1
6063 Xalpha_comma, TRUE, FALSE,
6064 EL_CHAR(','), -1, -1
6067 Xalpha_minus, TRUE, FALSE,
6068 EL_CHAR('-'), -1, -1
6071 Xalpha_perio, TRUE, FALSE,
6072 EL_CHAR('.'), -1, -1
6075 Xalpha_colon, TRUE, FALSE,
6076 EL_CHAR(':'), -1, -1
6079 Xalpha_quest, TRUE, FALSE,
6080 EL_CHAR('?'), -1, -1
6083 Xalpha_a, TRUE, FALSE,
6084 EL_CHAR('A'), -1, -1
6087 Xalpha_b, TRUE, FALSE,
6088 EL_CHAR('B'), -1, -1
6091 Xalpha_c, TRUE, FALSE,
6092 EL_CHAR('C'), -1, -1
6095 Xalpha_d, TRUE, FALSE,
6096 EL_CHAR('D'), -1, -1
6099 Xalpha_e, TRUE, FALSE,
6100 EL_CHAR('E'), -1, -1
6103 Xalpha_f, TRUE, FALSE,
6104 EL_CHAR('F'), -1, -1
6107 Xalpha_g, TRUE, FALSE,
6108 EL_CHAR('G'), -1, -1
6111 Xalpha_h, TRUE, FALSE,
6112 EL_CHAR('H'), -1, -1
6115 Xalpha_i, TRUE, FALSE,
6116 EL_CHAR('I'), -1, -1
6119 Xalpha_j, TRUE, FALSE,
6120 EL_CHAR('J'), -1, -1
6123 Xalpha_k, TRUE, FALSE,
6124 EL_CHAR('K'), -1, -1
6127 Xalpha_l, TRUE, FALSE,
6128 EL_CHAR('L'), -1, -1
6131 Xalpha_m, TRUE, FALSE,
6132 EL_CHAR('M'), -1, -1
6135 Xalpha_n, TRUE, FALSE,
6136 EL_CHAR('N'), -1, -1
6139 Xalpha_o, TRUE, FALSE,
6140 EL_CHAR('O'), -1, -1
6143 Xalpha_p, TRUE, FALSE,
6144 EL_CHAR('P'), -1, -1
6147 Xalpha_q, TRUE, FALSE,
6148 EL_CHAR('Q'), -1, -1
6151 Xalpha_r, TRUE, FALSE,
6152 EL_CHAR('R'), -1, -1
6155 Xalpha_s, TRUE, FALSE,
6156 EL_CHAR('S'), -1, -1
6159 Xalpha_t, TRUE, FALSE,
6160 EL_CHAR('T'), -1, -1
6163 Xalpha_u, TRUE, FALSE,
6164 EL_CHAR('U'), -1, -1
6167 Xalpha_v, TRUE, FALSE,
6168 EL_CHAR('V'), -1, -1
6171 Xalpha_w, TRUE, FALSE,
6172 EL_CHAR('W'), -1, -1
6175 Xalpha_x, TRUE, FALSE,
6176 EL_CHAR('X'), -1, -1
6179 Xalpha_y, TRUE, FALSE,
6180 EL_CHAR('Y'), -1, -1
6183 Xalpha_z, TRUE, FALSE,
6184 EL_CHAR('Z'), -1, -1
6187 Xalpha_arrow_e, TRUE, FALSE,
6188 EL_CHAR('>'), -1, -1
6191 Xalpha_arrow_w, TRUE, FALSE,
6192 EL_CHAR('<'), -1, -1
6195 Xalpha_copyr, TRUE, FALSE,
6196 EL_CHAR('©'), -1, -1
6200 Xboom_bug, FALSE, FALSE,
6201 EL_BUG, ACTION_EXPLODING, -1
6204 Xboom_bomb, FALSE, FALSE,
6205 EL_BOMB, ACTION_EXPLODING, -1
6208 Xboom_android, FALSE, FALSE,
6209 EL_EMC_ANDROID, ACTION_OTHER, -1
6212 Xboom_1, FALSE, FALSE,
6213 EL_DEFAULT, ACTION_EXPLODING, -1
6216 Xboom_2, FALSE, FALSE,
6217 EL_DEFAULT, ACTION_EXPLODING, -1
6220 Znormal, FALSE, FALSE,
6224 Zdynamite, FALSE, FALSE,
6228 Zplayer, FALSE, FALSE,
6232 ZBORDER, FALSE, FALSE,
6242 static struct Mapping_EM_to_RND_player
6251 em_player_mapping_list[] =
6255 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6259 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6263 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6267 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6271 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6275 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6279 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6283 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6287 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6291 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6295 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6299 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6303 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6307 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6311 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6315 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6319 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6323 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6327 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6331 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6335 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6339 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6343 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6347 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6351 EL_PLAYER_1, ACTION_DEFAULT, -1,
6355 EL_PLAYER_2, ACTION_DEFAULT, -1,
6359 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6363 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6367 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6371 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6375 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6379 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6383 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6387 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6391 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6395 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6399 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6403 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6407 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6411 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6415 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6419 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6423 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6427 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6431 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6435 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6439 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6443 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6447 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6451 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6455 EL_PLAYER_3, ACTION_DEFAULT, -1,
6459 EL_PLAYER_4, ACTION_DEFAULT, -1,
6468 int map_element_RND_to_EM(int element_rnd)
6470 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6471 static boolean mapping_initialized = FALSE;
6473 if (!mapping_initialized)
6477 /* return "Xalpha_quest" for all undefined elements in mapping array */
6478 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6479 mapping_RND_to_EM[i] = Xalpha_quest;
6481 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6482 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6483 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6484 em_object_mapping_list[i].element_em;
6486 mapping_initialized = TRUE;
6489 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6490 return mapping_RND_to_EM[element_rnd];
6492 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6497 int map_element_EM_to_RND(int element_em)
6499 static unsigned short mapping_EM_to_RND[TILE_MAX];
6500 static boolean mapping_initialized = FALSE;
6502 if (!mapping_initialized)
6506 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6507 for (i = 0; i < TILE_MAX; i++)
6508 mapping_EM_to_RND[i] = EL_UNKNOWN;
6510 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6511 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6512 em_object_mapping_list[i].element_rnd;
6514 mapping_initialized = TRUE;
6517 if (element_em >= 0 && element_em < TILE_MAX)
6518 return mapping_EM_to_RND[element_em];
6520 Error(ERR_WARN, "invalid EM level element %d", element_em);
6525 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6527 struct LevelInfo_EM *level_em = level->native_em_level;
6528 struct LEVEL *lev = level_em->lev;
6531 for (i = 0; i < TILE_MAX; i++)
6532 lev->android_array[i] = Xblank;
6534 for (i = 0; i < level->num_android_clone_elements; i++)
6536 int element_rnd = level->android_clone_element[i];
6537 int element_em = map_element_RND_to_EM(element_rnd);
6539 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6540 if (em_object_mapping_list[j].element_rnd == element_rnd)
6541 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6545 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6547 struct LevelInfo_EM *level_em = level->native_em_level;
6548 struct LEVEL *lev = level_em->lev;
6551 level->num_android_clone_elements = 0;
6553 for (i = 0; i < TILE_MAX; i++)
6555 int element_em = lev->android_array[i];
6557 boolean element_found = FALSE;
6559 if (element_em == Xblank)
6562 element_rnd = map_element_EM_to_RND(element_em);
6564 for (j = 0; j < level->num_android_clone_elements; j++)
6565 if (level->android_clone_element[j] == element_rnd)
6566 element_found = TRUE;
6570 level->android_clone_element[level->num_android_clone_elements++] =
6573 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6578 if (level->num_android_clone_elements == 0)
6580 level->num_android_clone_elements = 1;
6581 level->android_clone_element[0] = EL_EMPTY;
6585 int map_direction_RND_to_EM(int direction)
6587 return (direction == MV_UP ? 0 :
6588 direction == MV_RIGHT ? 1 :
6589 direction == MV_DOWN ? 2 :
6590 direction == MV_LEFT ? 3 :
6594 int map_direction_EM_to_RND(int direction)
6596 return (direction == 0 ? MV_UP :
6597 direction == 1 ? MV_RIGHT :
6598 direction == 2 ? MV_DOWN :
6599 direction == 3 ? MV_LEFT :
6603 int map_element_RND_to_SP(int element_rnd)
6605 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6607 if (element_rnd >= EL_SP_START &&
6608 element_rnd <= EL_SP_END)
6609 element_sp = element_rnd - EL_SP_START;
6610 else if (element_rnd == EL_EMPTY_SPACE)
6612 else if (element_rnd == EL_INVISIBLE_WALL)
6618 int map_element_SP_to_RND(int element_sp)
6620 int element_rnd = EL_UNKNOWN;
6622 if (element_sp >= 0x00 &&
6624 element_rnd = EL_SP_START + element_sp;
6625 else if (element_sp == 0x28)
6626 element_rnd = EL_INVISIBLE_WALL;
6631 int map_action_SP_to_RND(int action_sp)
6635 case actActive: return ACTION_ACTIVE;
6636 case actImpact: return ACTION_IMPACT;
6637 case actExploding: return ACTION_EXPLODING;
6638 case actDigging: return ACTION_DIGGING;
6639 case actSnapping: return ACTION_SNAPPING;
6640 case actCollecting: return ACTION_COLLECTING;
6641 case actPassing: return ACTION_PASSING;
6642 case actPushing: return ACTION_PUSHING;
6643 case actDropping: return ACTION_DROPPING;
6645 default: return ACTION_DEFAULT;
6649 int get_next_element(int element)
6653 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6654 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6655 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6656 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6657 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6658 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6659 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6660 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6661 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6662 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6663 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6665 default: return element;
6669 int el_act_dir2img(int element, int action, int direction)
6671 element = GFX_ELEMENT(element);
6672 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6674 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6675 return element_info[element].direction_graphic[action][direction];
6678 static int el_act_dir2crm(int element, int action, int direction)
6680 element = GFX_ELEMENT(element);
6681 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6683 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6684 return element_info[element].direction_crumbled[action][direction];
6687 int el_act2img(int element, int action)
6689 element = GFX_ELEMENT(element);
6691 return element_info[element].graphic[action];
6694 int el_act2crm(int element, int action)
6696 element = GFX_ELEMENT(element);
6698 return element_info[element].crumbled[action];
6701 int el_dir2img(int element, int direction)
6703 element = GFX_ELEMENT(element);
6705 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6708 int el2baseimg(int element)
6710 return element_info[element].graphic[ACTION_DEFAULT];
6713 int el2img(int element)
6715 element = GFX_ELEMENT(element);
6717 return element_info[element].graphic[ACTION_DEFAULT];
6720 int el2edimg(int element)
6722 element = GFX_ELEMENT(element);
6724 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6727 int el2preimg(int element)
6729 element = GFX_ELEMENT(element);
6731 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6734 int el2panelimg(int element)
6736 element = GFX_ELEMENT(element);
6738 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6741 int font2baseimg(int font_nr)
6743 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6746 int getBeltNrFromBeltElement(int element)
6748 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6749 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6750 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6753 int getBeltNrFromBeltActiveElement(int element)
6755 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6756 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6757 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6760 int getBeltNrFromBeltSwitchElement(int element)
6762 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6763 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6764 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6767 int getBeltDirNrFromBeltElement(int element)
6769 static int belt_base_element[4] =
6771 EL_CONVEYOR_BELT_1_LEFT,
6772 EL_CONVEYOR_BELT_2_LEFT,
6773 EL_CONVEYOR_BELT_3_LEFT,
6774 EL_CONVEYOR_BELT_4_LEFT
6777 int belt_nr = getBeltNrFromBeltElement(element);
6778 int belt_dir_nr = element - belt_base_element[belt_nr];
6780 return (belt_dir_nr % 3);
6783 int getBeltDirNrFromBeltSwitchElement(int element)
6785 static int belt_base_element[4] =
6787 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6788 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6789 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6790 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6793 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6794 int belt_dir_nr = element - belt_base_element[belt_nr];
6796 return (belt_dir_nr % 3);
6799 int getBeltDirFromBeltElement(int element)
6801 static int belt_move_dir[3] =
6808 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6810 return belt_move_dir[belt_dir_nr];
6813 int getBeltDirFromBeltSwitchElement(int element)
6815 static int belt_move_dir[3] =
6822 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6824 return belt_move_dir[belt_dir_nr];
6827 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6829 static int belt_base_element[4] =
6831 EL_CONVEYOR_BELT_1_LEFT,
6832 EL_CONVEYOR_BELT_2_LEFT,
6833 EL_CONVEYOR_BELT_3_LEFT,
6834 EL_CONVEYOR_BELT_4_LEFT
6837 return belt_base_element[belt_nr] + belt_dir_nr;
6840 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6842 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6844 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6847 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6849 static int belt_base_element[4] =
6851 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6852 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6853 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6854 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6857 return belt_base_element[belt_nr] + belt_dir_nr;
6860 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6862 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6864 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6867 boolean getTeamMode_EM()
6869 return game.team_mode;
6872 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6874 int game_frame_delay_value;
6876 game_frame_delay_value =
6877 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6878 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6881 if (tape.playing && tape.warp_forward && !tape.pausing)
6882 game_frame_delay_value = 0;
6884 return game_frame_delay_value;
6887 unsigned int InitRND(int seed)
6889 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6890 return InitEngineRandom_EM(seed);
6891 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6892 return InitEngineRandom_SP(seed);
6894 return InitEngineRandom_RND(seed);
6897 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6898 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6900 inline static int get_effective_element_EM(int tile, int frame_em)
6902 int element = object_mapping[tile].element_rnd;
6903 int action = object_mapping[tile].action;
6904 boolean is_backside = object_mapping[tile].is_backside;
6905 boolean action_removing = (action == ACTION_DIGGING ||
6906 action == ACTION_SNAPPING ||
6907 action == ACTION_COLLECTING);
6913 case Yacid_splash_eB:
6914 case Yacid_splash_wB:
6915 return (frame_em > 5 ? EL_EMPTY : element);
6921 else /* frame_em == 7 */
6925 case Yacid_splash_eB:
6926 case Yacid_splash_wB:
6929 case Yemerald_stone:
6932 case Ydiamond_stone:
6936 case Xdrip_stretchB:
6955 case Xsand_stonein_1:
6956 case Xsand_stonein_2:
6957 case Xsand_stonein_3:
6958 case Xsand_stonein_4:
6962 return (is_backside || action_removing ? EL_EMPTY : element);
6967 inline static boolean check_linear_animation_EM(int tile)
6971 case Xsand_stonesand_1:
6972 case Xsand_stonesand_quickout_1:
6973 case Xsand_sandstone_1:
6974 case Xsand_stonein_1:
6975 case Xsand_stoneout_1:
6994 case Yacid_splash_eB:
6995 case Yacid_splash_wB:
6996 case Yemerald_stone:
7003 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7004 boolean has_crumbled_graphics,
7005 int crumbled, int sync_frame)
7007 /* if element can be crumbled, but certain action graphics are just empty
7008 space (like instantly snapping sand to empty space in 1 frame), do not
7009 treat these empty space graphics as crumbled graphics in EMC engine */
7010 if (crumbled == IMG_EMPTY_SPACE)
7011 has_crumbled_graphics = FALSE;
7013 if (has_crumbled_graphics)
7015 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7016 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7017 g_crumbled->anim_delay,
7018 g_crumbled->anim_mode,
7019 g_crumbled->anim_start_frame,
7022 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7023 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7025 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7027 g_em->has_crumbled_graphics = TRUE;
7031 g_em->crumbled_bitmap = NULL;
7032 g_em->crumbled_src_x = 0;
7033 g_em->crumbled_src_y = 0;
7034 g_em->crumbled_border_size = 0;
7036 g_em->has_crumbled_graphics = FALSE;
7040 void ResetGfxAnimation_EM(int x, int y, int tile)
7045 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7046 int tile, int frame_em, int x, int y)
7048 int action = object_mapping[tile].action;
7049 int direction = object_mapping[tile].direction;
7050 int effective_element = get_effective_element_EM(tile, frame_em);
7051 int graphic = (direction == MV_NONE ?
7052 el_act2img(effective_element, action) :
7053 el_act_dir2img(effective_element, action, direction));
7054 struct GraphicInfo *g = &graphic_info[graphic];
7056 boolean action_removing = (action == ACTION_DIGGING ||
7057 action == ACTION_SNAPPING ||
7058 action == ACTION_COLLECTING);
7059 boolean action_moving = (action == ACTION_FALLING ||
7060 action == ACTION_MOVING ||
7061 action == ACTION_PUSHING ||
7062 action == ACTION_EATING ||
7063 action == ACTION_FILLING ||
7064 action == ACTION_EMPTYING);
7065 boolean action_falling = (action == ACTION_FALLING ||
7066 action == ACTION_FILLING ||
7067 action == ACTION_EMPTYING);
7069 /* special case: graphic uses "2nd movement tile" and has defined
7070 7 frames for movement animation (or less) => use default graphic
7071 for last (8th) frame which ends the movement animation */
7072 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7074 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7075 graphic = (direction == MV_NONE ?
7076 el_act2img(effective_element, action) :
7077 el_act_dir2img(effective_element, action, direction));
7079 g = &graphic_info[graphic];
7082 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7086 else if (action_moving)
7088 boolean is_backside = object_mapping[tile].is_backside;
7092 int direction = object_mapping[tile].direction;
7093 int move_dir = (action_falling ? MV_DOWN : direction);
7098 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7099 if (g->double_movement && frame_em == 0)
7103 if (move_dir == MV_LEFT)
7104 GfxFrame[x - 1][y] = GfxFrame[x][y];
7105 else if (move_dir == MV_RIGHT)
7106 GfxFrame[x + 1][y] = GfxFrame[x][y];
7107 else if (move_dir == MV_UP)
7108 GfxFrame[x][y - 1] = GfxFrame[x][y];
7109 else if (move_dir == MV_DOWN)
7110 GfxFrame[x][y + 1] = GfxFrame[x][y];
7117 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7118 if (tile == Xsand_stonesand_quickout_1 ||
7119 tile == Xsand_stonesand_quickout_2)
7123 if (graphic_info[graphic].anim_global_sync)
7124 sync_frame = FrameCounter;
7125 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7126 sync_frame = GfxFrame[x][y];
7128 sync_frame = 0; /* playfield border (pseudo steel) */
7130 SetRandomAnimationValue(x, y);
7132 int frame = getAnimationFrame(g->anim_frames,
7135 g->anim_start_frame,
7138 g_em->unique_identifier =
7139 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7142 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7143 int tile, int frame_em, int x, int y)
7145 int action = object_mapping[tile].action;
7146 int direction = object_mapping[tile].direction;
7147 boolean is_backside = object_mapping[tile].is_backside;
7148 int effective_element = get_effective_element_EM(tile, frame_em);
7149 int effective_action = action;
7150 int graphic = (direction == MV_NONE ?
7151 el_act2img(effective_element, effective_action) :
7152 el_act_dir2img(effective_element, effective_action,
7154 int crumbled = (direction == MV_NONE ?
7155 el_act2crm(effective_element, effective_action) :
7156 el_act_dir2crm(effective_element, effective_action,
7158 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7159 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7160 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7161 struct GraphicInfo *g = &graphic_info[graphic];
7164 /* special case: graphic uses "2nd movement tile" and has defined
7165 7 frames for movement animation (or less) => use default graphic
7166 for last (8th) frame which ends the movement animation */
7167 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7169 effective_action = ACTION_DEFAULT;
7170 graphic = (direction == MV_NONE ?
7171 el_act2img(effective_element, effective_action) :
7172 el_act_dir2img(effective_element, effective_action,
7174 crumbled = (direction == MV_NONE ?
7175 el_act2crm(effective_element, effective_action) :
7176 el_act_dir2crm(effective_element, effective_action,
7179 g = &graphic_info[graphic];
7182 if (graphic_info[graphic].anim_global_sync)
7183 sync_frame = FrameCounter;
7184 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7185 sync_frame = GfxFrame[x][y];
7187 sync_frame = 0; /* playfield border (pseudo steel) */
7189 SetRandomAnimationValue(x, y);
7191 int frame = getAnimationFrame(g->anim_frames,
7194 g->anim_start_frame,
7197 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7198 g->double_movement && is_backside);
7200 /* (updating the "crumbled" graphic definitions is probably not really needed,
7201 as animations for crumbled graphics can't be longer than one EMC cycle) */
7202 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7206 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7207 int player_nr, int anim, int frame_em)
7209 int element = player_mapping[player_nr][anim].element_rnd;
7210 int action = player_mapping[player_nr][anim].action;
7211 int direction = player_mapping[player_nr][anim].direction;
7212 int graphic = (direction == MV_NONE ?
7213 el_act2img(element, action) :
7214 el_act_dir2img(element, action, direction));
7215 struct GraphicInfo *g = &graphic_info[graphic];
7218 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7220 stored_player[player_nr].StepFrame = frame_em;
7222 sync_frame = stored_player[player_nr].Frame;
7224 int frame = getAnimationFrame(g->anim_frames,
7227 g->anim_start_frame,
7230 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7231 &g_em->src_x, &g_em->src_y, FALSE);
7234 void InitGraphicInfo_EM(void)
7239 int num_em_gfx_errors = 0;
7241 if (graphic_info_em_object[0][0].bitmap == NULL)
7243 /* EM graphics not yet initialized in em_open_all() */
7248 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7251 /* always start with reliable default values */
7252 for (i = 0; i < TILE_MAX; i++)
7254 object_mapping[i].element_rnd = EL_UNKNOWN;
7255 object_mapping[i].is_backside = FALSE;
7256 object_mapping[i].action = ACTION_DEFAULT;
7257 object_mapping[i].direction = MV_NONE;
7260 /* always start with reliable default values */
7261 for (p = 0; p < MAX_PLAYERS; p++)
7263 for (i = 0; i < SPR_MAX; i++)
7265 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7266 player_mapping[p][i].action = ACTION_DEFAULT;
7267 player_mapping[p][i].direction = MV_NONE;
7271 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7273 int e = em_object_mapping_list[i].element_em;
7275 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7276 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7278 if (em_object_mapping_list[i].action != -1)
7279 object_mapping[e].action = em_object_mapping_list[i].action;
7281 if (em_object_mapping_list[i].direction != -1)
7282 object_mapping[e].direction =
7283 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7286 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7288 int a = em_player_mapping_list[i].action_em;
7289 int p = em_player_mapping_list[i].player_nr;
7291 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7293 if (em_player_mapping_list[i].action != -1)
7294 player_mapping[p][a].action = em_player_mapping_list[i].action;
7296 if (em_player_mapping_list[i].direction != -1)
7297 player_mapping[p][a].direction =
7298 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7301 for (i = 0; i < TILE_MAX; i++)
7303 int element = object_mapping[i].element_rnd;
7304 int action = object_mapping[i].action;
7305 int direction = object_mapping[i].direction;
7306 boolean is_backside = object_mapping[i].is_backside;
7307 boolean action_exploding = ((action == ACTION_EXPLODING ||
7308 action == ACTION_SMASHED_BY_ROCK ||
7309 action == ACTION_SMASHED_BY_SPRING) &&
7310 element != EL_DIAMOND);
7311 boolean action_active = (action == ACTION_ACTIVE);
7312 boolean action_other = (action == ACTION_OTHER);
7314 for (j = 0; j < 8; j++)
7316 int effective_element = get_effective_element_EM(i, j);
7317 int effective_action = (j < 7 ? action :
7318 i == Xdrip_stretch ? action :
7319 i == Xdrip_stretchB ? action :
7320 i == Ydrip_s1 ? action :
7321 i == Ydrip_s1B ? action :
7322 i == Xball_1B ? action :
7323 i == Xball_2 ? action :
7324 i == Xball_2B ? action :
7325 i == Yball_eat ? action :
7326 i == Ykey_1_eat ? action :
7327 i == Ykey_2_eat ? action :
7328 i == Ykey_3_eat ? action :
7329 i == Ykey_4_eat ? action :
7330 i == Ykey_5_eat ? action :
7331 i == Ykey_6_eat ? action :
7332 i == Ykey_7_eat ? action :
7333 i == Ykey_8_eat ? action :
7334 i == Ylenses_eat ? action :
7335 i == Ymagnify_eat ? action :
7336 i == Ygrass_eat ? action :
7337 i == Ydirt_eat ? action :
7338 i == Xsand_stonein_1 ? action :
7339 i == Xsand_stonein_2 ? action :
7340 i == Xsand_stonein_3 ? action :
7341 i == Xsand_stonein_4 ? action :
7342 i == Xsand_stoneout_1 ? action :
7343 i == Xsand_stoneout_2 ? action :
7344 i == Xboom_android ? ACTION_EXPLODING :
7345 action_exploding ? ACTION_EXPLODING :
7346 action_active ? action :
7347 action_other ? action :
7349 int graphic = (el_act_dir2img(effective_element, effective_action,
7351 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7353 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7354 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7355 boolean has_action_graphics = (graphic != base_graphic);
7356 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7357 struct GraphicInfo *g = &graphic_info[graphic];
7358 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7361 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7362 boolean special_animation = (action != ACTION_DEFAULT &&
7363 g->anim_frames == 3 &&
7364 g->anim_delay == 2 &&
7365 g->anim_mode & ANIM_LINEAR);
7366 int sync_frame = (i == Xdrip_stretch ? 7 :
7367 i == Xdrip_stretchB ? 7 :
7368 i == Ydrip_s2 ? j + 8 :
7369 i == Ydrip_s2B ? j + 8 :
7378 i == Xfake_acid_1 ? 0 :
7379 i == Xfake_acid_2 ? 10 :
7380 i == Xfake_acid_3 ? 20 :
7381 i == Xfake_acid_4 ? 30 :
7382 i == Xfake_acid_5 ? 40 :
7383 i == Xfake_acid_6 ? 50 :
7384 i == Xfake_acid_7 ? 60 :
7385 i == Xfake_acid_8 ? 70 :
7387 i == Xball_2B ? j + 8 :
7388 i == Yball_eat ? j + 1 :
7389 i == Ykey_1_eat ? j + 1 :
7390 i == Ykey_2_eat ? j + 1 :
7391 i == Ykey_3_eat ? j + 1 :
7392 i == Ykey_4_eat ? j + 1 :
7393 i == Ykey_5_eat ? j + 1 :
7394 i == Ykey_6_eat ? j + 1 :
7395 i == Ykey_7_eat ? j + 1 :
7396 i == Ykey_8_eat ? j + 1 :
7397 i == Ylenses_eat ? j + 1 :
7398 i == Ymagnify_eat ? j + 1 :
7399 i == Ygrass_eat ? j + 1 :
7400 i == Ydirt_eat ? j + 1 :
7401 i == Xamoeba_1 ? 0 :
7402 i == Xamoeba_2 ? 1 :
7403 i == Xamoeba_3 ? 2 :
7404 i == Xamoeba_4 ? 3 :
7405 i == Xamoeba_5 ? 0 :
7406 i == Xamoeba_6 ? 1 :
7407 i == Xamoeba_7 ? 2 :
7408 i == Xamoeba_8 ? 3 :
7409 i == Xexit_2 ? j + 8 :
7410 i == Xexit_3 ? j + 16 :
7411 i == Xdynamite_1 ? 0 :
7412 i == Xdynamite_2 ? 8 :
7413 i == Xdynamite_3 ? 16 :
7414 i == Xdynamite_4 ? 24 :
7415 i == Xsand_stonein_1 ? j + 1 :
7416 i == Xsand_stonein_2 ? j + 9 :
7417 i == Xsand_stonein_3 ? j + 17 :
7418 i == Xsand_stonein_4 ? j + 25 :
7419 i == Xsand_stoneout_1 && j == 0 ? 0 :
7420 i == Xsand_stoneout_1 && j == 1 ? 0 :
7421 i == Xsand_stoneout_1 && j == 2 ? 1 :
7422 i == Xsand_stoneout_1 && j == 3 ? 2 :
7423 i == Xsand_stoneout_1 && j == 4 ? 2 :
7424 i == Xsand_stoneout_1 && j == 5 ? 3 :
7425 i == Xsand_stoneout_1 && j == 6 ? 4 :
7426 i == Xsand_stoneout_1 && j == 7 ? 4 :
7427 i == Xsand_stoneout_2 && j == 0 ? 5 :
7428 i == Xsand_stoneout_2 && j == 1 ? 6 :
7429 i == Xsand_stoneout_2 && j == 2 ? 7 :
7430 i == Xsand_stoneout_2 && j == 3 ? 8 :
7431 i == Xsand_stoneout_2 && j == 4 ? 9 :
7432 i == Xsand_stoneout_2 && j == 5 ? 11 :
7433 i == Xsand_stoneout_2 && j == 6 ? 13 :
7434 i == Xsand_stoneout_2 && j == 7 ? 15 :
7435 i == Xboom_bug && j == 1 ? 2 :
7436 i == Xboom_bug && j == 2 ? 2 :
7437 i == Xboom_bug && j == 3 ? 4 :
7438 i == Xboom_bug && j == 4 ? 4 :
7439 i == Xboom_bug && j == 5 ? 2 :
7440 i == Xboom_bug && j == 6 ? 2 :
7441 i == Xboom_bug && j == 7 ? 0 :
7442 i == Xboom_bomb && j == 1 ? 2 :
7443 i == Xboom_bomb && j == 2 ? 2 :
7444 i == Xboom_bomb && j == 3 ? 4 :
7445 i == Xboom_bomb && j == 4 ? 4 :
7446 i == Xboom_bomb && j == 5 ? 2 :
7447 i == Xboom_bomb && j == 6 ? 2 :
7448 i == Xboom_bomb && j == 7 ? 0 :
7449 i == Xboom_android && j == 7 ? 6 :
7450 i == Xboom_1 && j == 1 ? 2 :
7451 i == Xboom_1 && j == 2 ? 2 :
7452 i == Xboom_1 && j == 3 ? 4 :
7453 i == Xboom_1 && j == 4 ? 4 :
7454 i == Xboom_1 && j == 5 ? 6 :
7455 i == Xboom_1 && j == 6 ? 6 :
7456 i == Xboom_1 && j == 7 ? 8 :
7457 i == Xboom_2 && j == 0 ? 8 :
7458 i == Xboom_2 && j == 1 ? 8 :
7459 i == Xboom_2 && j == 2 ? 10 :
7460 i == Xboom_2 && j == 3 ? 10 :
7461 i == Xboom_2 && j == 4 ? 10 :
7462 i == Xboom_2 && j == 5 ? 12 :
7463 i == Xboom_2 && j == 6 ? 12 :
7464 i == Xboom_2 && j == 7 ? 12 :
7465 special_animation && j == 4 ? 3 :
7466 effective_action != action ? 0 :
7470 Bitmap *debug_bitmap = g_em->bitmap;
7471 int debug_src_x = g_em->src_x;
7472 int debug_src_y = g_em->src_y;
7475 int frame = getAnimationFrame(g->anim_frames,
7478 g->anim_start_frame,
7481 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7482 g->double_movement && is_backside);
7484 g_em->bitmap = src_bitmap;
7485 g_em->src_x = src_x;
7486 g_em->src_y = src_y;
7487 g_em->src_offset_x = 0;
7488 g_em->src_offset_y = 0;
7489 g_em->dst_offset_x = 0;
7490 g_em->dst_offset_y = 0;
7491 g_em->width = TILEX;
7492 g_em->height = TILEY;
7494 g_em->preserve_background = FALSE;
7496 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7499 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7500 effective_action == ACTION_MOVING ||
7501 effective_action == ACTION_PUSHING ||
7502 effective_action == ACTION_EATING)) ||
7503 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7504 effective_action == ACTION_EMPTYING)))
7507 (effective_action == ACTION_FALLING ||
7508 effective_action == ACTION_FILLING ||
7509 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7510 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7511 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7512 int num_steps = (i == Ydrip_s1 ? 16 :
7513 i == Ydrip_s1B ? 16 :
7514 i == Ydrip_s2 ? 16 :
7515 i == Ydrip_s2B ? 16 :
7516 i == Xsand_stonein_1 ? 32 :
7517 i == Xsand_stonein_2 ? 32 :
7518 i == Xsand_stonein_3 ? 32 :
7519 i == Xsand_stonein_4 ? 32 :
7520 i == Xsand_stoneout_1 ? 16 :
7521 i == Xsand_stoneout_2 ? 16 : 8);
7522 int cx = ABS(dx) * (TILEX / num_steps);
7523 int cy = ABS(dy) * (TILEY / num_steps);
7524 int step_frame = (i == Ydrip_s2 ? j + 8 :
7525 i == Ydrip_s2B ? j + 8 :
7526 i == Xsand_stonein_2 ? j + 8 :
7527 i == Xsand_stonein_3 ? j + 16 :
7528 i == Xsand_stonein_4 ? j + 24 :
7529 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7530 int step = (is_backside ? step_frame : num_steps - step_frame);
7532 if (is_backside) /* tile where movement starts */
7534 if (dx < 0 || dy < 0)
7536 g_em->src_offset_x = cx * step;
7537 g_em->src_offset_y = cy * step;
7541 g_em->dst_offset_x = cx * step;
7542 g_em->dst_offset_y = cy * step;
7545 else /* tile where movement ends */
7547 if (dx < 0 || dy < 0)
7549 g_em->dst_offset_x = cx * step;
7550 g_em->dst_offset_y = cy * step;
7554 g_em->src_offset_x = cx * step;
7555 g_em->src_offset_y = cy * step;
7559 g_em->width = TILEX - cx * step;
7560 g_em->height = TILEY - cy * step;
7563 /* create unique graphic identifier to decide if tile must be redrawn */
7564 /* bit 31 - 16 (16 bit): EM style graphic
7565 bit 15 - 12 ( 4 bit): EM style frame
7566 bit 11 - 6 ( 6 bit): graphic width
7567 bit 5 - 0 ( 6 bit): graphic height */
7568 g_em->unique_identifier =
7569 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7573 /* skip check for EMC elements not contained in original EMC artwork */
7574 if (element == EL_EMC_FAKE_ACID)
7577 if (g_em->bitmap != debug_bitmap ||
7578 g_em->src_x != debug_src_x ||
7579 g_em->src_y != debug_src_y ||
7580 g_em->src_offset_x != 0 ||
7581 g_em->src_offset_y != 0 ||
7582 g_em->dst_offset_x != 0 ||
7583 g_em->dst_offset_y != 0 ||
7584 g_em->width != TILEX ||
7585 g_em->height != TILEY)
7587 static int last_i = -1;
7595 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7596 i, element, element_info[element].token_name,
7597 element_action_info[effective_action].suffix, direction);
7599 if (element != effective_element)
7600 printf(" [%d ('%s')]",
7602 element_info[effective_element].token_name);
7606 if (g_em->bitmap != debug_bitmap)
7607 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7608 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7610 if (g_em->src_x != debug_src_x ||
7611 g_em->src_y != debug_src_y)
7612 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7613 j, (is_backside ? 'B' : 'F'),
7614 g_em->src_x, g_em->src_y,
7615 g_em->src_x / 32, g_em->src_y / 32,
7616 debug_src_x, debug_src_y,
7617 debug_src_x / 32, debug_src_y / 32);
7619 if (g_em->src_offset_x != 0 ||
7620 g_em->src_offset_y != 0 ||
7621 g_em->dst_offset_x != 0 ||
7622 g_em->dst_offset_y != 0)
7623 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7625 g_em->src_offset_x, g_em->src_offset_y,
7626 g_em->dst_offset_x, g_em->dst_offset_y);
7628 if (g_em->width != TILEX ||
7629 g_em->height != TILEY)
7630 printf(" %d (%d): size %d,%d should be %d,%d\n",
7632 g_em->width, g_em->height, TILEX, TILEY);
7634 num_em_gfx_errors++;
7641 for (i = 0; i < TILE_MAX; i++)
7643 for (j = 0; j < 8; j++)
7645 int element = object_mapping[i].element_rnd;
7646 int action = object_mapping[i].action;
7647 int direction = object_mapping[i].direction;
7648 boolean is_backside = object_mapping[i].is_backside;
7649 int graphic_action = el_act_dir2img(element, action, direction);
7650 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7652 if ((action == ACTION_SMASHED_BY_ROCK ||
7653 action == ACTION_SMASHED_BY_SPRING ||
7654 action == ACTION_EATING) &&
7655 graphic_action == graphic_default)
7657 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7658 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7659 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7660 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7663 /* no separate animation for "smashed by rock" -- use rock instead */
7664 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7665 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7667 g_em->bitmap = g_xx->bitmap;
7668 g_em->src_x = g_xx->src_x;
7669 g_em->src_y = g_xx->src_y;
7670 g_em->src_offset_x = g_xx->src_offset_x;
7671 g_em->src_offset_y = g_xx->src_offset_y;
7672 g_em->dst_offset_x = g_xx->dst_offset_x;
7673 g_em->dst_offset_y = g_xx->dst_offset_y;
7674 g_em->width = g_xx->width;
7675 g_em->height = g_xx->height;
7676 g_em->unique_identifier = g_xx->unique_identifier;
7679 g_em->preserve_background = TRUE;
7684 for (p = 0; p < MAX_PLAYERS; p++)
7686 for (i = 0; i < SPR_MAX; i++)
7688 int element = player_mapping[p][i].element_rnd;
7689 int action = player_mapping[p][i].action;
7690 int direction = player_mapping[p][i].direction;
7692 for (j = 0; j < 8; j++)
7694 int effective_element = element;
7695 int effective_action = action;
7696 int graphic = (direction == MV_NONE ?
7697 el_act2img(effective_element, effective_action) :
7698 el_act_dir2img(effective_element, effective_action,
7700 struct GraphicInfo *g = &graphic_info[graphic];
7701 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7707 Bitmap *debug_bitmap = g_em->bitmap;
7708 int debug_src_x = g_em->src_x;
7709 int debug_src_y = g_em->src_y;
7712 int frame = getAnimationFrame(g->anim_frames,
7715 g->anim_start_frame,
7718 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7720 g_em->bitmap = src_bitmap;
7721 g_em->src_x = src_x;
7722 g_em->src_y = src_y;
7723 g_em->src_offset_x = 0;
7724 g_em->src_offset_y = 0;
7725 g_em->dst_offset_x = 0;
7726 g_em->dst_offset_y = 0;
7727 g_em->width = TILEX;
7728 g_em->height = TILEY;
7732 /* skip check for EMC elements not contained in original EMC artwork */
7733 if (element == EL_PLAYER_3 ||
7734 element == EL_PLAYER_4)
7737 if (g_em->bitmap != debug_bitmap ||
7738 g_em->src_x != debug_src_x ||
7739 g_em->src_y != debug_src_y)
7741 static int last_i = -1;
7749 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7750 p, i, element, element_info[element].token_name,
7751 element_action_info[effective_action].suffix, direction);
7753 if (element != effective_element)
7754 printf(" [%d ('%s')]",
7756 element_info[effective_element].token_name);
7760 if (g_em->bitmap != debug_bitmap)
7761 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7762 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7764 if (g_em->src_x != debug_src_x ||
7765 g_em->src_y != debug_src_y)
7766 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7768 g_em->src_x, g_em->src_y,
7769 g_em->src_x / 32, g_em->src_y / 32,
7770 debug_src_x, debug_src_y,
7771 debug_src_x / 32, debug_src_y / 32);
7773 num_em_gfx_errors++;
7783 printf("::: [%d errors found]\n", num_em_gfx_errors);
7789 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7790 boolean any_player_moving,
7791 boolean player_is_dropping)
7793 if (tape.single_step && tape.recording && !tape.pausing)
7794 if (frame == 0 && !player_is_dropping)
7795 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7798 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7799 boolean murphy_is_dropping)
7801 if (tape.single_step && tape.recording && !tape.pausing)
7802 if (murphy_is_waiting)
7803 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7806 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7807 int graphic, int sync_frame, int x, int y)
7809 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7811 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7814 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7816 return (IS_NEXT_FRAME(sync_frame, graphic));
7819 int getGraphicInfo_Delay(int graphic)
7821 return graphic_info[graphic].anim_delay;
7824 void PlayMenuSoundExt(int sound)
7826 if (sound == SND_UNDEFINED)
7829 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7830 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7833 if (IS_LOOP_SOUND(sound))
7834 PlaySoundLoop(sound);
7839 void PlayMenuSound()
7841 PlayMenuSoundExt(menu.sound[game_status]);
7844 void PlayMenuSoundStereo(int sound, int stereo_position)
7846 if (sound == SND_UNDEFINED)
7849 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7850 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7853 if (IS_LOOP_SOUND(sound))
7854 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7856 PlaySoundStereo(sound, stereo_position);
7859 void PlayMenuSoundIfLoopExt(int sound)
7861 if (sound == SND_UNDEFINED)
7864 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7865 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7868 if (IS_LOOP_SOUND(sound))
7869 PlaySoundLoop(sound);
7872 void PlayMenuSoundIfLoop()
7874 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7877 void PlayMenuMusicExt(int music)
7879 if (music == MUS_UNDEFINED)
7882 if (!setup.sound_music)
7888 void PlayMenuMusic()
7890 PlayMenuMusicExt(menu.music[game_status]);
7893 void PlaySoundActivating()
7896 PlaySound(SND_MENU_ITEM_ACTIVATING);
7900 void PlaySoundSelecting()
7903 PlaySound(SND_MENU_ITEM_SELECTING);
7907 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7909 boolean change_fullscreen = (setup.fullscreen !=
7910 video.fullscreen_enabled);
7911 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7912 !strEqual(setup.fullscreen_mode,
7913 video.fullscreen_mode_current));
7914 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7915 setup.window_scaling_percent !=
7916 video.window_scaling_percent);
7918 if (change_window_scaling_percent && video.fullscreen_enabled)
7921 if (!change_window_scaling_percent && !video.fullscreen_available)
7924 #if defined(TARGET_SDL2)
7925 if (change_window_scaling_percent)
7927 SDLSetWindowScaling(setup.window_scaling_percent);
7931 else if (change_fullscreen)
7933 SDLSetWindowFullscreen(setup.fullscreen);
7935 /* set setup value according to successfully changed fullscreen mode */
7936 setup.fullscreen = video.fullscreen_enabled;
7942 if (change_fullscreen ||
7943 change_fullscreen_mode ||
7944 change_window_scaling_percent)
7946 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
7948 /* save backbuffer content which gets lost when toggling fullscreen mode */
7949 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7951 if (change_fullscreen_mode)
7953 /* keep fullscreen, but change fullscreen mode (screen resolution) */
7954 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
7957 if (change_window_scaling_percent)
7959 /* keep window mode, but change window scaling */
7960 video.fullscreen_enabled = TRUE; /* force new window scaling */
7963 /* toggle fullscreen */
7964 ChangeVideoModeIfNeeded(setup.fullscreen);
7966 /* set setup value according to successfully changed fullscreen mode */
7967 setup.fullscreen = video.fullscreen_enabled;
7969 /* restore backbuffer content from temporary backbuffer backup bitmap */
7970 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7972 FreeBitmap(tmp_backbuffer);
7974 /* update visible window/screen */
7975 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7979 void ChangeViewportPropertiesIfNeeded()
7981 int gfx_game_mode = game_status;
7982 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
7984 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
7985 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
7986 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
7987 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
7988 int border_size = vp_playfield->border_size;
7989 int new_sx = vp_playfield->x + border_size;
7990 int new_sy = vp_playfield->y + border_size;
7991 int new_sxsize = vp_playfield->width - 2 * border_size;
7992 int new_sysize = vp_playfield->height - 2 * border_size;
7993 int new_real_sx = vp_playfield->x;
7994 int new_real_sy = vp_playfield->y;
7995 int new_full_sxsize = vp_playfield->width;
7996 int new_full_sysize = vp_playfield->height;
7997 int new_dx = vp_door_1->x;
7998 int new_dy = vp_door_1->y;
7999 int new_dxsize = vp_door_1->width;
8000 int new_dysize = vp_door_1->height;
8001 int new_vx = vp_door_2->x;
8002 int new_vy = vp_door_2->y;
8003 int new_vxsize = vp_door_2->width;
8004 int new_vysize = vp_door_2->height;
8005 int new_ex = vp_door_3->x;
8006 int new_ey = vp_door_3->y;
8007 int new_exsize = vp_door_3->width;
8008 int new_eysize = vp_door_3->height;
8009 int new_tilesize_var =
8010 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8012 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8013 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8014 int new_scr_fieldx = new_sxsize / tilesize;
8015 int new_scr_fieldy = new_sysize / tilesize;
8016 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8017 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8018 boolean init_gfx_buffers = FALSE;
8019 boolean init_video_buffer = FALSE;
8020 boolean init_gadgets_and_toons = FALSE;
8021 boolean init_em_graphics = FALSE;
8023 if (viewport.window.width != WIN_XSIZE ||
8024 viewport.window.height != WIN_YSIZE)
8026 WIN_XSIZE = viewport.window.width;
8027 WIN_YSIZE = viewport.window.height;
8029 init_video_buffer = TRUE;
8030 init_gfx_buffers = TRUE;
8032 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8035 if (new_scr_fieldx != SCR_FIELDX ||
8036 new_scr_fieldy != SCR_FIELDY)
8038 /* this always toggles between MAIN and GAME when using small tile size */
8040 SCR_FIELDX = new_scr_fieldx;
8041 SCR_FIELDY = new_scr_fieldy;
8043 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8054 new_sxsize != SXSIZE ||
8055 new_sysize != SYSIZE ||
8056 new_dxsize != DXSIZE ||
8057 new_dysize != DYSIZE ||
8058 new_vxsize != VXSIZE ||
8059 new_vysize != VYSIZE ||
8060 new_exsize != EXSIZE ||
8061 new_eysize != EYSIZE ||
8062 new_real_sx != REAL_SX ||
8063 new_real_sy != REAL_SY ||
8064 new_full_sxsize != FULL_SXSIZE ||
8065 new_full_sysize != FULL_SYSIZE ||
8066 new_tilesize_var != TILESIZE_VAR
8069 if (new_tilesize_var != TILESIZE_VAR)
8071 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8073 // changing tile size invalidates scroll values of engine snapshots
8074 FreeEngineSnapshot();
8076 // changing tile size requires update of graphic mapping for EM engine
8077 init_em_graphics = TRUE;
8088 SXSIZE = new_sxsize;
8089 SYSIZE = new_sysize;
8090 DXSIZE = new_dxsize;
8091 DYSIZE = new_dysize;
8092 VXSIZE = new_vxsize;
8093 VYSIZE = new_vysize;
8094 EXSIZE = new_exsize;
8095 EYSIZE = new_eysize;
8096 REAL_SX = new_real_sx;
8097 REAL_SY = new_real_sy;
8098 FULL_SXSIZE = new_full_sxsize;
8099 FULL_SYSIZE = new_full_sysize;
8100 TILESIZE_VAR = new_tilesize_var;
8102 init_gfx_buffers = TRUE;
8103 init_gadgets_and_toons = TRUE;
8105 // printf("::: viewports: init_gfx_buffers\n");
8106 // printf("::: viewports: init_gadgets_and_toons\n");
8109 if (init_gfx_buffers)
8111 // printf("::: init_gfx_buffers\n");
8113 SCR_FIELDX = new_scr_fieldx_buffers;
8114 SCR_FIELDY = new_scr_fieldy_buffers;
8118 SCR_FIELDX = new_scr_fieldx;
8119 SCR_FIELDY = new_scr_fieldy;
8122 if (init_video_buffer)
8124 // printf("::: init_video_buffer\n");
8126 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8128 SetDrawDeactivationMask(REDRAW_NONE);
8129 SetDrawBackgroundMask(REDRAW_FIELD);
8132 if (init_gadgets_and_toons)
8134 // printf("::: init_gadgets_and_toons\n");
8140 if (init_em_graphics)
8142 InitGraphicInfo_EM();