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 if (setup.quick_doors)
2391 if (action == ACTION_OPENING)
2392 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2393 else if (action == ACTION_CLOSING)
2394 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2397 for (x = xstart, y = ystart; x <= xend && y <= yend; x += xstep, y += ystep)
2399 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2400 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2401 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2402 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2403 int src_x = sx_center - width / 2;
2404 int src_y = sy_center - height / 2;
2405 int dst_x = sx_center - xsize * tile_size / 2;
2406 int dst_y = sy_center - ysize * tile_size / 2;
2407 int xsize_size_left = (xsize - 1) * tile_size;
2408 int ysize_size_top = (ysize - 1) * tile_size;
2409 int max_xsize_pos = (max_xsize - 1) * tile_size;
2410 int max_ysize_pos = (max_ysize - 1) * tile_size;
2413 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2415 for (yy = 0; yy < 2; yy++)
2417 for (xx = 0; xx < 2; xx++)
2419 int src_xx = src_x + xx * max_xsize_pos;
2420 int src_yy = src_y + yy * max_ysize_pos;
2421 int dst_xx = dst_x + xx * xsize_size_left;
2422 int dst_yy = dst_y + yy * ysize_size_top;
2423 int xx_size = (xx ? tile_size : xsize_size_left);
2424 int yy_size = (yy ? tile_size : ysize_size_top);
2427 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2428 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2430 BlitBitmap(bitmap_db_cross, backbuffer,
2431 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2435 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2440 WaitUntilDelayReached(&anim_delay, anim_delay_value / 2);
2445 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2447 int last_game_status = game_status; /* save current game status */
2448 int graphic = IMG_BACKGROUND_REQUEST;
2449 int sound_opening = SND_REQUEST_OPENING;
2450 int sound_closing = SND_REQUEST_CLOSING;
2451 int anim_mode = graphic_info[graphic].anim_mode;
2452 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2453 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2455 if (game_status == GAME_MODE_PLAYING)
2456 BlitScreenToBitmap(backbuffer);
2458 SetDrawtoField(DRAW_BACKBUFFER);
2460 // SetDrawBackgroundMask(REDRAW_NONE);
2462 if (action == ACTION_OPENING)
2464 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2466 if (req_state & REQ_ASK)
2468 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2469 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2471 else if (req_state & REQ_CONFIRM)
2473 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2475 else if (req_state & REQ_PLAYER)
2477 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2478 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2479 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2480 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2483 DrawEnvelopeRequest(text);
2485 if (game_status != GAME_MODE_MAIN)
2489 /* force DOOR font inside door area */
2490 game_status = GAME_MODE_PSEUDO_DOOR;
2492 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2494 if (action == ACTION_OPENING)
2496 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2498 if (anim_mode == ANIM_DEFAULT)
2499 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2501 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2506 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2508 if (anim_mode != ANIM_NONE)
2509 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2511 if (anim_mode == ANIM_DEFAULT)
2512 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2515 game.envelope_active = FALSE;
2517 game_status = last_game_status; /* restore current game status */
2519 if (action == ACTION_CLOSING)
2521 if (game_status != GAME_MODE_MAIN)
2524 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2527 // SetDrawBackgroundMask(last_draw_background_mask);
2529 redraw_mask |= REDRAW_FIELD;
2531 if (game_status == GAME_MODE_MAIN)
2536 if (action == ACTION_CLOSING &&
2537 game_status == GAME_MODE_PLAYING &&
2538 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2539 SetDrawtoField(DRAW_BUFFERED);
2542 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2546 int graphic = el2preimg(element);
2548 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2549 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2552 void DrawLevel(int draw_background_mask)
2556 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2557 SetDrawBackgroundMask(draw_background_mask);
2561 for (x = BX1; x <= BX2; x++)
2562 for (y = BY1; y <= BY2; y++)
2563 DrawScreenField(x, y);
2565 redraw_mask |= REDRAW_FIELD;
2568 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2572 for (x = 0; x < size_x; x++)
2573 for (y = 0; y < size_y; y++)
2574 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2576 redraw_mask |= REDRAW_FIELD;
2579 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2581 boolean show_level_border = (BorderElement != EL_EMPTY);
2582 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2583 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2584 int tile_size = preview.tile_size;
2585 int preview_width = preview.xsize * tile_size;
2586 int preview_height = preview.ysize * tile_size;
2587 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2588 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2589 int real_preview_width = real_preview_xsize * tile_size;
2590 int real_preview_height = real_preview_ysize * tile_size;
2591 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2592 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2595 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2598 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2600 dst_x += (preview_width - real_preview_width) / 2;
2601 dst_y += (preview_height - real_preview_height) / 2;
2603 for (x = 0; x < real_preview_xsize; x++)
2605 for (y = 0; y < real_preview_ysize; y++)
2607 int lx = from_x + x + (show_level_border ? -1 : 0);
2608 int ly = from_y + y + (show_level_border ? -1 : 0);
2609 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2610 getBorderElement(lx, ly));
2612 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2613 element, tile_size);
2617 redraw_mask |= REDRAW_MICROLEVEL;
2620 #define MICROLABEL_EMPTY 0
2621 #define MICROLABEL_LEVEL_NAME 1
2622 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2623 #define MICROLABEL_LEVEL_AUTHOR 3
2624 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2625 #define MICROLABEL_IMPORTED_FROM 5
2626 #define MICROLABEL_IMPORTED_BY_HEAD 6
2627 #define MICROLABEL_IMPORTED_BY 7
2629 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2631 int max_text_width = SXSIZE;
2632 int font_width = getFontWidth(font_nr);
2634 if (pos->align == ALIGN_CENTER)
2635 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2636 else if (pos->align == ALIGN_RIGHT)
2637 max_text_width = pos->x;
2639 max_text_width = SXSIZE - pos->x;
2641 return max_text_width / font_width;
2644 static void DrawPreviewLevelLabelExt(int mode)
2646 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2647 char label_text[MAX_OUTPUT_LINESIZE + 1];
2648 int max_len_label_text;
2649 int font_nr = pos->font;
2652 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2655 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2656 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2657 mode == MICROLABEL_IMPORTED_BY_HEAD)
2658 font_nr = pos->font_alt;
2660 max_len_label_text = getMaxTextLength(pos, font_nr);
2662 if (pos->size != -1)
2663 max_len_label_text = pos->size;
2665 for (i = 0; i < max_len_label_text; i++)
2666 label_text[i] = ' ';
2667 label_text[max_len_label_text] = '\0';
2669 if (strlen(label_text) > 0)
2670 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2673 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2674 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2675 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2676 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2677 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2678 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2679 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2680 max_len_label_text);
2681 label_text[max_len_label_text] = '\0';
2683 if (strlen(label_text) > 0)
2684 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2686 redraw_mask |= REDRAW_MICROLEVEL;
2689 static void DrawPreviewLevelExt(boolean restart)
2691 static unsigned int scroll_delay = 0;
2692 static unsigned int label_delay = 0;
2693 static int from_x, from_y, scroll_direction;
2694 static int label_state, label_counter;
2695 unsigned int scroll_delay_value = preview.step_delay;
2696 boolean show_level_border = (BorderElement != EL_EMPTY);
2697 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2698 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2699 int last_game_status = game_status; /* save current game status */
2706 if (preview.anim_mode == ANIM_CENTERED)
2708 if (level_xsize > preview.xsize)
2709 from_x = (level_xsize - preview.xsize) / 2;
2710 if (level_ysize > preview.ysize)
2711 from_y = (level_ysize - preview.ysize) / 2;
2714 from_x += preview.xoffset;
2715 from_y += preview.yoffset;
2717 scroll_direction = MV_RIGHT;
2721 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2722 DrawPreviewLevelLabelExt(label_state);
2724 /* initialize delay counters */
2725 DelayReached(&scroll_delay, 0);
2726 DelayReached(&label_delay, 0);
2728 if (leveldir_current->name)
2730 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2731 char label_text[MAX_OUTPUT_LINESIZE + 1];
2732 int font_nr = pos->font;
2733 int max_len_label_text = getMaxTextLength(pos, font_nr);
2735 if (pos->size != -1)
2736 max_len_label_text = pos->size;
2738 strncpy(label_text, leveldir_current->name, max_len_label_text);
2739 label_text[max_len_label_text] = '\0';
2741 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2742 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2745 game_status = last_game_status; /* restore current game status */
2750 /* scroll preview level, if needed */
2751 if (preview.anim_mode != ANIM_NONE &&
2752 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2753 DelayReached(&scroll_delay, scroll_delay_value))
2755 switch (scroll_direction)
2760 from_x -= preview.step_offset;
2761 from_x = (from_x < 0 ? 0 : from_x);
2764 scroll_direction = MV_UP;
2768 if (from_x < level_xsize - preview.xsize)
2770 from_x += preview.step_offset;
2771 from_x = (from_x > level_xsize - preview.xsize ?
2772 level_xsize - preview.xsize : from_x);
2775 scroll_direction = MV_DOWN;
2781 from_y -= preview.step_offset;
2782 from_y = (from_y < 0 ? 0 : from_y);
2785 scroll_direction = MV_RIGHT;
2789 if (from_y < level_ysize - preview.ysize)
2791 from_y += preview.step_offset;
2792 from_y = (from_y > level_ysize - preview.ysize ?
2793 level_ysize - preview.ysize : from_y);
2796 scroll_direction = MV_LEFT;
2803 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2806 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2807 /* redraw micro level label, if needed */
2808 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2809 !strEqual(level.author, ANONYMOUS_NAME) &&
2810 !strEqual(level.author, leveldir_current->name) &&
2811 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2813 int max_label_counter = 23;
2815 if (leveldir_current->imported_from != NULL &&
2816 strlen(leveldir_current->imported_from) > 0)
2817 max_label_counter += 14;
2818 if (leveldir_current->imported_by != NULL &&
2819 strlen(leveldir_current->imported_by) > 0)
2820 max_label_counter += 14;
2822 label_counter = (label_counter + 1) % max_label_counter;
2823 label_state = (label_counter >= 0 && label_counter <= 7 ?
2824 MICROLABEL_LEVEL_NAME :
2825 label_counter >= 9 && label_counter <= 12 ?
2826 MICROLABEL_LEVEL_AUTHOR_HEAD :
2827 label_counter >= 14 && label_counter <= 21 ?
2828 MICROLABEL_LEVEL_AUTHOR :
2829 label_counter >= 23 && label_counter <= 26 ?
2830 MICROLABEL_IMPORTED_FROM_HEAD :
2831 label_counter >= 28 && label_counter <= 35 ?
2832 MICROLABEL_IMPORTED_FROM :
2833 label_counter >= 37 && label_counter <= 40 ?
2834 MICROLABEL_IMPORTED_BY_HEAD :
2835 label_counter >= 42 && label_counter <= 49 ?
2836 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2838 if (leveldir_current->imported_from == NULL &&
2839 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2840 label_state == MICROLABEL_IMPORTED_FROM))
2841 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2842 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2844 DrawPreviewLevelLabelExt(label_state);
2847 game_status = last_game_status; /* restore current game status */
2850 void DrawPreviewLevelInitial()
2852 DrawPreviewLevelExt(TRUE);
2855 void DrawPreviewLevelAnimation()
2857 DrawPreviewLevelExt(FALSE);
2860 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2861 int graphic, int sync_frame, int mask_mode)
2863 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2865 if (mask_mode == USE_MASKING)
2866 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2868 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2871 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2872 int graphic, int sync_frame,
2875 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2877 if (mask_mode == USE_MASKING)
2878 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2880 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2883 inline void DrawGraphicAnimation(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_VAR, FY + y * TILEY_VAR,
2891 graphic, GfxFrame[lx][ly], NO_MASKING);
2893 MarkTileDirty(x, y);
2896 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2898 int lx = LEVELX(x), ly = LEVELY(y);
2900 if (!IN_SCR_FIELD(x, y))
2903 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2904 graphic, GfxFrame[lx][ly], NO_MASKING);
2905 MarkTileDirty(x, y);
2908 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2910 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2913 void DrawLevelElementAnimation(int x, int y, int element)
2915 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2917 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2920 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2922 int sx = SCREENX(x), sy = SCREENY(y);
2924 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2927 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2930 DrawGraphicAnimation(sx, sy, graphic);
2933 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2934 DrawLevelFieldCrumbled(x, y);
2936 if (GFX_CRUMBLED(Feld[x][y]))
2937 DrawLevelFieldCrumbled(x, y);
2941 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2943 int sx = SCREENX(x), sy = SCREENY(y);
2946 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2949 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2951 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2954 DrawGraphicAnimation(sx, sy, graphic);
2956 if (GFX_CRUMBLED(element))
2957 DrawLevelFieldCrumbled(x, y);
2960 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2962 if (player->use_murphy)
2964 /* this works only because currently only one player can be "murphy" ... */
2965 static int last_horizontal_dir = MV_LEFT;
2966 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2968 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2969 last_horizontal_dir = move_dir;
2971 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2973 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2975 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2981 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2984 static boolean equalGraphics(int graphic1, int graphic2)
2986 struct GraphicInfo *g1 = &graphic_info[graphic1];
2987 struct GraphicInfo *g2 = &graphic_info[graphic2];
2989 return (g1->bitmap == g2->bitmap &&
2990 g1->src_x == g2->src_x &&
2991 g1->src_y == g2->src_y &&
2992 g1->anim_frames == g2->anim_frames &&
2993 g1->anim_delay == g2->anim_delay &&
2994 g1->anim_mode == g2->anim_mode);
2997 void DrawAllPlayers()
3001 for (i = 0; i < MAX_PLAYERS; i++)
3002 if (stored_player[i].active)
3003 DrawPlayer(&stored_player[i]);
3006 void DrawPlayerField(int x, int y)
3008 if (!IS_PLAYER(x, y))
3011 DrawPlayer(PLAYERINFO(x, y));
3014 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3016 void DrawPlayer(struct PlayerInfo *player)
3018 int jx = player->jx;
3019 int jy = player->jy;
3020 int move_dir = player->MovDir;
3021 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3022 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3023 int last_jx = (player->is_moving ? jx - dx : jx);
3024 int last_jy = (player->is_moving ? jy - dy : jy);
3025 int next_jx = jx + dx;
3026 int next_jy = jy + dy;
3027 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3028 boolean player_is_opaque = FALSE;
3029 int sx = SCREENX(jx), sy = SCREENY(jy);
3030 int sxx = 0, syy = 0;
3031 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3033 int action = ACTION_DEFAULT;
3034 int last_player_graphic = getPlayerGraphic(player, move_dir);
3035 int last_player_frame = player->Frame;
3038 /* GfxElement[][] is set to the element the player is digging or collecting;
3039 remove also for off-screen player if the player is not moving anymore */
3040 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3041 GfxElement[jx][jy] = EL_UNDEFINED;
3043 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3047 if (!IN_LEV_FIELD(jx, jy))
3049 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3050 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3051 printf("DrawPlayerField(): This should never happen!\n");
3056 if (element == EL_EXPLOSION)
3059 action = (player->is_pushing ? ACTION_PUSHING :
3060 player->is_digging ? ACTION_DIGGING :
3061 player->is_collecting ? ACTION_COLLECTING :
3062 player->is_moving ? ACTION_MOVING :
3063 player->is_snapping ? ACTION_SNAPPING :
3064 player->is_dropping ? ACTION_DROPPING :
3065 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3067 if (player->is_waiting)
3068 move_dir = player->dir_waiting;
3070 InitPlayerGfxAnimation(player, action, move_dir);
3072 /* ----------------------------------------------------------------------- */
3073 /* draw things in the field the player is leaving, if needed */
3074 /* ----------------------------------------------------------------------- */
3076 if (player->is_moving)
3078 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3080 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3082 if (last_element == EL_DYNAMITE_ACTIVE ||
3083 last_element == EL_EM_DYNAMITE_ACTIVE ||
3084 last_element == EL_SP_DISK_RED_ACTIVE)
3085 DrawDynamite(last_jx, last_jy);
3087 DrawLevelFieldThruMask(last_jx, last_jy);
3089 else if (last_element == EL_DYNAMITE_ACTIVE ||
3090 last_element == EL_EM_DYNAMITE_ACTIVE ||
3091 last_element == EL_SP_DISK_RED_ACTIVE)
3092 DrawDynamite(last_jx, last_jy);
3094 /* !!! this is not enough to prevent flickering of players which are
3095 moving next to each others without a free tile between them -- this
3096 can only be solved by drawing all players layer by layer (first the
3097 background, then the foreground etc.) !!! => TODO */
3098 else if (!IS_PLAYER(last_jx, last_jy))
3099 DrawLevelField(last_jx, last_jy);
3102 DrawLevelField(last_jx, last_jy);
3105 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3106 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3109 if (!IN_SCR_FIELD(sx, sy))
3112 /* ----------------------------------------------------------------------- */
3113 /* draw things behind the player, if needed */
3114 /* ----------------------------------------------------------------------- */
3117 DrawLevelElement(jx, jy, Back[jx][jy]);
3118 else if (IS_ACTIVE_BOMB(element))
3119 DrawLevelElement(jx, jy, EL_EMPTY);
3122 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3124 int old_element = GfxElement[jx][jy];
3125 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3126 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3128 if (GFX_CRUMBLED(old_element))
3129 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3131 DrawGraphic(sx, sy, old_graphic, frame);
3133 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3134 player_is_opaque = TRUE;
3138 GfxElement[jx][jy] = EL_UNDEFINED;
3140 /* make sure that pushed elements are drawn with correct frame rate */
3141 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3143 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3144 GfxFrame[jx][jy] = player->StepFrame;
3146 DrawLevelField(jx, jy);
3150 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3151 /* ----------------------------------------------------------------------- */
3152 /* draw player himself */
3153 /* ----------------------------------------------------------------------- */
3155 graphic = getPlayerGraphic(player, move_dir);
3157 /* in the case of changed player action or direction, prevent the current
3158 animation frame from being restarted for identical animations */
3159 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3160 player->Frame = last_player_frame;
3162 frame = getGraphicAnimationFrame(graphic, player->Frame);
3166 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3167 sxx = player->GfxPos;
3169 syy = player->GfxPos;
3172 if (!setup.soft_scrolling && ScreenMovPos)
3175 if (player_is_opaque)
3176 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3178 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3180 if (SHIELD_ON(player))
3182 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3183 IMG_SHIELD_NORMAL_ACTIVE);
3184 int frame = getGraphicAnimationFrame(graphic, -1);
3186 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3190 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3193 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3194 sxx = player->GfxPos;
3196 syy = player->GfxPos;
3200 /* ----------------------------------------------------------------------- */
3201 /* draw things the player is pushing, if needed */
3202 /* ----------------------------------------------------------------------- */
3204 if (player->is_pushing && player->is_moving)
3206 int px = SCREENX(jx), py = SCREENY(jy);
3207 int pxx = (TILEX - ABS(sxx)) * dx;
3208 int pyy = (TILEY - ABS(syy)) * dy;
3209 int gfx_frame = GfxFrame[jx][jy];
3215 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3217 element = Feld[next_jx][next_jy];
3218 gfx_frame = GfxFrame[next_jx][next_jy];
3221 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3223 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3224 frame = getGraphicAnimationFrame(graphic, sync_frame);
3226 /* draw background element under pushed element (like the Sokoban field) */
3227 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3229 /* this allows transparent pushing animation over non-black background */
3232 DrawLevelElement(jx, jy, Back[jx][jy]);
3234 DrawLevelElement(jx, jy, EL_EMPTY);
3236 if (Back[next_jx][next_jy])
3237 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3239 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3241 else if (Back[next_jx][next_jy])
3242 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3245 /* do not draw (EM style) pushing animation when pushing is finished */
3246 /* (two-tile animations usually do not contain start and end frame) */
3247 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3248 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3250 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3252 /* masked drawing is needed for EMC style (double) movement graphics */
3253 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3254 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3258 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3259 /* ----------------------------------------------------------------------- */
3260 /* draw player himself */
3261 /* ----------------------------------------------------------------------- */
3263 graphic = getPlayerGraphic(player, move_dir);
3265 /* in the case of changed player action or direction, prevent the current
3266 animation frame from being restarted for identical animations */
3267 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3268 player->Frame = last_player_frame;
3270 frame = getGraphicAnimationFrame(graphic, player->Frame);
3274 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3275 sxx = player->GfxPos;
3277 syy = player->GfxPos;
3280 if (!setup.soft_scrolling && ScreenMovPos)
3283 if (player_is_opaque)
3284 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3286 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3288 if (SHIELD_ON(player))
3290 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3291 IMG_SHIELD_NORMAL_ACTIVE);
3292 int frame = getGraphicAnimationFrame(graphic, -1);
3294 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3298 /* ----------------------------------------------------------------------- */
3299 /* draw things in front of player (active dynamite or dynabombs) */
3300 /* ----------------------------------------------------------------------- */
3302 if (IS_ACTIVE_BOMB(element))
3304 graphic = el2img(element);
3305 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3307 if (game.emulation == EMU_SUPAPLEX)
3308 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3310 DrawGraphicThruMask(sx, sy, graphic, frame);
3313 if (player_is_moving && last_element == EL_EXPLOSION)
3315 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3316 GfxElement[last_jx][last_jy] : EL_EMPTY);
3317 int graphic = el_act2img(element, ACTION_EXPLODING);
3318 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3319 int phase = ExplodePhase[last_jx][last_jy] - 1;
3320 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3323 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3326 /* ----------------------------------------------------------------------- */
3327 /* draw elements the player is just walking/passing through/under */
3328 /* ----------------------------------------------------------------------- */
3330 if (player_is_moving)
3332 /* handle the field the player is leaving ... */
3333 if (IS_ACCESSIBLE_INSIDE(last_element))
3334 DrawLevelField(last_jx, last_jy);
3335 else if (IS_ACCESSIBLE_UNDER(last_element))
3336 DrawLevelFieldThruMask(last_jx, last_jy);
3339 /* do not redraw accessible elements if the player is just pushing them */
3340 if (!player_is_moving || !player->is_pushing)
3342 /* ... and the field the player is entering */
3343 if (IS_ACCESSIBLE_INSIDE(element))
3344 DrawLevelField(jx, jy);
3345 else if (IS_ACCESSIBLE_UNDER(element))
3346 DrawLevelFieldThruMask(jx, jy);
3349 MarkTileDirty(sx, sy);
3352 /* ------------------------------------------------------------------------- */
3354 void WaitForEventToContinue()
3356 boolean still_wait = TRUE;
3358 /* simulate releasing mouse button over last gadget, if still pressed */
3360 HandleGadgets(-1, -1, 0);
3362 button_status = MB_RELEASED;
3376 case EVENT_BUTTONPRESS:
3377 case EVENT_KEYPRESS:
3381 case EVENT_KEYRELEASE:
3382 ClearPlayerAction();
3386 HandleOtherEvents(&event);
3390 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3397 /* don't eat all CPU time */
3402 #define MAX_REQUEST_LINES 13
3403 #define MAX_REQUEST_LINE_FONT1_LEN 7
3404 #define MAX_REQUEST_LINE_FONT2_LEN 10
3406 static int RequestHandleEvents(unsigned int req_state)
3408 int last_game_status = game_status; /* save current game status */
3412 button_status = MB_RELEASED;
3414 request_gadget_id = -1;
3427 case EVENT_BUTTONPRESS:
3428 case EVENT_BUTTONRELEASE:
3429 case EVENT_MOTIONNOTIFY:
3431 if (event.type == EVENT_MOTIONNOTIFY)
3433 if (!PointerInWindow(window))
3434 continue; /* window and pointer are on different screens */
3439 motion_status = TRUE;
3440 mx = ((MotionEvent *) &event)->x;
3441 my = ((MotionEvent *) &event)->y;
3445 motion_status = FALSE;
3446 mx = ((ButtonEvent *) &event)->x;
3447 my = ((ButtonEvent *) &event)->y;
3448 if (event.type == EVENT_BUTTONPRESS)
3449 button_status = ((ButtonEvent *) &event)->button;
3451 button_status = MB_RELEASED;
3454 /* this sets 'request_gadget_id' */
3455 HandleGadgets(mx, my, button_status);
3457 switch (request_gadget_id)
3459 case TOOL_CTRL_ID_YES:
3462 case TOOL_CTRL_ID_NO:
3465 case TOOL_CTRL_ID_CONFIRM:
3466 result = TRUE | FALSE;
3469 case TOOL_CTRL_ID_PLAYER_1:
3472 case TOOL_CTRL_ID_PLAYER_2:
3475 case TOOL_CTRL_ID_PLAYER_3:
3478 case TOOL_CTRL_ID_PLAYER_4:
3489 case EVENT_KEYPRESS:
3490 switch (GetEventKey((KeyEvent *)&event, TRUE))
3493 if (req_state & REQ_CONFIRM)
3498 #if defined(TARGET_SDL2)
3505 #if defined(TARGET_SDL2)
3515 if (req_state & REQ_PLAYER)
3519 case EVENT_KEYRELEASE:
3520 ClearPlayerAction();
3524 HandleOtherEvents(&event);
3528 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3530 int joy = AnyJoystick();
3532 if (joy & JOY_BUTTON_1)
3534 else if (joy & JOY_BUTTON_2)
3538 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3540 HandleGameActions();
3546 if (!PendingEvent()) /* delay only if no pending events */
3550 game_status = GAME_MODE_PSEUDO_DOOR;
3554 game_status = last_game_status; /* restore current game status */
3560 static boolean RequestDoor(char *text, unsigned int req_state)
3562 unsigned int old_door_state;
3563 int last_game_status = game_status; /* save current game status */
3564 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3565 int font_nr = FONT_TEXT_2;
3570 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3572 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3573 font_nr = FONT_TEXT_1;
3576 if (game_status == GAME_MODE_PLAYING)
3577 BlitScreenToBitmap(backbuffer);
3579 /* disable deactivated drawing when quick-loading level tape recording */
3580 if (tape.playing && tape.deactivate_display)
3581 TapeDeactivateDisplayOff(TRUE);
3583 SetMouseCursor(CURSOR_DEFAULT);
3585 #if defined(NETWORK_AVALIABLE)
3586 /* pause network game while waiting for request to answer */
3587 if (options.network &&
3588 game_status == GAME_MODE_PLAYING &&
3589 req_state & REQUEST_WAIT_FOR_INPUT)
3590 SendToServer_PausePlaying();
3593 old_door_state = GetDoorState();
3595 /* simulate releasing mouse button over last gadget, if still pressed */
3597 HandleGadgets(-1, -1, 0);
3601 /* draw released gadget before proceeding */
3604 if (old_door_state & DOOR_OPEN_1)
3606 CloseDoor(DOOR_CLOSE_1);
3608 /* save old door content */
3609 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3610 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3613 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3614 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3616 /* clear door drawing field */
3617 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3619 /* force DOOR font inside door area */
3620 game_status = GAME_MODE_PSEUDO_DOOR;
3622 /* write text for request */
3623 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3625 char text_line[max_request_line_len + 1];
3631 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3633 tc = *(text_ptr + tx);
3634 // if (!tc || tc == ' ')
3635 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3639 if ((tc == '?' || tc == '!') && tl == 0)
3649 strncpy(text_line, text_ptr, tl);
3652 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3653 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3654 text_line, font_nr);
3656 text_ptr += tl + (tc == ' ' ? 1 : 0);
3657 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3660 game_status = last_game_status; /* restore current game status */
3662 if (req_state & REQ_ASK)
3664 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3665 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3667 else if (req_state & REQ_CONFIRM)
3669 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3671 else if (req_state & REQ_PLAYER)
3673 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3674 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3675 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3676 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3679 /* copy request gadgets to door backbuffer */
3680 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3682 OpenDoor(DOOR_OPEN_1);
3684 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3686 if (game_status == GAME_MODE_PLAYING)
3688 SetPanelBackground();
3689 SetDrawBackgroundMask(REDRAW_DOOR_1);
3693 SetDrawBackgroundMask(REDRAW_FIELD);
3699 if (game_status != GAME_MODE_MAIN)
3702 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3704 // ---------- handle request buttons ----------
3705 result = RequestHandleEvents(req_state);
3707 if (game_status != GAME_MODE_MAIN)
3712 if (!(req_state & REQ_STAY_OPEN))
3714 CloseDoor(DOOR_CLOSE_1);
3716 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3717 (req_state & REQ_REOPEN))
3718 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3723 if (game_status == GAME_MODE_PLAYING)
3725 SetPanelBackground();
3726 SetDrawBackgroundMask(REDRAW_DOOR_1);
3730 SetDrawBackgroundMask(REDRAW_FIELD);
3733 #if defined(NETWORK_AVALIABLE)
3734 /* continue network game after request */
3735 if (options.network &&
3736 game_status == GAME_MODE_PLAYING &&
3737 req_state & REQUEST_WAIT_FOR_INPUT)
3738 SendToServer_ContinuePlaying();
3741 /* restore deactivated drawing when quick-loading level tape recording */
3742 if (tape.playing && tape.deactivate_display)
3743 TapeDeactivateDisplayOn();
3748 static boolean RequestEnvelope(char *text, unsigned int req_state)
3752 if (game_status == GAME_MODE_PLAYING)
3753 BlitScreenToBitmap(backbuffer);
3755 /* disable deactivated drawing when quick-loading level tape recording */
3756 if (tape.playing && tape.deactivate_display)
3757 TapeDeactivateDisplayOff(TRUE);
3759 SetMouseCursor(CURSOR_DEFAULT);
3761 #if defined(NETWORK_AVALIABLE)
3762 /* pause network game while waiting for request to answer */
3763 if (options.network &&
3764 game_status == GAME_MODE_PLAYING &&
3765 req_state & REQUEST_WAIT_FOR_INPUT)
3766 SendToServer_PausePlaying();
3769 /* simulate releasing mouse button over last gadget, if still pressed */
3771 HandleGadgets(-1, -1, 0);
3775 // (replace with setting corresponding request background)
3776 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3777 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3779 /* clear door drawing field */
3780 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3782 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3784 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3786 if (game_status == GAME_MODE_PLAYING)
3788 SetPanelBackground();
3789 SetDrawBackgroundMask(REDRAW_DOOR_1);
3793 SetDrawBackgroundMask(REDRAW_FIELD);
3799 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3801 // ---------- handle request buttons ----------
3802 result = RequestHandleEvents(req_state);
3804 if (game_status != GAME_MODE_MAIN)
3809 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3813 if (game_status == GAME_MODE_PLAYING)
3815 SetPanelBackground();
3816 SetDrawBackgroundMask(REDRAW_DOOR_1);
3820 SetDrawBackgroundMask(REDRAW_FIELD);
3823 #if defined(NETWORK_AVALIABLE)
3824 /* continue network game after request */
3825 if (options.network &&
3826 game_status == GAME_MODE_PLAYING &&
3827 req_state & REQUEST_WAIT_FOR_INPUT)
3828 SendToServer_ContinuePlaying();
3831 /* restore deactivated drawing when quick-loading level tape recording */
3832 if (tape.playing && tape.deactivate_display)
3833 TapeDeactivateDisplayOn();
3838 boolean Request(char *text, unsigned int req_state)
3840 if (global.use_envelope_request)
3841 return RequestEnvelope(text, req_state);
3843 return RequestDoor(text, req_state);
3846 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3848 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3849 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3852 if (dpo1->sort_priority != dpo2->sort_priority)
3853 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3855 compare_result = dpo1->nr - dpo2->nr;
3857 return compare_result;
3860 void InitGraphicCompatibilityInfo_Doors()
3866 struct DoorInfo *door;
3870 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3871 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3873 { -1, -1, -1, NULL }
3875 struct Rect door_rect_list[] =
3877 { DX, DY, DXSIZE, DYSIZE },
3878 { VX, VY, VXSIZE, VYSIZE }
3882 for (i = 0; doors[i].door_token != -1; i++)
3884 int door_token = doors[i].door_token;
3885 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3886 int part_1 = doors[i].part_1;
3887 int part_8 = doors[i].part_8;
3888 int part_2 = part_1 + 1;
3889 int part_3 = part_1 + 2;
3890 struct DoorInfo *door = doors[i].door;
3891 struct Rect *door_rect = &door_rect_list[door_index];
3892 boolean door_gfx_redefined = FALSE;
3894 /* check if any door part graphic definitions have been redefined */
3896 for (j = 0; door_part_controls[j].door_token != -1; j++)
3898 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3899 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3901 if (dpc->door_token == door_token && fi->redefined)
3902 door_gfx_redefined = TRUE;
3905 /* check for old-style door graphic/animation modifications */
3907 if (!door_gfx_redefined)
3909 if (door->anim_mode & ANIM_STATIC_PANEL)
3911 door->panel.step_xoffset = 0;
3912 door->panel.step_yoffset = 0;
3915 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3917 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3918 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3919 int num_door_steps, num_panel_steps;
3921 /* remove door part graphics other than the two default wings */
3923 for (j = 0; door_part_controls[j].door_token != -1; j++)
3925 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3926 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3928 if (dpc->graphic >= part_3 &&
3929 dpc->graphic <= part_8)
3933 /* set graphics and screen positions of the default wings */
3935 g_part_1->width = door_rect->width;
3936 g_part_1->height = door_rect->height;
3937 g_part_2->width = door_rect->width;
3938 g_part_2->height = door_rect->height;
3939 g_part_2->src_x = door_rect->width;
3940 g_part_2->src_y = g_part_1->src_y;
3942 door->part_2.x = door->part_1.x;
3943 door->part_2.y = door->part_1.y;
3945 if (door->width != -1)
3947 g_part_1->width = door->width;
3948 g_part_2->width = door->width;
3950 // special treatment for graphics and screen position of right wing
3951 g_part_2->src_x += door_rect->width - door->width;
3952 door->part_2.x += door_rect->width - door->width;
3955 if (door->height != -1)
3957 g_part_1->height = door->height;
3958 g_part_2->height = door->height;
3960 // special treatment for graphics and screen position of bottom wing
3961 g_part_2->src_y += door_rect->height - door->height;
3962 door->part_2.y += door_rect->height - door->height;
3965 /* set animation delays for the default wings and panels */
3967 door->part_1.step_delay = door->step_delay;
3968 door->part_2.step_delay = door->step_delay;
3969 door->panel.step_delay = door->step_delay;
3971 /* set animation draw order for the default wings */
3973 door->part_1.sort_priority = 2; /* draw left wing over ... */
3974 door->part_2.sort_priority = 1; /* ... right wing */
3976 /* set animation draw offset for the default wings */
3978 if (door->anim_mode & ANIM_HORIZONTAL)
3980 door->part_1.step_xoffset = door->step_offset;
3981 door->part_1.step_yoffset = 0;
3982 door->part_2.step_xoffset = door->step_offset * -1;
3983 door->part_2.step_yoffset = 0;
3985 num_door_steps = g_part_1->width / door->step_offset;
3987 else // ANIM_VERTICAL
3989 door->part_1.step_xoffset = 0;
3990 door->part_1.step_yoffset = door->step_offset;
3991 door->part_2.step_xoffset = 0;
3992 door->part_2.step_yoffset = door->step_offset * -1;
3994 num_door_steps = g_part_1->height / door->step_offset;
3997 /* set animation draw offset for the default panels */
3999 if (door->step_offset > 1)
4001 num_panel_steps = 2 * door_rect->height / door->step_offset;
4002 door->panel.start_step = num_panel_steps - num_door_steps;
4006 num_panel_steps = door_rect->height / door->step_offset;
4007 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4008 door->panel.step_delay *= 2;
4019 for (i = 0; door_part_controls[i].door_token != -1; i++)
4021 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4022 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4024 /* initialize "start_step_opening" and "start_step_closing", if needed */
4025 if (dpc->pos->start_step_opening == 0 &&
4026 dpc->pos->start_step_closing == 0)
4028 // dpc->pos->start_step_opening = dpc->pos->start_step;
4029 dpc->pos->start_step_closing = dpc->pos->start_step;
4032 /* fill structure for door part draw order (sorted below) */
4034 dpo->sort_priority = dpc->pos->sort_priority;
4037 /* sort door part controls according to sort_priority and graphic number */
4038 qsort(door_part_order, MAX_DOOR_PARTS,
4039 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4042 unsigned int OpenDoor(unsigned int door_state)
4044 if (door_state & DOOR_COPY_BACK)
4046 if (door_state & DOOR_OPEN_1)
4047 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4048 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4050 if (door_state & DOOR_OPEN_2)
4051 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4052 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4054 door_state &= ~DOOR_COPY_BACK;
4057 return MoveDoor(door_state);
4060 unsigned int CloseDoor(unsigned int door_state)
4062 unsigned int old_door_state = GetDoorState();
4064 if (!(door_state & DOOR_NO_COPY_BACK))
4066 if (old_door_state & DOOR_OPEN_1)
4067 BlitBitmap(backbuffer, bitmap_db_door_1,
4068 DX, DY, DXSIZE, DYSIZE, 0, 0);
4070 if (old_door_state & DOOR_OPEN_2)
4071 BlitBitmap(backbuffer, bitmap_db_door_2,
4072 VX, VY, VXSIZE, VYSIZE, 0, 0);
4074 door_state &= ~DOOR_NO_COPY_BACK;
4077 return MoveDoor(door_state);
4080 unsigned int GetDoorState()
4082 return MoveDoor(DOOR_GET_STATE);
4085 unsigned int SetDoorState(unsigned int door_state)
4087 return MoveDoor(door_state | DOOR_SET_STATE);
4090 int euclid(int a, int b)
4092 return (b ? euclid(b, a % b) : a);
4095 unsigned int MoveDoor(unsigned int door_state)
4097 struct Rect door_rect_list[] =
4099 { DX, DY, DXSIZE, DYSIZE },
4100 { VX, VY, VXSIZE, VYSIZE }
4102 static int door1 = DOOR_OPEN_1;
4103 static int door2 = DOOR_CLOSE_2;
4104 unsigned int door_delay = 0;
4105 unsigned int door_delay_value;
4108 if (door_1.width < 0 || door_1.width > DXSIZE)
4109 door_1.width = DXSIZE;
4110 if (door_1.height < 0 || door_1.height > DYSIZE)
4111 door_1.height = DYSIZE;
4112 if (door_2.width < 0 || door_2.width > VXSIZE)
4113 door_2.width = VXSIZE;
4114 if (door_2.height < 0 || door_2.height > VYSIZE)
4115 door_2.height = VYSIZE;
4117 if (door_state == DOOR_GET_STATE)
4118 return (door1 | door2);
4120 if (door_state & DOOR_SET_STATE)
4122 if (door_state & DOOR_ACTION_1)
4123 door1 = door_state & DOOR_ACTION_1;
4124 if (door_state & DOOR_ACTION_2)
4125 door2 = door_state & DOOR_ACTION_2;
4127 return (door1 | door2);
4130 if (!(door_state & DOOR_FORCE_REDRAW))
4132 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4133 door_state &= ~DOOR_OPEN_1;
4134 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4135 door_state &= ~DOOR_CLOSE_1;
4136 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4137 door_state &= ~DOOR_OPEN_2;
4138 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4139 door_state &= ~DOOR_CLOSE_2;
4142 if (global.autoplay_leveldir)
4144 door_state |= DOOR_NO_DELAY;
4145 door_state &= ~DOOR_CLOSE_ALL;
4148 if (game_status == GAME_MODE_EDITOR)
4149 door_state |= DOOR_NO_DELAY;
4151 if (door_state & DOOR_ACTION)
4153 boolean door_panel_drawn[NUM_DOORS];
4154 boolean panel_has_doors[NUM_DOORS];
4155 boolean door_part_skip[MAX_DOOR_PARTS];
4156 boolean door_part_done[MAX_DOOR_PARTS];
4157 boolean door_part_done_all;
4158 int num_steps[MAX_DOOR_PARTS];
4159 int max_move_delay = 0; // delay for complete animations of all doors
4160 int max_step_delay = 0; // delay (ms) between two animation frames
4161 int num_move_steps = 0; // number of animation steps for all doors
4162 int current_move_delay = 0;
4166 for (i = 0; i < NUM_DOORS; i++)
4167 panel_has_doors[i] = FALSE;
4169 for (i = 0; i < MAX_DOOR_PARTS; i++)
4171 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4172 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4173 int door_token = dpc->door_token;
4175 door_part_done[i] = FALSE;
4176 door_part_skip[i] = (!(door_state & door_token) ||
4180 for (i = 0; i < MAX_DOOR_PARTS; i++)
4182 int nr = door_part_order[i].nr;
4183 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4184 struct DoorPartPosInfo *pos = dpc->pos;
4185 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4186 int door_token = dpc->door_token;
4187 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4188 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4189 int step_xoffset = ABS(pos->step_xoffset);
4190 int step_yoffset = ABS(pos->step_yoffset);
4191 int step_delay = pos->step_delay;
4192 int current_door_state = door_state & door_token;
4193 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4194 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4195 boolean part_opening = (is_panel ? door_closing : door_opening);
4196 int start_step = (part_opening ? pos->start_step_opening :
4197 pos->start_step_closing);
4198 float move_xsize = (step_xoffset ? g->width : 0);
4199 float move_ysize = (step_yoffset ? g->height : 0);
4200 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4201 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4202 int move_steps = (move_xsteps && move_ysteps ?
4203 MIN(move_xsteps, move_ysteps) :
4204 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4205 int move_delay = move_steps * step_delay;
4207 if (door_part_skip[nr])
4211 panel_has_doors[door_index] = TRUE;
4213 max_move_delay = MAX(max_move_delay, move_delay);
4214 max_step_delay = (max_step_delay == 0 ? step_delay :
4215 euclid(max_step_delay, step_delay));
4216 num_steps[nr] = move_steps;
4219 num_move_steps = max_move_delay / max_step_delay;
4221 door_delay_value = max_step_delay;
4223 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4225 start = num_move_steps - 1;
4229 /* opening door sound has priority over simultaneously closing door */
4230 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4231 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4232 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4233 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4236 for (k = start; k < num_move_steps; k++)
4238 door_part_done_all = TRUE;
4240 for (i = 0; i < NUM_DOORS; i++)
4241 door_panel_drawn[i] = FALSE;
4243 for (i = 0; i < MAX_DOOR_PARTS; i++)
4245 int nr = door_part_order[i].nr;
4246 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4247 struct DoorPartPosInfo *pos = dpc->pos;
4248 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4249 int door_token = dpc->door_token;
4250 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4251 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4252 struct Rect *door_rect = &door_rect_list[door_index];
4253 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4255 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4256 int current_door_state = door_state & door_token;
4257 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4258 boolean door_closing = !door_opening;
4259 boolean part_opening = (is_panel ? door_closing : door_opening);
4260 boolean part_closing = !part_opening;
4261 int start_step = (part_opening ? pos->start_step_opening :
4262 pos->start_step_closing);
4263 int step_delay = pos->step_delay;
4264 int step_factor = step_delay / max_step_delay;
4265 int k1 = (step_factor ? k / step_factor + 1 : k);
4266 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4267 int kk = (k2 < 0 ? 0 : k2);
4268 int src_x, src_y, src_xx, src_yy;
4269 int dst_x, dst_y, dst_xx, dst_yy;
4272 if (door_part_skip[nr])
4275 if (!(door_state & door_token))
4283 if (!door_panel_drawn[door_index])
4285 ClearRectangle(drawto, door_rect->x, door_rect->y,
4286 door_rect->width, door_rect->height);
4288 door_panel_drawn[door_index] = TRUE;
4291 // draw opening or closing door parts
4293 if (pos->step_xoffset < 0) // door part on right side
4296 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4299 if (dst_xx + width > door_rect->width)
4300 width = door_rect->width - dst_xx;
4302 else // door part on left side
4305 dst_xx = pos->x - kk * pos->step_xoffset;
4309 src_xx = ABS(dst_xx);
4313 width = g->width - src_xx;
4315 // printf("::: k == %d [%d] \n", k, start_step);
4318 if (pos->step_yoffset < 0) // door part on bottom side
4321 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4324 if (dst_yy + height > door_rect->height)
4325 height = door_rect->height - dst_yy;
4327 else // door part on top side
4330 dst_yy = pos->y - kk * pos->step_yoffset;
4334 src_yy = ABS(dst_yy);
4338 height = g->height - src_yy;
4348 src_x = g->src_x + src_xx;
4349 src_y = g->src_y + src_yy;
4352 dst_x = door_rect->x + dst_xx;
4353 dst_y = door_rect->y + dst_yy;
4355 if (width >= 0 && width <= g->width &&
4356 height >= 0 && height <= g->height)
4358 if (is_panel || !pos->draw_masked)
4359 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4362 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4366 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4368 if ((part_opening && (width < 0 || height < 0)) ||
4369 (part_closing && (width >= g->width && height >= g->height)))
4370 door_part_done[nr] = TRUE;
4372 // continue door part animations, but not panel after door has closed
4373 if (!door_part_done[nr] &&
4374 !(is_panel && door_closing && panel_has_doors[door_index]))
4375 door_part_done_all = FALSE;
4378 if (!(door_state & DOOR_NO_DELAY))
4382 if (game_status == GAME_MODE_MAIN)
4385 WaitUntilDelayReached(&door_delay, door_delay_value);
4387 current_move_delay += max_step_delay;
4390 if (door_part_done_all)
4395 if (door_state & DOOR_ACTION_1)
4396 door1 = door_state & DOOR_ACTION_1;
4397 if (door_state & DOOR_ACTION_2)
4398 door2 = door_state & DOOR_ACTION_2;
4400 return (door1 | door2);
4403 void DrawSpecialEditorDoor()
4405 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4406 int top_border_width = gfx1->width;
4407 int top_border_height = gfx1->height;
4408 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4409 int ex = EX - outer_border;
4410 int ey = EY - outer_border;
4411 int vy = VY - outer_border;
4412 int exsize = EXSIZE + 2 * outer_border;
4414 CloseDoor(DOOR_CLOSE_2);
4416 /* draw bigger level editor toolbox window */
4417 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4418 top_border_width, top_border_height, ex, ey - top_border_height);
4419 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4420 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4422 redraw_mask |= REDRAW_ALL;
4425 void UndrawSpecialEditorDoor()
4427 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4428 int top_border_width = gfx1->width;
4429 int top_border_height = gfx1->height;
4430 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4431 int ex = EX - outer_border;
4432 int ey = EY - outer_border;
4433 int ey_top = ey - top_border_height;
4434 int exsize = EXSIZE + 2 * outer_border;
4435 int eysize = EYSIZE + 2 * outer_border;
4437 /* draw normal tape recorder window */
4438 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4440 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4441 ex, ey_top, top_border_width, top_border_height,
4443 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4444 ex, ey, exsize, eysize, ex, ey);
4448 // if screen background is set to "[NONE]", clear editor toolbox window
4449 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4450 ClearRectangle(drawto, ex, ey, exsize, eysize);
4453 redraw_mask |= REDRAW_ALL;
4457 /* ---------- new tool button stuff ---------------------------------------- */
4462 struct TextPosInfo *pos;
4465 } toolbutton_info[NUM_TOOL_BUTTONS] =
4468 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4469 TOOL_CTRL_ID_YES, "yes"
4472 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4473 TOOL_CTRL_ID_NO, "no"
4476 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4477 TOOL_CTRL_ID_CONFIRM, "confirm"
4480 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4481 TOOL_CTRL_ID_PLAYER_1, "player 1"
4484 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4485 TOOL_CTRL_ID_PLAYER_2, "player 2"
4488 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4489 TOOL_CTRL_ID_PLAYER_3, "player 3"
4492 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4493 TOOL_CTRL_ID_PLAYER_4, "player 4"
4497 void CreateToolButtons()
4501 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4503 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4504 struct TextPosInfo *pos = toolbutton_info[i].pos;
4505 struct GadgetInfo *gi;
4506 Bitmap *deco_bitmap = None;
4507 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4508 unsigned int event_mask = GD_EVENT_RELEASED;
4511 int gd_x = gfx->src_x;
4512 int gd_y = gfx->src_y;
4513 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4514 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4517 if (global.use_envelope_request)
4518 setRequestPosition(&dx, &dy, TRUE);
4520 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4522 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4524 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4525 pos->size, &deco_bitmap, &deco_x, &deco_y);
4526 deco_xpos = (gfx->width - pos->size) / 2;
4527 deco_ypos = (gfx->height - pos->size) / 2;
4530 gi = CreateGadget(GDI_CUSTOM_ID, id,
4531 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4532 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4533 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4534 GDI_WIDTH, gfx->width,
4535 GDI_HEIGHT, gfx->height,
4536 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4537 GDI_STATE, GD_BUTTON_UNPRESSED,
4538 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4539 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4540 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4541 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4542 GDI_DECORATION_SIZE, pos->size, pos->size,
4543 GDI_DECORATION_SHIFTING, 1, 1,
4544 GDI_DIRECT_DRAW, FALSE,
4545 GDI_EVENT_MASK, event_mask,
4546 GDI_CALLBACK_ACTION, HandleToolButtons,
4550 Error(ERR_EXIT, "cannot create gadget");
4552 tool_gadget[id] = gi;
4556 void FreeToolButtons()
4560 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4561 FreeGadget(tool_gadget[i]);
4564 static void UnmapToolButtons()
4568 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4569 UnmapGadget(tool_gadget[i]);
4572 static void HandleToolButtons(struct GadgetInfo *gi)
4574 request_gadget_id = gi->custom_id;
4577 static struct Mapping_EM_to_RND_object
4580 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4581 boolean is_backside; /* backside of moving element */
4587 em_object_mapping_list[] =
4590 Xblank, TRUE, FALSE,
4594 Yacid_splash_eB, FALSE, FALSE,
4595 EL_ACID_SPLASH_RIGHT, -1, -1
4598 Yacid_splash_wB, FALSE, FALSE,
4599 EL_ACID_SPLASH_LEFT, -1, -1
4602 #ifdef EM_ENGINE_BAD_ROLL
4604 Xstone_force_e, FALSE, FALSE,
4605 EL_ROCK, -1, MV_BIT_RIGHT
4608 Xstone_force_w, FALSE, FALSE,
4609 EL_ROCK, -1, MV_BIT_LEFT
4612 Xnut_force_e, FALSE, FALSE,
4613 EL_NUT, -1, MV_BIT_RIGHT
4616 Xnut_force_w, FALSE, FALSE,
4617 EL_NUT, -1, MV_BIT_LEFT
4620 Xspring_force_e, FALSE, FALSE,
4621 EL_SPRING, -1, MV_BIT_RIGHT
4624 Xspring_force_w, FALSE, FALSE,
4625 EL_SPRING, -1, MV_BIT_LEFT
4628 Xemerald_force_e, FALSE, FALSE,
4629 EL_EMERALD, -1, MV_BIT_RIGHT
4632 Xemerald_force_w, FALSE, FALSE,
4633 EL_EMERALD, -1, MV_BIT_LEFT
4636 Xdiamond_force_e, FALSE, FALSE,
4637 EL_DIAMOND, -1, MV_BIT_RIGHT
4640 Xdiamond_force_w, FALSE, FALSE,
4641 EL_DIAMOND, -1, MV_BIT_LEFT
4644 Xbomb_force_e, FALSE, FALSE,
4645 EL_BOMB, -1, MV_BIT_RIGHT
4648 Xbomb_force_w, FALSE, FALSE,
4649 EL_BOMB, -1, MV_BIT_LEFT
4651 #endif /* EM_ENGINE_BAD_ROLL */
4654 Xstone, TRUE, FALSE,
4658 Xstone_pause, FALSE, FALSE,
4662 Xstone_fall, FALSE, FALSE,
4666 Ystone_s, FALSE, FALSE,
4667 EL_ROCK, ACTION_FALLING, -1
4670 Ystone_sB, FALSE, TRUE,
4671 EL_ROCK, ACTION_FALLING, -1
4674 Ystone_e, FALSE, FALSE,
4675 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4678 Ystone_eB, FALSE, TRUE,
4679 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4682 Ystone_w, FALSE, FALSE,
4683 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4686 Ystone_wB, FALSE, TRUE,
4687 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4694 Xnut_pause, FALSE, FALSE,
4698 Xnut_fall, FALSE, FALSE,
4702 Ynut_s, FALSE, FALSE,
4703 EL_NUT, ACTION_FALLING, -1
4706 Ynut_sB, FALSE, TRUE,
4707 EL_NUT, ACTION_FALLING, -1
4710 Ynut_e, FALSE, FALSE,
4711 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4714 Ynut_eB, FALSE, TRUE,
4715 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4718 Ynut_w, FALSE, FALSE,
4719 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4722 Ynut_wB, FALSE, TRUE,
4723 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4726 Xbug_n, TRUE, FALSE,
4730 Xbug_e, TRUE, FALSE,
4731 EL_BUG_RIGHT, -1, -1
4734 Xbug_s, TRUE, FALSE,
4738 Xbug_w, TRUE, FALSE,
4742 Xbug_gon, FALSE, FALSE,
4746 Xbug_goe, FALSE, FALSE,
4747 EL_BUG_RIGHT, -1, -1
4750 Xbug_gos, FALSE, FALSE,
4754 Xbug_gow, FALSE, FALSE,
4758 Ybug_n, FALSE, FALSE,
4759 EL_BUG, ACTION_MOVING, MV_BIT_UP
4762 Ybug_nB, FALSE, TRUE,
4763 EL_BUG, ACTION_MOVING, MV_BIT_UP
4766 Ybug_e, FALSE, FALSE,
4767 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4770 Ybug_eB, FALSE, TRUE,
4771 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4774 Ybug_s, FALSE, FALSE,
4775 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4778 Ybug_sB, FALSE, TRUE,
4779 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4782 Ybug_w, FALSE, FALSE,
4783 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4786 Ybug_wB, FALSE, TRUE,
4787 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4790 Ybug_w_n, FALSE, FALSE,
4791 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4794 Ybug_n_e, FALSE, FALSE,
4795 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4798 Ybug_e_s, FALSE, FALSE,
4799 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4802 Ybug_s_w, FALSE, FALSE,
4803 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4806 Ybug_e_n, FALSE, FALSE,
4807 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4810 Ybug_s_e, FALSE, FALSE,
4811 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4814 Ybug_w_s, FALSE, FALSE,
4815 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4818 Ybug_n_w, FALSE, FALSE,
4819 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4822 Ybug_stone, FALSE, FALSE,
4823 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4826 Ybug_spring, FALSE, FALSE,
4827 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4830 Xtank_n, TRUE, FALSE,
4831 EL_SPACESHIP_UP, -1, -1
4834 Xtank_e, TRUE, FALSE,
4835 EL_SPACESHIP_RIGHT, -1, -1
4838 Xtank_s, TRUE, FALSE,
4839 EL_SPACESHIP_DOWN, -1, -1
4842 Xtank_w, TRUE, FALSE,
4843 EL_SPACESHIP_LEFT, -1, -1
4846 Xtank_gon, FALSE, FALSE,
4847 EL_SPACESHIP_UP, -1, -1
4850 Xtank_goe, FALSE, FALSE,
4851 EL_SPACESHIP_RIGHT, -1, -1
4854 Xtank_gos, FALSE, FALSE,
4855 EL_SPACESHIP_DOWN, -1, -1
4858 Xtank_gow, FALSE, FALSE,
4859 EL_SPACESHIP_LEFT, -1, -1
4862 Ytank_n, FALSE, FALSE,
4863 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4866 Ytank_nB, FALSE, TRUE,
4867 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4870 Ytank_e, FALSE, FALSE,
4871 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4874 Ytank_eB, FALSE, TRUE,
4875 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4878 Ytank_s, FALSE, FALSE,
4879 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4882 Ytank_sB, FALSE, TRUE,
4883 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4886 Ytank_w, FALSE, FALSE,
4887 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4890 Ytank_wB, FALSE, TRUE,
4891 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4894 Ytank_w_n, FALSE, FALSE,
4895 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4898 Ytank_n_e, FALSE, FALSE,
4899 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4902 Ytank_e_s, FALSE, FALSE,
4903 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4906 Ytank_s_w, FALSE, FALSE,
4907 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4910 Ytank_e_n, FALSE, FALSE,
4911 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4914 Ytank_s_e, FALSE, FALSE,
4915 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4918 Ytank_w_s, FALSE, FALSE,
4919 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4922 Ytank_n_w, FALSE, FALSE,
4923 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4926 Ytank_stone, FALSE, FALSE,
4927 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4930 Ytank_spring, FALSE, FALSE,
4931 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4934 Xandroid, TRUE, FALSE,
4935 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4938 Xandroid_1_n, FALSE, FALSE,
4939 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4942 Xandroid_2_n, FALSE, FALSE,
4943 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4946 Xandroid_1_e, FALSE, FALSE,
4947 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4950 Xandroid_2_e, FALSE, FALSE,
4951 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4954 Xandroid_1_w, FALSE, FALSE,
4955 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4958 Xandroid_2_w, FALSE, FALSE,
4959 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4962 Xandroid_1_s, FALSE, FALSE,
4963 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4966 Xandroid_2_s, FALSE, FALSE,
4967 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4970 Yandroid_n, FALSE, FALSE,
4971 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4974 Yandroid_nB, FALSE, TRUE,
4975 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4978 Yandroid_ne, FALSE, FALSE,
4979 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4982 Yandroid_neB, FALSE, TRUE,
4983 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4986 Yandroid_e, FALSE, FALSE,
4987 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4990 Yandroid_eB, FALSE, TRUE,
4991 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4994 Yandroid_se, FALSE, FALSE,
4995 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4998 Yandroid_seB, FALSE, TRUE,
4999 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5002 Yandroid_s, FALSE, FALSE,
5003 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5006 Yandroid_sB, FALSE, TRUE,
5007 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5010 Yandroid_sw, FALSE, FALSE,
5011 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5014 Yandroid_swB, FALSE, TRUE,
5015 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5018 Yandroid_w, FALSE, FALSE,
5019 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5022 Yandroid_wB, FALSE, TRUE,
5023 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5026 Yandroid_nw, FALSE, FALSE,
5027 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5030 Yandroid_nwB, FALSE, TRUE,
5031 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5034 Xspring, TRUE, FALSE,
5038 Xspring_pause, FALSE, FALSE,
5042 Xspring_e, FALSE, FALSE,
5046 Xspring_w, FALSE, FALSE,
5050 Xspring_fall, FALSE, FALSE,
5054 Yspring_s, FALSE, FALSE,
5055 EL_SPRING, ACTION_FALLING, -1
5058 Yspring_sB, FALSE, TRUE,
5059 EL_SPRING, ACTION_FALLING, -1
5062 Yspring_e, FALSE, FALSE,
5063 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5066 Yspring_eB, FALSE, TRUE,
5067 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5070 Yspring_w, FALSE, FALSE,
5071 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5074 Yspring_wB, FALSE, TRUE,
5075 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5078 Yspring_kill_e, FALSE, FALSE,
5079 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5082 Yspring_kill_eB, FALSE, TRUE,
5083 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5086 Yspring_kill_w, FALSE, FALSE,
5087 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5090 Yspring_kill_wB, FALSE, TRUE,
5091 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5094 Xeater_n, TRUE, FALSE,
5095 EL_YAMYAM_UP, -1, -1
5098 Xeater_e, TRUE, FALSE,
5099 EL_YAMYAM_RIGHT, -1, -1
5102 Xeater_w, TRUE, FALSE,
5103 EL_YAMYAM_LEFT, -1, -1
5106 Xeater_s, TRUE, FALSE,
5107 EL_YAMYAM_DOWN, -1, -1
5110 Yeater_n, FALSE, FALSE,
5111 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5114 Yeater_nB, FALSE, TRUE,
5115 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5118 Yeater_e, FALSE, FALSE,
5119 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5122 Yeater_eB, FALSE, TRUE,
5123 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5126 Yeater_s, FALSE, FALSE,
5127 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5130 Yeater_sB, FALSE, TRUE,
5131 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5134 Yeater_w, FALSE, FALSE,
5135 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5138 Yeater_wB, FALSE, TRUE,
5139 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5142 Yeater_stone, FALSE, FALSE,
5143 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5146 Yeater_spring, FALSE, FALSE,
5147 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5150 Xalien, TRUE, FALSE,
5154 Xalien_pause, FALSE, FALSE,
5158 Yalien_n, FALSE, FALSE,
5159 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5162 Yalien_nB, FALSE, TRUE,
5163 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5166 Yalien_e, FALSE, FALSE,
5167 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5170 Yalien_eB, FALSE, TRUE,
5171 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5174 Yalien_s, FALSE, FALSE,
5175 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5178 Yalien_sB, FALSE, TRUE,
5179 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5182 Yalien_w, FALSE, FALSE,
5183 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5186 Yalien_wB, FALSE, TRUE,
5187 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5190 Yalien_stone, FALSE, FALSE,
5191 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5194 Yalien_spring, FALSE, FALSE,
5195 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5198 Xemerald, TRUE, FALSE,
5202 Xemerald_pause, FALSE, FALSE,
5206 Xemerald_fall, FALSE, FALSE,
5210 Xemerald_shine, FALSE, FALSE,
5211 EL_EMERALD, ACTION_TWINKLING, -1
5214 Yemerald_s, FALSE, FALSE,
5215 EL_EMERALD, ACTION_FALLING, -1
5218 Yemerald_sB, FALSE, TRUE,
5219 EL_EMERALD, ACTION_FALLING, -1
5222 Yemerald_e, FALSE, FALSE,
5223 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5226 Yemerald_eB, FALSE, TRUE,
5227 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5230 Yemerald_w, FALSE, FALSE,
5231 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5234 Yemerald_wB, FALSE, TRUE,
5235 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5238 Yemerald_eat, FALSE, FALSE,
5239 EL_EMERALD, ACTION_COLLECTING, -1
5242 Yemerald_stone, FALSE, FALSE,
5243 EL_NUT, ACTION_BREAKING, -1
5246 Xdiamond, TRUE, FALSE,
5250 Xdiamond_pause, FALSE, FALSE,
5254 Xdiamond_fall, FALSE, FALSE,
5258 Xdiamond_shine, FALSE, FALSE,
5259 EL_DIAMOND, ACTION_TWINKLING, -1
5262 Ydiamond_s, FALSE, FALSE,
5263 EL_DIAMOND, ACTION_FALLING, -1
5266 Ydiamond_sB, FALSE, TRUE,
5267 EL_DIAMOND, ACTION_FALLING, -1
5270 Ydiamond_e, FALSE, FALSE,
5271 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5274 Ydiamond_eB, FALSE, TRUE,
5275 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5278 Ydiamond_w, FALSE, FALSE,
5279 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5282 Ydiamond_wB, FALSE, TRUE,
5283 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5286 Ydiamond_eat, FALSE, FALSE,
5287 EL_DIAMOND, ACTION_COLLECTING, -1
5290 Ydiamond_stone, FALSE, FALSE,
5291 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5294 Xdrip_fall, TRUE, FALSE,
5295 EL_AMOEBA_DROP, -1, -1
5298 Xdrip_stretch, FALSE, FALSE,
5299 EL_AMOEBA_DROP, ACTION_FALLING, -1
5302 Xdrip_stretchB, FALSE, TRUE,
5303 EL_AMOEBA_DROP, ACTION_FALLING, -1
5306 Xdrip_eat, FALSE, FALSE,
5307 EL_AMOEBA_DROP, ACTION_GROWING, -1
5310 Ydrip_s1, FALSE, FALSE,
5311 EL_AMOEBA_DROP, ACTION_FALLING, -1
5314 Ydrip_s1B, FALSE, TRUE,
5315 EL_AMOEBA_DROP, ACTION_FALLING, -1
5318 Ydrip_s2, FALSE, FALSE,
5319 EL_AMOEBA_DROP, ACTION_FALLING, -1
5322 Ydrip_s2B, FALSE, TRUE,
5323 EL_AMOEBA_DROP, ACTION_FALLING, -1
5330 Xbomb_pause, FALSE, FALSE,
5334 Xbomb_fall, FALSE, FALSE,
5338 Ybomb_s, FALSE, FALSE,
5339 EL_BOMB, ACTION_FALLING, -1
5342 Ybomb_sB, FALSE, TRUE,
5343 EL_BOMB, ACTION_FALLING, -1
5346 Ybomb_e, FALSE, FALSE,
5347 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5350 Ybomb_eB, FALSE, TRUE,
5351 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5354 Ybomb_w, FALSE, FALSE,
5355 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5358 Ybomb_wB, FALSE, TRUE,
5359 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5362 Ybomb_eat, FALSE, FALSE,
5363 EL_BOMB, ACTION_ACTIVATING, -1
5366 Xballoon, TRUE, FALSE,
5370 Yballoon_n, FALSE, FALSE,
5371 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5374 Yballoon_nB, FALSE, TRUE,
5375 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5378 Yballoon_e, FALSE, FALSE,
5379 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5382 Yballoon_eB, FALSE, TRUE,
5383 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5386 Yballoon_s, FALSE, FALSE,
5387 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5390 Yballoon_sB, FALSE, TRUE,
5391 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5394 Yballoon_w, FALSE, FALSE,
5395 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5398 Yballoon_wB, FALSE, TRUE,
5399 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5402 Xgrass, TRUE, FALSE,
5403 EL_EMC_GRASS, -1, -1
5406 Ygrass_nB, FALSE, FALSE,
5407 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5410 Ygrass_eB, FALSE, FALSE,
5411 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5414 Ygrass_sB, FALSE, FALSE,
5415 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5418 Ygrass_wB, FALSE, FALSE,
5419 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5426 Ydirt_nB, FALSE, FALSE,
5427 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5430 Ydirt_eB, FALSE, FALSE,
5431 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5434 Ydirt_sB, FALSE, FALSE,
5435 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5438 Ydirt_wB, FALSE, FALSE,
5439 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5442 Xacid_ne, TRUE, FALSE,
5443 EL_ACID_POOL_TOPRIGHT, -1, -1
5446 Xacid_se, TRUE, FALSE,
5447 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5450 Xacid_s, TRUE, FALSE,
5451 EL_ACID_POOL_BOTTOM, -1, -1
5454 Xacid_sw, TRUE, FALSE,
5455 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5458 Xacid_nw, TRUE, FALSE,
5459 EL_ACID_POOL_TOPLEFT, -1, -1
5462 Xacid_1, TRUE, FALSE,
5466 Xacid_2, FALSE, FALSE,
5470 Xacid_3, FALSE, FALSE,
5474 Xacid_4, FALSE, FALSE,
5478 Xacid_5, FALSE, FALSE,
5482 Xacid_6, FALSE, FALSE,
5486 Xacid_7, FALSE, FALSE,
5490 Xacid_8, FALSE, FALSE,
5494 Xball_1, TRUE, FALSE,
5495 EL_EMC_MAGIC_BALL, -1, -1
5498 Xball_1B, FALSE, FALSE,
5499 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5502 Xball_2, FALSE, FALSE,
5503 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5506 Xball_2B, FALSE, FALSE,
5507 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5510 Yball_eat, FALSE, FALSE,
5511 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5514 Ykey_1_eat, FALSE, FALSE,
5515 EL_EM_KEY_1, ACTION_COLLECTING, -1
5518 Ykey_2_eat, FALSE, FALSE,
5519 EL_EM_KEY_2, ACTION_COLLECTING, -1
5522 Ykey_3_eat, FALSE, FALSE,
5523 EL_EM_KEY_3, ACTION_COLLECTING, -1
5526 Ykey_4_eat, FALSE, FALSE,
5527 EL_EM_KEY_4, ACTION_COLLECTING, -1
5530 Ykey_5_eat, FALSE, FALSE,
5531 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5534 Ykey_6_eat, FALSE, FALSE,
5535 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5538 Ykey_7_eat, FALSE, FALSE,
5539 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5542 Ykey_8_eat, FALSE, FALSE,
5543 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5546 Ylenses_eat, FALSE, FALSE,
5547 EL_EMC_LENSES, ACTION_COLLECTING, -1
5550 Ymagnify_eat, FALSE, FALSE,
5551 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5554 Ygrass_eat, FALSE, FALSE,
5555 EL_EMC_GRASS, ACTION_SNAPPING, -1
5558 Ydirt_eat, FALSE, FALSE,
5559 EL_SAND, ACTION_SNAPPING, -1
5562 Xgrow_ns, TRUE, FALSE,
5563 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5566 Ygrow_ns_eat, FALSE, FALSE,
5567 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5570 Xgrow_ew, TRUE, FALSE,
5571 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5574 Ygrow_ew_eat, FALSE, FALSE,
5575 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5578 Xwonderwall, TRUE, FALSE,
5579 EL_MAGIC_WALL, -1, -1
5582 XwonderwallB, FALSE, FALSE,
5583 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5586 Xamoeba_1, TRUE, FALSE,
5587 EL_AMOEBA_DRY, ACTION_OTHER, -1
5590 Xamoeba_2, FALSE, FALSE,
5591 EL_AMOEBA_DRY, ACTION_OTHER, -1
5594 Xamoeba_3, FALSE, FALSE,
5595 EL_AMOEBA_DRY, ACTION_OTHER, -1
5598 Xamoeba_4, FALSE, FALSE,
5599 EL_AMOEBA_DRY, ACTION_OTHER, -1
5602 Xamoeba_5, TRUE, FALSE,
5603 EL_AMOEBA_WET, ACTION_OTHER, -1
5606 Xamoeba_6, FALSE, FALSE,
5607 EL_AMOEBA_WET, ACTION_OTHER, -1
5610 Xamoeba_7, FALSE, FALSE,
5611 EL_AMOEBA_WET, ACTION_OTHER, -1
5614 Xamoeba_8, FALSE, FALSE,
5615 EL_AMOEBA_WET, ACTION_OTHER, -1
5618 Xdoor_1, TRUE, FALSE,
5619 EL_EM_GATE_1, -1, -1
5622 Xdoor_2, TRUE, FALSE,
5623 EL_EM_GATE_2, -1, -1
5626 Xdoor_3, TRUE, FALSE,
5627 EL_EM_GATE_3, -1, -1
5630 Xdoor_4, TRUE, FALSE,
5631 EL_EM_GATE_4, -1, -1
5634 Xdoor_5, TRUE, FALSE,
5635 EL_EMC_GATE_5, -1, -1
5638 Xdoor_6, TRUE, FALSE,
5639 EL_EMC_GATE_6, -1, -1
5642 Xdoor_7, TRUE, FALSE,
5643 EL_EMC_GATE_7, -1, -1
5646 Xdoor_8, TRUE, FALSE,
5647 EL_EMC_GATE_8, -1, -1
5650 Xkey_1, TRUE, FALSE,
5654 Xkey_2, TRUE, FALSE,
5658 Xkey_3, TRUE, FALSE,
5662 Xkey_4, TRUE, FALSE,
5666 Xkey_5, TRUE, FALSE,
5667 EL_EMC_KEY_5, -1, -1
5670 Xkey_6, TRUE, FALSE,
5671 EL_EMC_KEY_6, -1, -1
5674 Xkey_7, TRUE, FALSE,
5675 EL_EMC_KEY_7, -1, -1
5678 Xkey_8, TRUE, FALSE,
5679 EL_EMC_KEY_8, -1, -1
5682 Xwind_n, TRUE, FALSE,
5683 EL_BALLOON_SWITCH_UP, -1, -1
5686 Xwind_e, TRUE, FALSE,
5687 EL_BALLOON_SWITCH_RIGHT, -1, -1
5690 Xwind_s, TRUE, FALSE,
5691 EL_BALLOON_SWITCH_DOWN, -1, -1
5694 Xwind_w, TRUE, FALSE,
5695 EL_BALLOON_SWITCH_LEFT, -1, -1
5698 Xwind_nesw, TRUE, FALSE,
5699 EL_BALLOON_SWITCH_ANY, -1, -1
5702 Xwind_stop, TRUE, FALSE,
5703 EL_BALLOON_SWITCH_NONE, -1, -1
5707 EL_EM_EXIT_CLOSED, -1, -1
5710 Xexit_1, TRUE, FALSE,
5711 EL_EM_EXIT_OPEN, -1, -1
5714 Xexit_2, FALSE, FALSE,
5715 EL_EM_EXIT_OPEN, -1, -1
5718 Xexit_3, FALSE, FALSE,
5719 EL_EM_EXIT_OPEN, -1, -1
5722 Xdynamite, TRUE, FALSE,
5723 EL_EM_DYNAMITE, -1, -1
5726 Ydynamite_eat, FALSE, FALSE,
5727 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5730 Xdynamite_1, TRUE, FALSE,
5731 EL_EM_DYNAMITE_ACTIVE, -1, -1
5734 Xdynamite_2, FALSE, FALSE,
5735 EL_EM_DYNAMITE_ACTIVE, -1, -1
5738 Xdynamite_3, FALSE, FALSE,
5739 EL_EM_DYNAMITE_ACTIVE, -1, -1
5742 Xdynamite_4, FALSE, FALSE,
5743 EL_EM_DYNAMITE_ACTIVE, -1, -1
5746 Xbumper, TRUE, FALSE,
5747 EL_EMC_SPRING_BUMPER, -1, -1
5750 XbumperB, FALSE, FALSE,
5751 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5754 Xwheel, TRUE, FALSE,
5755 EL_ROBOT_WHEEL, -1, -1
5758 XwheelB, FALSE, FALSE,
5759 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5762 Xswitch, TRUE, FALSE,
5763 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5766 XswitchB, FALSE, FALSE,
5767 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5771 EL_QUICKSAND_EMPTY, -1, -1
5774 Xsand_stone, TRUE, FALSE,
5775 EL_QUICKSAND_FULL, -1, -1
5778 Xsand_stonein_1, FALSE, TRUE,
5779 EL_ROCK, ACTION_FILLING, -1
5782 Xsand_stonein_2, FALSE, TRUE,
5783 EL_ROCK, ACTION_FILLING, -1
5786 Xsand_stonein_3, FALSE, TRUE,
5787 EL_ROCK, ACTION_FILLING, -1
5790 Xsand_stonein_4, FALSE, TRUE,
5791 EL_ROCK, ACTION_FILLING, -1
5794 Xsand_stonesand_1, FALSE, FALSE,
5795 EL_QUICKSAND_EMPTYING, -1, -1
5798 Xsand_stonesand_2, FALSE, FALSE,
5799 EL_QUICKSAND_EMPTYING, -1, -1
5802 Xsand_stonesand_3, FALSE, FALSE,
5803 EL_QUICKSAND_EMPTYING, -1, -1
5806 Xsand_stonesand_4, FALSE, FALSE,
5807 EL_QUICKSAND_EMPTYING, -1, -1
5810 Xsand_stonesand_quickout_1, FALSE, FALSE,
5811 EL_QUICKSAND_EMPTYING, -1, -1
5814 Xsand_stonesand_quickout_2, FALSE, FALSE,
5815 EL_QUICKSAND_EMPTYING, -1, -1
5818 Xsand_stoneout_1, FALSE, FALSE,
5819 EL_ROCK, ACTION_EMPTYING, -1
5822 Xsand_stoneout_2, FALSE, FALSE,
5823 EL_ROCK, ACTION_EMPTYING, -1
5826 Xsand_sandstone_1, FALSE, FALSE,
5827 EL_QUICKSAND_FILLING, -1, -1
5830 Xsand_sandstone_2, FALSE, FALSE,
5831 EL_QUICKSAND_FILLING, -1, -1
5834 Xsand_sandstone_3, FALSE, FALSE,
5835 EL_QUICKSAND_FILLING, -1, -1
5838 Xsand_sandstone_4, FALSE, FALSE,
5839 EL_QUICKSAND_FILLING, -1, -1
5842 Xplant, TRUE, FALSE,
5843 EL_EMC_PLANT, -1, -1
5846 Yplant, FALSE, FALSE,
5847 EL_EMC_PLANT, -1, -1
5850 Xlenses, TRUE, FALSE,
5851 EL_EMC_LENSES, -1, -1
5854 Xmagnify, TRUE, FALSE,
5855 EL_EMC_MAGNIFIER, -1, -1
5858 Xdripper, TRUE, FALSE,
5859 EL_EMC_DRIPPER, -1, -1
5862 XdripperB, FALSE, FALSE,
5863 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5866 Xfake_blank, TRUE, FALSE,
5867 EL_INVISIBLE_WALL, -1, -1
5870 Xfake_blankB, FALSE, FALSE,
5871 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5874 Xfake_grass, TRUE, FALSE,
5875 EL_EMC_FAKE_GRASS, -1, -1
5878 Xfake_grassB, FALSE, FALSE,
5879 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5882 Xfake_door_1, TRUE, FALSE,
5883 EL_EM_GATE_1_GRAY, -1, -1
5886 Xfake_door_2, TRUE, FALSE,
5887 EL_EM_GATE_2_GRAY, -1, -1
5890 Xfake_door_3, TRUE, FALSE,
5891 EL_EM_GATE_3_GRAY, -1, -1
5894 Xfake_door_4, TRUE, FALSE,
5895 EL_EM_GATE_4_GRAY, -1, -1
5898 Xfake_door_5, TRUE, FALSE,
5899 EL_EMC_GATE_5_GRAY, -1, -1
5902 Xfake_door_6, TRUE, FALSE,
5903 EL_EMC_GATE_6_GRAY, -1, -1
5906 Xfake_door_7, TRUE, FALSE,
5907 EL_EMC_GATE_7_GRAY, -1, -1
5910 Xfake_door_8, TRUE, FALSE,
5911 EL_EMC_GATE_8_GRAY, -1, -1
5914 Xfake_acid_1, TRUE, FALSE,
5915 EL_EMC_FAKE_ACID, -1, -1
5918 Xfake_acid_2, FALSE, FALSE,
5919 EL_EMC_FAKE_ACID, -1, -1
5922 Xfake_acid_3, FALSE, FALSE,
5923 EL_EMC_FAKE_ACID, -1, -1
5926 Xfake_acid_4, FALSE, FALSE,
5927 EL_EMC_FAKE_ACID, -1, -1
5930 Xfake_acid_5, FALSE, FALSE,
5931 EL_EMC_FAKE_ACID, -1, -1
5934 Xfake_acid_6, FALSE, FALSE,
5935 EL_EMC_FAKE_ACID, -1, -1
5938 Xfake_acid_7, FALSE, FALSE,
5939 EL_EMC_FAKE_ACID, -1, -1
5942 Xfake_acid_8, FALSE, FALSE,
5943 EL_EMC_FAKE_ACID, -1, -1
5946 Xsteel_1, TRUE, FALSE,
5947 EL_STEELWALL, -1, -1
5950 Xsteel_2, TRUE, FALSE,
5951 EL_EMC_STEELWALL_2, -1, -1
5954 Xsteel_3, TRUE, FALSE,
5955 EL_EMC_STEELWALL_3, -1, -1
5958 Xsteel_4, TRUE, FALSE,
5959 EL_EMC_STEELWALL_4, -1, -1
5962 Xwall_1, TRUE, FALSE,
5966 Xwall_2, TRUE, FALSE,
5967 EL_EMC_WALL_14, -1, -1
5970 Xwall_3, TRUE, FALSE,
5971 EL_EMC_WALL_15, -1, -1
5974 Xwall_4, TRUE, FALSE,
5975 EL_EMC_WALL_16, -1, -1
5978 Xround_wall_1, TRUE, FALSE,
5979 EL_WALL_SLIPPERY, -1, -1
5982 Xround_wall_2, TRUE, FALSE,
5983 EL_EMC_WALL_SLIPPERY_2, -1, -1
5986 Xround_wall_3, TRUE, FALSE,
5987 EL_EMC_WALL_SLIPPERY_3, -1, -1
5990 Xround_wall_4, TRUE, FALSE,
5991 EL_EMC_WALL_SLIPPERY_4, -1, -1
5994 Xdecor_1, TRUE, FALSE,
5995 EL_EMC_WALL_8, -1, -1
5998 Xdecor_2, TRUE, FALSE,
5999 EL_EMC_WALL_6, -1, -1
6002 Xdecor_3, TRUE, FALSE,
6003 EL_EMC_WALL_4, -1, -1
6006 Xdecor_4, TRUE, FALSE,
6007 EL_EMC_WALL_7, -1, -1
6010 Xdecor_5, TRUE, FALSE,
6011 EL_EMC_WALL_5, -1, -1
6014 Xdecor_6, TRUE, FALSE,
6015 EL_EMC_WALL_9, -1, -1
6018 Xdecor_7, TRUE, FALSE,
6019 EL_EMC_WALL_10, -1, -1
6022 Xdecor_8, TRUE, FALSE,
6023 EL_EMC_WALL_1, -1, -1
6026 Xdecor_9, TRUE, FALSE,
6027 EL_EMC_WALL_2, -1, -1
6030 Xdecor_10, TRUE, FALSE,
6031 EL_EMC_WALL_3, -1, -1
6034 Xdecor_11, TRUE, FALSE,
6035 EL_EMC_WALL_11, -1, -1
6038 Xdecor_12, TRUE, FALSE,
6039 EL_EMC_WALL_12, -1, -1
6042 Xalpha_0, TRUE, FALSE,
6043 EL_CHAR('0'), -1, -1
6046 Xalpha_1, TRUE, FALSE,
6047 EL_CHAR('1'), -1, -1
6050 Xalpha_2, TRUE, FALSE,
6051 EL_CHAR('2'), -1, -1
6054 Xalpha_3, TRUE, FALSE,
6055 EL_CHAR('3'), -1, -1
6058 Xalpha_4, TRUE, FALSE,
6059 EL_CHAR('4'), -1, -1
6062 Xalpha_5, TRUE, FALSE,
6063 EL_CHAR('5'), -1, -1
6066 Xalpha_6, TRUE, FALSE,
6067 EL_CHAR('6'), -1, -1
6070 Xalpha_7, TRUE, FALSE,
6071 EL_CHAR('7'), -1, -1
6074 Xalpha_8, TRUE, FALSE,
6075 EL_CHAR('8'), -1, -1
6078 Xalpha_9, TRUE, FALSE,
6079 EL_CHAR('9'), -1, -1
6082 Xalpha_excla, TRUE, FALSE,
6083 EL_CHAR('!'), -1, -1
6086 Xalpha_quote, TRUE, FALSE,
6087 EL_CHAR('"'), -1, -1
6090 Xalpha_comma, TRUE, FALSE,
6091 EL_CHAR(','), -1, -1
6094 Xalpha_minus, TRUE, FALSE,
6095 EL_CHAR('-'), -1, -1
6098 Xalpha_perio, TRUE, FALSE,
6099 EL_CHAR('.'), -1, -1
6102 Xalpha_colon, TRUE, FALSE,
6103 EL_CHAR(':'), -1, -1
6106 Xalpha_quest, TRUE, FALSE,
6107 EL_CHAR('?'), -1, -1
6110 Xalpha_a, TRUE, FALSE,
6111 EL_CHAR('A'), -1, -1
6114 Xalpha_b, TRUE, FALSE,
6115 EL_CHAR('B'), -1, -1
6118 Xalpha_c, TRUE, FALSE,
6119 EL_CHAR('C'), -1, -1
6122 Xalpha_d, TRUE, FALSE,
6123 EL_CHAR('D'), -1, -1
6126 Xalpha_e, TRUE, FALSE,
6127 EL_CHAR('E'), -1, -1
6130 Xalpha_f, TRUE, FALSE,
6131 EL_CHAR('F'), -1, -1
6134 Xalpha_g, TRUE, FALSE,
6135 EL_CHAR('G'), -1, -1
6138 Xalpha_h, TRUE, FALSE,
6139 EL_CHAR('H'), -1, -1
6142 Xalpha_i, TRUE, FALSE,
6143 EL_CHAR('I'), -1, -1
6146 Xalpha_j, TRUE, FALSE,
6147 EL_CHAR('J'), -1, -1
6150 Xalpha_k, TRUE, FALSE,
6151 EL_CHAR('K'), -1, -1
6154 Xalpha_l, TRUE, FALSE,
6155 EL_CHAR('L'), -1, -1
6158 Xalpha_m, TRUE, FALSE,
6159 EL_CHAR('M'), -1, -1
6162 Xalpha_n, TRUE, FALSE,
6163 EL_CHAR('N'), -1, -1
6166 Xalpha_o, TRUE, FALSE,
6167 EL_CHAR('O'), -1, -1
6170 Xalpha_p, TRUE, FALSE,
6171 EL_CHAR('P'), -1, -1
6174 Xalpha_q, TRUE, FALSE,
6175 EL_CHAR('Q'), -1, -1
6178 Xalpha_r, TRUE, FALSE,
6179 EL_CHAR('R'), -1, -1
6182 Xalpha_s, TRUE, FALSE,
6183 EL_CHAR('S'), -1, -1
6186 Xalpha_t, TRUE, FALSE,
6187 EL_CHAR('T'), -1, -1
6190 Xalpha_u, TRUE, FALSE,
6191 EL_CHAR('U'), -1, -1
6194 Xalpha_v, TRUE, FALSE,
6195 EL_CHAR('V'), -1, -1
6198 Xalpha_w, TRUE, FALSE,
6199 EL_CHAR('W'), -1, -1
6202 Xalpha_x, TRUE, FALSE,
6203 EL_CHAR('X'), -1, -1
6206 Xalpha_y, TRUE, FALSE,
6207 EL_CHAR('Y'), -1, -1
6210 Xalpha_z, TRUE, FALSE,
6211 EL_CHAR('Z'), -1, -1
6214 Xalpha_arrow_e, TRUE, FALSE,
6215 EL_CHAR('>'), -1, -1
6218 Xalpha_arrow_w, TRUE, FALSE,
6219 EL_CHAR('<'), -1, -1
6222 Xalpha_copyr, TRUE, FALSE,
6223 EL_CHAR('©'), -1, -1
6227 Xboom_bug, FALSE, FALSE,
6228 EL_BUG, ACTION_EXPLODING, -1
6231 Xboom_bomb, FALSE, FALSE,
6232 EL_BOMB, ACTION_EXPLODING, -1
6235 Xboom_android, FALSE, FALSE,
6236 EL_EMC_ANDROID, ACTION_OTHER, -1
6239 Xboom_1, FALSE, FALSE,
6240 EL_DEFAULT, ACTION_EXPLODING, -1
6243 Xboom_2, FALSE, FALSE,
6244 EL_DEFAULT, ACTION_EXPLODING, -1
6247 Znormal, FALSE, FALSE,
6251 Zdynamite, FALSE, FALSE,
6255 Zplayer, FALSE, FALSE,
6259 ZBORDER, FALSE, FALSE,
6269 static struct Mapping_EM_to_RND_player
6278 em_player_mapping_list[] =
6282 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6286 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6290 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6294 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6298 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6302 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6306 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6310 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6314 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6318 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6322 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6326 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6330 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6334 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6338 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6342 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6346 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6350 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6354 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6358 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6362 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6366 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6370 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6374 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6378 EL_PLAYER_1, ACTION_DEFAULT, -1,
6382 EL_PLAYER_2, ACTION_DEFAULT, -1,
6386 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6390 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6394 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6398 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6402 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6406 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6410 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6414 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6418 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6422 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6426 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6430 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6434 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6438 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6442 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6446 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6450 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6454 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6458 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6462 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6466 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6470 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6474 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6478 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6482 EL_PLAYER_3, ACTION_DEFAULT, -1,
6486 EL_PLAYER_4, ACTION_DEFAULT, -1,
6495 int map_element_RND_to_EM(int element_rnd)
6497 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6498 static boolean mapping_initialized = FALSE;
6500 if (!mapping_initialized)
6504 /* return "Xalpha_quest" for all undefined elements in mapping array */
6505 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6506 mapping_RND_to_EM[i] = Xalpha_quest;
6508 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6509 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6510 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6511 em_object_mapping_list[i].element_em;
6513 mapping_initialized = TRUE;
6516 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6517 return mapping_RND_to_EM[element_rnd];
6519 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6524 int map_element_EM_to_RND(int element_em)
6526 static unsigned short mapping_EM_to_RND[TILE_MAX];
6527 static boolean mapping_initialized = FALSE;
6529 if (!mapping_initialized)
6533 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6534 for (i = 0; i < TILE_MAX; i++)
6535 mapping_EM_to_RND[i] = EL_UNKNOWN;
6537 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6538 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6539 em_object_mapping_list[i].element_rnd;
6541 mapping_initialized = TRUE;
6544 if (element_em >= 0 && element_em < TILE_MAX)
6545 return mapping_EM_to_RND[element_em];
6547 Error(ERR_WARN, "invalid EM level element %d", element_em);
6552 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6554 struct LevelInfo_EM *level_em = level->native_em_level;
6555 struct LEVEL *lev = level_em->lev;
6558 for (i = 0; i < TILE_MAX; i++)
6559 lev->android_array[i] = Xblank;
6561 for (i = 0; i < level->num_android_clone_elements; i++)
6563 int element_rnd = level->android_clone_element[i];
6564 int element_em = map_element_RND_to_EM(element_rnd);
6566 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6567 if (em_object_mapping_list[j].element_rnd == element_rnd)
6568 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6572 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6574 struct LevelInfo_EM *level_em = level->native_em_level;
6575 struct LEVEL *lev = level_em->lev;
6578 level->num_android_clone_elements = 0;
6580 for (i = 0; i < TILE_MAX; i++)
6582 int element_em = lev->android_array[i];
6584 boolean element_found = FALSE;
6586 if (element_em == Xblank)
6589 element_rnd = map_element_EM_to_RND(element_em);
6591 for (j = 0; j < level->num_android_clone_elements; j++)
6592 if (level->android_clone_element[j] == element_rnd)
6593 element_found = TRUE;
6597 level->android_clone_element[level->num_android_clone_elements++] =
6600 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6605 if (level->num_android_clone_elements == 0)
6607 level->num_android_clone_elements = 1;
6608 level->android_clone_element[0] = EL_EMPTY;
6612 int map_direction_RND_to_EM(int direction)
6614 return (direction == MV_UP ? 0 :
6615 direction == MV_RIGHT ? 1 :
6616 direction == MV_DOWN ? 2 :
6617 direction == MV_LEFT ? 3 :
6621 int map_direction_EM_to_RND(int direction)
6623 return (direction == 0 ? MV_UP :
6624 direction == 1 ? MV_RIGHT :
6625 direction == 2 ? MV_DOWN :
6626 direction == 3 ? MV_LEFT :
6630 int map_element_RND_to_SP(int element_rnd)
6632 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6634 if (element_rnd >= EL_SP_START &&
6635 element_rnd <= EL_SP_END)
6636 element_sp = element_rnd - EL_SP_START;
6637 else if (element_rnd == EL_EMPTY_SPACE)
6639 else if (element_rnd == EL_INVISIBLE_WALL)
6645 int map_element_SP_to_RND(int element_sp)
6647 int element_rnd = EL_UNKNOWN;
6649 if (element_sp >= 0x00 &&
6651 element_rnd = EL_SP_START + element_sp;
6652 else if (element_sp == 0x28)
6653 element_rnd = EL_INVISIBLE_WALL;
6658 int map_action_SP_to_RND(int action_sp)
6662 case actActive: return ACTION_ACTIVE;
6663 case actImpact: return ACTION_IMPACT;
6664 case actExploding: return ACTION_EXPLODING;
6665 case actDigging: return ACTION_DIGGING;
6666 case actSnapping: return ACTION_SNAPPING;
6667 case actCollecting: return ACTION_COLLECTING;
6668 case actPassing: return ACTION_PASSING;
6669 case actPushing: return ACTION_PUSHING;
6670 case actDropping: return ACTION_DROPPING;
6672 default: return ACTION_DEFAULT;
6676 int get_next_element(int element)
6680 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6681 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6682 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6683 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6684 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6685 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6686 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6687 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6688 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6689 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6690 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6692 default: return element;
6696 int el_act_dir2img(int element, int action, int direction)
6698 element = GFX_ELEMENT(element);
6699 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6701 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6702 return element_info[element].direction_graphic[action][direction];
6705 static int el_act_dir2crm(int element, int action, int direction)
6707 element = GFX_ELEMENT(element);
6708 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6710 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6711 return element_info[element].direction_crumbled[action][direction];
6714 int el_act2img(int element, int action)
6716 element = GFX_ELEMENT(element);
6718 return element_info[element].graphic[action];
6721 int el_act2crm(int element, int action)
6723 element = GFX_ELEMENT(element);
6725 return element_info[element].crumbled[action];
6728 int el_dir2img(int element, int direction)
6730 element = GFX_ELEMENT(element);
6732 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6735 int el2baseimg(int element)
6737 return element_info[element].graphic[ACTION_DEFAULT];
6740 int el2img(int element)
6742 element = GFX_ELEMENT(element);
6744 return element_info[element].graphic[ACTION_DEFAULT];
6747 int el2edimg(int element)
6749 element = GFX_ELEMENT(element);
6751 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6754 int el2preimg(int element)
6756 element = GFX_ELEMENT(element);
6758 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6761 int el2panelimg(int element)
6763 element = GFX_ELEMENT(element);
6765 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6768 int font2baseimg(int font_nr)
6770 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6773 int getBeltNrFromBeltElement(int element)
6775 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6776 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6777 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6780 int getBeltNrFromBeltActiveElement(int element)
6782 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6783 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6784 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6787 int getBeltNrFromBeltSwitchElement(int element)
6789 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6790 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6791 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6794 int getBeltDirNrFromBeltElement(int element)
6796 static int belt_base_element[4] =
6798 EL_CONVEYOR_BELT_1_LEFT,
6799 EL_CONVEYOR_BELT_2_LEFT,
6800 EL_CONVEYOR_BELT_3_LEFT,
6801 EL_CONVEYOR_BELT_4_LEFT
6804 int belt_nr = getBeltNrFromBeltElement(element);
6805 int belt_dir_nr = element - belt_base_element[belt_nr];
6807 return (belt_dir_nr % 3);
6810 int getBeltDirNrFromBeltSwitchElement(int element)
6812 static int belt_base_element[4] =
6814 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6815 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6816 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6817 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6820 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6821 int belt_dir_nr = element - belt_base_element[belt_nr];
6823 return (belt_dir_nr % 3);
6826 int getBeltDirFromBeltElement(int element)
6828 static int belt_move_dir[3] =
6835 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6837 return belt_move_dir[belt_dir_nr];
6840 int getBeltDirFromBeltSwitchElement(int element)
6842 static int belt_move_dir[3] =
6849 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6851 return belt_move_dir[belt_dir_nr];
6854 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6856 static int belt_base_element[4] =
6858 EL_CONVEYOR_BELT_1_LEFT,
6859 EL_CONVEYOR_BELT_2_LEFT,
6860 EL_CONVEYOR_BELT_3_LEFT,
6861 EL_CONVEYOR_BELT_4_LEFT
6864 return belt_base_element[belt_nr] + belt_dir_nr;
6867 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6869 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6871 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6874 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6876 static int belt_base_element[4] =
6878 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6879 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6880 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6881 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6884 return belt_base_element[belt_nr] + belt_dir_nr;
6887 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6889 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6891 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6894 boolean getTeamMode_EM()
6896 return game.team_mode;
6899 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6901 int game_frame_delay_value;
6903 game_frame_delay_value =
6904 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6905 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6908 if (tape.playing && tape.warp_forward && !tape.pausing)
6909 game_frame_delay_value = 0;
6911 return game_frame_delay_value;
6914 unsigned int InitRND(int seed)
6916 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6917 return InitEngineRandom_EM(seed);
6918 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6919 return InitEngineRandom_SP(seed);
6921 return InitEngineRandom_RND(seed);
6924 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6925 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6927 inline static int get_effective_element_EM(int tile, int frame_em)
6929 int element = object_mapping[tile].element_rnd;
6930 int action = object_mapping[tile].action;
6931 boolean is_backside = object_mapping[tile].is_backside;
6932 boolean action_removing = (action == ACTION_DIGGING ||
6933 action == ACTION_SNAPPING ||
6934 action == ACTION_COLLECTING);
6940 case Yacid_splash_eB:
6941 case Yacid_splash_wB:
6942 return (frame_em > 5 ? EL_EMPTY : element);
6948 else /* frame_em == 7 */
6952 case Yacid_splash_eB:
6953 case Yacid_splash_wB:
6956 case Yemerald_stone:
6959 case Ydiamond_stone:
6963 case Xdrip_stretchB:
6982 case Xsand_stonein_1:
6983 case Xsand_stonein_2:
6984 case Xsand_stonein_3:
6985 case Xsand_stonein_4:
6989 return (is_backside || action_removing ? EL_EMPTY : element);
6994 inline static boolean check_linear_animation_EM(int tile)
6998 case Xsand_stonesand_1:
6999 case Xsand_stonesand_quickout_1:
7000 case Xsand_sandstone_1:
7001 case Xsand_stonein_1:
7002 case Xsand_stoneout_1:
7021 case Yacid_splash_eB:
7022 case Yacid_splash_wB:
7023 case Yemerald_stone:
7030 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7031 boolean has_crumbled_graphics,
7032 int crumbled, int sync_frame)
7034 /* if element can be crumbled, but certain action graphics are just empty
7035 space (like instantly snapping sand to empty space in 1 frame), do not
7036 treat these empty space graphics as crumbled graphics in EMC engine */
7037 if (crumbled == IMG_EMPTY_SPACE)
7038 has_crumbled_graphics = FALSE;
7040 if (has_crumbled_graphics)
7042 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7043 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7044 g_crumbled->anim_delay,
7045 g_crumbled->anim_mode,
7046 g_crumbled->anim_start_frame,
7049 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7050 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7052 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7054 g_em->has_crumbled_graphics = TRUE;
7058 g_em->crumbled_bitmap = NULL;
7059 g_em->crumbled_src_x = 0;
7060 g_em->crumbled_src_y = 0;
7061 g_em->crumbled_border_size = 0;
7063 g_em->has_crumbled_graphics = FALSE;
7067 void ResetGfxAnimation_EM(int x, int y, int tile)
7072 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7073 int tile, int frame_em, int x, int y)
7075 int action = object_mapping[tile].action;
7076 int direction = object_mapping[tile].direction;
7077 int effective_element = get_effective_element_EM(tile, frame_em);
7078 int graphic = (direction == MV_NONE ?
7079 el_act2img(effective_element, action) :
7080 el_act_dir2img(effective_element, action, direction));
7081 struct GraphicInfo *g = &graphic_info[graphic];
7083 boolean action_removing = (action == ACTION_DIGGING ||
7084 action == ACTION_SNAPPING ||
7085 action == ACTION_COLLECTING);
7086 boolean action_moving = (action == ACTION_FALLING ||
7087 action == ACTION_MOVING ||
7088 action == ACTION_PUSHING ||
7089 action == ACTION_EATING ||
7090 action == ACTION_FILLING ||
7091 action == ACTION_EMPTYING);
7092 boolean action_falling = (action == ACTION_FALLING ||
7093 action == ACTION_FILLING ||
7094 action == ACTION_EMPTYING);
7096 /* special case: graphic uses "2nd movement tile" and has defined
7097 7 frames for movement animation (or less) => use default graphic
7098 for last (8th) frame which ends the movement animation */
7099 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7101 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7102 graphic = (direction == MV_NONE ?
7103 el_act2img(effective_element, action) :
7104 el_act_dir2img(effective_element, action, direction));
7106 g = &graphic_info[graphic];
7109 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7113 else if (action_moving)
7115 boolean is_backside = object_mapping[tile].is_backside;
7119 int direction = object_mapping[tile].direction;
7120 int move_dir = (action_falling ? MV_DOWN : direction);
7125 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7126 if (g->double_movement && frame_em == 0)
7130 if (move_dir == MV_LEFT)
7131 GfxFrame[x - 1][y] = GfxFrame[x][y];
7132 else if (move_dir == MV_RIGHT)
7133 GfxFrame[x + 1][y] = GfxFrame[x][y];
7134 else if (move_dir == MV_UP)
7135 GfxFrame[x][y - 1] = GfxFrame[x][y];
7136 else if (move_dir == MV_DOWN)
7137 GfxFrame[x][y + 1] = GfxFrame[x][y];
7144 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7145 if (tile == Xsand_stonesand_quickout_1 ||
7146 tile == Xsand_stonesand_quickout_2)
7150 if (graphic_info[graphic].anim_global_sync)
7151 sync_frame = FrameCounter;
7152 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7153 sync_frame = GfxFrame[x][y];
7155 sync_frame = 0; /* playfield border (pseudo steel) */
7157 SetRandomAnimationValue(x, y);
7159 int frame = getAnimationFrame(g->anim_frames,
7162 g->anim_start_frame,
7165 g_em->unique_identifier =
7166 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7169 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7170 int tile, int frame_em, int x, int y)
7172 int action = object_mapping[tile].action;
7173 int direction = object_mapping[tile].direction;
7174 boolean is_backside = object_mapping[tile].is_backside;
7175 int effective_element = get_effective_element_EM(tile, frame_em);
7176 int effective_action = action;
7177 int graphic = (direction == MV_NONE ?
7178 el_act2img(effective_element, effective_action) :
7179 el_act_dir2img(effective_element, effective_action,
7181 int crumbled = (direction == MV_NONE ?
7182 el_act2crm(effective_element, effective_action) :
7183 el_act_dir2crm(effective_element, effective_action,
7185 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7186 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7187 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7188 struct GraphicInfo *g = &graphic_info[graphic];
7191 /* special case: graphic uses "2nd movement tile" and has defined
7192 7 frames for movement animation (or less) => use default graphic
7193 for last (8th) frame which ends the movement animation */
7194 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7196 effective_action = ACTION_DEFAULT;
7197 graphic = (direction == MV_NONE ?
7198 el_act2img(effective_element, effective_action) :
7199 el_act_dir2img(effective_element, effective_action,
7201 crumbled = (direction == MV_NONE ?
7202 el_act2crm(effective_element, effective_action) :
7203 el_act_dir2crm(effective_element, effective_action,
7206 g = &graphic_info[graphic];
7209 if (graphic_info[graphic].anim_global_sync)
7210 sync_frame = FrameCounter;
7211 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7212 sync_frame = GfxFrame[x][y];
7214 sync_frame = 0; /* playfield border (pseudo steel) */
7216 SetRandomAnimationValue(x, y);
7218 int frame = getAnimationFrame(g->anim_frames,
7221 g->anim_start_frame,
7224 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7225 g->double_movement && is_backside);
7227 /* (updating the "crumbled" graphic definitions is probably not really needed,
7228 as animations for crumbled graphics can't be longer than one EMC cycle) */
7229 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7233 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7234 int player_nr, int anim, int frame_em)
7236 int element = player_mapping[player_nr][anim].element_rnd;
7237 int action = player_mapping[player_nr][anim].action;
7238 int direction = player_mapping[player_nr][anim].direction;
7239 int graphic = (direction == MV_NONE ?
7240 el_act2img(element, action) :
7241 el_act_dir2img(element, action, direction));
7242 struct GraphicInfo *g = &graphic_info[graphic];
7245 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7247 stored_player[player_nr].StepFrame = frame_em;
7249 sync_frame = stored_player[player_nr].Frame;
7251 int frame = getAnimationFrame(g->anim_frames,
7254 g->anim_start_frame,
7257 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7258 &g_em->src_x, &g_em->src_y, FALSE);
7261 void InitGraphicInfo_EM(void)
7266 int num_em_gfx_errors = 0;
7268 if (graphic_info_em_object[0][0].bitmap == NULL)
7270 /* EM graphics not yet initialized in em_open_all() */
7275 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7278 /* always start with reliable default values */
7279 for (i = 0; i < TILE_MAX; i++)
7281 object_mapping[i].element_rnd = EL_UNKNOWN;
7282 object_mapping[i].is_backside = FALSE;
7283 object_mapping[i].action = ACTION_DEFAULT;
7284 object_mapping[i].direction = MV_NONE;
7287 /* always start with reliable default values */
7288 for (p = 0; p < MAX_PLAYERS; p++)
7290 for (i = 0; i < SPR_MAX; i++)
7292 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7293 player_mapping[p][i].action = ACTION_DEFAULT;
7294 player_mapping[p][i].direction = MV_NONE;
7298 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7300 int e = em_object_mapping_list[i].element_em;
7302 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7303 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7305 if (em_object_mapping_list[i].action != -1)
7306 object_mapping[e].action = em_object_mapping_list[i].action;
7308 if (em_object_mapping_list[i].direction != -1)
7309 object_mapping[e].direction =
7310 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7313 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7315 int a = em_player_mapping_list[i].action_em;
7316 int p = em_player_mapping_list[i].player_nr;
7318 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7320 if (em_player_mapping_list[i].action != -1)
7321 player_mapping[p][a].action = em_player_mapping_list[i].action;
7323 if (em_player_mapping_list[i].direction != -1)
7324 player_mapping[p][a].direction =
7325 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7328 for (i = 0; i < TILE_MAX; i++)
7330 int element = object_mapping[i].element_rnd;
7331 int action = object_mapping[i].action;
7332 int direction = object_mapping[i].direction;
7333 boolean is_backside = object_mapping[i].is_backside;
7334 boolean action_exploding = ((action == ACTION_EXPLODING ||
7335 action == ACTION_SMASHED_BY_ROCK ||
7336 action == ACTION_SMASHED_BY_SPRING) &&
7337 element != EL_DIAMOND);
7338 boolean action_active = (action == ACTION_ACTIVE);
7339 boolean action_other = (action == ACTION_OTHER);
7341 for (j = 0; j < 8; j++)
7343 int effective_element = get_effective_element_EM(i, j);
7344 int effective_action = (j < 7 ? action :
7345 i == Xdrip_stretch ? action :
7346 i == Xdrip_stretchB ? action :
7347 i == Ydrip_s1 ? action :
7348 i == Ydrip_s1B ? action :
7349 i == Xball_1B ? action :
7350 i == Xball_2 ? action :
7351 i == Xball_2B ? action :
7352 i == Yball_eat ? action :
7353 i == Ykey_1_eat ? action :
7354 i == Ykey_2_eat ? action :
7355 i == Ykey_3_eat ? action :
7356 i == Ykey_4_eat ? action :
7357 i == Ykey_5_eat ? action :
7358 i == Ykey_6_eat ? action :
7359 i == Ykey_7_eat ? action :
7360 i == Ykey_8_eat ? action :
7361 i == Ylenses_eat ? action :
7362 i == Ymagnify_eat ? action :
7363 i == Ygrass_eat ? action :
7364 i == Ydirt_eat ? action :
7365 i == Xsand_stonein_1 ? action :
7366 i == Xsand_stonein_2 ? action :
7367 i == Xsand_stonein_3 ? action :
7368 i == Xsand_stonein_4 ? action :
7369 i == Xsand_stoneout_1 ? action :
7370 i == Xsand_stoneout_2 ? action :
7371 i == Xboom_android ? ACTION_EXPLODING :
7372 action_exploding ? ACTION_EXPLODING :
7373 action_active ? action :
7374 action_other ? action :
7376 int graphic = (el_act_dir2img(effective_element, effective_action,
7378 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7380 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7381 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7382 boolean has_action_graphics = (graphic != base_graphic);
7383 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7384 struct GraphicInfo *g = &graphic_info[graphic];
7385 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7388 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7389 boolean special_animation = (action != ACTION_DEFAULT &&
7390 g->anim_frames == 3 &&
7391 g->anim_delay == 2 &&
7392 g->anim_mode & ANIM_LINEAR);
7393 int sync_frame = (i == Xdrip_stretch ? 7 :
7394 i == Xdrip_stretchB ? 7 :
7395 i == Ydrip_s2 ? j + 8 :
7396 i == Ydrip_s2B ? j + 8 :
7405 i == Xfake_acid_1 ? 0 :
7406 i == Xfake_acid_2 ? 10 :
7407 i == Xfake_acid_3 ? 20 :
7408 i == Xfake_acid_4 ? 30 :
7409 i == Xfake_acid_5 ? 40 :
7410 i == Xfake_acid_6 ? 50 :
7411 i == Xfake_acid_7 ? 60 :
7412 i == Xfake_acid_8 ? 70 :
7414 i == Xball_2B ? j + 8 :
7415 i == Yball_eat ? j + 1 :
7416 i == Ykey_1_eat ? j + 1 :
7417 i == Ykey_2_eat ? j + 1 :
7418 i == Ykey_3_eat ? j + 1 :
7419 i == Ykey_4_eat ? j + 1 :
7420 i == Ykey_5_eat ? j + 1 :
7421 i == Ykey_6_eat ? j + 1 :
7422 i == Ykey_7_eat ? j + 1 :
7423 i == Ykey_8_eat ? j + 1 :
7424 i == Ylenses_eat ? j + 1 :
7425 i == Ymagnify_eat ? j + 1 :
7426 i == Ygrass_eat ? j + 1 :
7427 i == Ydirt_eat ? j + 1 :
7428 i == Xamoeba_1 ? 0 :
7429 i == Xamoeba_2 ? 1 :
7430 i == Xamoeba_3 ? 2 :
7431 i == Xamoeba_4 ? 3 :
7432 i == Xamoeba_5 ? 0 :
7433 i == Xamoeba_6 ? 1 :
7434 i == Xamoeba_7 ? 2 :
7435 i == Xamoeba_8 ? 3 :
7436 i == Xexit_2 ? j + 8 :
7437 i == Xexit_3 ? j + 16 :
7438 i == Xdynamite_1 ? 0 :
7439 i == Xdynamite_2 ? 8 :
7440 i == Xdynamite_3 ? 16 :
7441 i == Xdynamite_4 ? 24 :
7442 i == Xsand_stonein_1 ? j + 1 :
7443 i == Xsand_stonein_2 ? j + 9 :
7444 i == Xsand_stonein_3 ? j + 17 :
7445 i == Xsand_stonein_4 ? j + 25 :
7446 i == Xsand_stoneout_1 && j == 0 ? 0 :
7447 i == Xsand_stoneout_1 && j == 1 ? 0 :
7448 i == Xsand_stoneout_1 && j == 2 ? 1 :
7449 i == Xsand_stoneout_1 && j == 3 ? 2 :
7450 i == Xsand_stoneout_1 && j == 4 ? 2 :
7451 i == Xsand_stoneout_1 && j == 5 ? 3 :
7452 i == Xsand_stoneout_1 && j == 6 ? 4 :
7453 i == Xsand_stoneout_1 && j == 7 ? 4 :
7454 i == Xsand_stoneout_2 && j == 0 ? 5 :
7455 i == Xsand_stoneout_2 && j == 1 ? 6 :
7456 i == Xsand_stoneout_2 && j == 2 ? 7 :
7457 i == Xsand_stoneout_2 && j == 3 ? 8 :
7458 i == Xsand_stoneout_2 && j == 4 ? 9 :
7459 i == Xsand_stoneout_2 && j == 5 ? 11 :
7460 i == Xsand_stoneout_2 && j == 6 ? 13 :
7461 i == Xsand_stoneout_2 && j == 7 ? 15 :
7462 i == Xboom_bug && j == 1 ? 2 :
7463 i == Xboom_bug && j == 2 ? 2 :
7464 i == Xboom_bug && j == 3 ? 4 :
7465 i == Xboom_bug && j == 4 ? 4 :
7466 i == Xboom_bug && j == 5 ? 2 :
7467 i == Xboom_bug && j == 6 ? 2 :
7468 i == Xboom_bug && j == 7 ? 0 :
7469 i == Xboom_bomb && j == 1 ? 2 :
7470 i == Xboom_bomb && j == 2 ? 2 :
7471 i == Xboom_bomb && j == 3 ? 4 :
7472 i == Xboom_bomb && j == 4 ? 4 :
7473 i == Xboom_bomb && j == 5 ? 2 :
7474 i == Xboom_bomb && j == 6 ? 2 :
7475 i == Xboom_bomb && j == 7 ? 0 :
7476 i == Xboom_android && j == 7 ? 6 :
7477 i == Xboom_1 && j == 1 ? 2 :
7478 i == Xboom_1 && j == 2 ? 2 :
7479 i == Xboom_1 && j == 3 ? 4 :
7480 i == Xboom_1 && j == 4 ? 4 :
7481 i == Xboom_1 && j == 5 ? 6 :
7482 i == Xboom_1 && j == 6 ? 6 :
7483 i == Xboom_1 && j == 7 ? 8 :
7484 i == Xboom_2 && j == 0 ? 8 :
7485 i == Xboom_2 && j == 1 ? 8 :
7486 i == Xboom_2 && j == 2 ? 10 :
7487 i == Xboom_2 && j == 3 ? 10 :
7488 i == Xboom_2 && j == 4 ? 10 :
7489 i == Xboom_2 && j == 5 ? 12 :
7490 i == Xboom_2 && j == 6 ? 12 :
7491 i == Xboom_2 && j == 7 ? 12 :
7492 special_animation && j == 4 ? 3 :
7493 effective_action != action ? 0 :
7497 Bitmap *debug_bitmap = g_em->bitmap;
7498 int debug_src_x = g_em->src_x;
7499 int debug_src_y = g_em->src_y;
7502 int frame = getAnimationFrame(g->anim_frames,
7505 g->anim_start_frame,
7508 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7509 g->double_movement && is_backside);
7511 g_em->bitmap = src_bitmap;
7512 g_em->src_x = src_x;
7513 g_em->src_y = src_y;
7514 g_em->src_offset_x = 0;
7515 g_em->src_offset_y = 0;
7516 g_em->dst_offset_x = 0;
7517 g_em->dst_offset_y = 0;
7518 g_em->width = TILEX;
7519 g_em->height = TILEY;
7521 g_em->preserve_background = FALSE;
7523 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7526 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7527 effective_action == ACTION_MOVING ||
7528 effective_action == ACTION_PUSHING ||
7529 effective_action == ACTION_EATING)) ||
7530 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7531 effective_action == ACTION_EMPTYING)))
7534 (effective_action == ACTION_FALLING ||
7535 effective_action == ACTION_FILLING ||
7536 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7537 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7538 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7539 int num_steps = (i == Ydrip_s1 ? 16 :
7540 i == Ydrip_s1B ? 16 :
7541 i == Ydrip_s2 ? 16 :
7542 i == Ydrip_s2B ? 16 :
7543 i == Xsand_stonein_1 ? 32 :
7544 i == Xsand_stonein_2 ? 32 :
7545 i == Xsand_stonein_3 ? 32 :
7546 i == Xsand_stonein_4 ? 32 :
7547 i == Xsand_stoneout_1 ? 16 :
7548 i == Xsand_stoneout_2 ? 16 : 8);
7549 int cx = ABS(dx) * (TILEX / num_steps);
7550 int cy = ABS(dy) * (TILEY / num_steps);
7551 int step_frame = (i == Ydrip_s2 ? j + 8 :
7552 i == Ydrip_s2B ? j + 8 :
7553 i == Xsand_stonein_2 ? j + 8 :
7554 i == Xsand_stonein_3 ? j + 16 :
7555 i == Xsand_stonein_4 ? j + 24 :
7556 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7557 int step = (is_backside ? step_frame : num_steps - step_frame);
7559 if (is_backside) /* tile where movement starts */
7561 if (dx < 0 || dy < 0)
7563 g_em->src_offset_x = cx * step;
7564 g_em->src_offset_y = cy * step;
7568 g_em->dst_offset_x = cx * step;
7569 g_em->dst_offset_y = cy * step;
7572 else /* tile where movement ends */
7574 if (dx < 0 || dy < 0)
7576 g_em->dst_offset_x = cx * step;
7577 g_em->dst_offset_y = cy * step;
7581 g_em->src_offset_x = cx * step;
7582 g_em->src_offset_y = cy * step;
7586 g_em->width = TILEX - cx * step;
7587 g_em->height = TILEY - cy * step;
7590 /* create unique graphic identifier to decide if tile must be redrawn */
7591 /* bit 31 - 16 (16 bit): EM style graphic
7592 bit 15 - 12 ( 4 bit): EM style frame
7593 bit 11 - 6 ( 6 bit): graphic width
7594 bit 5 - 0 ( 6 bit): graphic height */
7595 g_em->unique_identifier =
7596 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7600 /* skip check for EMC elements not contained in original EMC artwork */
7601 if (element == EL_EMC_FAKE_ACID)
7604 if (g_em->bitmap != debug_bitmap ||
7605 g_em->src_x != debug_src_x ||
7606 g_em->src_y != debug_src_y ||
7607 g_em->src_offset_x != 0 ||
7608 g_em->src_offset_y != 0 ||
7609 g_em->dst_offset_x != 0 ||
7610 g_em->dst_offset_y != 0 ||
7611 g_em->width != TILEX ||
7612 g_em->height != TILEY)
7614 static int last_i = -1;
7622 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7623 i, element, element_info[element].token_name,
7624 element_action_info[effective_action].suffix, direction);
7626 if (element != effective_element)
7627 printf(" [%d ('%s')]",
7629 element_info[effective_element].token_name);
7633 if (g_em->bitmap != debug_bitmap)
7634 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7635 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7637 if (g_em->src_x != debug_src_x ||
7638 g_em->src_y != debug_src_y)
7639 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7640 j, (is_backside ? 'B' : 'F'),
7641 g_em->src_x, g_em->src_y,
7642 g_em->src_x / 32, g_em->src_y / 32,
7643 debug_src_x, debug_src_y,
7644 debug_src_x / 32, debug_src_y / 32);
7646 if (g_em->src_offset_x != 0 ||
7647 g_em->src_offset_y != 0 ||
7648 g_em->dst_offset_x != 0 ||
7649 g_em->dst_offset_y != 0)
7650 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7652 g_em->src_offset_x, g_em->src_offset_y,
7653 g_em->dst_offset_x, g_em->dst_offset_y);
7655 if (g_em->width != TILEX ||
7656 g_em->height != TILEY)
7657 printf(" %d (%d): size %d,%d should be %d,%d\n",
7659 g_em->width, g_em->height, TILEX, TILEY);
7661 num_em_gfx_errors++;
7668 for (i = 0; i < TILE_MAX; i++)
7670 for (j = 0; j < 8; j++)
7672 int element = object_mapping[i].element_rnd;
7673 int action = object_mapping[i].action;
7674 int direction = object_mapping[i].direction;
7675 boolean is_backside = object_mapping[i].is_backside;
7676 int graphic_action = el_act_dir2img(element, action, direction);
7677 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7679 if ((action == ACTION_SMASHED_BY_ROCK ||
7680 action == ACTION_SMASHED_BY_SPRING ||
7681 action == ACTION_EATING) &&
7682 graphic_action == graphic_default)
7684 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7685 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7686 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7687 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7690 /* no separate animation for "smashed by rock" -- use rock instead */
7691 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7692 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7694 g_em->bitmap = g_xx->bitmap;
7695 g_em->src_x = g_xx->src_x;
7696 g_em->src_y = g_xx->src_y;
7697 g_em->src_offset_x = g_xx->src_offset_x;
7698 g_em->src_offset_y = g_xx->src_offset_y;
7699 g_em->dst_offset_x = g_xx->dst_offset_x;
7700 g_em->dst_offset_y = g_xx->dst_offset_y;
7701 g_em->width = g_xx->width;
7702 g_em->height = g_xx->height;
7703 g_em->unique_identifier = g_xx->unique_identifier;
7706 g_em->preserve_background = TRUE;
7711 for (p = 0; p < MAX_PLAYERS; p++)
7713 for (i = 0; i < SPR_MAX; i++)
7715 int element = player_mapping[p][i].element_rnd;
7716 int action = player_mapping[p][i].action;
7717 int direction = player_mapping[p][i].direction;
7719 for (j = 0; j < 8; j++)
7721 int effective_element = element;
7722 int effective_action = action;
7723 int graphic = (direction == MV_NONE ?
7724 el_act2img(effective_element, effective_action) :
7725 el_act_dir2img(effective_element, effective_action,
7727 struct GraphicInfo *g = &graphic_info[graphic];
7728 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7734 Bitmap *debug_bitmap = g_em->bitmap;
7735 int debug_src_x = g_em->src_x;
7736 int debug_src_y = g_em->src_y;
7739 int frame = getAnimationFrame(g->anim_frames,
7742 g->anim_start_frame,
7745 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7747 g_em->bitmap = src_bitmap;
7748 g_em->src_x = src_x;
7749 g_em->src_y = src_y;
7750 g_em->src_offset_x = 0;
7751 g_em->src_offset_y = 0;
7752 g_em->dst_offset_x = 0;
7753 g_em->dst_offset_y = 0;
7754 g_em->width = TILEX;
7755 g_em->height = TILEY;
7759 /* skip check for EMC elements not contained in original EMC artwork */
7760 if (element == EL_PLAYER_3 ||
7761 element == EL_PLAYER_4)
7764 if (g_em->bitmap != debug_bitmap ||
7765 g_em->src_x != debug_src_x ||
7766 g_em->src_y != debug_src_y)
7768 static int last_i = -1;
7776 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7777 p, i, element, element_info[element].token_name,
7778 element_action_info[effective_action].suffix, direction);
7780 if (element != effective_element)
7781 printf(" [%d ('%s')]",
7783 element_info[effective_element].token_name);
7787 if (g_em->bitmap != debug_bitmap)
7788 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7789 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7791 if (g_em->src_x != debug_src_x ||
7792 g_em->src_y != debug_src_y)
7793 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7795 g_em->src_x, g_em->src_y,
7796 g_em->src_x / 32, g_em->src_y / 32,
7797 debug_src_x, debug_src_y,
7798 debug_src_x / 32, debug_src_y / 32);
7800 num_em_gfx_errors++;
7810 printf("::: [%d errors found]\n", num_em_gfx_errors);
7816 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7817 boolean any_player_moving,
7818 boolean player_is_dropping)
7820 if (tape.single_step && tape.recording && !tape.pausing)
7821 if (frame == 0 && !player_is_dropping)
7822 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7825 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7826 boolean murphy_is_dropping)
7828 if (tape.single_step && tape.recording && !tape.pausing)
7829 if (murphy_is_waiting)
7830 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7833 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7834 int graphic, int sync_frame, int x, int y)
7836 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7838 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7841 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7843 return (IS_NEXT_FRAME(sync_frame, graphic));
7846 int getGraphicInfo_Delay(int graphic)
7848 return graphic_info[graphic].anim_delay;
7851 void PlayMenuSoundExt(int sound)
7853 if (sound == SND_UNDEFINED)
7856 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7857 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7860 if (IS_LOOP_SOUND(sound))
7861 PlaySoundLoop(sound);
7866 void PlayMenuSound()
7868 PlayMenuSoundExt(menu.sound[game_status]);
7871 void PlayMenuSoundStereo(int sound, int stereo_position)
7873 if (sound == SND_UNDEFINED)
7876 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7877 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7880 if (IS_LOOP_SOUND(sound))
7881 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7883 PlaySoundStereo(sound, stereo_position);
7886 void PlayMenuSoundIfLoopExt(int sound)
7888 if (sound == SND_UNDEFINED)
7891 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7892 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7895 if (IS_LOOP_SOUND(sound))
7896 PlaySoundLoop(sound);
7899 void PlayMenuSoundIfLoop()
7901 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7904 void PlayMenuMusicExt(int music)
7906 if (music == MUS_UNDEFINED)
7909 if (!setup.sound_music)
7915 void PlayMenuMusic()
7917 PlayMenuMusicExt(menu.music[game_status]);
7920 void PlaySoundActivating()
7923 PlaySound(SND_MENU_ITEM_ACTIVATING);
7927 void PlaySoundSelecting()
7930 PlaySound(SND_MENU_ITEM_SELECTING);
7934 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7936 boolean change_fullscreen = (setup.fullscreen !=
7937 video.fullscreen_enabled);
7938 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7939 !strEqual(setup.fullscreen_mode,
7940 video.fullscreen_mode_current));
7941 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7942 setup.window_scaling_percent !=
7943 video.window_scaling_percent);
7945 if (change_window_scaling_percent && video.fullscreen_enabled)
7948 if (!change_window_scaling_percent && !video.fullscreen_available)
7951 #if defined(TARGET_SDL2)
7952 if (change_window_scaling_percent)
7954 SDLSetWindowScaling(setup.window_scaling_percent);
7958 else if (change_fullscreen)
7960 SDLSetWindowFullscreen(setup.fullscreen);
7962 /* set setup value according to successfully changed fullscreen mode */
7963 setup.fullscreen = video.fullscreen_enabled;
7969 if (change_fullscreen ||
7970 change_fullscreen_mode ||
7971 change_window_scaling_percent)
7973 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
7975 /* save backbuffer content which gets lost when toggling fullscreen mode */
7976 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7978 if (change_fullscreen_mode)
7980 /* keep fullscreen, but change fullscreen mode (screen resolution) */
7981 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
7984 if (change_window_scaling_percent)
7986 /* keep window mode, but change window scaling */
7987 video.fullscreen_enabled = TRUE; /* force new window scaling */
7990 /* toggle fullscreen */
7991 ChangeVideoModeIfNeeded(setup.fullscreen);
7993 /* set setup value according to successfully changed fullscreen mode */
7994 setup.fullscreen = video.fullscreen_enabled;
7996 /* restore backbuffer content from temporary backbuffer backup bitmap */
7997 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7999 FreeBitmap(tmp_backbuffer);
8001 /* update visible window/screen */
8002 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8006 void ChangeViewportPropertiesIfNeeded()
8008 int gfx_game_mode = game_status;
8009 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8011 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8012 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8013 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8014 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8015 int border_size = vp_playfield->border_size;
8016 int new_sx = vp_playfield->x + border_size;
8017 int new_sy = vp_playfield->y + border_size;
8018 int new_sxsize = vp_playfield->width - 2 * border_size;
8019 int new_sysize = vp_playfield->height - 2 * border_size;
8020 int new_real_sx = vp_playfield->x;
8021 int new_real_sy = vp_playfield->y;
8022 int new_full_sxsize = vp_playfield->width;
8023 int new_full_sysize = vp_playfield->height;
8024 int new_dx = vp_door_1->x;
8025 int new_dy = vp_door_1->y;
8026 int new_dxsize = vp_door_1->width;
8027 int new_dysize = vp_door_1->height;
8028 int new_vx = vp_door_2->x;
8029 int new_vy = vp_door_2->y;
8030 int new_vxsize = vp_door_2->width;
8031 int new_vysize = vp_door_2->height;
8032 int new_ex = vp_door_3->x;
8033 int new_ey = vp_door_3->y;
8034 int new_exsize = vp_door_3->width;
8035 int new_eysize = vp_door_3->height;
8036 int new_tilesize_var =
8037 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8039 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8040 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8041 int new_scr_fieldx = new_sxsize / tilesize;
8042 int new_scr_fieldy = new_sysize / tilesize;
8043 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8044 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8045 boolean init_gfx_buffers = FALSE;
8046 boolean init_video_buffer = FALSE;
8047 boolean init_gadgets_and_toons = FALSE;
8048 boolean init_em_graphics = FALSE;
8050 if (viewport.window.width != WIN_XSIZE ||
8051 viewport.window.height != WIN_YSIZE)
8053 WIN_XSIZE = viewport.window.width;
8054 WIN_YSIZE = viewport.window.height;
8056 init_video_buffer = TRUE;
8057 init_gfx_buffers = TRUE;
8059 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8062 if (new_scr_fieldx != SCR_FIELDX ||
8063 new_scr_fieldy != SCR_FIELDY)
8065 /* this always toggles between MAIN and GAME when using small tile size */
8067 SCR_FIELDX = new_scr_fieldx;
8068 SCR_FIELDY = new_scr_fieldy;
8070 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8081 new_sxsize != SXSIZE ||
8082 new_sysize != SYSIZE ||
8083 new_dxsize != DXSIZE ||
8084 new_dysize != DYSIZE ||
8085 new_vxsize != VXSIZE ||
8086 new_vysize != VYSIZE ||
8087 new_exsize != EXSIZE ||
8088 new_eysize != EYSIZE ||
8089 new_real_sx != REAL_SX ||
8090 new_real_sy != REAL_SY ||
8091 new_full_sxsize != FULL_SXSIZE ||
8092 new_full_sysize != FULL_SYSIZE ||
8093 new_tilesize_var != TILESIZE_VAR
8096 if (new_tilesize_var != TILESIZE_VAR)
8098 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8100 // changing tile size invalidates scroll values of engine snapshots
8101 FreeEngineSnapshot();
8103 // changing tile size requires update of graphic mapping for EM engine
8104 init_em_graphics = TRUE;
8115 SXSIZE = new_sxsize;
8116 SYSIZE = new_sysize;
8117 DXSIZE = new_dxsize;
8118 DYSIZE = new_dysize;
8119 VXSIZE = new_vxsize;
8120 VYSIZE = new_vysize;
8121 EXSIZE = new_exsize;
8122 EYSIZE = new_eysize;
8123 REAL_SX = new_real_sx;
8124 REAL_SY = new_real_sy;
8125 FULL_SXSIZE = new_full_sxsize;
8126 FULL_SYSIZE = new_full_sysize;
8127 TILESIZE_VAR = new_tilesize_var;
8129 init_gfx_buffers = TRUE;
8130 init_gadgets_and_toons = TRUE;
8132 // printf("::: viewports: init_gfx_buffers\n");
8133 // printf("::: viewports: init_gadgets_and_toons\n");
8136 if (init_gfx_buffers)
8138 // printf("::: init_gfx_buffers\n");
8140 SCR_FIELDX = new_scr_fieldx_buffers;
8141 SCR_FIELDY = new_scr_fieldy_buffers;
8145 SCR_FIELDX = new_scr_fieldx;
8146 SCR_FIELDY = new_scr_fieldy;
8149 if (init_video_buffer)
8151 // printf("::: init_video_buffer\n");
8153 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8155 SetDrawDeactivationMask(REDRAW_NONE);
8156 SetDrawBackgroundMask(REDRAW_FIELD);
8159 if (init_gadgets_and_toons)
8161 // printf("::: init_gadgets_and_toons\n");
8167 if (init_em_graphics)
8169 InitGraphicInfo_EM();