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 FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_DOOR_1_GFX_PART_1,
74 IMG_DOOR_1_GFX_PART_2,
79 IMG_DOOR_1_GFX_PART_3,
84 IMG_DOOR_1_GFX_PART_4,
89 IMG_DOOR_1_GFX_PART_5,
94 IMG_DOOR_1_GFX_PART_6,
99 IMG_DOOR_1_GFX_PART_7,
104 IMG_DOOR_1_GFX_PART_8,
110 IMG_DOOR_2_GFX_PART_1,
115 IMG_DOOR_2_GFX_PART_2,
120 IMG_DOOR_2_GFX_PART_3,
125 IMG_DOOR_2_GFX_PART_4,
130 IMG_DOOR_2_GFX_PART_5,
135 IMG_DOOR_2_GFX_PART_6,
140 IMG_DOOR_2_GFX_PART_7,
145 IMG_DOOR_2_GFX_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static unsigned int sync_frame_delay = 0;
178 static unsigned int sync_frame_delay_value = GAME_FRAME_DELAY;
180 static char *print_if_not_empty(int element)
182 static char *s = NULL;
183 char *token_name = element_info[element].token_name;
188 s = checked_malloc(strlen(token_name) + 10 + 1);
190 if (element != EL_EMPTY)
191 sprintf(s, "%d\t['%s']", element, token_name);
193 sprintf(s, "%d", element);
198 void DumpTile(int x, int y)
203 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
209 printf_line("-", 79);
210 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
211 printf_line("-", 79);
213 if (!IN_LEV_FIELD(x, y))
215 printf("(not in level field)\n");
221 printf(" Feld: %d\t['%s']\n", Feld[x][y],
222 element_info[Feld[x][y]].token_name);
223 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
224 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
225 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
226 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
227 printf(" MovPos: %d\n", MovPos[x][y]);
228 printf(" MovDir: %d\n", MovDir[x][y]);
229 printf(" MovDelay: %d\n", MovDelay[x][y]);
230 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
231 printf(" CustomValue: %d\n", CustomValue[x][y]);
232 printf(" GfxElement: %d\n", GfxElement[x][y]);
233 printf(" GfxAction: %d\n", GfxAction[x][y]);
234 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
238 void SetDrawtoField(int mode)
240 if (mode == DRAW_FIELDBUFFER)
246 BX2 = SCR_FIELDX + 1;
247 BY2 = SCR_FIELDY + 1;
249 drawto_field = fieldbuffer;
251 else /* DRAW_BACKBUFFER */
257 BX2 = SCR_FIELDX - 1;
258 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 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
294 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
296 if (x == -1 && y == -1)
299 if (draw_target == DRAW_BORDER_TO_SCREEN)
300 BlitToScreenMasked(bitmap, x, y, width, height, x, y);
302 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
305 static void DrawMaskedBorderExt_FIELD(int draw_target)
307 if (global.border_status >= GAME_MODE_TITLE &&
308 global.border_status <= GAME_MODE_PLAYING &&
309 border.draw_masked[global.border_status])
310 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
314 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
316 // when drawing to backbuffer, never draw border over open doors
317 if (draw_target == DRAW_BORDER_TO_BACKBUFFER &&
318 (GetDoorState() & DOOR_OPEN_1))
321 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
322 (global.border_status != GAME_MODE_EDITOR ||
323 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
324 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
327 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
329 // when drawing to backbuffer, never draw border over open doors
330 if (draw_target == DRAW_BORDER_TO_BACKBUFFER &&
331 (GetDoorState() & DOOR_OPEN_2))
334 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
335 global.border_status != GAME_MODE_EDITOR)
336 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
339 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
341 /* currently not available */
344 static void DrawMaskedBorderExt_ALL(int draw_target)
346 DrawMaskedBorderExt_FIELD(draw_target);
347 DrawMaskedBorderExt_DOOR_1(draw_target);
348 DrawMaskedBorderExt_DOOR_2(draw_target);
349 DrawMaskedBorderExt_DOOR_3(draw_target);
352 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
354 /* never draw masked screen borders on borderless screens */
355 if (game_status == GAME_MODE_LOADING ||
356 game_status == GAME_MODE_TITLE)
359 if (redraw_mask & REDRAW_ALL)
360 DrawMaskedBorderExt_ALL(draw_target);
363 if (redraw_mask & REDRAW_FIELD)
364 DrawMaskedBorderExt_FIELD(draw_target);
365 if (redraw_mask & REDRAW_DOOR_1)
366 DrawMaskedBorderExt_DOOR_1(draw_target);
367 if (redraw_mask & REDRAW_DOOR_2)
368 DrawMaskedBorderExt_DOOR_2(draw_target);
369 if (redraw_mask & REDRAW_DOOR_3)
370 DrawMaskedBorderExt_DOOR_3(draw_target);
374 void DrawMaskedBorder_FIELD()
376 DrawMaskedBorderExt_FIELD(DRAW_BORDER_TO_BACKBUFFER);
379 void DrawMaskedBorder(int redraw_mask)
381 DrawMaskedBorderExt(redraw_mask, DRAW_BORDER_TO_BACKBUFFER);
384 void DrawMaskedBorderToScreen(int draw_target)
386 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
389 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
391 int fx = FX, fy = FY;
392 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
393 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
395 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
396 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
397 int dx_var = dx * TILESIZE_VAR / TILESIZE;
398 int dy_var = dy * TILESIZE_VAR / TILESIZE;
401 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
402 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
404 if (EVEN(SCR_FIELDX))
406 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
407 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
409 fx += (dx_var > 0 ? TILEX_VAR : 0);
416 if (EVEN(SCR_FIELDY))
418 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
419 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
421 fy += (dy_var > 0 ? TILEY_VAR : 0);
428 if (full_lev_fieldx <= SCR_FIELDX)
430 if (EVEN(SCR_FIELDX))
431 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
433 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
436 if (full_lev_fieldy <= SCR_FIELDY)
438 if (EVEN(SCR_FIELDY))
439 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
441 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
444 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
447 void BlitScreenToBitmap(Bitmap *target_bitmap)
449 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
450 BlitScreenToBitmap_EM(target_bitmap);
451 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
452 BlitScreenToBitmap_SP(target_bitmap);
453 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
454 BlitScreenToBitmap_RND(target_bitmap);
456 redraw_mask |= REDRAW_FIELD;
459 void DrawFramesPerSecond()
462 int font_nr = FONT_TEXT_2;
463 int font_width = getFontWidth(font_nr);
465 sprintf(text, "%04.1f fps", global.frames_per_second);
467 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
468 font_nr, BLIT_OPAQUE);
472 static void PrintFrameTimeDebugging()
474 static unsigned int last_counter = 0;
475 unsigned int counter = Counter();
476 int diff_1 = counter - last_counter;
477 int diff_2 = diff_1 - GAME_FRAME_DELAY;
479 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
480 char diff_bar[2 * diff_2_max + 5];
484 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
486 for (i = 0; i < diff_2_max; i++)
487 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
488 i >= diff_2_max - diff_2_cut ? '-' : ' ');
490 diff_bar[pos++] = '|';
492 for (i = 0; i < diff_2_max; i++)
493 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
495 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
497 diff_bar[pos++] = '\0';
499 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
502 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
505 last_counter = counter;
511 static int last_redraw_mask = REDRAW_NONE;
513 // force screen redraw in every frame to continue drawing global animations
514 // (but always use the last redraw mask to prevent unwanted side effects)
515 if (redraw_mask == REDRAW_NONE)
516 redraw_mask = last_redraw_mask;
518 last_redraw_mask = redraw_mask;
521 // masked border now drawn immediately when blitting backbuffer to window
523 // draw masked border to all viewports, if defined
524 DrawMaskedBorder(redraw_mask);
527 // draw frames per second (only if debug mode is enabled)
528 if (redraw_mask & REDRAW_FPS)
529 DrawFramesPerSecond();
531 // redraw complete window if both playfield and (some) doors need redraw
532 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
533 redraw_mask = REDRAW_ALL;
535 /* although redrawing the whole window would be fine for normal gameplay,
536 being able to only redraw the playfield is required for deactivating
537 certain drawing areas (mainly playfield) to work, which is needed for
538 warp-forward to be fast enough (by skipping redraw of most frames) */
540 if (redraw_mask & REDRAW_ALL)
542 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
544 else if (redraw_mask & REDRAW_FIELD)
546 BlitBitmap(backbuffer, window,
547 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
549 else if (redraw_mask & REDRAW_DOORS)
551 if (redraw_mask & REDRAW_DOOR_1)
552 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
554 if (redraw_mask & REDRAW_DOOR_2)
555 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
557 if (redraw_mask & REDRAW_DOOR_3)
558 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
561 redraw_mask = REDRAW_NONE;
564 PrintFrameTimeDebugging();
568 static void FadeCrossSaveBackbuffer()
570 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
573 static void FadeCrossRestoreBackbuffer()
575 int redraw_mask_last = redraw_mask;
577 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
579 // do not change redraw mask when restoring backbuffer after cross-fading
580 redraw_mask = redraw_mask_last;
583 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
585 static int fade_type_skip = FADE_TYPE_NONE;
586 void (*draw_border_function)(void) = NULL;
587 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
588 int x, y, width, height;
589 int fade_delay, post_delay;
591 if (fade_type == FADE_TYPE_FADE_OUT)
593 if (fade_type_skip != FADE_TYPE_NONE)
595 /* skip all fade operations until specified fade operation */
596 if (fade_type & fade_type_skip)
597 fade_type_skip = FADE_TYPE_NONE;
603 FadeCrossSaveBackbuffer();
606 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
609 FadeCrossSaveBackbuffer();
616 redraw_mask |= fade_mask;
618 if (fade_type == FADE_TYPE_SKIP)
620 fade_type_skip = fade_mode;
625 fade_delay = fading.fade_delay;
626 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
628 if (fade_type_skip != FADE_TYPE_NONE)
630 /* skip all fade operations until specified fade operation */
631 if (fade_type & fade_type_skip)
632 fade_type_skip = FADE_TYPE_NONE;
637 if (global.autoplay_leveldir)
642 if (fade_mask == REDRAW_FIELD)
647 height = FADE_SYSIZE;
649 if (border.draw_masked_when_fading)
650 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
652 DrawMaskedBorder_FIELD(); /* draw once */
654 else /* REDRAW_ALL */
662 if (!setup.fade_screens ||
664 fading.fade_mode == FADE_MODE_NONE)
666 if (fade_mode == FADE_MODE_FADE_OUT)
669 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
671 redraw_mask &= ~fade_mask;
676 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
677 draw_border_function);
679 if (fade_type == FADE_TYPE_FADE_OUT)
680 FadeCrossRestoreBackbuffer();
682 redraw_mask &= ~fade_mask;
685 static void SetScreenStates_BeforeFadingIn()
689 static void SetScreenStates_AfterFadingIn()
691 global.anim_status = global.anim_status_next;
693 // force update of global animation status in case of rapid screen changes
694 redraw_mask = REDRAW_ALL;
698 static void SetScreenStates_BeforeFadingOut()
700 global.anim_status = GAME_MODE_PSEUDO_FADING;
703 static void SetScreenStates_AfterFadingOut()
705 global.border_status = game_status;
708 void FadeIn(int fade_mask)
710 SetScreenStates_BeforeFadingIn();
713 DrawMaskedBorder(REDRAW_ALL);
716 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
717 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
719 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
723 FADE_SXSIZE = FULL_SXSIZE;
724 FADE_SYSIZE = FULL_SYSIZE;
726 SetScreenStates_AfterFadingIn();
729 void FadeOut(int fade_mask)
731 SetScreenStates_BeforeFadingOut();
734 DrawMaskedBorder(REDRAW_ALL);
737 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
738 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
740 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
742 SetScreenStates_AfterFadingOut();
745 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
747 static struct TitleFadingInfo fading_leave_stored;
750 fading_leave_stored = fading_leave;
752 fading = fading_leave_stored;
755 void FadeSetEnterMenu()
757 fading = menu.enter_menu;
759 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
762 void FadeSetLeaveMenu()
764 fading = menu.leave_menu;
766 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
769 void FadeSetEnterScreen()
771 fading = menu.enter_screen[game_status];
773 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
776 void FadeSetNextScreen()
778 fading = menu.next_screen[game_status];
780 // (do not overwrite fade mode set by FadeSetEnterScreen)
781 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
784 void FadeSetLeaveScreen()
786 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
789 void FadeSetFromType(int type)
791 if (type & TYPE_ENTER_SCREEN)
792 FadeSetEnterScreen();
793 else if (type & TYPE_ENTER)
795 else if (type & TYPE_LEAVE)
799 void FadeSetDisabled()
801 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
803 fading = fading_none;
806 void FadeSkipNextFadeIn()
808 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
811 void FadeSkipNextFadeOut()
813 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
816 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
818 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
820 return (graphic == IMG_UNDEFINED ? NULL :
821 graphic_info[graphic].bitmap != NULL || redefined ?
822 graphic_info[graphic].bitmap :
823 graphic_info[default_graphic].bitmap);
826 Bitmap *getBackgroundBitmap(int graphic)
828 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
831 Bitmap *getGlobalBorderBitmap(int graphic)
833 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
836 Bitmap *getGlobalBorderBitmapFromStatus(int status)
839 (status == GAME_MODE_MAIN ||
840 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
841 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
842 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
843 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
846 return getGlobalBorderBitmap(graphic);
849 void SetWindowBackgroundImageIfDefined(int graphic)
851 if (graphic_info[graphic].bitmap)
852 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
855 void SetMainBackgroundImageIfDefined(int graphic)
857 if (graphic_info[graphic].bitmap)
858 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
861 void SetDoorBackgroundImageIfDefined(int graphic)
863 if (graphic_info[graphic].bitmap)
864 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
867 void SetWindowBackgroundImage(int graphic)
869 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
872 void SetMainBackgroundImage(int graphic)
874 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
877 void SetDoorBackgroundImage(int graphic)
879 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
882 void SetPanelBackground()
884 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
886 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
887 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
889 SetDoorBackgroundBitmap(bitmap_db_panel);
892 void DrawBackground(int x, int y, int width, int height)
894 /* "drawto" might still point to playfield buffer here (hall of fame) */
895 ClearRectangleOnBackground(backbuffer, x, y, width, height);
897 if (IN_GFX_FIELD_FULL(x, y))
898 redraw_mask |= REDRAW_FIELD;
899 else if (IN_GFX_DOOR_1(x, y))
900 redraw_mask |= REDRAW_DOOR_1;
901 else if (IN_GFX_DOOR_2(x, y))
902 redraw_mask |= REDRAW_DOOR_2;
903 else if (IN_GFX_DOOR_3(x, y))
904 redraw_mask |= REDRAW_DOOR_3;
907 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
909 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
911 if (font->bitmap == NULL)
914 DrawBackground(x, y, width, height);
917 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
919 struct GraphicInfo *g = &graphic_info[graphic];
921 if (g->bitmap == NULL)
924 DrawBackground(x, y, width, height);
927 static int game_status_last = -1;
928 static Bitmap *global_border_bitmap_last = NULL;
929 static Bitmap *global_border_bitmap = NULL;
930 static int real_sx_last = -1, real_sy_last = -1;
931 static int full_sxsize_last = -1, full_sysize_last = -1;
932 static int dx_last = -1, dy_last = -1;
933 static int dxsize_last = -1, dysize_last = -1;
934 static int vx_last = -1, vy_last = -1;
935 static int vxsize_last = -1, vysize_last = -1;
937 boolean CheckIfGlobalBorderHasChanged()
939 // if game status has not changed, global border has not changed either
940 if (game_status == game_status_last)
943 // determine and store new global border bitmap for current game status
944 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
946 return (global_border_bitmap_last != global_border_bitmap);
949 boolean CheckIfGlobalBorderRedrawIsNeeded()
951 // if game status has not changed, nothing has to be redrawn
952 if (game_status == game_status_last)
955 // redraw if last screen was title screen
956 if (game_status_last == GAME_MODE_TITLE)
959 // redraw if global screen border has changed
960 if (CheckIfGlobalBorderHasChanged())
963 // redraw if position or size of playfield area has changed
964 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
965 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
968 // redraw if position or size of door area has changed
969 if (dx_last != DX || dy_last != DY ||
970 dxsize_last != DXSIZE || dysize_last != DYSIZE)
973 // redraw if position or size of tape area has changed
974 if (vx_last != VX || vy_last != VY ||
975 vxsize_last != VXSIZE || vysize_last != VYSIZE)
981 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
984 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
986 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
989 void RedrawGlobalBorder()
991 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
993 RedrawGlobalBorderFromBitmap(bitmap);
995 redraw_mask = REDRAW_ALL;
998 static void RedrawGlobalBorderIfNeeded()
1000 if (game_status == game_status_last)
1003 // copy current draw buffer to later copy back areas that have not changed
1004 if (game_status_last != GAME_MODE_TITLE)
1005 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1007 if (CheckIfGlobalBorderRedrawIsNeeded())
1009 // redraw global screen border (or clear, if defined to be empty)
1010 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1012 // copy previous playfield and door areas, if they are defined on both
1013 // previous and current screen and if they still have the same size
1015 if (real_sx_last != -1 && real_sy_last != -1 &&
1016 REAL_SX != -1 && REAL_SY != -1 &&
1017 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1018 BlitBitmap(bitmap_db_store, backbuffer,
1019 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1022 if (dx_last != -1 && dy_last != -1 &&
1023 DX != -1 && DY != -1 &&
1024 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1025 BlitBitmap(bitmap_db_store, backbuffer,
1026 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1028 if (vx_last != -1 && vy_last != -1 &&
1029 VX != -1 && VY != -1 &&
1030 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1031 BlitBitmap(bitmap_db_store, backbuffer,
1032 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1034 redraw_mask = REDRAW_ALL;
1037 game_status_last = game_status;
1039 global_border_bitmap_last = global_border_bitmap;
1041 real_sx_last = REAL_SX;
1042 real_sy_last = REAL_SY;
1043 full_sxsize_last = FULL_SXSIZE;
1044 full_sysize_last = FULL_SYSIZE;
1047 dxsize_last = DXSIZE;
1048 dysize_last = DYSIZE;
1051 vxsize_last = VXSIZE;
1052 vysize_last = VYSIZE;
1057 RedrawGlobalBorderIfNeeded();
1059 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1060 /* (when entering hall of fame after playing) */
1061 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1063 /* !!! maybe this should be done before clearing the background !!! */
1064 if (game_status == GAME_MODE_PLAYING)
1066 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1067 SetDrawtoField(DRAW_FIELDBUFFER);
1071 SetDrawtoField(DRAW_BACKBUFFER);
1075 void MarkTileDirty(int x, int y)
1077 redraw_mask |= REDRAW_FIELD;
1080 void SetBorderElement()
1084 BorderElement = EL_EMPTY;
1086 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1088 for (x = 0; x < lev_fieldx; x++)
1090 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1091 BorderElement = EL_STEELWALL;
1093 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1099 void FloodFillLevel(int from_x, int from_y, int fill_element,
1100 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1101 int max_fieldx, int max_fieldy)
1105 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1106 static int safety = 0;
1108 /* check if starting field still has the desired content */
1109 if (field[from_x][from_y] == fill_element)
1114 if (safety > max_fieldx * max_fieldy)
1115 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1117 old_element = field[from_x][from_y];
1118 field[from_x][from_y] = fill_element;
1120 for (i = 0; i < 4; i++)
1122 x = from_x + check[i][0];
1123 y = from_y + check[i][1];
1125 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1126 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1132 void SetRandomAnimationValue(int x, int y)
1134 gfx.anim_random_frame = GfxRandom[x][y];
1137 int getGraphicAnimationFrame(int graphic, int sync_frame)
1139 /* animation synchronized with global frame counter, not move position */
1140 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1141 sync_frame = FrameCounter;
1143 return getAnimationFrame(graphic_info[graphic].anim_frames,
1144 graphic_info[graphic].anim_delay,
1145 graphic_info[graphic].anim_mode,
1146 graphic_info[graphic].anim_start_frame,
1150 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1151 Bitmap **bitmap, int *x, int *y,
1152 boolean get_backside)
1154 struct GraphicInfo *g = &graphic_info[graphic];
1155 Bitmap *src_bitmap = g->bitmap;
1156 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1157 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1158 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1160 // if no in-game graphics defined, always use standard graphic size
1161 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1162 tilesize = TILESIZE;
1164 if (tilesize == gfx.standard_tile_size)
1165 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1166 else if (tilesize == game.tile_size)
1167 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1169 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1171 if (g->offset_y == 0) /* frames are ordered horizontally */
1173 int max_width = g->anim_frames_per_line * g->width;
1174 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1176 src_x = pos % max_width;
1177 src_y = src_y % g->height + pos / max_width * g->height;
1179 else if (g->offset_x == 0) /* frames are ordered vertically */
1181 int max_height = g->anim_frames_per_line * g->height;
1182 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1184 src_x = src_x % g->width + pos / max_height * g->width;
1185 src_y = pos % max_height;
1187 else /* frames are ordered diagonally */
1189 src_x = src_x + frame * g->offset_x;
1190 src_y = src_y + frame * g->offset_y;
1193 *bitmap = src_bitmap;
1194 *x = src_x * tilesize / g->tile_size;
1195 *y = src_y * tilesize / g->tile_size;
1198 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1199 int *x, int *y, boolean get_backside)
1201 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1205 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1206 Bitmap **bitmap, int *x, int *y)
1208 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1211 void getFixedGraphicSource(int graphic, int frame,
1212 Bitmap **bitmap, int *x, int *y)
1214 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1217 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1219 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1222 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1223 int *x, int *y, boolean get_backside)
1225 struct GraphicInfo *g = &graphic_info[graphic];
1226 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1227 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1229 if (TILESIZE_VAR != TILESIZE)
1230 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1233 *bitmap = g->bitmap;
1235 if (g->offset_y == 0) /* frames are ordered horizontally */
1237 int max_width = g->anim_frames_per_line * g->width;
1238 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1240 *x = pos % max_width;
1241 *y = src_y % g->height + pos / max_width * g->height;
1243 else if (g->offset_x == 0) /* frames are ordered vertically */
1245 int max_height = g->anim_frames_per_line * g->height;
1246 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1248 *x = src_x % g->width + pos / max_height * g->width;
1249 *y = pos % max_height;
1251 else /* frames are ordered diagonally */
1253 *x = src_x + frame * g->offset_x;
1254 *y = src_y + frame * g->offset_y;
1257 *x = *x * TILESIZE_VAR / g->tile_size;
1258 *y = *y * TILESIZE_VAR / g->tile_size;
1261 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1263 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1266 void DrawGraphic(int x, int y, int graphic, int frame)
1269 if (!IN_SCR_FIELD(x, y))
1271 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1272 printf("DrawGraphic(): This should never happen!\n");
1277 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1280 MarkTileDirty(x, y);
1283 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1286 if (!IN_SCR_FIELD(x, y))
1288 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1289 printf("DrawGraphic(): This should never happen!\n");
1294 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1296 MarkTileDirty(x, y);
1299 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1305 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1307 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1310 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1316 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1317 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1320 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1323 if (!IN_SCR_FIELD(x, y))
1325 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1326 printf("DrawGraphicThruMask(): This should never happen!\n");
1331 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1334 MarkTileDirty(x, y);
1337 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1340 if (!IN_SCR_FIELD(x, y))
1342 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1343 printf("DrawGraphicThruMask(): This should never happen!\n");
1348 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1350 MarkTileDirty(x, y);
1353 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1359 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1361 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1365 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1366 int graphic, int frame)
1371 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1373 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1377 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1379 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1381 MarkTileDirty(x / tilesize, y / tilesize);
1384 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1390 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1391 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1394 void DrawMiniGraphic(int x, int y, int graphic)
1396 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1397 MarkTileDirty(x / 2, y / 2);
1400 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1405 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1406 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1409 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1410 int graphic, int frame,
1411 int cut_mode, int mask_mode)
1416 int width = TILEX, height = TILEY;
1419 if (dx || dy) /* shifted graphic */
1421 if (x < BX1) /* object enters playfield from the left */
1428 else if (x > BX2) /* object enters playfield from the right */
1434 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1440 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1442 else if (dx) /* general horizontal movement */
1443 MarkTileDirty(x + SIGN(dx), y);
1445 if (y < BY1) /* object enters playfield from the top */
1447 if (cut_mode == CUT_BELOW) /* object completely above top border */
1455 else if (y > BY2) /* object enters playfield from the bottom */
1461 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1467 else if (dy > 0 && cut_mode == CUT_ABOVE)
1469 if (y == BY2) /* object completely above bottom border */
1475 MarkTileDirty(x, y + 1);
1476 } /* object leaves playfield to the bottom */
1477 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1479 else if (dy) /* general vertical movement */
1480 MarkTileDirty(x, y + SIGN(dy));
1484 if (!IN_SCR_FIELD(x, y))
1486 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1487 printf("DrawGraphicShifted(): This should never happen!\n");
1492 width = width * TILESIZE_VAR / TILESIZE;
1493 height = height * TILESIZE_VAR / TILESIZE;
1494 cx = cx * TILESIZE_VAR / TILESIZE;
1495 cy = cy * TILESIZE_VAR / TILESIZE;
1496 dx = dx * TILESIZE_VAR / TILESIZE;
1497 dy = dy * TILESIZE_VAR / TILESIZE;
1499 if (width > 0 && height > 0)
1501 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1506 dst_x = FX + x * TILEX_VAR + dx;
1507 dst_y = FY + y * TILEY_VAR + dy;
1509 if (mask_mode == USE_MASKING)
1510 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1513 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1516 MarkTileDirty(x, y);
1520 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1521 int graphic, int frame,
1522 int cut_mode, int mask_mode)
1527 int width = TILEX_VAR, height = TILEY_VAR;
1530 int x2 = x + SIGN(dx);
1531 int y2 = y + SIGN(dy);
1533 /* movement with two-tile animations must be sync'ed with movement position,
1534 not with current GfxFrame (which can be higher when using slow movement) */
1535 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1536 int anim_frames = graphic_info[graphic].anim_frames;
1538 /* (we also need anim_delay here for movement animations with less frames) */
1539 int anim_delay = graphic_info[graphic].anim_delay;
1540 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1542 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1543 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1545 /* re-calculate animation frame for two-tile movement animation */
1546 frame = getGraphicAnimationFrame(graphic, sync_frame);
1548 /* check if movement start graphic inside screen area and should be drawn */
1549 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1551 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1553 dst_x = FX + x1 * TILEX_VAR;
1554 dst_y = FY + y1 * TILEY_VAR;
1556 if (mask_mode == USE_MASKING)
1557 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1560 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1563 MarkTileDirty(x1, y1);
1566 /* check if movement end graphic inside screen area and should be drawn */
1567 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1569 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1571 dst_x = FX + x2 * TILEX_VAR;
1572 dst_y = FY + y2 * TILEY_VAR;
1574 if (mask_mode == USE_MASKING)
1575 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1578 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1581 MarkTileDirty(x2, y2);
1585 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1586 int graphic, int frame,
1587 int cut_mode, int mask_mode)
1591 DrawGraphic(x, y, graphic, frame);
1596 if (graphic_info[graphic].double_movement) /* EM style movement images */
1597 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1599 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1602 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1603 int frame, int cut_mode)
1605 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1608 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1609 int cut_mode, int mask_mode)
1611 int lx = LEVELX(x), ly = LEVELY(y);
1615 if (IN_LEV_FIELD(lx, ly))
1617 SetRandomAnimationValue(lx, ly);
1619 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1620 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1622 /* do not use double (EM style) movement graphic when not moving */
1623 if (graphic_info[graphic].double_movement && !dx && !dy)
1625 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1626 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1629 else /* border element */
1631 graphic = el2img(element);
1632 frame = getGraphicAnimationFrame(graphic, -1);
1635 if (element == EL_EXPANDABLE_WALL)
1637 boolean left_stopped = FALSE, right_stopped = FALSE;
1639 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1640 left_stopped = TRUE;
1641 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1642 right_stopped = TRUE;
1644 if (left_stopped && right_stopped)
1646 else if (left_stopped)
1648 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1649 frame = graphic_info[graphic].anim_frames - 1;
1651 else if (right_stopped)
1653 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1654 frame = graphic_info[graphic].anim_frames - 1;
1659 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1660 else if (mask_mode == USE_MASKING)
1661 DrawGraphicThruMask(x, y, graphic, frame);
1663 DrawGraphic(x, y, graphic, frame);
1666 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1667 int cut_mode, int mask_mode)
1669 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1670 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1671 cut_mode, mask_mode);
1674 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1677 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1680 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1683 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1686 void DrawLevelElementThruMask(int x, int y, int element)
1688 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1691 void DrawLevelFieldThruMask(int x, int y)
1693 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1696 /* !!! implementation of quicksand is totally broken !!! */
1697 #define IS_CRUMBLED_TILE(x, y, e) \
1698 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1699 !IS_MOVING(x, y) || \
1700 (e) == EL_QUICKSAND_EMPTYING || \
1701 (e) == EL_QUICKSAND_FAST_EMPTYING))
1703 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1708 int width, height, cx, cy;
1709 int sx = SCREENX(x), sy = SCREENY(y);
1710 int crumbled_border_size = graphic_info[graphic].border_size;
1713 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1715 for (i = 1; i < 4; i++)
1717 int dxx = (i & 1 ? dx : 0);
1718 int dyy = (i & 2 ? dy : 0);
1721 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1724 /* check if neighbour field is of same crumble type */
1725 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1726 graphic_info[graphic].class ==
1727 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1729 /* return if check prevents inner corner */
1730 if (same == (dxx == dx && dyy == dy))
1734 /* if we reach this point, we have an inner corner */
1736 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1738 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1739 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1740 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1741 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1743 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1744 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1747 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1752 int width, height, bx, by, cx, cy;
1753 int sx = SCREENX(x), sy = SCREENY(y);
1754 int crumbled_border_size = graphic_info[graphic].border_size;
1755 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1756 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1759 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1761 /* draw simple, sloppy, non-corner-accurate crumbled border */
1763 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1764 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1765 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1766 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1768 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1769 FX + sx * TILEX_VAR + cx,
1770 FY + sy * TILEY_VAR + cy);
1772 /* (remaining middle border part must be at least as big as corner part) */
1773 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1774 crumbled_border_size >= TILESIZE / 3)
1777 /* correct corners of crumbled border, if needed */
1779 for (i = -1; i <= 1; i += 2)
1781 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1782 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1783 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1786 /* check if neighbour field is of same crumble type */
1787 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1788 graphic_info[graphic].class ==
1789 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1791 /* no crumbled corner, but continued crumbled border */
1793 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1794 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1795 int b1 = (i == 1 ? crumbled_border_size_var :
1796 TILESIZE_VAR - 2 * crumbled_border_size_var);
1798 width = crumbled_border_size_var;
1799 height = crumbled_border_size_var;
1801 if (dir == 1 || dir == 2)
1816 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1818 FX + sx * TILEX_VAR + cx,
1819 FY + sy * TILEY_VAR + cy);
1824 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1826 int sx = SCREENX(x), sy = SCREENY(y);
1829 static int xy[4][2] =
1837 if (!IN_LEV_FIELD(x, y))
1840 element = TILE_GFX_ELEMENT(x, y);
1842 /* crumble field itself */
1843 if (IS_CRUMBLED_TILE(x, y, element))
1845 if (!IN_SCR_FIELD(sx, sy))
1848 for (i = 0; i < 4; i++)
1850 int xx = x + xy[i][0];
1851 int yy = y + xy[i][1];
1853 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1856 /* check if neighbour field is of same crumble type */
1857 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1858 graphic_info[graphic].class ==
1859 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1862 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1865 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1866 graphic_info[graphic].anim_frames == 2)
1868 for (i = 0; i < 4; i++)
1870 int dx = (i & 1 ? +1 : -1);
1871 int dy = (i & 2 ? +1 : -1);
1873 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1877 MarkTileDirty(sx, sy);
1879 else /* center field not crumbled -- crumble neighbour fields */
1881 for (i = 0; i < 4; i++)
1883 int xx = x + xy[i][0];
1884 int yy = y + xy[i][1];
1885 int sxx = sx + xy[i][0];
1886 int syy = sy + xy[i][1];
1888 if (!IN_LEV_FIELD(xx, yy) ||
1889 !IN_SCR_FIELD(sxx, syy))
1892 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1895 element = TILE_GFX_ELEMENT(xx, yy);
1897 if (!IS_CRUMBLED_TILE(xx, yy, element))
1900 graphic = el_act2crm(element, ACTION_DEFAULT);
1902 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1904 MarkTileDirty(sxx, syy);
1909 void DrawLevelFieldCrumbled(int x, int y)
1913 if (!IN_LEV_FIELD(x, y))
1916 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1917 GfxElement[x][y] != EL_UNDEFINED &&
1918 GFX_CRUMBLED(GfxElement[x][y]))
1920 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1925 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1927 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1930 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1933 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1934 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1935 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1936 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1937 int sx = SCREENX(x), sy = SCREENY(y);
1939 DrawGraphic(sx, sy, graphic1, frame1);
1940 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1943 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1945 int sx = SCREENX(x), sy = SCREENY(y);
1946 static int xy[4][2] =
1955 for (i = 0; i < 4; i++)
1957 int xx = x + xy[i][0];
1958 int yy = y + xy[i][1];
1959 int sxx = sx + xy[i][0];
1960 int syy = sy + xy[i][1];
1962 if (!IN_LEV_FIELD(xx, yy) ||
1963 !IN_SCR_FIELD(sxx, syy) ||
1964 !GFX_CRUMBLED(Feld[xx][yy]) ||
1968 DrawLevelField(xx, yy);
1972 static int getBorderElement(int x, int y)
1976 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1977 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1978 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1979 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1980 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1981 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1982 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1984 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1985 int steel_position = (x == -1 && y == -1 ? 0 :
1986 x == lev_fieldx && y == -1 ? 1 :
1987 x == -1 && y == lev_fieldy ? 2 :
1988 x == lev_fieldx && y == lev_fieldy ? 3 :
1989 x == -1 || x == lev_fieldx ? 4 :
1990 y == -1 || y == lev_fieldy ? 5 : 6);
1992 return border[steel_position][steel_type];
1995 void DrawScreenElement(int x, int y, int element)
1997 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1998 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2001 void DrawLevelElement(int x, int y, int element)
2003 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2004 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2007 void DrawScreenField(int x, int y)
2009 int lx = LEVELX(x), ly = LEVELY(y);
2010 int element, content;
2012 if (!IN_LEV_FIELD(lx, ly))
2014 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2017 element = getBorderElement(lx, ly);
2019 DrawScreenElement(x, y, element);
2024 element = Feld[lx][ly];
2025 content = Store[lx][ly];
2027 if (IS_MOVING(lx, ly))
2029 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2030 boolean cut_mode = NO_CUTTING;
2032 if (element == EL_QUICKSAND_EMPTYING ||
2033 element == EL_QUICKSAND_FAST_EMPTYING ||
2034 element == EL_MAGIC_WALL_EMPTYING ||
2035 element == EL_BD_MAGIC_WALL_EMPTYING ||
2036 element == EL_DC_MAGIC_WALL_EMPTYING ||
2037 element == EL_AMOEBA_DROPPING)
2038 cut_mode = CUT_ABOVE;
2039 else if (element == EL_QUICKSAND_FILLING ||
2040 element == EL_QUICKSAND_FAST_FILLING ||
2041 element == EL_MAGIC_WALL_FILLING ||
2042 element == EL_BD_MAGIC_WALL_FILLING ||
2043 element == EL_DC_MAGIC_WALL_FILLING)
2044 cut_mode = CUT_BELOW;
2046 if (cut_mode == CUT_ABOVE)
2047 DrawScreenElement(x, y, element);
2049 DrawScreenElement(x, y, EL_EMPTY);
2052 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2053 else if (cut_mode == NO_CUTTING)
2054 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2057 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2059 if (cut_mode == CUT_BELOW &&
2060 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2061 DrawLevelElement(lx, ly + 1, element);
2064 if (content == EL_ACID)
2066 int dir = MovDir[lx][ly];
2067 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2068 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2070 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2073 else if (IS_BLOCKED(lx, ly))
2078 boolean cut_mode = NO_CUTTING;
2079 int element_old, content_old;
2081 Blocked2Moving(lx, ly, &oldx, &oldy);
2084 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2085 MovDir[oldx][oldy] == MV_RIGHT);
2087 element_old = Feld[oldx][oldy];
2088 content_old = Store[oldx][oldy];
2090 if (element_old == EL_QUICKSAND_EMPTYING ||
2091 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2092 element_old == EL_MAGIC_WALL_EMPTYING ||
2093 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2094 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2095 element_old == EL_AMOEBA_DROPPING)
2096 cut_mode = CUT_ABOVE;
2098 DrawScreenElement(x, y, EL_EMPTY);
2101 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2103 else if (cut_mode == NO_CUTTING)
2104 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2107 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2110 else if (IS_DRAWABLE(element))
2111 DrawScreenElement(x, y, element);
2113 DrawScreenElement(x, y, EL_EMPTY);
2116 void DrawLevelField(int x, int y)
2118 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2119 DrawScreenField(SCREENX(x), SCREENY(y));
2120 else if (IS_MOVING(x, y))
2124 Moving2Blocked(x, y, &newx, &newy);
2125 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2126 DrawScreenField(SCREENX(newx), SCREENY(newy));
2128 else if (IS_BLOCKED(x, y))
2132 Blocked2Moving(x, y, &oldx, &oldy);
2133 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2134 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2138 void DrawSizedElement(int x, int y, int element, int tilesize)
2142 graphic = el2edimg(element);
2143 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2146 void DrawMiniElement(int x, int y, int element)
2150 graphic = el2edimg(element);
2151 DrawMiniGraphic(x, y, graphic);
2154 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2157 int x = sx + scroll_x, y = sy + scroll_y;
2159 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2160 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2161 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2162 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2164 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2167 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2169 int x = sx + scroll_x, y = sy + scroll_y;
2171 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2172 DrawMiniElement(sx, sy, EL_EMPTY);
2173 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2174 DrawMiniElement(sx, sy, Feld[x][y]);
2176 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2179 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2180 int x, int y, int xsize, int ysize,
2181 int tile_width, int tile_height)
2185 int dst_x = startx + x * tile_width;
2186 int dst_y = starty + y * tile_height;
2187 int width = graphic_info[graphic].width;
2188 int height = graphic_info[graphic].height;
2189 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2190 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2191 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2192 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2193 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2194 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2195 boolean draw_masked = graphic_info[graphic].draw_masked;
2197 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2199 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2201 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2205 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2206 inner_sx + (x - 1) * tile_width % inner_width);
2207 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2208 inner_sy + (y - 1) * tile_height % inner_height);
2211 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2214 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2218 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2219 int x, int y, int xsize, int ysize, int font_nr)
2221 int font_width = getFontWidth(font_nr);
2222 int font_height = getFontHeight(font_nr);
2224 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2225 font_width, font_height);
2228 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2230 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2231 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2232 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2233 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2234 boolean no_delay = (tape.warp_forward);
2235 unsigned int anim_delay = 0;
2236 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2237 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2238 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2239 int font_width = getFontWidth(font_nr);
2240 int font_height = getFontHeight(font_nr);
2241 int max_xsize = level.envelope[envelope_nr].xsize;
2242 int max_ysize = level.envelope[envelope_nr].ysize;
2243 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2244 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2245 int xend = max_xsize;
2246 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2247 int xstep = (xstart < xend ? 1 : 0);
2248 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2250 int end = MAX(xend - xstart, yend - ystart);
2253 for (i = start; i <= end; i++)
2255 int last_frame = end; // last frame of this "for" loop
2256 int x = xstart + i * xstep;
2257 int y = ystart + i * ystep;
2258 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2259 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2260 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2261 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2264 SetDrawtoField(DRAW_FIELDBUFFER);
2266 BlitScreenToBitmap(backbuffer);
2268 SetDrawtoField(DRAW_BACKBUFFER);
2270 for (yy = 0; yy < ysize; yy++)
2271 for (xx = 0; xx < xsize; xx++)
2272 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2274 DrawTextBuffer(sx + font_width, sy + font_height,
2275 level.envelope[envelope_nr].text, font_nr, max_xsize,
2276 xsize - 2, ysize - 2, 0, mask_mode,
2277 level.envelope[envelope_nr].autowrap,
2278 level.envelope[envelope_nr].centered, FALSE);
2280 redraw_mask |= REDRAW_FIELD;
2283 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2287 void ShowEnvelope(int envelope_nr)
2289 int element = EL_ENVELOPE_1 + envelope_nr;
2290 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2291 int sound_opening = element_info[element].sound[ACTION_OPENING];
2292 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2293 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2294 boolean no_delay = (tape.warp_forward);
2295 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2296 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2297 int anim_mode = graphic_info[graphic].anim_mode;
2298 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2299 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2301 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2303 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2305 if (anim_mode == ANIM_DEFAULT)
2306 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2308 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2311 Delay(wait_delay_value);
2313 WaitForEventToContinue();
2315 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2317 if (anim_mode != ANIM_NONE)
2318 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2320 if (anim_mode == ANIM_DEFAULT)
2321 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2323 game.envelope_active = FALSE;
2325 SetDrawtoField(DRAW_FIELDBUFFER);
2327 redraw_mask |= REDRAW_FIELD;
2331 static void setRequestBasePosition(int *x, int *y)
2333 int sx_base, sy_base;
2335 if (request.x != -1)
2336 sx_base = request.x;
2337 else if (request.align == ALIGN_LEFT)
2339 else if (request.align == ALIGN_RIGHT)
2340 sx_base = SX + SXSIZE;
2342 sx_base = SX + SXSIZE / 2;
2344 if (request.y != -1)
2345 sy_base = request.y;
2346 else if (request.valign == VALIGN_TOP)
2348 else if (request.valign == VALIGN_BOTTOM)
2349 sy_base = SY + SYSIZE;
2351 sy_base = SY + SYSIZE / 2;
2357 static void setRequestPositionExt(int *x, int *y, int width, int height,
2358 boolean add_border_size)
2360 int border_size = request.border_size;
2361 int sx_base, sy_base;
2364 setRequestBasePosition(&sx_base, &sy_base);
2366 if (request.align == ALIGN_LEFT)
2368 else if (request.align == ALIGN_RIGHT)
2369 sx = sx_base - width;
2371 sx = sx_base - width / 2;
2373 if (request.valign == VALIGN_TOP)
2375 else if (request.valign == VALIGN_BOTTOM)
2376 sy = sy_base - height;
2378 sy = sy_base - height / 2;
2380 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2381 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2383 if (add_border_size)
2393 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2395 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2398 void DrawEnvelopeRequest(char *text)
2400 int last_game_status = game_status; /* save current game status */
2401 char *text_final = text;
2402 char *text_door_style = NULL;
2403 int graphic = IMG_BACKGROUND_REQUEST;
2404 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2405 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2406 int font_nr = FONT_REQUEST;
2407 int font_width = getFontWidth(font_nr);
2408 int font_height = getFontHeight(font_nr);
2409 int border_size = request.border_size;
2410 int line_spacing = request.line_spacing;
2411 int line_height = font_height + line_spacing;
2412 int max_text_width = request.width - 2 * border_size;
2413 int max_text_height = request.height - 2 * border_size;
2414 int line_length = max_text_width / font_width;
2415 int max_lines = max_text_height / line_height;
2416 int text_width = line_length * font_width;
2417 int width = request.width;
2418 int height = request.height;
2419 int tile_size = MAX(request.step_offset, 1);
2420 int x_steps = width / tile_size;
2421 int y_steps = height / tile_size;
2422 int sx_offset = border_size;
2423 int sy_offset = border_size;
2427 if (request.centered)
2428 sx_offset = (request.width - text_width) / 2;
2430 if (request.wrap_single_words && !request.autowrap)
2432 char *src_text_ptr, *dst_text_ptr;
2434 text_door_style = checked_malloc(2 * strlen(text) + 1);
2436 src_text_ptr = text;
2437 dst_text_ptr = text_door_style;
2439 while (*src_text_ptr)
2441 if (*src_text_ptr == ' ' ||
2442 *src_text_ptr == '?' ||
2443 *src_text_ptr == '!')
2444 *dst_text_ptr++ = '\n';
2446 if (*src_text_ptr != ' ')
2447 *dst_text_ptr++ = *src_text_ptr;
2452 *dst_text_ptr = '\0';
2454 text_final = text_door_style;
2457 setRequestPosition(&sx, &sy, FALSE);
2459 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2461 for (y = 0; y < y_steps; y++)
2462 for (x = 0; x < x_steps; x++)
2463 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2464 x, y, x_steps, y_steps,
2465 tile_size, tile_size);
2467 /* force DOOR font inside door area */
2468 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
2470 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2471 line_length, -1, max_lines, line_spacing, mask_mode,
2472 request.autowrap, request.centered, FALSE);
2474 SetGameStatus(last_game_status); /* restore current game status */
2476 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2477 RedrawGadget(tool_gadget[i]);
2479 // store readily prepared envelope request for later use when animating
2480 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2482 if (text_door_style)
2483 free(text_door_style);
2486 void AnimateEnvelopeRequest(int anim_mode, int action)
2488 int graphic = IMG_BACKGROUND_REQUEST;
2489 boolean draw_masked = graphic_info[graphic].draw_masked;
2490 int delay_value_normal = request.step_delay;
2491 int delay_value_fast = delay_value_normal / 2;
2492 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2493 boolean no_delay = (tape.warp_forward);
2494 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2495 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2496 unsigned int anim_delay = 0;
2498 int tile_size = MAX(request.step_offset, 1);
2499 int max_xsize = request.width / tile_size;
2500 int max_ysize = request.height / tile_size;
2501 int max_xsize_inner = max_xsize - 2;
2502 int max_ysize_inner = max_ysize - 2;
2504 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2505 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2506 int xend = max_xsize_inner;
2507 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2508 int xstep = (xstart < xend ? 1 : 0);
2509 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2511 int end = MAX(xend - xstart, yend - ystart);
2514 if (setup.quick_doors)
2521 for (i = start; i <= end; i++)
2523 int last_frame = end; // last frame of this "for" loop
2524 int x = xstart + i * xstep;
2525 int y = ystart + i * ystep;
2526 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2527 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2528 int xsize_size_left = (xsize - 1) * tile_size;
2529 int ysize_size_top = (ysize - 1) * tile_size;
2530 int max_xsize_pos = (max_xsize - 1) * tile_size;
2531 int max_ysize_pos = (max_ysize - 1) * tile_size;
2532 int width = xsize * tile_size;
2533 int height = ysize * tile_size;
2538 setRequestPosition(&src_x, &src_y, FALSE);
2539 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2541 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2543 for (yy = 0; yy < 2; yy++)
2545 for (xx = 0; xx < 2; xx++)
2547 int src_xx = src_x + xx * max_xsize_pos;
2548 int src_yy = src_y + yy * max_ysize_pos;
2549 int dst_xx = dst_x + xx * xsize_size_left;
2550 int dst_yy = dst_y + yy * ysize_size_top;
2551 int xx_size = (xx ? tile_size : xsize_size_left);
2552 int yy_size = (yy ? tile_size : ysize_size_top);
2555 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2556 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2558 BlitBitmap(bitmap_db_cross, backbuffer,
2559 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2563 redraw_mask |= REDRAW_FIELD;
2568 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2572 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2574 int graphic = IMG_BACKGROUND_REQUEST;
2575 int sound_opening = SND_REQUEST_OPENING;
2576 int sound_closing = SND_REQUEST_CLOSING;
2577 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2578 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2579 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2580 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2581 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2583 if (game_status == GAME_MODE_PLAYING)
2584 BlitScreenToBitmap(backbuffer);
2586 SetDrawtoField(DRAW_BACKBUFFER);
2588 // SetDrawBackgroundMask(REDRAW_NONE);
2590 if (action == ACTION_OPENING)
2592 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2594 if (req_state & REQ_ASK)
2596 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2597 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2599 else if (req_state & REQ_CONFIRM)
2601 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2603 else if (req_state & REQ_PLAYER)
2605 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2606 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2607 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2608 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2611 DrawEnvelopeRequest(text);
2613 if (game_status != GAME_MODE_MAIN)
2617 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2619 if (action == ACTION_OPENING)
2621 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2623 if (anim_mode == ANIM_DEFAULT)
2624 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2626 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2630 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2632 if (anim_mode != ANIM_NONE)
2633 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2635 if (anim_mode == ANIM_DEFAULT)
2636 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2639 game.envelope_active = FALSE;
2641 if (action == ACTION_CLOSING)
2643 if (game_status != GAME_MODE_MAIN)
2646 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2649 // SetDrawBackgroundMask(last_draw_background_mask);
2651 redraw_mask |= REDRAW_FIELD;
2653 if (game_status == GAME_MODE_MAIN)
2658 if (action == ACTION_CLOSING &&
2659 game_status == GAME_MODE_PLAYING &&
2660 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2661 SetDrawtoField(DRAW_FIELDBUFFER);
2664 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2668 int graphic = el2preimg(element);
2670 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2671 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2674 void DrawLevel(int draw_background_mask)
2678 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2679 SetDrawBackgroundMask(draw_background_mask);
2683 for (x = BX1; x <= BX2; x++)
2684 for (y = BY1; y <= BY2; y++)
2685 DrawScreenField(x, y);
2687 redraw_mask |= REDRAW_FIELD;
2690 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2695 for (x = 0; x < size_x; x++)
2696 for (y = 0; y < size_y; y++)
2697 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2699 redraw_mask |= REDRAW_FIELD;
2702 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2706 for (x = 0; x < size_x; x++)
2707 for (y = 0; y < size_y; y++)
2708 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2710 redraw_mask |= REDRAW_FIELD;
2713 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2715 boolean show_level_border = (BorderElement != EL_EMPTY);
2716 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2717 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2718 int tile_size = preview.tile_size;
2719 int preview_width = preview.xsize * tile_size;
2720 int preview_height = preview.ysize * tile_size;
2721 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2722 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2723 int real_preview_width = real_preview_xsize * tile_size;
2724 int real_preview_height = real_preview_ysize * tile_size;
2725 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2726 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2729 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2732 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2734 dst_x += (preview_width - real_preview_width) / 2;
2735 dst_y += (preview_height - real_preview_height) / 2;
2737 for (x = 0; x < real_preview_xsize; x++)
2739 for (y = 0; y < real_preview_ysize; y++)
2741 int lx = from_x + x + (show_level_border ? -1 : 0);
2742 int ly = from_y + y + (show_level_border ? -1 : 0);
2743 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2744 getBorderElement(lx, ly));
2746 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2747 element, tile_size);
2751 redraw_mask |= REDRAW_FIELD;
2754 #define MICROLABEL_EMPTY 0
2755 #define MICROLABEL_LEVEL_NAME 1
2756 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2757 #define MICROLABEL_LEVEL_AUTHOR 3
2758 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2759 #define MICROLABEL_IMPORTED_FROM 5
2760 #define MICROLABEL_IMPORTED_BY_HEAD 6
2761 #define MICROLABEL_IMPORTED_BY 7
2763 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2765 int max_text_width = SXSIZE;
2766 int font_width = getFontWidth(font_nr);
2768 if (pos->align == ALIGN_CENTER)
2769 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2770 else if (pos->align == ALIGN_RIGHT)
2771 max_text_width = pos->x;
2773 max_text_width = SXSIZE - pos->x;
2775 return max_text_width / font_width;
2778 static void DrawPreviewLevelLabelExt(int mode)
2780 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2781 char label_text[MAX_OUTPUT_LINESIZE + 1];
2782 int max_len_label_text;
2783 int font_nr = pos->font;
2786 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2789 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2790 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2791 mode == MICROLABEL_IMPORTED_BY_HEAD)
2792 font_nr = pos->font_alt;
2794 max_len_label_text = getMaxTextLength(pos, font_nr);
2796 if (pos->size != -1)
2797 max_len_label_text = pos->size;
2799 for (i = 0; i < max_len_label_text; i++)
2800 label_text[i] = ' ';
2801 label_text[max_len_label_text] = '\0';
2803 if (strlen(label_text) > 0)
2804 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2807 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2808 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2809 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2810 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2811 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2812 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2813 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2814 max_len_label_text);
2815 label_text[max_len_label_text] = '\0';
2817 if (strlen(label_text) > 0)
2818 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2820 redraw_mask |= REDRAW_FIELD;
2823 static void DrawPreviewLevelExt(boolean restart)
2825 static unsigned int scroll_delay = 0;
2826 static unsigned int label_delay = 0;
2827 static int from_x, from_y, scroll_direction;
2828 static int label_state, label_counter;
2829 unsigned int scroll_delay_value = preview.step_delay;
2830 boolean show_level_border = (BorderElement != EL_EMPTY);
2831 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2832 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2833 int last_game_status = game_status; /* save current game status */
2840 if (preview.anim_mode == ANIM_CENTERED)
2842 if (level_xsize > preview.xsize)
2843 from_x = (level_xsize - preview.xsize) / 2;
2844 if (level_ysize > preview.ysize)
2845 from_y = (level_ysize - preview.ysize) / 2;
2848 from_x += preview.xoffset;
2849 from_y += preview.yoffset;
2851 scroll_direction = MV_RIGHT;
2855 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2856 DrawPreviewLevelLabelExt(label_state);
2858 /* initialize delay counters */
2859 DelayReached(&scroll_delay, 0);
2860 DelayReached(&label_delay, 0);
2862 if (leveldir_current->name)
2864 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2865 char label_text[MAX_OUTPUT_LINESIZE + 1];
2866 int font_nr = pos->font;
2867 int max_len_label_text = getMaxTextLength(pos, font_nr);
2869 if (pos->size != -1)
2870 max_len_label_text = pos->size;
2872 strncpy(label_text, leveldir_current->name, max_len_label_text);
2873 label_text[max_len_label_text] = '\0';
2875 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2876 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2879 SetGameStatus(last_game_status); /* restore current game status */
2884 /* scroll preview level, if needed */
2885 if (preview.anim_mode != ANIM_NONE &&
2886 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2887 DelayReached(&scroll_delay, scroll_delay_value))
2889 switch (scroll_direction)
2894 from_x -= preview.step_offset;
2895 from_x = (from_x < 0 ? 0 : from_x);
2898 scroll_direction = MV_UP;
2902 if (from_x < level_xsize - preview.xsize)
2904 from_x += preview.step_offset;
2905 from_x = (from_x > level_xsize - preview.xsize ?
2906 level_xsize - preview.xsize : from_x);
2909 scroll_direction = MV_DOWN;
2915 from_y -= preview.step_offset;
2916 from_y = (from_y < 0 ? 0 : from_y);
2919 scroll_direction = MV_RIGHT;
2923 if (from_y < level_ysize - preview.ysize)
2925 from_y += preview.step_offset;
2926 from_y = (from_y > level_ysize - preview.ysize ?
2927 level_ysize - preview.ysize : from_y);
2930 scroll_direction = MV_LEFT;
2937 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2940 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2941 /* redraw micro level label, if needed */
2942 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2943 !strEqual(level.author, ANONYMOUS_NAME) &&
2944 !strEqual(level.author, leveldir_current->name) &&
2945 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2947 int max_label_counter = 23;
2949 if (leveldir_current->imported_from != NULL &&
2950 strlen(leveldir_current->imported_from) > 0)
2951 max_label_counter += 14;
2952 if (leveldir_current->imported_by != NULL &&
2953 strlen(leveldir_current->imported_by) > 0)
2954 max_label_counter += 14;
2956 label_counter = (label_counter + 1) % max_label_counter;
2957 label_state = (label_counter >= 0 && label_counter <= 7 ?
2958 MICROLABEL_LEVEL_NAME :
2959 label_counter >= 9 && label_counter <= 12 ?
2960 MICROLABEL_LEVEL_AUTHOR_HEAD :
2961 label_counter >= 14 && label_counter <= 21 ?
2962 MICROLABEL_LEVEL_AUTHOR :
2963 label_counter >= 23 && label_counter <= 26 ?
2964 MICROLABEL_IMPORTED_FROM_HEAD :
2965 label_counter >= 28 && label_counter <= 35 ?
2966 MICROLABEL_IMPORTED_FROM :
2967 label_counter >= 37 && label_counter <= 40 ?
2968 MICROLABEL_IMPORTED_BY_HEAD :
2969 label_counter >= 42 && label_counter <= 49 ?
2970 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2972 if (leveldir_current->imported_from == NULL &&
2973 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2974 label_state == MICROLABEL_IMPORTED_FROM))
2975 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2976 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2978 DrawPreviewLevelLabelExt(label_state);
2981 SetGameStatus(last_game_status); /* restore current game status */
2984 void DrawPreviewLevelInitial()
2986 DrawPreviewLevelExt(TRUE);
2989 void DrawPreviewLevelAnimation()
2991 DrawPreviewLevelExt(FALSE);
2994 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2995 int graphic, int sync_frame,
2998 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3000 if (mask_mode == USE_MASKING)
3001 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3003 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3006 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3007 int graphic, int sync_frame, int mask_mode)
3009 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3011 if (mask_mode == USE_MASKING)
3012 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3014 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3017 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3019 int lx = LEVELX(x), ly = LEVELY(y);
3021 if (!IN_SCR_FIELD(x, y))
3024 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3025 graphic, GfxFrame[lx][ly], NO_MASKING);
3027 MarkTileDirty(x, y);
3030 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3032 int lx = LEVELX(x), ly = LEVELY(y);
3034 if (!IN_SCR_FIELD(x, y))
3037 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3038 graphic, GfxFrame[lx][ly], NO_MASKING);
3039 MarkTileDirty(x, y);
3042 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3044 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3047 void DrawLevelElementAnimation(int x, int y, int element)
3049 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3051 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3054 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3056 int sx = SCREENX(x), sy = SCREENY(y);
3058 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3061 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3064 DrawGraphicAnimation(sx, sy, graphic);
3067 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3068 DrawLevelFieldCrumbled(x, y);
3070 if (GFX_CRUMBLED(Feld[x][y]))
3071 DrawLevelFieldCrumbled(x, y);
3075 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3077 int sx = SCREENX(x), sy = SCREENY(y);
3080 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3083 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3085 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3088 DrawGraphicAnimation(sx, sy, graphic);
3090 if (GFX_CRUMBLED(element))
3091 DrawLevelFieldCrumbled(x, y);
3094 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3096 if (player->use_murphy)
3098 /* this works only because currently only one player can be "murphy" ... */
3099 static int last_horizontal_dir = MV_LEFT;
3100 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3102 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3103 last_horizontal_dir = move_dir;
3105 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3107 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3109 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3115 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3118 static boolean equalGraphics(int graphic1, int graphic2)
3120 struct GraphicInfo *g1 = &graphic_info[graphic1];
3121 struct GraphicInfo *g2 = &graphic_info[graphic2];
3123 return (g1->bitmap == g2->bitmap &&
3124 g1->src_x == g2->src_x &&
3125 g1->src_y == g2->src_y &&
3126 g1->anim_frames == g2->anim_frames &&
3127 g1->anim_delay == g2->anim_delay &&
3128 g1->anim_mode == g2->anim_mode);
3131 void DrawAllPlayers()
3135 for (i = 0; i < MAX_PLAYERS; i++)
3136 if (stored_player[i].active)
3137 DrawPlayer(&stored_player[i]);
3140 void DrawPlayerField(int x, int y)
3142 if (!IS_PLAYER(x, y))
3145 DrawPlayer(PLAYERINFO(x, y));
3148 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3150 void DrawPlayer(struct PlayerInfo *player)
3152 int jx = player->jx;
3153 int jy = player->jy;
3154 int move_dir = player->MovDir;
3155 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3156 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3157 int last_jx = (player->is_moving ? jx - dx : jx);
3158 int last_jy = (player->is_moving ? jy - dy : jy);
3159 int next_jx = jx + dx;
3160 int next_jy = jy + dy;
3161 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3162 boolean player_is_opaque = FALSE;
3163 int sx = SCREENX(jx), sy = SCREENY(jy);
3164 int sxx = 0, syy = 0;
3165 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3167 int action = ACTION_DEFAULT;
3168 int last_player_graphic = getPlayerGraphic(player, move_dir);
3169 int last_player_frame = player->Frame;
3172 /* GfxElement[][] is set to the element the player is digging or collecting;
3173 remove also for off-screen player if the player is not moving anymore */
3174 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3175 GfxElement[jx][jy] = EL_UNDEFINED;
3177 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3181 if (!IN_LEV_FIELD(jx, jy))
3183 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3184 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3185 printf("DrawPlayerField(): This should never happen!\n");
3190 if (element == EL_EXPLOSION)
3193 action = (player->is_pushing ? ACTION_PUSHING :
3194 player->is_digging ? ACTION_DIGGING :
3195 player->is_collecting ? ACTION_COLLECTING :
3196 player->is_moving ? ACTION_MOVING :
3197 player->is_snapping ? ACTION_SNAPPING :
3198 player->is_dropping ? ACTION_DROPPING :
3199 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3201 if (player->is_waiting)
3202 move_dir = player->dir_waiting;
3204 InitPlayerGfxAnimation(player, action, move_dir);
3206 /* ----------------------------------------------------------------------- */
3207 /* draw things in the field the player is leaving, if needed */
3208 /* ----------------------------------------------------------------------- */
3210 if (player->is_moving)
3212 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3214 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3216 if (last_element == EL_DYNAMITE_ACTIVE ||
3217 last_element == EL_EM_DYNAMITE_ACTIVE ||
3218 last_element == EL_SP_DISK_RED_ACTIVE)
3219 DrawDynamite(last_jx, last_jy);
3221 DrawLevelFieldThruMask(last_jx, last_jy);
3223 else if (last_element == EL_DYNAMITE_ACTIVE ||
3224 last_element == EL_EM_DYNAMITE_ACTIVE ||
3225 last_element == EL_SP_DISK_RED_ACTIVE)
3226 DrawDynamite(last_jx, last_jy);
3228 /* !!! this is not enough to prevent flickering of players which are
3229 moving next to each others without a free tile between them -- this
3230 can only be solved by drawing all players layer by layer (first the
3231 background, then the foreground etc.) !!! => TODO */
3232 else if (!IS_PLAYER(last_jx, last_jy))
3233 DrawLevelField(last_jx, last_jy);
3236 DrawLevelField(last_jx, last_jy);
3239 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3240 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3243 if (!IN_SCR_FIELD(sx, sy))
3246 /* ----------------------------------------------------------------------- */
3247 /* draw things behind the player, if needed */
3248 /* ----------------------------------------------------------------------- */
3251 DrawLevelElement(jx, jy, Back[jx][jy]);
3252 else if (IS_ACTIVE_BOMB(element))
3253 DrawLevelElement(jx, jy, EL_EMPTY);
3256 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3258 int old_element = GfxElement[jx][jy];
3259 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3260 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3262 if (GFX_CRUMBLED(old_element))
3263 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3265 DrawGraphic(sx, sy, old_graphic, frame);
3267 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3268 player_is_opaque = TRUE;
3272 GfxElement[jx][jy] = EL_UNDEFINED;
3274 /* make sure that pushed elements are drawn with correct frame rate */
3275 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3277 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3278 GfxFrame[jx][jy] = player->StepFrame;
3280 DrawLevelField(jx, jy);
3284 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3285 /* ----------------------------------------------------------------------- */
3286 /* draw player himself */
3287 /* ----------------------------------------------------------------------- */
3289 graphic = getPlayerGraphic(player, move_dir);
3291 /* in the case of changed player action or direction, prevent the current
3292 animation frame from being restarted for identical animations */
3293 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3294 player->Frame = last_player_frame;
3296 frame = getGraphicAnimationFrame(graphic, player->Frame);
3300 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3301 sxx = player->GfxPos;
3303 syy = player->GfxPos;
3306 if (player_is_opaque)
3307 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3309 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3311 if (SHIELD_ON(player))
3313 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3314 IMG_SHIELD_NORMAL_ACTIVE);
3315 int frame = getGraphicAnimationFrame(graphic, -1);
3317 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3321 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3324 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3325 sxx = player->GfxPos;
3327 syy = player->GfxPos;
3331 /* ----------------------------------------------------------------------- */
3332 /* draw things the player is pushing, if needed */
3333 /* ----------------------------------------------------------------------- */
3335 if (player->is_pushing && player->is_moving)
3337 int px = SCREENX(jx), py = SCREENY(jy);
3338 int pxx = (TILEX - ABS(sxx)) * dx;
3339 int pyy = (TILEY - ABS(syy)) * dy;
3340 int gfx_frame = GfxFrame[jx][jy];
3346 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3348 element = Feld[next_jx][next_jy];
3349 gfx_frame = GfxFrame[next_jx][next_jy];
3352 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3354 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3355 frame = getGraphicAnimationFrame(graphic, sync_frame);
3357 /* draw background element under pushed element (like the Sokoban field) */
3358 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3360 /* this allows transparent pushing animation over non-black background */
3363 DrawLevelElement(jx, jy, Back[jx][jy]);
3365 DrawLevelElement(jx, jy, EL_EMPTY);
3367 if (Back[next_jx][next_jy])
3368 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3370 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3372 else if (Back[next_jx][next_jy])
3373 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3376 /* do not draw (EM style) pushing animation when pushing is finished */
3377 /* (two-tile animations usually do not contain start and end frame) */
3378 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3379 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3381 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3383 /* masked drawing is needed for EMC style (double) movement graphics */
3384 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3385 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3389 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3390 /* ----------------------------------------------------------------------- */
3391 /* draw player himself */
3392 /* ----------------------------------------------------------------------- */
3394 graphic = getPlayerGraphic(player, move_dir);
3396 /* in the case of changed player action or direction, prevent the current
3397 animation frame from being restarted for identical animations */
3398 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3399 player->Frame = last_player_frame;
3401 frame = getGraphicAnimationFrame(graphic, player->Frame);
3405 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3406 sxx = player->GfxPos;
3408 syy = player->GfxPos;
3411 if (player_is_opaque)
3412 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3414 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3416 if (SHIELD_ON(player))
3418 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3419 IMG_SHIELD_NORMAL_ACTIVE);
3420 int frame = getGraphicAnimationFrame(graphic, -1);
3422 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3426 /* ----------------------------------------------------------------------- */
3427 /* draw things in front of player (active dynamite or dynabombs) */
3428 /* ----------------------------------------------------------------------- */
3430 if (IS_ACTIVE_BOMB(element))
3432 graphic = el2img(element);
3433 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3435 if (game.emulation == EMU_SUPAPLEX)
3436 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3438 DrawGraphicThruMask(sx, sy, graphic, frame);
3441 if (player_is_moving && last_element == EL_EXPLOSION)
3443 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3444 GfxElement[last_jx][last_jy] : EL_EMPTY);
3445 int graphic = el_act2img(element, ACTION_EXPLODING);
3446 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3447 int phase = ExplodePhase[last_jx][last_jy] - 1;
3448 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3451 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3454 /* ----------------------------------------------------------------------- */
3455 /* draw elements the player is just walking/passing through/under */
3456 /* ----------------------------------------------------------------------- */
3458 if (player_is_moving)
3460 /* handle the field the player is leaving ... */
3461 if (IS_ACCESSIBLE_INSIDE(last_element))
3462 DrawLevelField(last_jx, last_jy);
3463 else if (IS_ACCESSIBLE_UNDER(last_element))
3464 DrawLevelFieldThruMask(last_jx, last_jy);
3467 /* do not redraw accessible elements if the player is just pushing them */
3468 if (!player_is_moving || !player->is_pushing)
3470 /* ... and the field the player is entering */
3471 if (IS_ACCESSIBLE_INSIDE(element))
3472 DrawLevelField(jx, jy);
3473 else if (IS_ACCESSIBLE_UNDER(element))
3474 DrawLevelFieldThruMask(jx, jy);
3477 MarkTileDirty(sx, sy);
3480 /* ------------------------------------------------------------------------- */
3482 void WaitForEventToContinue()
3484 boolean still_wait = TRUE;
3486 /* simulate releasing mouse button over last gadget, if still pressed */
3488 HandleGadgets(-1, -1, 0);
3490 button_status = MB_RELEASED;
3504 case EVENT_BUTTONPRESS:
3505 case EVENT_KEYPRESS:
3509 case EVENT_KEYRELEASE:
3510 ClearPlayerAction();
3514 HandleOtherEvents(&event);
3518 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3525 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3529 #define MAX_REQUEST_LINES 13
3530 #define MAX_REQUEST_LINE_FONT1_LEN 7
3531 #define MAX_REQUEST_LINE_FONT2_LEN 10
3533 static int RequestHandleEvents(unsigned int req_state)
3535 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3536 local_player->LevelSolved_GameEnd);
3537 int width = request.width;
3538 int height = request.height;
3542 setRequestPosition(&sx, &sy, FALSE);
3544 button_status = MB_RELEASED;
3546 request_gadget_id = -1;
3553 SetDrawtoField(DRAW_FIELDBUFFER);
3555 HandleGameActions();
3557 SetDrawtoField(DRAW_BACKBUFFER);
3559 if (global.use_envelope_request)
3561 /* copy current state of request area to middle of playfield area */
3562 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3570 while (NextValidEvent(&event))
3574 case EVENT_BUTTONPRESS:
3575 case EVENT_BUTTONRELEASE:
3576 case EVENT_MOTIONNOTIFY:
3580 if (event.type == EVENT_MOTIONNOTIFY)
3585 motion_status = TRUE;
3586 mx = ((MotionEvent *) &event)->x;
3587 my = ((MotionEvent *) &event)->y;
3591 motion_status = FALSE;
3592 mx = ((ButtonEvent *) &event)->x;
3593 my = ((ButtonEvent *) &event)->y;
3594 if (event.type == EVENT_BUTTONPRESS)
3595 button_status = ((ButtonEvent *) &event)->button;
3597 button_status = MB_RELEASED;
3600 /* this sets 'request_gadget_id' */
3601 HandleGadgets(mx, my, button_status);
3603 switch (request_gadget_id)
3605 case TOOL_CTRL_ID_YES:
3608 case TOOL_CTRL_ID_NO:
3611 case TOOL_CTRL_ID_CONFIRM:
3612 result = TRUE | FALSE;
3615 case TOOL_CTRL_ID_PLAYER_1:
3618 case TOOL_CTRL_ID_PLAYER_2:
3621 case TOOL_CTRL_ID_PLAYER_3:
3624 case TOOL_CTRL_ID_PLAYER_4:
3635 case EVENT_KEYPRESS:
3636 switch (GetEventKey((KeyEvent *)&event, TRUE))
3639 if (req_state & REQ_CONFIRM)
3644 #if defined(TARGET_SDL2)
3651 #if defined(TARGET_SDL2)
3661 if (req_state & REQ_PLAYER)
3665 case EVENT_KEYRELEASE:
3666 ClearPlayerAction();
3670 HandleOtherEvents(&event);
3675 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3677 int joy = AnyJoystick();
3679 if (joy & JOY_BUTTON_1)
3681 else if (joy & JOY_BUTTON_2)
3687 if (global.use_envelope_request)
3689 /* copy back current state of pressed buttons inside request area */
3690 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3700 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3706 static boolean RequestDoor(char *text, unsigned int req_state)
3708 unsigned int old_door_state;
3709 int last_game_status = game_status; /* save current game status */
3710 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3711 int font_nr = FONT_TEXT_2;
3716 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3718 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3719 font_nr = FONT_TEXT_1;
3722 if (game_status == GAME_MODE_PLAYING)
3723 BlitScreenToBitmap(backbuffer);
3725 /* disable deactivated drawing when quick-loading level tape recording */
3726 if (tape.playing && tape.deactivate_display)
3727 TapeDeactivateDisplayOff(TRUE);
3729 SetMouseCursor(CURSOR_DEFAULT);
3731 #if defined(NETWORK_AVALIABLE)
3732 /* pause network game while waiting for request to answer */
3733 if (options.network &&
3734 game_status == GAME_MODE_PLAYING &&
3735 req_state & REQUEST_WAIT_FOR_INPUT)
3736 SendToServer_PausePlaying();
3739 old_door_state = GetDoorState();
3741 /* simulate releasing mouse button over last gadget, if still pressed */
3743 HandleGadgets(-1, -1, 0);
3747 /* draw released gadget before proceeding */
3750 if (old_door_state & DOOR_OPEN_1)
3752 CloseDoor(DOOR_CLOSE_1);
3754 /* save old door content */
3755 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3756 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3759 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3760 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3762 /* clear door drawing field */
3763 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3765 /* force DOOR font inside door area */
3766 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
3768 /* write text for request */
3769 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3771 char text_line[max_request_line_len + 1];
3777 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3779 tc = *(text_ptr + tx);
3780 // if (!tc || tc == ' ')
3781 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3785 if ((tc == '?' || tc == '!') && tl == 0)
3795 strncpy(text_line, text_ptr, tl);
3798 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3799 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3800 text_line, font_nr);
3802 text_ptr += tl + (tc == ' ' ? 1 : 0);
3803 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3806 SetGameStatus(last_game_status); /* restore current game status */
3808 if (req_state & REQ_ASK)
3810 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3811 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3813 else if (req_state & REQ_CONFIRM)
3815 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3817 else if (req_state & REQ_PLAYER)
3819 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3820 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3821 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3822 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3825 /* copy request gadgets to door backbuffer */
3826 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3828 OpenDoor(DOOR_OPEN_1);
3830 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3832 if (game_status == GAME_MODE_PLAYING)
3834 SetPanelBackground();
3835 SetDrawBackgroundMask(REDRAW_DOOR_1);
3839 SetDrawBackgroundMask(REDRAW_FIELD);
3845 if (game_status != GAME_MODE_MAIN)
3848 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3850 // ---------- handle request buttons ----------
3851 result = RequestHandleEvents(req_state);
3853 if (game_status != GAME_MODE_MAIN)
3858 if (!(req_state & REQ_STAY_OPEN))
3860 CloseDoor(DOOR_CLOSE_1);
3862 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3863 (req_state & REQ_REOPEN))
3864 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3869 if (game_status == GAME_MODE_PLAYING)
3871 SetPanelBackground();
3872 SetDrawBackgroundMask(REDRAW_DOOR_1);
3876 SetDrawBackgroundMask(REDRAW_FIELD);
3879 #if defined(NETWORK_AVALIABLE)
3880 /* continue network game after request */
3881 if (options.network &&
3882 game_status == GAME_MODE_PLAYING &&
3883 req_state & REQUEST_WAIT_FOR_INPUT)
3884 SendToServer_ContinuePlaying();
3887 /* restore deactivated drawing when quick-loading level tape recording */
3888 if (tape.playing && tape.deactivate_display)
3889 TapeDeactivateDisplayOn();
3894 static boolean RequestEnvelope(char *text, unsigned int req_state)
3898 if (game_status == GAME_MODE_PLAYING)
3899 BlitScreenToBitmap(backbuffer);
3901 /* disable deactivated drawing when quick-loading level tape recording */
3902 if (tape.playing && tape.deactivate_display)
3903 TapeDeactivateDisplayOff(TRUE);
3905 SetMouseCursor(CURSOR_DEFAULT);
3907 #if defined(NETWORK_AVALIABLE)
3908 /* pause network game while waiting for request to answer */
3909 if (options.network &&
3910 game_status == GAME_MODE_PLAYING &&
3911 req_state & REQUEST_WAIT_FOR_INPUT)
3912 SendToServer_PausePlaying();
3915 /* simulate releasing mouse button over last gadget, if still pressed */
3917 HandleGadgets(-1, -1, 0);
3921 // (replace with setting corresponding request background)
3922 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3923 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3925 /* clear door drawing field */
3926 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3928 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3930 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3932 if (game_status == GAME_MODE_PLAYING)
3934 SetPanelBackground();
3935 SetDrawBackgroundMask(REDRAW_DOOR_1);
3939 SetDrawBackgroundMask(REDRAW_FIELD);
3945 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3947 // ---------- handle request buttons ----------
3948 result = RequestHandleEvents(req_state);
3950 if (game_status != GAME_MODE_MAIN)
3955 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3959 if (game_status == GAME_MODE_PLAYING)
3961 SetPanelBackground();
3962 SetDrawBackgroundMask(REDRAW_DOOR_1);
3966 SetDrawBackgroundMask(REDRAW_FIELD);
3969 #if defined(NETWORK_AVALIABLE)
3970 /* continue network game after request */
3971 if (options.network &&
3972 game_status == GAME_MODE_PLAYING &&
3973 req_state & REQUEST_WAIT_FOR_INPUT)
3974 SendToServer_ContinuePlaying();
3977 /* restore deactivated drawing when quick-loading level tape recording */
3978 if (tape.playing && tape.deactivate_display)
3979 TapeDeactivateDisplayOn();
3984 boolean Request(char *text, unsigned int req_state)
3986 if (global.use_envelope_request)
3987 return RequestEnvelope(text, req_state);
3989 return RequestDoor(text, req_state);
3992 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3994 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3995 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3998 if (dpo1->sort_priority != dpo2->sort_priority)
3999 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4001 compare_result = dpo1->nr - dpo2->nr;
4003 return compare_result;
4006 void InitGraphicCompatibilityInfo_Doors()
4012 struct DoorInfo *door;
4016 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
4017 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
4019 { -1, -1, -1, NULL }
4021 struct Rect door_rect_list[] =
4023 { DX, DY, DXSIZE, DYSIZE },
4024 { VX, VY, VXSIZE, VYSIZE }
4028 for (i = 0; doors[i].door_token != -1; i++)
4030 int door_token = doors[i].door_token;
4031 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4032 int part_1 = doors[i].part_1;
4033 int part_8 = doors[i].part_8;
4034 int part_2 = part_1 + 1;
4035 int part_3 = part_1 + 2;
4036 struct DoorInfo *door = doors[i].door;
4037 struct Rect *door_rect = &door_rect_list[door_index];
4038 boolean door_gfx_redefined = FALSE;
4040 /* check if any door part graphic definitions have been redefined */
4042 for (j = 0; door_part_controls[j].door_token != -1; j++)
4044 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4045 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4047 if (dpc->door_token == door_token && fi->redefined)
4048 door_gfx_redefined = TRUE;
4051 /* check for old-style door graphic/animation modifications */
4053 if (!door_gfx_redefined)
4055 if (door->anim_mode & ANIM_STATIC_PANEL)
4057 door->panel.step_xoffset = 0;
4058 door->panel.step_yoffset = 0;
4061 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4063 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4064 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4065 int num_door_steps, num_panel_steps;
4067 /* remove door part graphics other than the two default wings */
4069 for (j = 0; door_part_controls[j].door_token != -1; j++)
4071 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4072 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4074 if (dpc->graphic >= part_3 &&
4075 dpc->graphic <= part_8)
4079 /* set graphics and screen positions of the default wings */
4081 g_part_1->width = door_rect->width;
4082 g_part_1->height = door_rect->height;
4083 g_part_2->width = door_rect->width;
4084 g_part_2->height = door_rect->height;
4085 g_part_2->src_x = door_rect->width;
4086 g_part_2->src_y = g_part_1->src_y;
4088 door->part_2.x = door->part_1.x;
4089 door->part_2.y = door->part_1.y;
4091 if (door->width != -1)
4093 g_part_1->width = door->width;
4094 g_part_2->width = door->width;
4096 // special treatment for graphics and screen position of right wing
4097 g_part_2->src_x += door_rect->width - door->width;
4098 door->part_2.x += door_rect->width - door->width;
4101 if (door->height != -1)
4103 g_part_1->height = door->height;
4104 g_part_2->height = door->height;
4106 // special treatment for graphics and screen position of bottom wing
4107 g_part_2->src_y += door_rect->height - door->height;
4108 door->part_2.y += door_rect->height - door->height;
4111 /* set animation delays for the default wings and panels */
4113 door->part_1.step_delay = door->step_delay;
4114 door->part_2.step_delay = door->step_delay;
4115 door->panel.step_delay = door->step_delay;
4117 /* set animation draw order for the default wings */
4119 door->part_1.sort_priority = 2; /* draw left wing over ... */
4120 door->part_2.sort_priority = 1; /* ... right wing */
4122 /* set animation draw offset for the default wings */
4124 if (door->anim_mode & ANIM_HORIZONTAL)
4126 door->part_1.step_xoffset = door->step_offset;
4127 door->part_1.step_yoffset = 0;
4128 door->part_2.step_xoffset = door->step_offset * -1;
4129 door->part_2.step_yoffset = 0;
4131 num_door_steps = g_part_1->width / door->step_offset;
4133 else // ANIM_VERTICAL
4135 door->part_1.step_xoffset = 0;
4136 door->part_1.step_yoffset = door->step_offset;
4137 door->part_2.step_xoffset = 0;
4138 door->part_2.step_yoffset = door->step_offset * -1;
4140 num_door_steps = g_part_1->height / door->step_offset;
4143 /* set animation draw offset for the default panels */
4145 if (door->step_offset > 1)
4147 num_panel_steps = 2 * door_rect->height / door->step_offset;
4148 door->panel.start_step = num_panel_steps - num_door_steps;
4149 door->panel.start_step_closing = door->panel.start_step;
4153 num_panel_steps = door_rect->height / door->step_offset;
4154 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4155 door->panel.start_step_closing = door->panel.start_step;
4156 door->panel.step_delay *= 2;
4167 for (i = 0; door_part_controls[i].door_token != -1; i++)
4169 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4170 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4172 /* initialize "start_step_opening" and "start_step_closing", if needed */
4173 if (dpc->pos->start_step_opening == 0 &&
4174 dpc->pos->start_step_closing == 0)
4176 // dpc->pos->start_step_opening = dpc->pos->start_step;
4177 dpc->pos->start_step_closing = dpc->pos->start_step;
4180 /* fill structure for door part draw order (sorted below) */
4182 dpo->sort_priority = dpc->pos->sort_priority;
4185 /* sort door part controls according to sort_priority and graphic number */
4186 qsort(door_part_order, MAX_DOOR_PARTS,
4187 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4190 unsigned int OpenDoor(unsigned int door_state)
4192 if (door_state & DOOR_COPY_BACK)
4194 if (door_state & DOOR_OPEN_1)
4195 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4196 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4198 if (door_state & DOOR_OPEN_2)
4199 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4200 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4202 door_state &= ~DOOR_COPY_BACK;
4205 return MoveDoor(door_state);
4208 unsigned int CloseDoor(unsigned int door_state)
4210 unsigned int old_door_state = GetDoorState();
4212 if (!(door_state & DOOR_NO_COPY_BACK))
4214 if (old_door_state & DOOR_OPEN_1)
4215 BlitBitmap(backbuffer, bitmap_db_door_1,
4216 DX, DY, DXSIZE, DYSIZE, 0, 0);
4218 if (old_door_state & DOOR_OPEN_2)
4219 BlitBitmap(backbuffer, bitmap_db_door_2,
4220 VX, VY, VXSIZE, VYSIZE, 0, 0);
4222 door_state &= ~DOOR_NO_COPY_BACK;
4225 return MoveDoor(door_state);
4228 unsigned int GetDoorState()
4230 return MoveDoor(DOOR_GET_STATE);
4233 unsigned int SetDoorState(unsigned int door_state)
4235 return MoveDoor(door_state | DOOR_SET_STATE);
4238 int euclid(int a, int b)
4240 return (b ? euclid(b, a % b) : a);
4243 unsigned int MoveDoor(unsigned int door_state)
4245 struct Rect door_rect_list[] =
4247 { DX, DY, DXSIZE, DYSIZE },
4248 { VX, VY, VXSIZE, VYSIZE }
4250 static int door1 = DOOR_CLOSE_1;
4251 static int door2 = DOOR_CLOSE_2;
4252 unsigned int door_delay = 0;
4253 unsigned int door_delay_value;
4256 if (door_state == DOOR_GET_STATE)
4257 return (door1 | door2);
4259 if (door_state & DOOR_SET_STATE)
4261 if (door_state & DOOR_ACTION_1)
4262 door1 = door_state & DOOR_ACTION_1;
4263 if (door_state & DOOR_ACTION_2)
4264 door2 = door_state & DOOR_ACTION_2;
4266 return (door1 | door2);
4269 if (!(door_state & DOOR_FORCE_REDRAW))
4271 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4272 door_state &= ~DOOR_OPEN_1;
4273 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4274 door_state &= ~DOOR_CLOSE_1;
4275 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4276 door_state &= ~DOOR_OPEN_2;
4277 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4278 door_state &= ~DOOR_CLOSE_2;
4281 if (global.autoplay_leveldir)
4283 door_state |= DOOR_NO_DELAY;
4284 door_state &= ~DOOR_CLOSE_ALL;
4287 if (game_status == GAME_MODE_EDITOR)
4288 door_state |= DOOR_NO_DELAY;
4290 if (door_state & DOOR_ACTION)
4292 boolean door_panel_drawn[NUM_DOORS];
4293 boolean panel_has_doors[NUM_DOORS];
4294 boolean door_part_skip[MAX_DOOR_PARTS];
4295 boolean door_part_done[MAX_DOOR_PARTS];
4296 boolean door_part_done_all;
4297 int num_steps[MAX_DOOR_PARTS];
4298 int max_move_delay = 0; // delay for complete animations of all doors
4299 int max_step_delay = 0; // delay (ms) between two animation frames
4300 int num_move_steps = 0; // number of animation steps for all doors
4301 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4302 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4303 int current_move_delay = 0;
4307 for (i = 0; i < NUM_DOORS; i++)
4308 panel_has_doors[i] = FALSE;
4310 for (i = 0; i < MAX_DOOR_PARTS; i++)
4312 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4313 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4314 int door_token = dpc->door_token;
4316 door_part_done[i] = FALSE;
4317 door_part_skip[i] = (!(door_state & door_token) ||
4321 for (i = 0; i < MAX_DOOR_PARTS; i++)
4323 int nr = door_part_order[i].nr;
4324 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4325 struct DoorPartPosInfo *pos = dpc->pos;
4326 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4327 int door_token = dpc->door_token;
4328 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4329 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4330 int step_xoffset = ABS(pos->step_xoffset);
4331 int step_yoffset = ABS(pos->step_yoffset);
4332 int step_delay = pos->step_delay;
4333 int current_door_state = door_state & door_token;
4334 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4335 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4336 boolean part_opening = (is_panel ? door_closing : door_opening);
4337 int start_step = (part_opening ? pos->start_step_opening :
4338 pos->start_step_closing);
4339 float move_xsize = (step_xoffset ? g->width : 0);
4340 float move_ysize = (step_yoffset ? g->height : 0);
4341 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4342 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4343 int move_steps = (move_xsteps && move_ysteps ?
4344 MIN(move_xsteps, move_ysteps) :
4345 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4346 int move_delay = move_steps * step_delay;
4348 if (door_part_skip[nr])
4351 max_move_delay = MAX(max_move_delay, move_delay);
4352 max_step_delay = (max_step_delay == 0 ? step_delay :
4353 euclid(max_step_delay, step_delay));
4354 num_steps[nr] = move_steps;
4358 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4360 panel_has_doors[door_index] = TRUE;
4364 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4366 num_move_steps = max_move_delay / max_step_delay;
4367 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4369 door_delay_value = max_step_delay;
4371 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4373 start = num_move_steps - 1;
4377 /* opening door sound has priority over simultaneously closing door */
4378 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4379 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4380 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4381 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4384 for (k = start; k < num_move_steps; k++)
4386 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4388 door_part_done_all = TRUE;
4390 for (i = 0; i < NUM_DOORS; i++)
4391 door_panel_drawn[i] = FALSE;
4393 for (i = 0; i < MAX_DOOR_PARTS; i++)
4395 int nr = door_part_order[i].nr;
4396 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4397 struct DoorPartPosInfo *pos = dpc->pos;
4398 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4399 int door_token = dpc->door_token;
4400 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4401 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4402 boolean is_panel_and_door_has_closed = FALSE;
4403 struct Rect *door_rect = &door_rect_list[door_index];
4404 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4406 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4407 int current_door_state = door_state & door_token;
4408 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4409 boolean door_closing = !door_opening;
4410 boolean part_opening = (is_panel ? door_closing : door_opening);
4411 boolean part_closing = !part_opening;
4412 int start_step = (part_opening ? pos->start_step_opening :
4413 pos->start_step_closing);
4414 int step_delay = pos->step_delay;
4415 int step_factor = step_delay / max_step_delay;
4416 int k1 = (step_factor ? k / step_factor + 1 : k);
4417 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4418 int kk = MAX(0, k2);
4421 int src_x, src_y, src_xx, src_yy;
4422 int dst_x, dst_y, dst_xx, dst_yy;
4425 if (door_part_skip[nr])
4428 if (!(door_state & door_token))
4436 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4437 int kk_door = MAX(0, k2_door);
4438 int sync_frame = kk_door * door_delay_value;
4439 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4441 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4446 if (!door_panel_drawn[door_index])
4448 ClearRectangle(drawto, door_rect->x, door_rect->y,
4449 door_rect->width, door_rect->height);
4451 door_panel_drawn[door_index] = TRUE;
4454 // draw opening or closing door parts
4456 if (pos->step_xoffset < 0) // door part on right side
4459 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4462 if (dst_xx + width > door_rect->width)
4463 width = door_rect->width - dst_xx;
4465 else // door part on left side
4468 dst_xx = pos->x - kk * pos->step_xoffset;
4472 src_xx = ABS(dst_xx);
4476 width = g->width - src_xx;
4478 if (width > door_rect->width)
4479 width = door_rect->width;
4481 // printf("::: k == %d [%d] \n", k, start_step);
4484 if (pos->step_yoffset < 0) // door part on bottom side
4487 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4490 if (dst_yy + height > door_rect->height)
4491 height = door_rect->height - dst_yy;
4493 else // door part on top side
4496 dst_yy = pos->y - kk * pos->step_yoffset;
4500 src_yy = ABS(dst_yy);
4504 height = g->height - src_yy;
4507 src_x = g_src_x + src_xx;
4508 src_y = g_src_y + src_yy;
4510 dst_x = door_rect->x + dst_xx;
4511 dst_y = door_rect->y + dst_yy;
4513 is_panel_and_door_has_closed =
4516 panel_has_doors[door_index] &&
4517 k >= num_move_steps_doors_only - 1);
4519 if (width >= 0 && width <= g->width &&
4520 height >= 0 && height <= g->height &&
4521 !is_panel_and_door_has_closed)
4523 if (is_panel || !pos->draw_masked)
4524 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4527 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4531 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4533 if ((part_opening && (width < 0 || height < 0)) ||
4534 (part_closing && (width >= g->width && height >= g->height)))
4535 door_part_done[nr] = TRUE;
4537 // continue door part animations, but not panel after door has closed
4538 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4539 door_part_done_all = FALSE;
4542 if (!(door_state & DOOR_NO_DELAY))
4546 if (game_status == GAME_MODE_MAIN)
4549 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4551 current_move_delay += max_step_delay;
4554 if (door_part_done_all)
4559 if (door_state & DOOR_ACTION_1)
4560 door1 = door_state & DOOR_ACTION_1;
4561 if (door_state & DOOR_ACTION_2)
4562 door2 = door_state & DOOR_ACTION_2;
4564 // draw masked border over door area
4565 DrawMaskedBorder(REDRAW_DOOR_1);
4566 DrawMaskedBorder(REDRAW_DOOR_2);
4568 return (door1 | door2);
4571 static boolean useSpecialEditorDoor()
4573 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4574 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4576 // do not draw special editor door if editor border defined or redefined
4577 if (graphic_info[graphic].bitmap != NULL || redefined)
4580 // do not draw special editor door if global border defined to be empty
4581 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4584 // do not draw special editor door if viewport definitions do not match
4588 EY + EYSIZE != VY + VYSIZE)
4594 void DrawSpecialEditorDoor()
4596 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4597 int top_border_width = gfx1->width;
4598 int top_border_height = gfx1->height;
4599 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4600 int ex = EX - outer_border;
4601 int ey = EY - outer_border;
4602 int vy = VY - outer_border;
4603 int exsize = EXSIZE + 2 * outer_border;
4605 if (!useSpecialEditorDoor())
4608 /* draw bigger level editor toolbox window */
4609 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4610 top_border_width, top_border_height, ex, ey - top_border_height);
4611 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4612 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4614 redraw_mask |= REDRAW_ALL;
4617 void UndrawSpecialEditorDoor()
4619 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4620 int top_border_width = gfx1->width;
4621 int top_border_height = gfx1->height;
4622 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4623 int ex = EX - outer_border;
4624 int ey = EY - outer_border;
4625 int ey_top = ey - top_border_height;
4626 int exsize = EXSIZE + 2 * outer_border;
4627 int eysize = EYSIZE + 2 * outer_border;
4629 if (!useSpecialEditorDoor())
4632 /* draw normal tape recorder window */
4633 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4635 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4636 ex, ey_top, top_border_width, top_border_height,
4638 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4639 ex, ey, exsize, eysize, ex, ey);
4643 // if screen background is set to "[NONE]", clear editor toolbox window
4644 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4645 ClearRectangle(drawto, ex, ey, exsize, eysize);
4648 redraw_mask |= REDRAW_ALL;
4652 /* ---------- new tool button stuff ---------------------------------------- */
4657 struct TextPosInfo *pos;
4660 } toolbutton_info[NUM_TOOL_BUTTONS] =
4663 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4664 TOOL_CTRL_ID_YES, "yes"
4667 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4668 TOOL_CTRL_ID_NO, "no"
4671 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4672 TOOL_CTRL_ID_CONFIRM, "confirm"
4675 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4676 TOOL_CTRL_ID_PLAYER_1, "player 1"
4679 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4680 TOOL_CTRL_ID_PLAYER_2, "player 2"
4683 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4684 TOOL_CTRL_ID_PLAYER_3, "player 3"
4687 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4688 TOOL_CTRL_ID_PLAYER_4, "player 4"
4692 void CreateToolButtons()
4696 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4698 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4699 struct TextPosInfo *pos = toolbutton_info[i].pos;
4700 struct GadgetInfo *gi;
4701 Bitmap *deco_bitmap = None;
4702 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4703 unsigned int event_mask = GD_EVENT_RELEASED;
4706 int gd_x = gfx->src_x;
4707 int gd_y = gfx->src_y;
4708 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4709 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4712 if (global.use_envelope_request)
4713 setRequestPosition(&dx, &dy, TRUE);
4715 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4717 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4719 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4720 pos->size, &deco_bitmap, &deco_x, &deco_y);
4721 deco_xpos = (gfx->width - pos->size) / 2;
4722 deco_ypos = (gfx->height - pos->size) / 2;
4725 gi = CreateGadget(GDI_CUSTOM_ID, id,
4726 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4727 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4728 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4729 GDI_WIDTH, gfx->width,
4730 GDI_HEIGHT, gfx->height,
4731 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4732 GDI_STATE, GD_BUTTON_UNPRESSED,
4733 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4734 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4735 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4736 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4737 GDI_DECORATION_SIZE, pos->size, pos->size,
4738 GDI_DECORATION_SHIFTING, 1, 1,
4739 GDI_DIRECT_DRAW, FALSE,
4740 GDI_EVENT_MASK, event_mask,
4741 GDI_CALLBACK_ACTION, HandleToolButtons,
4745 Error(ERR_EXIT, "cannot create gadget");
4747 tool_gadget[id] = gi;
4751 void FreeToolButtons()
4755 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4756 FreeGadget(tool_gadget[i]);
4759 static void UnmapToolButtons()
4763 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4764 UnmapGadget(tool_gadget[i]);
4767 static void HandleToolButtons(struct GadgetInfo *gi)
4769 request_gadget_id = gi->custom_id;
4772 static struct Mapping_EM_to_RND_object
4775 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4776 boolean is_backside; /* backside of moving element */
4782 em_object_mapping_list[] =
4785 Xblank, TRUE, FALSE,
4789 Yacid_splash_eB, FALSE, FALSE,
4790 EL_ACID_SPLASH_RIGHT, -1, -1
4793 Yacid_splash_wB, FALSE, FALSE,
4794 EL_ACID_SPLASH_LEFT, -1, -1
4797 #ifdef EM_ENGINE_BAD_ROLL
4799 Xstone_force_e, FALSE, FALSE,
4800 EL_ROCK, -1, MV_BIT_RIGHT
4803 Xstone_force_w, FALSE, FALSE,
4804 EL_ROCK, -1, MV_BIT_LEFT
4807 Xnut_force_e, FALSE, FALSE,
4808 EL_NUT, -1, MV_BIT_RIGHT
4811 Xnut_force_w, FALSE, FALSE,
4812 EL_NUT, -1, MV_BIT_LEFT
4815 Xspring_force_e, FALSE, FALSE,
4816 EL_SPRING, -1, MV_BIT_RIGHT
4819 Xspring_force_w, FALSE, FALSE,
4820 EL_SPRING, -1, MV_BIT_LEFT
4823 Xemerald_force_e, FALSE, FALSE,
4824 EL_EMERALD, -1, MV_BIT_RIGHT
4827 Xemerald_force_w, FALSE, FALSE,
4828 EL_EMERALD, -1, MV_BIT_LEFT
4831 Xdiamond_force_e, FALSE, FALSE,
4832 EL_DIAMOND, -1, MV_BIT_RIGHT
4835 Xdiamond_force_w, FALSE, FALSE,
4836 EL_DIAMOND, -1, MV_BIT_LEFT
4839 Xbomb_force_e, FALSE, FALSE,
4840 EL_BOMB, -1, MV_BIT_RIGHT
4843 Xbomb_force_w, FALSE, FALSE,
4844 EL_BOMB, -1, MV_BIT_LEFT
4846 #endif /* EM_ENGINE_BAD_ROLL */
4849 Xstone, TRUE, FALSE,
4853 Xstone_pause, FALSE, FALSE,
4857 Xstone_fall, FALSE, FALSE,
4861 Ystone_s, FALSE, FALSE,
4862 EL_ROCK, ACTION_FALLING, -1
4865 Ystone_sB, FALSE, TRUE,
4866 EL_ROCK, ACTION_FALLING, -1
4869 Ystone_e, FALSE, FALSE,
4870 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4873 Ystone_eB, FALSE, TRUE,
4874 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4877 Ystone_w, FALSE, FALSE,
4878 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4881 Ystone_wB, FALSE, TRUE,
4882 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4889 Xnut_pause, FALSE, FALSE,
4893 Xnut_fall, FALSE, FALSE,
4897 Ynut_s, FALSE, FALSE,
4898 EL_NUT, ACTION_FALLING, -1
4901 Ynut_sB, FALSE, TRUE,
4902 EL_NUT, ACTION_FALLING, -1
4905 Ynut_e, FALSE, FALSE,
4906 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4909 Ynut_eB, FALSE, TRUE,
4910 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4913 Ynut_w, FALSE, FALSE,
4914 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4917 Ynut_wB, FALSE, TRUE,
4918 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4921 Xbug_n, TRUE, FALSE,
4925 Xbug_e, TRUE, FALSE,
4926 EL_BUG_RIGHT, -1, -1
4929 Xbug_s, TRUE, FALSE,
4933 Xbug_w, TRUE, FALSE,
4937 Xbug_gon, FALSE, FALSE,
4941 Xbug_goe, FALSE, FALSE,
4942 EL_BUG_RIGHT, -1, -1
4945 Xbug_gos, FALSE, FALSE,
4949 Xbug_gow, FALSE, FALSE,
4953 Ybug_n, FALSE, FALSE,
4954 EL_BUG, ACTION_MOVING, MV_BIT_UP
4957 Ybug_nB, FALSE, TRUE,
4958 EL_BUG, ACTION_MOVING, MV_BIT_UP
4961 Ybug_e, FALSE, FALSE,
4962 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4965 Ybug_eB, FALSE, TRUE,
4966 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4969 Ybug_s, FALSE, FALSE,
4970 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4973 Ybug_sB, FALSE, TRUE,
4974 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4977 Ybug_w, FALSE, FALSE,
4978 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4981 Ybug_wB, FALSE, TRUE,
4982 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4985 Ybug_w_n, FALSE, FALSE,
4986 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4989 Ybug_n_e, FALSE, FALSE,
4990 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4993 Ybug_e_s, FALSE, FALSE,
4994 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4997 Ybug_s_w, FALSE, FALSE,
4998 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5001 Ybug_e_n, FALSE, FALSE,
5002 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5005 Ybug_s_e, FALSE, FALSE,
5006 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5009 Ybug_w_s, FALSE, FALSE,
5010 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5013 Ybug_n_w, FALSE, FALSE,
5014 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5017 Ybug_stone, FALSE, FALSE,
5018 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5021 Ybug_spring, FALSE, FALSE,
5022 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5025 Xtank_n, TRUE, FALSE,
5026 EL_SPACESHIP_UP, -1, -1
5029 Xtank_e, TRUE, FALSE,
5030 EL_SPACESHIP_RIGHT, -1, -1
5033 Xtank_s, TRUE, FALSE,
5034 EL_SPACESHIP_DOWN, -1, -1
5037 Xtank_w, TRUE, FALSE,
5038 EL_SPACESHIP_LEFT, -1, -1
5041 Xtank_gon, FALSE, FALSE,
5042 EL_SPACESHIP_UP, -1, -1
5045 Xtank_goe, FALSE, FALSE,
5046 EL_SPACESHIP_RIGHT, -1, -1
5049 Xtank_gos, FALSE, FALSE,
5050 EL_SPACESHIP_DOWN, -1, -1
5053 Xtank_gow, FALSE, FALSE,
5054 EL_SPACESHIP_LEFT, -1, -1
5057 Ytank_n, FALSE, FALSE,
5058 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5061 Ytank_nB, FALSE, TRUE,
5062 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5065 Ytank_e, FALSE, FALSE,
5066 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5069 Ytank_eB, FALSE, TRUE,
5070 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5073 Ytank_s, FALSE, FALSE,
5074 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5077 Ytank_sB, FALSE, TRUE,
5078 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5081 Ytank_w, FALSE, FALSE,
5082 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5085 Ytank_wB, FALSE, TRUE,
5086 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5089 Ytank_w_n, FALSE, FALSE,
5090 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5093 Ytank_n_e, FALSE, FALSE,
5094 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5097 Ytank_e_s, FALSE, FALSE,
5098 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5101 Ytank_s_w, FALSE, FALSE,
5102 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5105 Ytank_e_n, FALSE, FALSE,
5106 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5109 Ytank_s_e, FALSE, FALSE,
5110 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5113 Ytank_w_s, FALSE, FALSE,
5114 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5117 Ytank_n_w, FALSE, FALSE,
5118 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5121 Ytank_stone, FALSE, FALSE,
5122 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5125 Ytank_spring, FALSE, FALSE,
5126 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5129 Xandroid, TRUE, FALSE,
5130 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5133 Xandroid_1_n, FALSE, FALSE,
5134 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5137 Xandroid_2_n, FALSE, FALSE,
5138 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5141 Xandroid_1_e, FALSE, FALSE,
5142 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5145 Xandroid_2_e, FALSE, FALSE,
5146 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5149 Xandroid_1_w, FALSE, FALSE,
5150 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5153 Xandroid_2_w, FALSE, FALSE,
5154 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5157 Xandroid_1_s, FALSE, FALSE,
5158 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5161 Xandroid_2_s, FALSE, FALSE,
5162 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5165 Yandroid_n, FALSE, FALSE,
5166 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5169 Yandroid_nB, FALSE, TRUE,
5170 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5173 Yandroid_ne, FALSE, FALSE,
5174 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5177 Yandroid_neB, FALSE, TRUE,
5178 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5181 Yandroid_e, FALSE, FALSE,
5182 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5185 Yandroid_eB, FALSE, TRUE,
5186 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5189 Yandroid_se, FALSE, FALSE,
5190 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5193 Yandroid_seB, FALSE, TRUE,
5194 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5197 Yandroid_s, FALSE, FALSE,
5198 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5201 Yandroid_sB, FALSE, TRUE,
5202 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5205 Yandroid_sw, FALSE, FALSE,
5206 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5209 Yandroid_swB, FALSE, TRUE,
5210 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5213 Yandroid_w, FALSE, FALSE,
5214 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5217 Yandroid_wB, FALSE, TRUE,
5218 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5221 Yandroid_nw, FALSE, FALSE,
5222 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5225 Yandroid_nwB, FALSE, TRUE,
5226 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5229 Xspring, TRUE, FALSE,
5233 Xspring_pause, FALSE, FALSE,
5237 Xspring_e, FALSE, FALSE,
5241 Xspring_w, FALSE, FALSE,
5245 Xspring_fall, FALSE, FALSE,
5249 Yspring_s, FALSE, FALSE,
5250 EL_SPRING, ACTION_FALLING, -1
5253 Yspring_sB, FALSE, TRUE,
5254 EL_SPRING, ACTION_FALLING, -1
5257 Yspring_e, FALSE, FALSE,
5258 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5261 Yspring_eB, FALSE, TRUE,
5262 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5265 Yspring_w, FALSE, FALSE,
5266 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5269 Yspring_wB, FALSE, TRUE,
5270 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5273 Yspring_kill_e, FALSE, FALSE,
5274 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5277 Yspring_kill_eB, FALSE, TRUE,
5278 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5281 Yspring_kill_w, FALSE, FALSE,
5282 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5285 Yspring_kill_wB, FALSE, TRUE,
5286 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5289 Xeater_n, TRUE, FALSE,
5290 EL_YAMYAM_UP, -1, -1
5293 Xeater_e, TRUE, FALSE,
5294 EL_YAMYAM_RIGHT, -1, -1
5297 Xeater_w, TRUE, FALSE,
5298 EL_YAMYAM_LEFT, -1, -1
5301 Xeater_s, TRUE, FALSE,
5302 EL_YAMYAM_DOWN, -1, -1
5305 Yeater_n, FALSE, FALSE,
5306 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5309 Yeater_nB, FALSE, TRUE,
5310 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5313 Yeater_e, FALSE, FALSE,
5314 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5317 Yeater_eB, FALSE, TRUE,
5318 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5321 Yeater_s, FALSE, FALSE,
5322 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5325 Yeater_sB, FALSE, TRUE,
5326 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5329 Yeater_w, FALSE, FALSE,
5330 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5333 Yeater_wB, FALSE, TRUE,
5334 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5337 Yeater_stone, FALSE, FALSE,
5338 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5341 Yeater_spring, FALSE, FALSE,
5342 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5345 Xalien, TRUE, FALSE,
5349 Xalien_pause, FALSE, FALSE,
5353 Yalien_n, FALSE, FALSE,
5354 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5357 Yalien_nB, FALSE, TRUE,
5358 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5361 Yalien_e, FALSE, FALSE,
5362 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5365 Yalien_eB, FALSE, TRUE,
5366 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5369 Yalien_s, FALSE, FALSE,
5370 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5373 Yalien_sB, FALSE, TRUE,
5374 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5377 Yalien_w, FALSE, FALSE,
5378 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5381 Yalien_wB, FALSE, TRUE,
5382 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5385 Yalien_stone, FALSE, FALSE,
5386 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5389 Yalien_spring, FALSE, FALSE,
5390 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5393 Xemerald, TRUE, FALSE,
5397 Xemerald_pause, FALSE, FALSE,
5401 Xemerald_fall, FALSE, FALSE,
5405 Xemerald_shine, FALSE, FALSE,
5406 EL_EMERALD, ACTION_TWINKLING, -1
5409 Yemerald_s, FALSE, FALSE,
5410 EL_EMERALD, ACTION_FALLING, -1
5413 Yemerald_sB, FALSE, TRUE,
5414 EL_EMERALD, ACTION_FALLING, -1
5417 Yemerald_e, FALSE, FALSE,
5418 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5421 Yemerald_eB, FALSE, TRUE,
5422 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5425 Yemerald_w, FALSE, FALSE,
5426 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5429 Yemerald_wB, FALSE, TRUE,
5430 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5433 Yemerald_eat, FALSE, FALSE,
5434 EL_EMERALD, ACTION_COLLECTING, -1
5437 Yemerald_stone, FALSE, FALSE,
5438 EL_NUT, ACTION_BREAKING, -1
5441 Xdiamond, TRUE, FALSE,
5445 Xdiamond_pause, FALSE, FALSE,
5449 Xdiamond_fall, FALSE, FALSE,
5453 Xdiamond_shine, FALSE, FALSE,
5454 EL_DIAMOND, ACTION_TWINKLING, -1
5457 Ydiamond_s, FALSE, FALSE,
5458 EL_DIAMOND, ACTION_FALLING, -1
5461 Ydiamond_sB, FALSE, TRUE,
5462 EL_DIAMOND, ACTION_FALLING, -1
5465 Ydiamond_e, FALSE, FALSE,
5466 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5469 Ydiamond_eB, FALSE, TRUE,
5470 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5473 Ydiamond_w, FALSE, FALSE,
5474 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5477 Ydiamond_wB, FALSE, TRUE,
5478 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5481 Ydiamond_eat, FALSE, FALSE,
5482 EL_DIAMOND, ACTION_COLLECTING, -1
5485 Ydiamond_stone, FALSE, FALSE,
5486 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5489 Xdrip_fall, TRUE, FALSE,
5490 EL_AMOEBA_DROP, -1, -1
5493 Xdrip_stretch, FALSE, FALSE,
5494 EL_AMOEBA_DROP, ACTION_FALLING, -1
5497 Xdrip_stretchB, FALSE, TRUE,
5498 EL_AMOEBA_DROP, ACTION_FALLING, -1
5501 Xdrip_eat, FALSE, FALSE,
5502 EL_AMOEBA_DROP, ACTION_GROWING, -1
5505 Ydrip_s1, FALSE, FALSE,
5506 EL_AMOEBA_DROP, ACTION_FALLING, -1
5509 Ydrip_s1B, FALSE, TRUE,
5510 EL_AMOEBA_DROP, ACTION_FALLING, -1
5513 Ydrip_s2, FALSE, FALSE,
5514 EL_AMOEBA_DROP, ACTION_FALLING, -1
5517 Ydrip_s2B, FALSE, TRUE,
5518 EL_AMOEBA_DROP, ACTION_FALLING, -1
5525 Xbomb_pause, FALSE, FALSE,
5529 Xbomb_fall, FALSE, FALSE,
5533 Ybomb_s, FALSE, FALSE,
5534 EL_BOMB, ACTION_FALLING, -1
5537 Ybomb_sB, FALSE, TRUE,
5538 EL_BOMB, ACTION_FALLING, -1
5541 Ybomb_e, FALSE, FALSE,
5542 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5545 Ybomb_eB, FALSE, TRUE,
5546 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5549 Ybomb_w, FALSE, FALSE,
5550 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5553 Ybomb_wB, FALSE, TRUE,
5554 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5557 Ybomb_eat, FALSE, FALSE,
5558 EL_BOMB, ACTION_ACTIVATING, -1
5561 Xballoon, TRUE, FALSE,
5565 Yballoon_n, FALSE, FALSE,
5566 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5569 Yballoon_nB, FALSE, TRUE,
5570 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5573 Yballoon_e, FALSE, FALSE,
5574 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5577 Yballoon_eB, FALSE, TRUE,
5578 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5581 Yballoon_s, FALSE, FALSE,
5582 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5585 Yballoon_sB, FALSE, TRUE,
5586 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5589 Yballoon_w, FALSE, FALSE,
5590 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5593 Yballoon_wB, FALSE, TRUE,
5594 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5597 Xgrass, TRUE, FALSE,
5598 EL_EMC_GRASS, -1, -1
5601 Ygrass_nB, FALSE, FALSE,
5602 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5605 Ygrass_eB, FALSE, FALSE,
5606 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5609 Ygrass_sB, FALSE, FALSE,
5610 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5613 Ygrass_wB, FALSE, FALSE,
5614 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5621 Ydirt_nB, FALSE, FALSE,
5622 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5625 Ydirt_eB, FALSE, FALSE,
5626 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5629 Ydirt_sB, FALSE, FALSE,
5630 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5633 Ydirt_wB, FALSE, FALSE,
5634 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5637 Xacid_ne, TRUE, FALSE,
5638 EL_ACID_POOL_TOPRIGHT, -1, -1
5641 Xacid_se, TRUE, FALSE,
5642 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5645 Xacid_s, TRUE, FALSE,
5646 EL_ACID_POOL_BOTTOM, -1, -1
5649 Xacid_sw, TRUE, FALSE,
5650 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5653 Xacid_nw, TRUE, FALSE,
5654 EL_ACID_POOL_TOPLEFT, -1, -1
5657 Xacid_1, TRUE, FALSE,
5661 Xacid_2, FALSE, FALSE,
5665 Xacid_3, FALSE, FALSE,
5669 Xacid_4, FALSE, FALSE,
5673 Xacid_5, FALSE, FALSE,
5677 Xacid_6, FALSE, FALSE,
5681 Xacid_7, FALSE, FALSE,
5685 Xacid_8, FALSE, FALSE,
5689 Xball_1, TRUE, FALSE,
5690 EL_EMC_MAGIC_BALL, -1, -1
5693 Xball_1B, FALSE, FALSE,
5694 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5697 Xball_2, FALSE, FALSE,
5698 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5701 Xball_2B, FALSE, FALSE,
5702 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5705 Yball_eat, FALSE, FALSE,
5706 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5709 Ykey_1_eat, FALSE, FALSE,
5710 EL_EM_KEY_1, ACTION_COLLECTING, -1
5713 Ykey_2_eat, FALSE, FALSE,
5714 EL_EM_KEY_2, ACTION_COLLECTING, -1
5717 Ykey_3_eat, FALSE, FALSE,
5718 EL_EM_KEY_3, ACTION_COLLECTING, -1
5721 Ykey_4_eat, FALSE, FALSE,
5722 EL_EM_KEY_4, ACTION_COLLECTING, -1
5725 Ykey_5_eat, FALSE, FALSE,
5726 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5729 Ykey_6_eat, FALSE, FALSE,
5730 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5733 Ykey_7_eat, FALSE, FALSE,
5734 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5737 Ykey_8_eat, FALSE, FALSE,
5738 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5741 Ylenses_eat, FALSE, FALSE,
5742 EL_EMC_LENSES, ACTION_COLLECTING, -1
5745 Ymagnify_eat, FALSE, FALSE,
5746 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5749 Ygrass_eat, FALSE, FALSE,
5750 EL_EMC_GRASS, ACTION_SNAPPING, -1
5753 Ydirt_eat, FALSE, FALSE,
5754 EL_SAND, ACTION_SNAPPING, -1
5757 Xgrow_ns, TRUE, FALSE,
5758 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5761 Ygrow_ns_eat, FALSE, FALSE,
5762 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5765 Xgrow_ew, TRUE, FALSE,
5766 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5769 Ygrow_ew_eat, FALSE, FALSE,
5770 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5773 Xwonderwall, TRUE, FALSE,
5774 EL_MAGIC_WALL, -1, -1
5777 XwonderwallB, FALSE, FALSE,
5778 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5781 Xamoeba_1, TRUE, FALSE,
5782 EL_AMOEBA_DRY, ACTION_OTHER, -1
5785 Xamoeba_2, FALSE, FALSE,
5786 EL_AMOEBA_DRY, ACTION_OTHER, -1
5789 Xamoeba_3, FALSE, FALSE,
5790 EL_AMOEBA_DRY, ACTION_OTHER, -1
5793 Xamoeba_4, FALSE, FALSE,
5794 EL_AMOEBA_DRY, ACTION_OTHER, -1
5797 Xamoeba_5, TRUE, FALSE,
5798 EL_AMOEBA_WET, ACTION_OTHER, -1
5801 Xamoeba_6, FALSE, FALSE,
5802 EL_AMOEBA_WET, ACTION_OTHER, -1
5805 Xamoeba_7, FALSE, FALSE,
5806 EL_AMOEBA_WET, ACTION_OTHER, -1
5809 Xamoeba_8, FALSE, FALSE,
5810 EL_AMOEBA_WET, ACTION_OTHER, -1
5813 Xdoor_1, TRUE, FALSE,
5814 EL_EM_GATE_1, -1, -1
5817 Xdoor_2, TRUE, FALSE,
5818 EL_EM_GATE_2, -1, -1
5821 Xdoor_3, TRUE, FALSE,
5822 EL_EM_GATE_3, -1, -1
5825 Xdoor_4, TRUE, FALSE,
5826 EL_EM_GATE_4, -1, -1
5829 Xdoor_5, TRUE, FALSE,
5830 EL_EMC_GATE_5, -1, -1
5833 Xdoor_6, TRUE, FALSE,
5834 EL_EMC_GATE_6, -1, -1
5837 Xdoor_7, TRUE, FALSE,
5838 EL_EMC_GATE_7, -1, -1
5841 Xdoor_8, TRUE, FALSE,
5842 EL_EMC_GATE_8, -1, -1
5845 Xkey_1, TRUE, FALSE,
5849 Xkey_2, TRUE, FALSE,
5853 Xkey_3, TRUE, FALSE,
5857 Xkey_4, TRUE, FALSE,
5861 Xkey_5, TRUE, FALSE,
5862 EL_EMC_KEY_5, -1, -1
5865 Xkey_6, TRUE, FALSE,
5866 EL_EMC_KEY_6, -1, -1
5869 Xkey_7, TRUE, FALSE,
5870 EL_EMC_KEY_7, -1, -1
5873 Xkey_8, TRUE, FALSE,
5874 EL_EMC_KEY_8, -1, -1
5877 Xwind_n, TRUE, FALSE,
5878 EL_BALLOON_SWITCH_UP, -1, -1
5881 Xwind_e, TRUE, FALSE,
5882 EL_BALLOON_SWITCH_RIGHT, -1, -1
5885 Xwind_s, TRUE, FALSE,
5886 EL_BALLOON_SWITCH_DOWN, -1, -1
5889 Xwind_w, TRUE, FALSE,
5890 EL_BALLOON_SWITCH_LEFT, -1, -1
5893 Xwind_nesw, TRUE, FALSE,
5894 EL_BALLOON_SWITCH_ANY, -1, -1
5897 Xwind_stop, TRUE, FALSE,
5898 EL_BALLOON_SWITCH_NONE, -1, -1
5902 EL_EM_EXIT_CLOSED, -1, -1
5905 Xexit_1, TRUE, FALSE,
5906 EL_EM_EXIT_OPEN, -1, -1
5909 Xexit_2, FALSE, FALSE,
5910 EL_EM_EXIT_OPEN, -1, -1
5913 Xexit_3, FALSE, FALSE,
5914 EL_EM_EXIT_OPEN, -1, -1
5917 Xdynamite, TRUE, FALSE,
5918 EL_EM_DYNAMITE, -1, -1
5921 Ydynamite_eat, FALSE, FALSE,
5922 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5925 Xdynamite_1, TRUE, FALSE,
5926 EL_EM_DYNAMITE_ACTIVE, -1, -1
5929 Xdynamite_2, FALSE, FALSE,
5930 EL_EM_DYNAMITE_ACTIVE, -1, -1
5933 Xdynamite_3, FALSE, FALSE,
5934 EL_EM_DYNAMITE_ACTIVE, -1, -1
5937 Xdynamite_4, FALSE, FALSE,
5938 EL_EM_DYNAMITE_ACTIVE, -1, -1
5941 Xbumper, TRUE, FALSE,
5942 EL_EMC_SPRING_BUMPER, -1, -1
5945 XbumperB, FALSE, FALSE,
5946 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5949 Xwheel, TRUE, FALSE,
5950 EL_ROBOT_WHEEL, -1, -1
5953 XwheelB, FALSE, FALSE,
5954 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5957 Xswitch, TRUE, FALSE,
5958 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5961 XswitchB, FALSE, FALSE,
5962 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5966 EL_QUICKSAND_EMPTY, -1, -1
5969 Xsand_stone, TRUE, FALSE,
5970 EL_QUICKSAND_FULL, -1, -1
5973 Xsand_stonein_1, FALSE, TRUE,
5974 EL_ROCK, ACTION_FILLING, -1
5977 Xsand_stonein_2, FALSE, TRUE,
5978 EL_ROCK, ACTION_FILLING, -1
5981 Xsand_stonein_3, FALSE, TRUE,
5982 EL_ROCK, ACTION_FILLING, -1
5985 Xsand_stonein_4, FALSE, TRUE,
5986 EL_ROCK, ACTION_FILLING, -1
5989 Xsand_stonesand_1, FALSE, FALSE,
5990 EL_QUICKSAND_EMPTYING, -1, -1
5993 Xsand_stonesand_2, FALSE, FALSE,
5994 EL_QUICKSAND_EMPTYING, -1, -1
5997 Xsand_stonesand_3, FALSE, FALSE,
5998 EL_QUICKSAND_EMPTYING, -1, -1
6001 Xsand_stonesand_4, FALSE, FALSE,
6002 EL_QUICKSAND_EMPTYING, -1, -1
6005 Xsand_stonesand_quickout_1, FALSE, FALSE,
6006 EL_QUICKSAND_EMPTYING, -1, -1
6009 Xsand_stonesand_quickout_2, FALSE, FALSE,
6010 EL_QUICKSAND_EMPTYING, -1, -1
6013 Xsand_stoneout_1, FALSE, FALSE,
6014 EL_ROCK, ACTION_EMPTYING, -1
6017 Xsand_stoneout_2, FALSE, FALSE,
6018 EL_ROCK, ACTION_EMPTYING, -1
6021 Xsand_sandstone_1, FALSE, FALSE,
6022 EL_QUICKSAND_FILLING, -1, -1
6025 Xsand_sandstone_2, FALSE, FALSE,
6026 EL_QUICKSAND_FILLING, -1, -1
6029 Xsand_sandstone_3, FALSE, FALSE,
6030 EL_QUICKSAND_FILLING, -1, -1
6033 Xsand_sandstone_4, FALSE, FALSE,
6034 EL_QUICKSAND_FILLING, -1, -1
6037 Xplant, TRUE, FALSE,
6038 EL_EMC_PLANT, -1, -1
6041 Yplant, FALSE, FALSE,
6042 EL_EMC_PLANT, -1, -1
6045 Xlenses, TRUE, FALSE,
6046 EL_EMC_LENSES, -1, -1
6049 Xmagnify, TRUE, FALSE,
6050 EL_EMC_MAGNIFIER, -1, -1
6053 Xdripper, TRUE, FALSE,
6054 EL_EMC_DRIPPER, -1, -1
6057 XdripperB, FALSE, FALSE,
6058 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6061 Xfake_blank, TRUE, FALSE,
6062 EL_INVISIBLE_WALL, -1, -1
6065 Xfake_blankB, FALSE, FALSE,
6066 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6069 Xfake_grass, TRUE, FALSE,
6070 EL_EMC_FAKE_GRASS, -1, -1
6073 Xfake_grassB, FALSE, FALSE,
6074 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6077 Xfake_door_1, TRUE, FALSE,
6078 EL_EM_GATE_1_GRAY, -1, -1
6081 Xfake_door_2, TRUE, FALSE,
6082 EL_EM_GATE_2_GRAY, -1, -1
6085 Xfake_door_3, TRUE, FALSE,
6086 EL_EM_GATE_3_GRAY, -1, -1
6089 Xfake_door_4, TRUE, FALSE,
6090 EL_EM_GATE_4_GRAY, -1, -1
6093 Xfake_door_5, TRUE, FALSE,
6094 EL_EMC_GATE_5_GRAY, -1, -1
6097 Xfake_door_6, TRUE, FALSE,
6098 EL_EMC_GATE_6_GRAY, -1, -1
6101 Xfake_door_7, TRUE, FALSE,
6102 EL_EMC_GATE_7_GRAY, -1, -1
6105 Xfake_door_8, TRUE, FALSE,
6106 EL_EMC_GATE_8_GRAY, -1, -1
6109 Xfake_acid_1, TRUE, FALSE,
6110 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_2, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_3, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_4, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6125 Xfake_acid_5, FALSE, FALSE,
6126 EL_EMC_FAKE_ACID, -1, -1
6129 Xfake_acid_6, FALSE, FALSE,
6130 EL_EMC_FAKE_ACID, -1, -1
6133 Xfake_acid_7, FALSE, FALSE,
6134 EL_EMC_FAKE_ACID, -1, -1
6137 Xfake_acid_8, FALSE, FALSE,
6138 EL_EMC_FAKE_ACID, -1, -1
6141 Xsteel_1, TRUE, FALSE,
6142 EL_STEELWALL, -1, -1
6145 Xsteel_2, TRUE, FALSE,
6146 EL_EMC_STEELWALL_2, -1, -1
6149 Xsteel_3, TRUE, FALSE,
6150 EL_EMC_STEELWALL_3, -1, -1
6153 Xsteel_4, TRUE, FALSE,
6154 EL_EMC_STEELWALL_4, -1, -1
6157 Xwall_1, TRUE, FALSE,
6161 Xwall_2, TRUE, FALSE,
6162 EL_EMC_WALL_14, -1, -1
6165 Xwall_3, TRUE, FALSE,
6166 EL_EMC_WALL_15, -1, -1
6169 Xwall_4, TRUE, FALSE,
6170 EL_EMC_WALL_16, -1, -1
6173 Xround_wall_1, TRUE, FALSE,
6174 EL_WALL_SLIPPERY, -1, -1
6177 Xround_wall_2, TRUE, FALSE,
6178 EL_EMC_WALL_SLIPPERY_2, -1, -1
6181 Xround_wall_3, TRUE, FALSE,
6182 EL_EMC_WALL_SLIPPERY_3, -1, -1
6185 Xround_wall_4, TRUE, FALSE,
6186 EL_EMC_WALL_SLIPPERY_4, -1, -1
6189 Xdecor_1, TRUE, FALSE,
6190 EL_EMC_WALL_8, -1, -1
6193 Xdecor_2, TRUE, FALSE,
6194 EL_EMC_WALL_6, -1, -1
6197 Xdecor_3, TRUE, FALSE,
6198 EL_EMC_WALL_4, -1, -1
6201 Xdecor_4, TRUE, FALSE,
6202 EL_EMC_WALL_7, -1, -1
6205 Xdecor_5, TRUE, FALSE,
6206 EL_EMC_WALL_5, -1, -1
6209 Xdecor_6, TRUE, FALSE,
6210 EL_EMC_WALL_9, -1, -1
6213 Xdecor_7, TRUE, FALSE,
6214 EL_EMC_WALL_10, -1, -1
6217 Xdecor_8, TRUE, FALSE,
6218 EL_EMC_WALL_1, -1, -1
6221 Xdecor_9, TRUE, FALSE,
6222 EL_EMC_WALL_2, -1, -1
6225 Xdecor_10, TRUE, FALSE,
6226 EL_EMC_WALL_3, -1, -1
6229 Xdecor_11, TRUE, FALSE,
6230 EL_EMC_WALL_11, -1, -1
6233 Xdecor_12, TRUE, FALSE,
6234 EL_EMC_WALL_12, -1, -1
6237 Xalpha_0, TRUE, FALSE,
6238 EL_CHAR('0'), -1, -1
6241 Xalpha_1, TRUE, FALSE,
6242 EL_CHAR('1'), -1, -1
6245 Xalpha_2, TRUE, FALSE,
6246 EL_CHAR('2'), -1, -1
6249 Xalpha_3, TRUE, FALSE,
6250 EL_CHAR('3'), -1, -1
6253 Xalpha_4, TRUE, FALSE,
6254 EL_CHAR('4'), -1, -1
6257 Xalpha_5, TRUE, FALSE,
6258 EL_CHAR('5'), -1, -1
6261 Xalpha_6, TRUE, FALSE,
6262 EL_CHAR('6'), -1, -1
6265 Xalpha_7, TRUE, FALSE,
6266 EL_CHAR('7'), -1, -1
6269 Xalpha_8, TRUE, FALSE,
6270 EL_CHAR('8'), -1, -1
6273 Xalpha_9, TRUE, FALSE,
6274 EL_CHAR('9'), -1, -1
6277 Xalpha_excla, TRUE, FALSE,
6278 EL_CHAR('!'), -1, -1
6281 Xalpha_quote, TRUE, FALSE,
6282 EL_CHAR('"'), -1, -1
6285 Xalpha_comma, TRUE, FALSE,
6286 EL_CHAR(','), -1, -1
6289 Xalpha_minus, TRUE, FALSE,
6290 EL_CHAR('-'), -1, -1
6293 Xalpha_perio, TRUE, FALSE,
6294 EL_CHAR('.'), -1, -1
6297 Xalpha_colon, TRUE, FALSE,
6298 EL_CHAR(':'), -1, -1
6301 Xalpha_quest, TRUE, FALSE,
6302 EL_CHAR('?'), -1, -1
6305 Xalpha_a, TRUE, FALSE,
6306 EL_CHAR('A'), -1, -1
6309 Xalpha_b, TRUE, FALSE,
6310 EL_CHAR('B'), -1, -1
6313 Xalpha_c, TRUE, FALSE,
6314 EL_CHAR('C'), -1, -1
6317 Xalpha_d, TRUE, FALSE,
6318 EL_CHAR('D'), -1, -1
6321 Xalpha_e, TRUE, FALSE,
6322 EL_CHAR('E'), -1, -1
6325 Xalpha_f, TRUE, FALSE,
6326 EL_CHAR('F'), -1, -1
6329 Xalpha_g, TRUE, FALSE,
6330 EL_CHAR('G'), -1, -1
6333 Xalpha_h, TRUE, FALSE,
6334 EL_CHAR('H'), -1, -1
6337 Xalpha_i, TRUE, FALSE,
6338 EL_CHAR('I'), -1, -1
6341 Xalpha_j, TRUE, FALSE,
6342 EL_CHAR('J'), -1, -1
6345 Xalpha_k, TRUE, FALSE,
6346 EL_CHAR('K'), -1, -1
6349 Xalpha_l, TRUE, FALSE,
6350 EL_CHAR('L'), -1, -1
6353 Xalpha_m, TRUE, FALSE,
6354 EL_CHAR('M'), -1, -1
6357 Xalpha_n, TRUE, FALSE,
6358 EL_CHAR('N'), -1, -1
6361 Xalpha_o, TRUE, FALSE,
6362 EL_CHAR('O'), -1, -1
6365 Xalpha_p, TRUE, FALSE,
6366 EL_CHAR('P'), -1, -1
6369 Xalpha_q, TRUE, FALSE,
6370 EL_CHAR('Q'), -1, -1
6373 Xalpha_r, TRUE, FALSE,
6374 EL_CHAR('R'), -1, -1
6377 Xalpha_s, TRUE, FALSE,
6378 EL_CHAR('S'), -1, -1
6381 Xalpha_t, TRUE, FALSE,
6382 EL_CHAR('T'), -1, -1
6385 Xalpha_u, TRUE, FALSE,
6386 EL_CHAR('U'), -1, -1
6389 Xalpha_v, TRUE, FALSE,
6390 EL_CHAR('V'), -1, -1
6393 Xalpha_w, TRUE, FALSE,
6394 EL_CHAR('W'), -1, -1
6397 Xalpha_x, TRUE, FALSE,
6398 EL_CHAR('X'), -1, -1
6401 Xalpha_y, TRUE, FALSE,
6402 EL_CHAR('Y'), -1, -1
6405 Xalpha_z, TRUE, FALSE,
6406 EL_CHAR('Z'), -1, -1
6409 Xalpha_arrow_e, TRUE, FALSE,
6410 EL_CHAR('>'), -1, -1
6413 Xalpha_arrow_w, TRUE, FALSE,
6414 EL_CHAR('<'), -1, -1
6417 Xalpha_copyr, TRUE, FALSE,
6418 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6422 Xboom_bug, FALSE, FALSE,
6423 EL_BUG, ACTION_EXPLODING, -1
6426 Xboom_bomb, FALSE, FALSE,
6427 EL_BOMB, ACTION_EXPLODING, -1
6430 Xboom_android, FALSE, FALSE,
6431 EL_EMC_ANDROID, ACTION_OTHER, -1
6434 Xboom_1, FALSE, FALSE,
6435 EL_DEFAULT, ACTION_EXPLODING, -1
6438 Xboom_2, FALSE, FALSE,
6439 EL_DEFAULT, ACTION_EXPLODING, -1
6442 Znormal, FALSE, FALSE,
6446 Zdynamite, FALSE, FALSE,
6450 Zplayer, FALSE, FALSE,
6454 ZBORDER, FALSE, FALSE,
6464 static struct Mapping_EM_to_RND_player
6473 em_player_mapping_list[] =
6477 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6481 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6485 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6489 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6493 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6497 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6501 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6505 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6509 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6513 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6517 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6521 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6525 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6529 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6533 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6537 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6541 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6545 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6549 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6553 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6557 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6561 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6565 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6569 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6573 EL_PLAYER_1, ACTION_DEFAULT, -1,
6577 EL_PLAYER_2, ACTION_DEFAULT, -1,
6581 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6585 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6589 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6593 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6597 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6601 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6605 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6609 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6613 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6617 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6621 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6625 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6629 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6633 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6637 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6641 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6645 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6649 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6653 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6657 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6661 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6665 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6669 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6673 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6677 EL_PLAYER_3, ACTION_DEFAULT, -1,
6681 EL_PLAYER_4, ACTION_DEFAULT, -1,
6690 int map_element_RND_to_EM(int element_rnd)
6692 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6693 static boolean mapping_initialized = FALSE;
6695 if (!mapping_initialized)
6699 /* return "Xalpha_quest" for all undefined elements in mapping array */
6700 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6701 mapping_RND_to_EM[i] = Xalpha_quest;
6703 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6704 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6705 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6706 em_object_mapping_list[i].element_em;
6708 mapping_initialized = TRUE;
6711 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6712 return mapping_RND_to_EM[element_rnd];
6714 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6719 int map_element_EM_to_RND(int element_em)
6721 static unsigned short mapping_EM_to_RND[TILE_MAX];
6722 static boolean mapping_initialized = FALSE;
6724 if (!mapping_initialized)
6728 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6729 for (i = 0; i < TILE_MAX; i++)
6730 mapping_EM_to_RND[i] = EL_UNKNOWN;
6732 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6733 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6734 em_object_mapping_list[i].element_rnd;
6736 mapping_initialized = TRUE;
6739 if (element_em >= 0 && element_em < TILE_MAX)
6740 return mapping_EM_to_RND[element_em];
6742 Error(ERR_WARN, "invalid EM level element %d", element_em);
6747 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6749 struct LevelInfo_EM *level_em = level->native_em_level;
6750 struct LEVEL *lev = level_em->lev;
6753 for (i = 0; i < TILE_MAX; i++)
6754 lev->android_array[i] = Xblank;
6756 for (i = 0; i < level->num_android_clone_elements; i++)
6758 int element_rnd = level->android_clone_element[i];
6759 int element_em = map_element_RND_to_EM(element_rnd);
6761 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6762 if (em_object_mapping_list[j].element_rnd == element_rnd)
6763 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6767 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6769 struct LevelInfo_EM *level_em = level->native_em_level;
6770 struct LEVEL *lev = level_em->lev;
6773 level->num_android_clone_elements = 0;
6775 for (i = 0; i < TILE_MAX; i++)
6777 int element_em = lev->android_array[i];
6779 boolean element_found = FALSE;
6781 if (element_em == Xblank)
6784 element_rnd = map_element_EM_to_RND(element_em);
6786 for (j = 0; j < level->num_android_clone_elements; j++)
6787 if (level->android_clone_element[j] == element_rnd)
6788 element_found = TRUE;
6792 level->android_clone_element[level->num_android_clone_elements++] =
6795 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6800 if (level->num_android_clone_elements == 0)
6802 level->num_android_clone_elements = 1;
6803 level->android_clone_element[0] = EL_EMPTY;
6807 int map_direction_RND_to_EM(int direction)
6809 return (direction == MV_UP ? 0 :
6810 direction == MV_RIGHT ? 1 :
6811 direction == MV_DOWN ? 2 :
6812 direction == MV_LEFT ? 3 :
6816 int map_direction_EM_to_RND(int direction)
6818 return (direction == 0 ? MV_UP :
6819 direction == 1 ? MV_RIGHT :
6820 direction == 2 ? MV_DOWN :
6821 direction == 3 ? MV_LEFT :
6825 int map_element_RND_to_SP(int element_rnd)
6827 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6829 if (element_rnd >= EL_SP_START &&
6830 element_rnd <= EL_SP_END)
6831 element_sp = element_rnd - EL_SP_START;
6832 else if (element_rnd == EL_EMPTY_SPACE)
6834 else if (element_rnd == EL_INVISIBLE_WALL)
6840 int map_element_SP_to_RND(int element_sp)
6842 int element_rnd = EL_UNKNOWN;
6844 if (element_sp >= 0x00 &&
6846 element_rnd = EL_SP_START + element_sp;
6847 else if (element_sp == 0x28)
6848 element_rnd = EL_INVISIBLE_WALL;
6853 int map_action_SP_to_RND(int action_sp)
6857 case actActive: return ACTION_ACTIVE;
6858 case actImpact: return ACTION_IMPACT;
6859 case actExploding: return ACTION_EXPLODING;
6860 case actDigging: return ACTION_DIGGING;
6861 case actSnapping: return ACTION_SNAPPING;
6862 case actCollecting: return ACTION_COLLECTING;
6863 case actPassing: return ACTION_PASSING;
6864 case actPushing: return ACTION_PUSHING;
6865 case actDropping: return ACTION_DROPPING;
6867 default: return ACTION_DEFAULT;
6871 int get_next_element(int element)
6875 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6876 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6877 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6878 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6879 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6880 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6881 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6882 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6883 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6884 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6885 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6887 default: return element;
6891 int el_act_dir2img(int element, int action, int direction)
6893 element = GFX_ELEMENT(element);
6894 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6896 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6897 return element_info[element].direction_graphic[action][direction];
6900 static int el_act_dir2crm(int element, int action, int direction)
6902 element = GFX_ELEMENT(element);
6903 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6905 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6906 return element_info[element].direction_crumbled[action][direction];
6909 int el_act2img(int element, int action)
6911 element = GFX_ELEMENT(element);
6913 return element_info[element].graphic[action];
6916 int el_act2crm(int element, int action)
6918 element = GFX_ELEMENT(element);
6920 return element_info[element].crumbled[action];
6923 int el_dir2img(int element, int direction)
6925 element = GFX_ELEMENT(element);
6927 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6930 int el2baseimg(int element)
6932 return element_info[element].graphic[ACTION_DEFAULT];
6935 int el2img(int element)
6937 element = GFX_ELEMENT(element);
6939 return element_info[element].graphic[ACTION_DEFAULT];
6942 int el2edimg(int element)
6944 element = GFX_ELEMENT(element);
6946 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6949 int el2preimg(int element)
6951 element = GFX_ELEMENT(element);
6953 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6956 int el2panelimg(int element)
6958 element = GFX_ELEMENT(element);
6960 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6963 int font2baseimg(int font_nr)
6965 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6968 int getBeltNrFromBeltElement(int element)
6970 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6971 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6972 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6975 int getBeltNrFromBeltActiveElement(int element)
6977 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6978 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6979 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6982 int getBeltNrFromBeltSwitchElement(int element)
6984 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6985 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6986 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6989 int getBeltDirNrFromBeltElement(int element)
6991 static int belt_base_element[4] =
6993 EL_CONVEYOR_BELT_1_LEFT,
6994 EL_CONVEYOR_BELT_2_LEFT,
6995 EL_CONVEYOR_BELT_3_LEFT,
6996 EL_CONVEYOR_BELT_4_LEFT
6999 int belt_nr = getBeltNrFromBeltElement(element);
7000 int belt_dir_nr = element - belt_base_element[belt_nr];
7002 return (belt_dir_nr % 3);
7005 int getBeltDirNrFromBeltSwitchElement(int element)
7007 static int belt_base_element[4] =
7009 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7010 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7011 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7012 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7015 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7016 int belt_dir_nr = element - belt_base_element[belt_nr];
7018 return (belt_dir_nr % 3);
7021 int getBeltDirFromBeltElement(int element)
7023 static int belt_move_dir[3] =
7030 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7032 return belt_move_dir[belt_dir_nr];
7035 int getBeltDirFromBeltSwitchElement(int element)
7037 static int belt_move_dir[3] =
7044 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7046 return belt_move_dir[belt_dir_nr];
7049 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7051 static int belt_base_element[4] =
7053 EL_CONVEYOR_BELT_1_LEFT,
7054 EL_CONVEYOR_BELT_2_LEFT,
7055 EL_CONVEYOR_BELT_3_LEFT,
7056 EL_CONVEYOR_BELT_4_LEFT
7059 return belt_base_element[belt_nr] + belt_dir_nr;
7062 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7064 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7066 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7069 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7071 static int belt_base_element[4] =
7073 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7074 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7075 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7076 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7079 return belt_base_element[belt_nr] + belt_dir_nr;
7082 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7084 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7086 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7089 boolean getTeamMode_EM()
7091 return game.team_mode;
7094 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7096 int game_frame_delay_value;
7098 game_frame_delay_value =
7099 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7100 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7103 if (tape.playing && tape.warp_forward && !tape.pausing)
7104 game_frame_delay_value = 0;
7106 return game_frame_delay_value;
7109 unsigned int InitRND(int seed)
7111 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7112 return InitEngineRandom_EM(seed);
7113 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7114 return InitEngineRandom_SP(seed);
7116 return InitEngineRandom_RND(seed);
7119 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7120 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7122 inline static int get_effective_element_EM(int tile, int frame_em)
7124 int element = object_mapping[tile].element_rnd;
7125 int action = object_mapping[tile].action;
7126 boolean is_backside = object_mapping[tile].is_backside;
7127 boolean action_removing = (action == ACTION_DIGGING ||
7128 action == ACTION_SNAPPING ||
7129 action == ACTION_COLLECTING);
7135 case Yacid_splash_eB:
7136 case Yacid_splash_wB:
7137 return (frame_em > 5 ? EL_EMPTY : element);
7143 else /* frame_em == 7 */
7147 case Yacid_splash_eB:
7148 case Yacid_splash_wB:
7151 case Yemerald_stone:
7154 case Ydiamond_stone:
7158 case Xdrip_stretchB:
7177 case Xsand_stonein_1:
7178 case Xsand_stonein_2:
7179 case Xsand_stonein_3:
7180 case Xsand_stonein_4:
7184 return (is_backside || action_removing ? EL_EMPTY : element);
7189 inline static boolean check_linear_animation_EM(int tile)
7193 case Xsand_stonesand_1:
7194 case Xsand_stonesand_quickout_1:
7195 case Xsand_sandstone_1:
7196 case Xsand_stonein_1:
7197 case Xsand_stoneout_1:
7216 case Yacid_splash_eB:
7217 case Yacid_splash_wB:
7218 case Yemerald_stone:
7225 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7226 boolean has_crumbled_graphics,
7227 int crumbled, int sync_frame)
7229 /* if element can be crumbled, but certain action graphics are just empty
7230 space (like instantly snapping sand to empty space in 1 frame), do not
7231 treat these empty space graphics as crumbled graphics in EMC engine */
7232 if (crumbled == IMG_EMPTY_SPACE)
7233 has_crumbled_graphics = FALSE;
7235 if (has_crumbled_graphics)
7237 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7238 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7239 g_crumbled->anim_delay,
7240 g_crumbled->anim_mode,
7241 g_crumbled->anim_start_frame,
7244 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7245 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7247 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7249 g_em->has_crumbled_graphics = TRUE;
7253 g_em->crumbled_bitmap = NULL;
7254 g_em->crumbled_src_x = 0;
7255 g_em->crumbled_src_y = 0;
7256 g_em->crumbled_border_size = 0;
7258 g_em->has_crumbled_graphics = FALSE;
7262 void ResetGfxAnimation_EM(int x, int y, int tile)
7267 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7268 int tile, int frame_em, int x, int y)
7270 int action = object_mapping[tile].action;
7271 int direction = object_mapping[tile].direction;
7272 int effective_element = get_effective_element_EM(tile, frame_em);
7273 int graphic = (direction == MV_NONE ?
7274 el_act2img(effective_element, action) :
7275 el_act_dir2img(effective_element, action, direction));
7276 struct GraphicInfo *g = &graphic_info[graphic];
7278 boolean action_removing = (action == ACTION_DIGGING ||
7279 action == ACTION_SNAPPING ||
7280 action == ACTION_COLLECTING);
7281 boolean action_moving = (action == ACTION_FALLING ||
7282 action == ACTION_MOVING ||
7283 action == ACTION_PUSHING ||
7284 action == ACTION_EATING ||
7285 action == ACTION_FILLING ||
7286 action == ACTION_EMPTYING);
7287 boolean action_falling = (action == ACTION_FALLING ||
7288 action == ACTION_FILLING ||
7289 action == ACTION_EMPTYING);
7291 /* special case: graphic uses "2nd movement tile" and has defined
7292 7 frames for movement animation (or less) => use default graphic
7293 for last (8th) frame which ends the movement animation */
7294 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7296 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7297 graphic = (direction == MV_NONE ?
7298 el_act2img(effective_element, action) :
7299 el_act_dir2img(effective_element, action, direction));
7301 g = &graphic_info[graphic];
7304 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7308 else if (action_moving)
7310 boolean is_backside = object_mapping[tile].is_backside;
7314 int direction = object_mapping[tile].direction;
7315 int move_dir = (action_falling ? MV_DOWN : direction);
7320 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7321 if (g->double_movement && frame_em == 0)
7325 if (move_dir == MV_LEFT)
7326 GfxFrame[x - 1][y] = GfxFrame[x][y];
7327 else if (move_dir == MV_RIGHT)
7328 GfxFrame[x + 1][y] = GfxFrame[x][y];
7329 else if (move_dir == MV_UP)
7330 GfxFrame[x][y - 1] = GfxFrame[x][y];
7331 else if (move_dir == MV_DOWN)
7332 GfxFrame[x][y + 1] = GfxFrame[x][y];
7339 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7340 if (tile == Xsand_stonesand_quickout_1 ||
7341 tile == Xsand_stonesand_quickout_2)
7345 if (graphic_info[graphic].anim_global_sync)
7346 sync_frame = FrameCounter;
7347 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7348 sync_frame = GfxFrame[x][y];
7350 sync_frame = 0; /* playfield border (pseudo steel) */
7352 SetRandomAnimationValue(x, y);
7354 int frame = getAnimationFrame(g->anim_frames,
7357 g->anim_start_frame,
7360 g_em->unique_identifier =
7361 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7364 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7365 int tile, int frame_em, int x, int y)
7367 int action = object_mapping[tile].action;
7368 int direction = object_mapping[tile].direction;
7369 boolean is_backside = object_mapping[tile].is_backside;
7370 int effective_element = get_effective_element_EM(tile, frame_em);
7371 int effective_action = action;
7372 int graphic = (direction == MV_NONE ?
7373 el_act2img(effective_element, effective_action) :
7374 el_act_dir2img(effective_element, effective_action,
7376 int crumbled = (direction == MV_NONE ?
7377 el_act2crm(effective_element, effective_action) :
7378 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_crumbled_graphics = (base_crumbled != base_graphic);
7383 struct GraphicInfo *g = &graphic_info[graphic];
7386 /* special case: graphic uses "2nd movement tile" and has defined
7387 7 frames for movement animation (or less) => use default graphic
7388 for last (8th) frame which ends the movement animation */
7389 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7391 effective_action = ACTION_DEFAULT;
7392 graphic = (direction == MV_NONE ?
7393 el_act2img(effective_element, effective_action) :
7394 el_act_dir2img(effective_element, effective_action,
7396 crumbled = (direction == MV_NONE ?
7397 el_act2crm(effective_element, effective_action) :
7398 el_act_dir2crm(effective_element, effective_action,
7401 g = &graphic_info[graphic];
7404 if (graphic_info[graphic].anim_global_sync)
7405 sync_frame = FrameCounter;
7406 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7407 sync_frame = GfxFrame[x][y];
7409 sync_frame = 0; /* playfield border (pseudo steel) */
7411 SetRandomAnimationValue(x, y);
7413 int frame = getAnimationFrame(g->anim_frames,
7416 g->anim_start_frame,
7419 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7420 g->double_movement && is_backside);
7422 /* (updating the "crumbled" graphic definitions is probably not really needed,
7423 as animations for crumbled graphics can't be longer than one EMC cycle) */
7424 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7428 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7429 int player_nr, int anim, int frame_em)
7431 int element = player_mapping[player_nr][anim].element_rnd;
7432 int action = player_mapping[player_nr][anim].action;
7433 int direction = player_mapping[player_nr][anim].direction;
7434 int graphic = (direction == MV_NONE ?
7435 el_act2img(element, action) :
7436 el_act_dir2img(element, action, direction));
7437 struct GraphicInfo *g = &graphic_info[graphic];
7440 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7442 stored_player[player_nr].StepFrame = frame_em;
7444 sync_frame = stored_player[player_nr].Frame;
7446 int frame = getAnimationFrame(g->anim_frames,
7449 g->anim_start_frame,
7452 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7453 &g_em->src_x, &g_em->src_y, FALSE);
7456 void InitGraphicInfo_EM(void)
7461 int num_em_gfx_errors = 0;
7463 if (graphic_info_em_object[0][0].bitmap == NULL)
7465 /* EM graphics not yet initialized in em_open_all() */
7470 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7473 /* always start with reliable default values */
7474 for (i = 0; i < TILE_MAX; i++)
7476 object_mapping[i].element_rnd = EL_UNKNOWN;
7477 object_mapping[i].is_backside = FALSE;
7478 object_mapping[i].action = ACTION_DEFAULT;
7479 object_mapping[i].direction = MV_NONE;
7482 /* always start with reliable default values */
7483 for (p = 0; p < MAX_PLAYERS; p++)
7485 for (i = 0; i < SPR_MAX; i++)
7487 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7488 player_mapping[p][i].action = ACTION_DEFAULT;
7489 player_mapping[p][i].direction = MV_NONE;
7493 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7495 int e = em_object_mapping_list[i].element_em;
7497 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7498 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7500 if (em_object_mapping_list[i].action != -1)
7501 object_mapping[e].action = em_object_mapping_list[i].action;
7503 if (em_object_mapping_list[i].direction != -1)
7504 object_mapping[e].direction =
7505 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7508 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7510 int a = em_player_mapping_list[i].action_em;
7511 int p = em_player_mapping_list[i].player_nr;
7513 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7515 if (em_player_mapping_list[i].action != -1)
7516 player_mapping[p][a].action = em_player_mapping_list[i].action;
7518 if (em_player_mapping_list[i].direction != -1)
7519 player_mapping[p][a].direction =
7520 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7523 for (i = 0; i < TILE_MAX; i++)
7525 int element = object_mapping[i].element_rnd;
7526 int action = object_mapping[i].action;
7527 int direction = object_mapping[i].direction;
7528 boolean is_backside = object_mapping[i].is_backside;
7529 boolean action_exploding = ((action == ACTION_EXPLODING ||
7530 action == ACTION_SMASHED_BY_ROCK ||
7531 action == ACTION_SMASHED_BY_SPRING) &&
7532 element != EL_DIAMOND);
7533 boolean action_active = (action == ACTION_ACTIVE);
7534 boolean action_other = (action == ACTION_OTHER);
7536 for (j = 0; j < 8; j++)
7538 int effective_element = get_effective_element_EM(i, j);
7539 int effective_action = (j < 7 ? action :
7540 i == Xdrip_stretch ? action :
7541 i == Xdrip_stretchB ? action :
7542 i == Ydrip_s1 ? action :
7543 i == Ydrip_s1B ? action :
7544 i == Xball_1B ? action :
7545 i == Xball_2 ? action :
7546 i == Xball_2B ? action :
7547 i == Yball_eat ? action :
7548 i == Ykey_1_eat ? action :
7549 i == Ykey_2_eat ? action :
7550 i == Ykey_3_eat ? action :
7551 i == Ykey_4_eat ? action :
7552 i == Ykey_5_eat ? action :
7553 i == Ykey_6_eat ? action :
7554 i == Ykey_7_eat ? action :
7555 i == Ykey_8_eat ? action :
7556 i == Ylenses_eat ? action :
7557 i == Ymagnify_eat ? action :
7558 i == Ygrass_eat ? action :
7559 i == Ydirt_eat ? action :
7560 i == Xsand_stonein_1 ? action :
7561 i == Xsand_stonein_2 ? action :
7562 i == Xsand_stonein_3 ? action :
7563 i == Xsand_stonein_4 ? action :
7564 i == Xsand_stoneout_1 ? action :
7565 i == Xsand_stoneout_2 ? action :
7566 i == Xboom_android ? ACTION_EXPLODING :
7567 action_exploding ? ACTION_EXPLODING :
7568 action_active ? action :
7569 action_other ? action :
7571 int graphic = (el_act_dir2img(effective_element, effective_action,
7573 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7575 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7576 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7577 boolean has_action_graphics = (graphic != base_graphic);
7578 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7579 struct GraphicInfo *g = &graphic_info[graphic];
7580 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7583 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7584 boolean special_animation = (action != ACTION_DEFAULT &&
7585 g->anim_frames == 3 &&
7586 g->anim_delay == 2 &&
7587 g->anim_mode & ANIM_LINEAR);
7588 int sync_frame = (i == Xdrip_stretch ? 7 :
7589 i == Xdrip_stretchB ? 7 :
7590 i == Ydrip_s2 ? j + 8 :
7591 i == Ydrip_s2B ? j + 8 :
7600 i == Xfake_acid_1 ? 0 :
7601 i == Xfake_acid_2 ? 10 :
7602 i == Xfake_acid_3 ? 20 :
7603 i == Xfake_acid_4 ? 30 :
7604 i == Xfake_acid_5 ? 40 :
7605 i == Xfake_acid_6 ? 50 :
7606 i == Xfake_acid_7 ? 60 :
7607 i == Xfake_acid_8 ? 70 :
7609 i == Xball_2B ? j + 8 :
7610 i == Yball_eat ? j + 1 :
7611 i == Ykey_1_eat ? j + 1 :
7612 i == Ykey_2_eat ? j + 1 :
7613 i == Ykey_3_eat ? j + 1 :
7614 i == Ykey_4_eat ? j + 1 :
7615 i == Ykey_5_eat ? j + 1 :
7616 i == Ykey_6_eat ? j + 1 :
7617 i == Ykey_7_eat ? j + 1 :
7618 i == Ykey_8_eat ? j + 1 :
7619 i == Ylenses_eat ? j + 1 :
7620 i == Ymagnify_eat ? j + 1 :
7621 i == Ygrass_eat ? j + 1 :
7622 i == Ydirt_eat ? j + 1 :
7623 i == Xamoeba_1 ? 0 :
7624 i == Xamoeba_2 ? 1 :
7625 i == Xamoeba_3 ? 2 :
7626 i == Xamoeba_4 ? 3 :
7627 i == Xamoeba_5 ? 0 :
7628 i == Xamoeba_6 ? 1 :
7629 i == Xamoeba_7 ? 2 :
7630 i == Xamoeba_8 ? 3 :
7631 i == Xexit_2 ? j + 8 :
7632 i == Xexit_3 ? j + 16 :
7633 i == Xdynamite_1 ? 0 :
7634 i == Xdynamite_2 ? 8 :
7635 i == Xdynamite_3 ? 16 :
7636 i == Xdynamite_4 ? 24 :
7637 i == Xsand_stonein_1 ? j + 1 :
7638 i == Xsand_stonein_2 ? j + 9 :
7639 i == Xsand_stonein_3 ? j + 17 :
7640 i == Xsand_stonein_4 ? j + 25 :
7641 i == Xsand_stoneout_1 && j == 0 ? 0 :
7642 i == Xsand_stoneout_1 && j == 1 ? 0 :
7643 i == Xsand_stoneout_1 && j == 2 ? 1 :
7644 i == Xsand_stoneout_1 && j == 3 ? 2 :
7645 i == Xsand_stoneout_1 && j == 4 ? 2 :
7646 i == Xsand_stoneout_1 && j == 5 ? 3 :
7647 i == Xsand_stoneout_1 && j == 6 ? 4 :
7648 i == Xsand_stoneout_1 && j == 7 ? 4 :
7649 i == Xsand_stoneout_2 && j == 0 ? 5 :
7650 i == Xsand_stoneout_2 && j == 1 ? 6 :
7651 i == Xsand_stoneout_2 && j == 2 ? 7 :
7652 i == Xsand_stoneout_2 && j == 3 ? 8 :
7653 i == Xsand_stoneout_2 && j == 4 ? 9 :
7654 i == Xsand_stoneout_2 && j == 5 ? 11 :
7655 i == Xsand_stoneout_2 && j == 6 ? 13 :
7656 i == Xsand_stoneout_2 && j == 7 ? 15 :
7657 i == Xboom_bug && j == 1 ? 2 :
7658 i == Xboom_bug && j == 2 ? 2 :
7659 i == Xboom_bug && j == 3 ? 4 :
7660 i == Xboom_bug && j == 4 ? 4 :
7661 i == Xboom_bug && j == 5 ? 2 :
7662 i == Xboom_bug && j == 6 ? 2 :
7663 i == Xboom_bug && j == 7 ? 0 :
7664 i == Xboom_bomb && j == 1 ? 2 :
7665 i == Xboom_bomb && j == 2 ? 2 :
7666 i == Xboom_bomb && j == 3 ? 4 :
7667 i == Xboom_bomb && j == 4 ? 4 :
7668 i == Xboom_bomb && j == 5 ? 2 :
7669 i == Xboom_bomb && j == 6 ? 2 :
7670 i == Xboom_bomb && j == 7 ? 0 :
7671 i == Xboom_android && j == 7 ? 6 :
7672 i == Xboom_1 && j == 1 ? 2 :
7673 i == Xboom_1 && j == 2 ? 2 :
7674 i == Xboom_1 && j == 3 ? 4 :
7675 i == Xboom_1 && j == 4 ? 4 :
7676 i == Xboom_1 && j == 5 ? 6 :
7677 i == Xboom_1 && j == 6 ? 6 :
7678 i == Xboom_1 && j == 7 ? 8 :
7679 i == Xboom_2 && j == 0 ? 8 :
7680 i == Xboom_2 && j == 1 ? 8 :
7681 i == Xboom_2 && j == 2 ? 10 :
7682 i == Xboom_2 && j == 3 ? 10 :
7683 i == Xboom_2 && j == 4 ? 10 :
7684 i == Xboom_2 && j == 5 ? 12 :
7685 i == Xboom_2 && j == 6 ? 12 :
7686 i == Xboom_2 && j == 7 ? 12 :
7687 special_animation && j == 4 ? 3 :
7688 effective_action != action ? 0 :
7692 Bitmap *debug_bitmap = g_em->bitmap;
7693 int debug_src_x = g_em->src_x;
7694 int debug_src_y = g_em->src_y;
7697 int frame = getAnimationFrame(g->anim_frames,
7700 g->anim_start_frame,
7703 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7704 g->double_movement && is_backside);
7706 g_em->bitmap = src_bitmap;
7707 g_em->src_x = src_x;
7708 g_em->src_y = src_y;
7709 g_em->src_offset_x = 0;
7710 g_em->src_offset_y = 0;
7711 g_em->dst_offset_x = 0;
7712 g_em->dst_offset_y = 0;
7713 g_em->width = TILEX;
7714 g_em->height = TILEY;
7716 g_em->preserve_background = FALSE;
7718 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7721 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7722 effective_action == ACTION_MOVING ||
7723 effective_action == ACTION_PUSHING ||
7724 effective_action == ACTION_EATING)) ||
7725 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7726 effective_action == ACTION_EMPTYING)))
7729 (effective_action == ACTION_FALLING ||
7730 effective_action == ACTION_FILLING ||
7731 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7732 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7733 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7734 int num_steps = (i == Ydrip_s1 ? 16 :
7735 i == Ydrip_s1B ? 16 :
7736 i == Ydrip_s2 ? 16 :
7737 i == Ydrip_s2B ? 16 :
7738 i == Xsand_stonein_1 ? 32 :
7739 i == Xsand_stonein_2 ? 32 :
7740 i == Xsand_stonein_3 ? 32 :
7741 i == Xsand_stonein_4 ? 32 :
7742 i == Xsand_stoneout_1 ? 16 :
7743 i == Xsand_stoneout_2 ? 16 : 8);
7744 int cx = ABS(dx) * (TILEX / num_steps);
7745 int cy = ABS(dy) * (TILEY / num_steps);
7746 int step_frame = (i == Ydrip_s2 ? j + 8 :
7747 i == Ydrip_s2B ? j + 8 :
7748 i == Xsand_stonein_2 ? j + 8 :
7749 i == Xsand_stonein_3 ? j + 16 :
7750 i == Xsand_stonein_4 ? j + 24 :
7751 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7752 int step = (is_backside ? step_frame : num_steps - step_frame);
7754 if (is_backside) /* tile where movement starts */
7756 if (dx < 0 || dy < 0)
7758 g_em->src_offset_x = cx * step;
7759 g_em->src_offset_y = cy * step;
7763 g_em->dst_offset_x = cx * step;
7764 g_em->dst_offset_y = cy * step;
7767 else /* tile where movement ends */
7769 if (dx < 0 || dy < 0)
7771 g_em->dst_offset_x = cx * step;
7772 g_em->dst_offset_y = cy * step;
7776 g_em->src_offset_x = cx * step;
7777 g_em->src_offset_y = cy * step;
7781 g_em->width = TILEX - cx * step;
7782 g_em->height = TILEY - cy * step;
7785 /* create unique graphic identifier to decide if tile must be redrawn */
7786 /* bit 31 - 16 (16 bit): EM style graphic
7787 bit 15 - 12 ( 4 bit): EM style frame
7788 bit 11 - 6 ( 6 bit): graphic width
7789 bit 5 - 0 ( 6 bit): graphic height */
7790 g_em->unique_identifier =
7791 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7795 /* skip check for EMC elements not contained in original EMC artwork */
7796 if (element == EL_EMC_FAKE_ACID)
7799 if (g_em->bitmap != debug_bitmap ||
7800 g_em->src_x != debug_src_x ||
7801 g_em->src_y != debug_src_y ||
7802 g_em->src_offset_x != 0 ||
7803 g_em->src_offset_y != 0 ||
7804 g_em->dst_offset_x != 0 ||
7805 g_em->dst_offset_y != 0 ||
7806 g_em->width != TILEX ||
7807 g_em->height != TILEY)
7809 static int last_i = -1;
7817 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7818 i, element, element_info[element].token_name,
7819 element_action_info[effective_action].suffix, direction);
7821 if (element != effective_element)
7822 printf(" [%d ('%s')]",
7824 element_info[effective_element].token_name);
7828 if (g_em->bitmap != debug_bitmap)
7829 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7830 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7832 if (g_em->src_x != debug_src_x ||
7833 g_em->src_y != debug_src_y)
7834 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7835 j, (is_backside ? 'B' : 'F'),
7836 g_em->src_x, g_em->src_y,
7837 g_em->src_x / 32, g_em->src_y / 32,
7838 debug_src_x, debug_src_y,
7839 debug_src_x / 32, debug_src_y / 32);
7841 if (g_em->src_offset_x != 0 ||
7842 g_em->src_offset_y != 0 ||
7843 g_em->dst_offset_x != 0 ||
7844 g_em->dst_offset_y != 0)
7845 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7847 g_em->src_offset_x, g_em->src_offset_y,
7848 g_em->dst_offset_x, g_em->dst_offset_y);
7850 if (g_em->width != TILEX ||
7851 g_em->height != TILEY)
7852 printf(" %d (%d): size %d,%d should be %d,%d\n",
7854 g_em->width, g_em->height, TILEX, TILEY);
7856 num_em_gfx_errors++;
7863 for (i = 0; i < TILE_MAX; i++)
7865 for (j = 0; j < 8; j++)
7867 int element = object_mapping[i].element_rnd;
7868 int action = object_mapping[i].action;
7869 int direction = object_mapping[i].direction;
7870 boolean is_backside = object_mapping[i].is_backside;
7871 int graphic_action = el_act_dir2img(element, action, direction);
7872 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7874 if ((action == ACTION_SMASHED_BY_ROCK ||
7875 action == ACTION_SMASHED_BY_SPRING ||
7876 action == ACTION_EATING) &&
7877 graphic_action == graphic_default)
7879 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7880 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7881 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7882 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7885 /* no separate animation for "smashed by rock" -- use rock instead */
7886 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7887 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7889 g_em->bitmap = g_xx->bitmap;
7890 g_em->src_x = g_xx->src_x;
7891 g_em->src_y = g_xx->src_y;
7892 g_em->src_offset_x = g_xx->src_offset_x;
7893 g_em->src_offset_y = g_xx->src_offset_y;
7894 g_em->dst_offset_x = g_xx->dst_offset_x;
7895 g_em->dst_offset_y = g_xx->dst_offset_y;
7896 g_em->width = g_xx->width;
7897 g_em->height = g_xx->height;
7898 g_em->unique_identifier = g_xx->unique_identifier;
7901 g_em->preserve_background = TRUE;
7906 for (p = 0; p < MAX_PLAYERS; p++)
7908 for (i = 0; i < SPR_MAX; i++)
7910 int element = player_mapping[p][i].element_rnd;
7911 int action = player_mapping[p][i].action;
7912 int direction = player_mapping[p][i].direction;
7914 for (j = 0; j < 8; j++)
7916 int effective_element = element;
7917 int effective_action = action;
7918 int graphic = (direction == MV_NONE ?
7919 el_act2img(effective_element, effective_action) :
7920 el_act_dir2img(effective_element, effective_action,
7922 struct GraphicInfo *g = &graphic_info[graphic];
7923 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7929 Bitmap *debug_bitmap = g_em->bitmap;
7930 int debug_src_x = g_em->src_x;
7931 int debug_src_y = g_em->src_y;
7934 int frame = getAnimationFrame(g->anim_frames,
7937 g->anim_start_frame,
7940 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7942 g_em->bitmap = src_bitmap;
7943 g_em->src_x = src_x;
7944 g_em->src_y = src_y;
7945 g_em->src_offset_x = 0;
7946 g_em->src_offset_y = 0;
7947 g_em->dst_offset_x = 0;
7948 g_em->dst_offset_y = 0;
7949 g_em->width = TILEX;
7950 g_em->height = TILEY;
7954 /* skip check for EMC elements not contained in original EMC artwork */
7955 if (element == EL_PLAYER_3 ||
7956 element == EL_PLAYER_4)
7959 if (g_em->bitmap != debug_bitmap ||
7960 g_em->src_x != debug_src_x ||
7961 g_em->src_y != debug_src_y)
7963 static int last_i = -1;
7971 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7972 p, i, element, element_info[element].token_name,
7973 element_action_info[effective_action].suffix, direction);
7975 if (element != effective_element)
7976 printf(" [%d ('%s')]",
7978 element_info[effective_element].token_name);
7982 if (g_em->bitmap != debug_bitmap)
7983 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7984 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7986 if (g_em->src_x != debug_src_x ||
7987 g_em->src_y != debug_src_y)
7988 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7990 g_em->src_x, g_em->src_y,
7991 g_em->src_x / 32, g_em->src_y / 32,
7992 debug_src_x, debug_src_y,
7993 debug_src_x / 32, debug_src_y / 32);
7995 num_em_gfx_errors++;
8005 printf("::: [%d errors found]\n", num_em_gfx_errors);
8011 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8012 boolean any_player_moving,
8013 boolean any_player_snapping,
8014 boolean any_player_dropping)
8016 static boolean player_was_waiting = TRUE;
8018 if (frame == 0 && !any_player_dropping)
8020 if (!player_was_waiting)
8022 if (!SaveEngineSnapshotToList())
8025 player_was_waiting = TRUE;
8028 else if (any_player_moving || any_player_snapping || any_player_dropping)
8030 player_was_waiting = FALSE;
8034 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8035 boolean murphy_is_dropping)
8037 static boolean player_was_waiting = TRUE;
8039 if (murphy_is_waiting)
8041 if (!player_was_waiting)
8043 if (!SaveEngineSnapshotToList())
8046 player_was_waiting = TRUE;
8051 player_was_waiting = FALSE;
8055 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8056 boolean any_player_moving,
8057 boolean any_player_snapping,
8058 boolean any_player_dropping)
8060 if (tape.single_step && tape.recording && !tape.pausing)
8061 if (frame == 0 && !any_player_dropping)
8062 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8064 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8065 any_player_snapping, any_player_dropping);
8068 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8069 boolean murphy_is_dropping)
8071 if (tape.single_step && tape.recording && !tape.pausing)
8072 if (murphy_is_waiting)
8073 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8075 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8078 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8079 int graphic, int sync_frame, int x, int y)
8081 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8083 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8086 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8088 return (IS_NEXT_FRAME(sync_frame, graphic));
8091 int getGraphicInfo_Delay(int graphic)
8093 return graphic_info[graphic].anim_delay;
8096 void PlayMenuSoundExt(int sound)
8098 if (sound == SND_UNDEFINED)
8101 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8102 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8105 if (IS_LOOP_SOUND(sound))
8106 PlaySoundLoop(sound);
8111 void PlayMenuSound()
8113 PlayMenuSoundExt(menu.sound[game_status]);
8116 void PlayMenuSoundStereo(int sound, int stereo_position)
8118 if (sound == SND_UNDEFINED)
8121 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8122 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8125 if (IS_LOOP_SOUND(sound))
8126 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8128 PlaySoundStereo(sound, stereo_position);
8131 void PlayMenuSoundIfLoopExt(int sound)
8133 if (sound == SND_UNDEFINED)
8136 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8137 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8140 if (IS_LOOP_SOUND(sound))
8141 PlaySoundLoop(sound);
8144 void PlayMenuSoundIfLoop()
8146 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8149 void PlayMenuMusicExt(int music)
8151 if (music == MUS_UNDEFINED)
8154 if (!setup.sound_music)
8160 void PlayMenuMusic()
8162 PlayMenuMusicExt(menu.music[game_status]);
8165 void PlaySoundActivating()
8168 PlaySound(SND_MENU_ITEM_ACTIVATING);
8172 void PlaySoundSelecting()
8175 PlaySound(SND_MENU_ITEM_SELECTING);
8179 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8181 boolean change_fullscreen = (setup.fullscreen !=
8182 video.fullscreen_enabled);
8183 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8184 !strEqual(setup.fullscreen_mode,
8185 video.fullscreen_mode_current));
8186 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8187 setup.window_scaling_percent !=
8188 video.window_scaling_percent);
8190 if (change_window_scaling_percent && video.fullscreen_enabled)
8193 if (!change_window_scaling_percent && !video.fullscreen_available)
8196 #if defined(TARGET_SDL2)
8197 if (change_window_scaling_percent)
8199 SDLSetWindowScaling(setup.window_scaling_percent);
8203 else if (change_fullscreen)
8205 SDLSetWindowFullscreen(setup.fullscreen);
8207 /* set setup value according to successfully changed fullscreen mode */
8208 setup.fullscreen = video.fullscreen_enabled;
8214 if (change_fullscreen ||
8215 change_fullscreen_mode ||
8216 change_window_scaling_percent)
8218 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8220 /* save backbuffer content which gets lost when toggling fullscreen mode */
8221 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8223 if (change_fullscreen_mode)
8225 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8226 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8229 if (change_window_scaling_percent)
8231 /* keep window mode, but change window scaling */
8232 video.fullscreen_enabled = TRUE; /* force new window scaling */
8235 /* toggle fullscreen */
8236 ChangeVideoModeIfNeeded(setup.fullscreen);
8238 /* set setup value according to successfully changed fullscreen mode */
8239 setup.fullscreen = video.fullscreen_enabled;
8241 /* restore backbuffer content from temporary backbuffer backup bitmap */
8242 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8244 FreeBitmap(tmp_backbuffer);
8246 /* update visible window/screen */
8247 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8251 void JoinRectangles(int *x, int *y, int *width, int *height,
8252 int x2, int y2, int width2, int height2)
8254 // do not join with "off-screen" rectangle
8255 if (x2 == -1 || y2 == -1)
8260 *width = MAX(*width, width2);
8261 *height = MAX(*height, height2);
8264 void SetGameStatus(int game_status_new)
8266 game_status = game_status_new;
8268 global.anim_status_next = game_status;
8271 void ChangeViewportPropertiesIfNeeded()
8273 int gfx_game_mode = game_status;
8274 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8276 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8277 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8278 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8279 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8280 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8281 int new_win_xsize = vp_window->width;
8282 int new_win_ysize = vp_window->height;
8283 int border_size = vp_playfield->border_size;
8284 int new_sx = vp_playfield->x + border_size;
8285 int new_sy = vp_playfield->y + border_size;
8286 int new_sxsize = vp_playfield->width - 2 * border_size;
8287 int new_sysize = vp_playfield->height - 2 * border_size;
8288 int new_real_sx = vp_playfield->x;
8289 int new_real_sy = vp_playfield->y;
8290 int new_full_sxsize = vp_playfield->width;
8291 int new_full_sysize = vp_playfield->height;
8292 int new_dx = vp_door_1->x;
8293 int new_dy = vp_door_1->y;
8294 int new_dxsize = vp_door_1->width;
8295 int new_dysize = vp_door_1->height;
8296 int new_vx = vp_door_2->x;
8297 int new_vy = vp_door_2->y;
8298 int new_vxsize = vp_door_2->width;
8299 int new_vysize = vp_door_2->height;
8300 int new_ex = vp_door_3->x;
8301 int new_ey = vp_door_3->y;
8302 int new_exsize = vp_door_3->width;
8303 int new_eysize = vp_door_3->height;
8304 int new_tilesize_var =
8305 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8307 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8308 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8309 int new_scr_fieldx = new_sxsize / tilesize;
8310 int new_scr_fieldy = new_sysize / tilesize;
8311 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8312 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8313 boolean init_gfx_buffers = FALSE;
8314 boolean init_video_buffer = FALSE;
8315 boolean init_gadgets_and_toons = FALSE;
8316 boolean init_em_graphics = FALSE;
8318 if (new_win_xsize != WIN_XSIZE ||
8319 new_win_ysize != WIN_YSIZE)
8321 WIN_XSIZE = new_win_xsize;
8322 WIN_YSIZE = new_win_ysize;
8324 init_video_buffer = TRUE;
8325 init_gfx_buffers = TRUE;
8326 init_gadgets_and_toons = TRUE;
8328 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8331 if (new_scr_fieldx != SCR_FIELDX ||
8332 new_scr_fieldy != SCR_FIELDY)
8334 /* this always toggles between MAIN and GAME when using small tile size */
8336 SCR_FIELDX = new_scr_fieldx;
8337 SCR_FIELDY = new_scr_fieldy;
8339 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8350 new_sxsize != SXSIZE ||
8351 new_sysize != SYSIZE ||
8352 new_dxsize != DXSIZE ||
8353 new_dysize != DYSIZE ||
8354 new_vxsize != VXSIZE ||
8355 new_vysize != VYSIZE ||
8356 new_exsize != EXSIZE ||
8357 new_eysize != EYSIZE ||
8358 new_real_sx != REAL_SX ||
8359 new_real_sy != REAL_SY ||
8360 new_full_sxsize != FULL_SXSIZE ||
8361 new_full_sysize != FULL_SYSIZE ||
8362 new_tilesize_var != TILESIZE_VAR
8365 // ------------------------------------------------------------------------
8366 // determine next fading area for changed viewport definitions
8367 // ------------------------------------------------------------------------
8369 // start with current playfield area (default fading area)
8372 FADE_SXSIZE = FULL_SXSIZE;
8373 FADE_SYSIZE = FULL_SYSIZE;
8375 // add new playfield area if position or size has changed
8376 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8377 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8379 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8380 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8383 // add current and new door 1 area if position or size has changed
8384 if (new_dx != DX || new_dy != DY ||
8385 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8387 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8388 DX, DY, DXSIZE, DYSIZE);
8389 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8390 new_dx, new_dy, new_dxsize, new_dysize);
8393 // add current and new door 2 area if position or size has changed
8394 if (new_dx != VX || new_dy != VY ||
8395 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8397 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8398 VX, VY, VXSIZE, VYSIZE);
8399 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8400 new_vx, new_vy, new_vxsize, new_vysize);
8403 // ------------------------------------------------------------------------
8404 // handle changed tile size
8405 // ------------------------------------------------------------------------
8407 if (new_tilesize_var != TILESIZE_VAR)
8409 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8411 // changing tile size invalidates scroll values of engine snapshots
8412 FreeEngineSnapshotSingle();
8414 // changing tile size requires update of graphic mapping for EM engine
8415 init_em_graphics = TRUE;
8426 SXSIZE = new_sxsize;
8427 SYSIZE = new_sysize;
8428 DXSIZE = new_dxsize;
8429 DYSIZE = new_dysize;
8430 VXSIZE = new_vxsize;
8431 VYSIZE = new_vysize;
8432 EXSIZE = new_exsize;
8433 EYSIZE = new_eysize;
8434 REAL_SX = new_real_sx;
8435 REAL_SY = new_real_sy;
8436 FULL_SXSIZE = new_full_sxsize;
8437 FULL_SYSIZE = new_full_sysize;
8438 TILESIZE_VAR = new_tilesize_var;
8440 init_gfx_buffers = TRUE;
8441 init_gadgets_and_toons = TRUE;
8443 // printf("::: viewports: init_gfx_buffers\n");
8444 // printf("::: viewports: init_gadgets_and_toons\n");
8447 if (init_gfx_buffers)
8449 // printf("::: init_gfx_buffers\n");
8451 SCR_FIELDX = new_scr_fieldx_buffers;
8452 SCR_FIELDY = new_scr_fieldy_buffers;
8456 SCR_FIELDX = new_scr_fieldx;
8457 SCR_FIELDY = new_scr_fieldy;
8459 SetDrawDeactivationMask(REDRAW_NONE);
8460 SetDrawBackgroundMask(REDRAW_FIELD);
8463 if (init_video_buffer)
8465 // printf("::: init_video_buffer\n");
8467 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8468 InitImageTextures();
8471 if (init_gadgets_and_toons)
8473 // printf("::: init_gadgets_and_toons\n");
8477 InitGlobalAnimations();
8480 if (init_em_graphics)
8482 InitGraphicInfo_EM();