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,
292 boolean blit_to_screen)
294 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
296 if (x == -1 && y == -1)
300 BlitToScreenMasked(bitmap, x, y, width, height, x, y);
302 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
305 static void DrawMaskedBorderExt_FIELD(boolean blit_to_screen)
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(boolean blit_to_screen)
316 // only draw border over closed doors when drawing to backbuffer
317 if (!blit_to_screen && (GetDoorState() & DOOR_OPEN_1))
320 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
321 (global.border_status != GAME_MODE_EDITOR ||
322 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
323 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, blit_to_screen);
326 static void DrawMaskedBorderExt_DOOR_2(boolean blit_to_screen)
328 // only draw border over closed doors when drawing to backbuffer
329 if (!blit_to_screen && (GetDoorState() & DOOR_OPEN_2))
332 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
333 global.border_status != GAME_MODE_EDITOR)
334 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, blit_to_screen);
337 static void DrawMaskedBorderExt_DOOR_3(boolean blit_to_screen)
339 /* currently not available */
342 static void DrawMaskedBorderExt_ALL(boolean blit_to_screen)
344 DrawMaskedBorderExt_FIELD(blit_to_screen);
345 DrawMaskedBorderExt_DOOR_1(blit_to_screen);
346 DrawMaskedBorderExt_DOOR_2(blit_to_screen);
347 DrawMaskedBorderExt_DOOR_3(blit_to_screen);
350 static void DrawMaskedBorderExt(int redraw_mask, boolean blit_to_screen)
352 /* never draw masked screen borders on borderless screens */
353 if (game_status == GAME_MODE_LOADING ||
354 game_status == GAME_MODE_TITLE)
357 if (redraw_mask & REDRAW_ALL)
358 DrawMaskedBorderExt_ALL(blit_to_screen);
361 if (redraw_mask & REDRAW_FIELD)
362 DrawMaskedBorderExt_FIELD(blit_to_screen);
363 if (redraw_mask & REDRAW_DOOR_1)
364 DrawMaskedBorderExt_DOOR_1(blit_to_screen);
365 if (redraw_mask & REDRAW_DOOR_2)
366 DrawMaskedBorderExt_DOOR_2(blit_to_screen);
367 if (redraw_mask & REDRAW_DOOR_3)
368 DrawMaskedBorderExt_DOOR_3(blit_to_screen);
372 void DrawMaskedBorder_FIELD()
374 DrawMaskedBorderExt_FIELD(FALSE);
377 void DrawMaskedBorder(int redraw_mask)
379 DrawMaskedBorderExt(redraw_mask, FALSE);
382 void DrawMaskedBorderToScreen(int redraw_mask)
384 DrawMaskedBorderExt(redraw_mask, TRUE);
387 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
389 int fx = FX, fy = FY;
390 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
391 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
393 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
394 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
395 int dx_var = dx * TILESIZE_VAR / TILESIZE;
396 int dy_var = dy * TILESIZE_VAR / TILESIZE;
399 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
400 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
402 if (EVEN(SCR_FIELDX))
404 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
405 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
407 fx += (dx_var > 0 ? TILEX_VAR : 0);
414 if (EVEN(SCR_FIELDY))
416 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
417 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
419 fy += (dy_var > 0 ? TILEY_VAR : 0);
426 if (full_lev_fieldx <= SCR_FIELDX)
428 if (EVEN(SCR_FIELDX))
429 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
431 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
434 if (full_lev_fieldy <= SCR_FIELDY)
436 if (EVEN(SCR_FIELDY))
437 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
439 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
442 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
445 void BlitScreenToBitmap(Bitmap *target_bitmap)
447 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
448 BlitScreenToBitmap_EM(target_bitmap);
449 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
450 BlitScreenToBitmap_SP(target_bitmap);
451 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
452 BlitScreenToBitmap_RND(target_bitmap);
454 redraw_mask |= REDRAW_FIELD;
457 void DrawFramesPerSecond()
460 int font_nr = FONT_TEXT_2;
461 int font_width = getFontWidth(font_nr);
463 sprintf(text, "%04.1f fps", global.frames_per_second);
465 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
466 font_nr, BLIT_OPAQUE);
470 static void PrintFrameTimeDebugging()
472 static unsigned int last_counter = 0;
473 unsigned int counter = Counter();
474 int diff_1 = counter - last_counter;
475 int diff_2 = diff_1 - GAME_FRAME_DELAY;
477 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
478 char diff_bar[2 * diff_2_max + 5];
482 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
484 for (i = 0; i < diff_2_max; i++)
485 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
486 i >= diff_2_max - diff_2_cut ? '-' : ' ');
488 diff_bar[pos++] = '|';
490 for (i = 0; i < diff_2_max; i++)
491 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
493 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
495 diff_bar[pos++] = '\0';
497 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
500 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
503 last_counter = counter;
509 if (redraw_mask == REDRAW_NONE)
513 // masked border now drawn immediately when blitting backbuffer to window
515 // draw masked border to all viewports, if defined
516 DrawMaskedBorder(redraw_mask);
519 // draw frames per second (only if debug mode is enabled)
520 if (redraw_mask & REDRAW_FPS)
521 DrawFramesPerSecond();
523 // redraw complete window if both playfield and (some) doors need redraw
524 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
525 redraw_mask = REDRAW_ALL;
527 /* although redrawing the whole window would be fine for normal gameplay,
528 being able to only redraw the playfield is required for deactivating
529 certain drawing areas (mainly playfield) to work, which is needed for
530 warp-forward to be fast enough (by skipping redraw of most frames) */
532 if (redraw_mask & REDRAW_ALL)
534 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
536 else if (redraw_mask & REDRAW_FIELD)
538 BlitBitmap(backbuffer, window,
539 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
541 else if (redraw_mask & REDRAW_DOORS)
543 if (redraw_mask & REDRAW_DOOR_1)
544 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
546 if (redraw_mask & REDRAW_DOOR_2)
547 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
549 if (redraw_mask & REDRAW_DOOR_3)
550 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
553 redraw_mask = REDRAW_NONE;
555 // force screen redraw in every frame to continue drawing global animations
556 redraw_mask = REDRAW_FIELD;
559 PrintFrameTimeDebugging();
563 static void FadeCrossSaveBackbuffer()
565 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
568 static void FadeCrossRestoreBackbuffer()
570 int redraw_mask_last = redraw_mask;
572 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
574 // do not change redraw mask when restoring backbuffer after cross-fading
575 redraw_mask = redraw_mask_last;
578 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
580 static int fade_type_skip = FADE_TYPE_NONE;
581 void (*draw_border_function)(void) = NULL;
582 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
583 int x, y, width, height;
584 int fade_delay, post_delay;
586 if (fade_type == FADE_TYPE_FADE_OUT)
588 if (fade_type_skip != FADE_TYPE_NONE)
590 /* skip all fade operations until specified fade operation */
591 if (fade_type & fade_type_skip)
592 fade_type_skip = FADE_TYPE_NONE;
598 FadeCrossSaveBackbuffer();
601 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
604 FadeCrossSaveBackbuffer();
611 redraw_mask |= fade_mask;
613 if (fade_type == FADE_TYPE_SKIP)
615 fade_type_skip = fade_mode;
620 fade_delay = fading.fade_delay;
621 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
623 if (fade_type_skip != FADE_TYPE_NONE)
625 /* skip all fade operations until specified fade operation */
626 if (fade_type & fade_type_skip)
627 fade_type_skip = FADE_TYPE_NONE;
632 if (global.autoplay_leveldir)
637 if (fade_mask == REDRAW_FIELD)
642 height = FADE_SYSIZE;
644 if (border.draw_masked_when_fading)
645 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
647 DrawMaskedBorder_FIELD(); /* draw once */
649 else /* REDRAW_ALL */
657 if (!setup.fade_screens ||
659 fading.fade_mode == FADE_MODE_NONE)
661 if (fade_mode == FADE_MODE_FADE_OUT)
664 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
666 redraw_mask &= ~fade_mask;
671 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
672 draw_border_function);
674 if (fade_type == FADE_TYPE_FADE_OUT)
675 FadeCrossRestoreBackbuffer();
677 redraw_mask &= ~fade_mask;
680 static void SetAnimStatus_BeforeFadingOut()
682 global.anim_status = GAME_MODE_PSEUDO_FADING;
685 static void SetAnimStatus_AfterFadingIn()
687 global.anim_status = global.anim_status_next;
689 // force update of global animation status in case of rapid screen changes
690 redraw_mask = REDRAW_ALL;
694 void FadeIn(int fade_mask)
697 DrawMaskedBorder(REDRAW_ALL);
700 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
701 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
703 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
707 FADE_SXSIZE = FULL_SXSIZE;
708 FADE_SYSIZE = FULL_SYSIZE;
710 SetAnimStatus_AfterFadingIn();
713 void FadeOut(int fade_mask)
715 SetAnimStatus_BeforeFadingOut();
718 DrawMaskedBorder(REDRAW_ALL);
721 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
722 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
724 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
726 global.border_status = game_status;
729 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
731 static struct TitleFadingInfo fading_leave_stored;
734 fading_leave_stored = fading_leave;
736 fading = fading_leave_stored;
739 void FadeSetEnterMenu()
741 fading = menu.enter_menu;
743 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
746 void FadeSetLeaveMenu()
748 fading = menu.leave_menu;
750 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
753 void FadeSetEnterScreen()
755 fading = menu.enter_screen[game_status];
757 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
760 void FadeSetNextScreen()
762 fading = menu.next_screen[game_status];
764 // (do not overwrite fade mode set by FadeSetEnterScreen)
765 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
768 void FadeSetLeaveScreen()
770 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
773 void FadeSetFromType(int type)
775 if (type & TYPE_ENTER_SCREEN)
776 FadeSetEnterScreen();
777 else if (type & TYPE_ENTER)
779 else if (type & TYPE_LEAVE)
783 void FadeSetDisabled()
785 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
787 fading = fading_none;
790 void FadeSkipNextFadeIn()
792 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
795 void FadeSkipNextFadeOut()
797 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
800 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
802 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
804 return (graphic == IMG_UNDEFINED ? NULL :
805 graphic_info[graphic].bitmap != NULL || redefined ?
806 graphic_info[graphic].bitmap :
807 graphic_info[default_graphic].bitmap);
810 Bitmap *getBackgroundBitmap(int graphic)
812 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
815 Bitmap *getGlobalBorderBitmap(int graphic)
817 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
820 Bitmap *getGlobalBorderBitmapFromGameStatus()
823 (game_status == GAME_MODE_MAIN ||
824 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
825 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
826 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
827 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
830 return getGlobalBorderBitmap(graphic);
833 void SetWindowBackgroundImageIfDefined(int graphic)
835 if (graphic_info[graphic].bitmap)
836 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
839 void SetMainBackgroundImageIfDefined(int graphic)
841 if (graphic_info[graphic].bitmap)
842 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
845 void SetDoorBackgroundImageIfDefined(int graphic)
847 if (graphic_info[graphic].bitmap)
848 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
851 void SetWindowBackgroundImage(int graphic)
853 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
856 void SetMainBackgroundImage(int graphic)
858 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
861 void SetDoorBackgroundImage(int graphic)
863 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
866 void SetPanelBackground()
868 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
870 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
871 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
873 SetDoorBackgroundBitmap(bitmap_db_panel);
876 void DrawBackground(int x, int y, int width, int height)
878 /* "drawto" might still point to playfield buffer here (hall of fame) */
879 ClearRectangleOnBackground(backbuffer, x, y, width, height);
881 if (IN_GFX_FIELD_FULL(x, y))
882 redraw_mask |= REDRAW_FIELD;
883 else if (IN_GFX_DOOR_1(x, y))
884 redraw_mask |= REDRAW_DOOR_1;
885 else if (IN_GFX_DOOR_2(x, y))
886 redraw_mask |= REDRAW_DOOR_2;
887 else if (IN_GFX_DOOR_3(x, y))
888 redraw_mask |= REDRAW_DOOR_3;
891 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
893 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
895 if (font->bitmap == NULL)
898 DrawBackground(x, y, width, height);
901 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
903 struct GraphicInfo *g = &graphic_info[graphic];
905 if (g->bitmap == NULL)
908 DrawBackground(x, y, width, height);
911 static int game_status_last = -1;
912 static Bitmap *global_border_bitmap_last = NULL;
913 static Bitmap *global_border_bitmap = NULL;
914 static int real_sx_last = -1, real_sy_last = -1;
915 static int full_sxsize_last = -1, full_sysize_last = -1;
916 static int dx_last = -1, dy_last = -1;
917 static int dxsize_last = -1, dysize_last = -1;
918 static int vx_last = -1, vy_last = -1;
919 static int vxsize_last = -1, vysize_last = -1;
921 boolean CheckIfGlobalBorderHasChanged()
923 // if game status has not changed, global border has not changed either
924 if (game_status == game_status_last)
927 // determine and store new global border bitmap for current game status
928 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
930 return (global_border_bitmap_last != global_border_bitmap);
933 boolean CheckIfGlobalBorderRedrawIsNeeded()
935 // if game status has not changed, nothing has to be redrawn
936 if (game_status == game_status_last)
939 // redraw if last screen was title screen
940 if (game_status_last == GAME_MODE_TITLE)
943 // redraw if global screen border has changed
944 if (CheckIfGlobalBorderHasChanged())
947 // redraw if position or size of playfield area has changed
948 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
949 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
952 // redraw if position or size of door area has changed
953 if (dx_last != DX || dy_last != DY ||
954 dxsize_last != DXSIZE || dysize_last != DYSIZE)
957 // redraw if position or size of tape area has changed
958 if (vx_last != VX || vy_last != VY ||
959 vxsize_last != VXSIZE || vysize_last != VYSIZE)
965 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
968 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
970 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
973 void RedrawGlobalBorder()
975 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
977 RedrawGlobalBorderFromBitmap(bitmap);
979 redraw_mask = REDRAW_ALL;
982 static void RedrawGlobalBorderIfNeeded()
984 if (game_status == game_status_last)
987 // copy current draw buffer to later copy back areas that have not changed
988 if (game_status_last != GAME_MODE_TITLE)
989 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
991 if (CheckIfGlobalBorderRedrawIsNeeded())
993 // redraw global screen border (or clear, if defined to be empty)
994 RedrawGlobalBorderFromBitmap(global_border_bitmap);
996 // copy previous playfield and door areas, if they are defined on both
997 // previous and current screen and if they still have the same size
999 if (real_sx_last != -1 && real_sy_last != -1 &&
1000 REAL_SX != -1 && REAL_SY != -1 &&
1001 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1002 BlitBitmap(bitmap_db_store, backbuffer,
1003 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1006 if (dx_last != -1 && dy_last != -1 &&
1007 DX != -1 && DY != -1 &&
1008 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1009 BlitBitmap(bitmap_db_store, backbuffer,
1010 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1012 if (vx_last != -1 && vy_last != -1 &&
1013 VX != -1 && VY != -1 &&
1014 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1015 BlitBitmap(bitmap_db_store, backbuffer,
1016 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1018 redraw_mask = REDRAW_ALL;
1021 game_status_last = game_status;
1023 global_border_bitmap_last = global_border_bitmap;
1025 real_sx_last = REAL_SX;
1026 real_sy_last = REAL_SY;
1027 full_sxsize_last = FULL_SXSIZE;
1028 full_sysize_last = FULL_SYSIZE;
1031 dxsize_last = DXSIZE;
1032 dysize_last = DYSIZE;
1035 vxsize_last = VXSIZE;
1036 vysize_last = VYSIZE;
1041 RedrawGlobalBorderIfNeeded();
1043 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1044 /* (when entering hall of fame after playing) */
1045 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1047 /* !!! maybe this should be done before clearing the background !!! */
1048 if (game_status == GAME_MODE_PLAYING)
1050 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1051 SetDrawtoField(DRAW_FIELDBUFFER);
1055 SetDrawtoField(DRAW_BACKBUFFER);
1059 void MarkTileDirty(int x, int y)
1061 redraw_mask |= REDRAW_FIELD;
1064 void SetBorderElement()
1068 BorderElement = EL_EMPTY;
1070 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1072 for (x = 0; x < lev_fieldx; x++)
1074 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1075 BorderElement = EL_STEELWALL;
1077 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1083 void FloodFillLevel(int from_x, int from_y, int fill_element,
1084 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1085 int max_fieldx, int max_fieldy)
1089 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1090 static int safety = 0;
1092 /* check if starting field still has the desired content */
1093 if (field[from_x][from_y] == fill_element)
1098 if (safety > max_fieldx * max_fieldy)
1099 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1101 old_element = field[from_x][from_y];
1102 field[from_x][from_y] = fill_element;
1104 for (i = 0; i < 4; i++)
1106 x = from_x + check[i][0];
1107 y = from_y + check[i][1];
1109 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1110 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1116 void SetRandomAnimationValue(int x, int y)
1118 gfx.anim_random_frame = GfxRandom[x][y];
1121 int getGraphicAnimationFrame(int graphic, int sync_frame)
1123 /* animation synchronized with global frame counter, not move position */
1124 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1125 sync_frame = FrameCounter;
1127 return getAnimationFrame(graphic_info[graphic].anim_frames,
1128 graphic_info[graphic].anim_delay,
1129 graphic_info[graphic].anim_mode,
1130 graphic_info[graphic].anim_start_frame,
1134 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1135 Bitmap **bitmap, int *x, int *y,
1136 boolean get_backside)
1138 struct GraphicInfo *g = &graphic_info[graphic];
1139 Bitmap *src_bitmap = g->bitmap;
1140 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1141 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1142 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1144 // if no in-game graphics defined, always use standard graphic size
1145 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1146 tilesize = TILESIZE;
1148 if (tilesize == gfx.standard_tile_size)
1149 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1150 else if (tilesize == game.tile_size)
1151 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1153 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1155 if (g->offset_y == 0) /* frames are ordered horizontally */
1157 int max_width = g->anim_frames_per_line * g->width;
1158 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1160 src_x = pos % max_width;
1161 src_y = src_y % g->height + pos / max_width * g->height;
1163 else if (g->offset_x == 0) /* frames are ordered vertically */
1165 int max_height = g->anim_frames_per_line * g->height;
1166 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1168 src_x = src_x % g->width + pos / max_height * g->width;
1169 src_y = pos % max_height;
1171 else /* frames are ordered diagonally */
1173 src_x = src_x + frame * g->offset_x;
1174 src_y = src_y + frame * g->offset_y;
1177 *bitmap = src_bitmap;
1178 *x = src_x * tilesize / g->tile_size;
1179 *y = src_y * tilesize / g->tile_size;
1182 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1183 int *x, int *y, boolean get_backside)
1185 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1189 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1190 Bitmap **bitmap, int *x, int *y)
1192 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1195 void getFixedGraphicSource(int graphic, int frame,
1196 Bitmap **bitmap, int *x, int *y)
1198 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1201 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1203 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1206 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1207 int *x, int *y, boolean get_backside)
1209 struct GraphicInfo *g = &graphic_info[graphic];
1210 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1211 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1213 if (TILESIZE_VAR != TILESIZE)
1214 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1217 *bitmap = g->bitmap;
1219 if (g->offset_y == 0) /* frames are ordered horizontally */
1221 int max_width = g->anim_frames_per_line * g->width;
1222 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1224 *x = pos % max_width;
1225 *y = src_y % g->height + pos / max_width * g->height;
1227 else if (g->offset_x == 0) /* frames are ordered vertically */
1229 int max_height = g->anim_frames_per_line * g->height;
1230 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1232 *x = src_x % g->width + pos / max_height * g->width;
1233 *y = pos % max_height;
1235 else /* frames are ordered diagonally */
1237 *x = src_x + frame * g->offset_x;
1238 *y = src_y + frame * g->offset_y;
1241 *x = *x * TILESIZE_VAR / g->tile_size;
1242 *y = *y * TILESIZE_VAR / g->tile_size;
1245 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1247 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1250 void DrawGraphic(int x, int y, int graphic, int frame)
1253 if (!IN_SCR_FIELD(x, y))
1255 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1256 printf("DrawGraphic(): This should never happen!\n");
1261 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1264 MarkTileDirty(x, y);
1267 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1270 if (!IN_SCR_FIELD(x, y))
1272 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1273 printf("DrawGraphic(): This should never happen!\n");
1278 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1280 MarkTileDirty(x, y);
1283 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1289 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1291 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1294 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1300 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1301 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1304 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1307 if (!IN_SCR_FIELD(x, y))
1309 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1310 printf("DrawGraphicThruMask(): This should never happen!\n");
1315 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1318 MarkTileDirty(x, y);
1321 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1324 if (!IN_SCR_FIELD(x, y))
1326 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1327 printf("DrawGraphicThruMask(): This should never happen!\n");
1332 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1334 MarkTileDirty(x, y);
1337 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1343 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1345 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1349 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1350 int graphic, int frame)
1355 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1357 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1361 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1363 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1365 MarkTileDirty(x / tilesize, y / tilesize);
1368 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1374 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1375 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1378 void DrawMiniGraphic(int x, int y, int graphic)
1380 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1381 MarkTileDirty(x / 2, y / 2);
1384 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1389 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1390 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1393 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1394 int graphic, int frame,
1395 int cut_mode, int mask_mode)
1400 int width = TILEX, height = TILEY;
1403 if (dx || dy) /* shifted graphic */
1405 if (x < BX1) /* object enters playfield from the left */
1412 else if (x > BX2) /* object enters playfield from the right */
1418 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1424 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1426 else if (dx) /* general horizontal movement */
1427 MarkTileDirty(x + SIGN(dx), y);
1429 if (y < BY1) /* object enters playfield from the top */
1431 if (cut_mode == CUT_BELOW) /* object completely above top border */
1439 else if (y > BY2) /* object enters playfield from the bottom */
1445 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1451 else if (dy > 0 && cut_mode == CUT_ABOVE)
1453 if (y == BY2) /* object completely above bottom border */
1459 MarkTileDirty(x, y + 1);
1460 } /* object leaves playfield to the bottom */
1461 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1463 else if (dy) /* general vertical movement */
1464 MarkTileDirty(x, y + SIGN(dy));
1468 if (!IN_SCR_FIELD(x, y))
1470 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1471 printf("DrawGraphicShifted(): This should never happen!\n");
1476 width = width * TILESIZE_VAR / TILESIZE;
1477 height = height * TILESIZE_VAR / TILESIZE;
1478 cx = cx * TILESIZE_VAR / TILESIZE;
1479 cy = cy * TILESIZE_VAR / TILESIZE;
1480 dx = dx * TILESIZE_VAR / TILESIZE;
1481 dy = dy * TILESIZE_VAR / TILESIZE;
1483 if (width > 0 && height > 0)
1485 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1490 dst_x = FX + x * TILEX_VAR + dx;
1491 dst_y = FY + y * TILEY_VAR + dy;
1493 if (mask_mode == USE_MASKING)
1494 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1497 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1500 MarkTileDirty(x, y);
1504 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1505 int graphic, int frame,
1506 int cut_mode, int mask_mode)
1511 int width = TILEX_VAR, height = TILEY_VAR;
1514 int x2 = x + SIGN(dx);
1515 int y2 = y + SIGN(dy);
1517 /* movement with two-tile animations must be sync'ed with movement position,
1518 not with current GfxFrame (which can be higher when using slow movement) */
1519 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1520 int anim_frames = graphic_info[graphic].anim_frames;
1522 /* (we also need anim_delay here for movement animations with less frames) */
1523 int anim_delay = graphic_info[graphic].anim_delay;
1524 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1526 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1527 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1529 /* re-calculate animation frame for two-tile movement animation */
1530 frame = getGraphicAnimationFrame(graphic, sync_frame);
1532 /* check if movement start graphic inside screen area and should be drawn */
1533 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1535 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1537 dst_x = FX + x1 * TILEX_VAR;
1538 dst_y = FY + y1 * TILEY_VAR;
1540 if (mask_mode == USE_MASKING)
1541 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1544 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1547 MarkTileDirty(x1, y1);
1550 /* check if movement end graphic inside screen area and should be drawn */
1551 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1553 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1555 dst_x = FX + x2 * TILEX_VAR;
1556 dst_y = FY + y2 * TILEY_VAR;
1558 if (mask_mode == USE_MASKING)
1559 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1562 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1565 MarkTileDirty(x2, y2);
1569 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1570 int graphic, int frame,
1571 int cut_mode, int mask_mode)
1575 DrawGraphic(x, y, graphic, frame);
1580 if (graphic_info[graphic].double_movement) /* EM style movement images */
1581 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1583 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1586 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1587 int frame, int cut_mode)
1589 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1592 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1593 int cut_mode, int mask_mode)
1595 int lx = LEVELX(x), ly = LEVELY(y);
1599 if (IN_LEV_FIELD(lx, ly))
1601 SetRandomAnimationValue(lx, ly);
1603 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1604 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1606 /* do not use double (EM style) movement graphic when not moving */
1607 if (graphic_info[graphic].double_movement && !dx && !dy)
1609 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1610 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1613 else /* border element */
1615 graphic = el2img(element);
1616 frame = getGraphicAnimationFrame(graphic, -1);
1619 if (element == EL_EXPANDABLE_WALL)
1621 boolean left_stopped = FALSE, right_stopped = FALSE;
1623 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1624 left_stopped = TRUE;
1625 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1626 right_stopped = TRUE;
1628 if (left_stopped && right_stopped)
1630 else if (left_stopped)
1632 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1633 frame = graphic_info[graphic].anim_frames - 1;
1635 else if (right_stopped)
1637 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1638 frame = graphic_info[graphic].anim_frames - 1;
1643 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1644 else if (mask_mode == USE_MASKING)
1645 DrawGraphicThruMask(x, y, graphic, frame);
1647 DrawGraphic(x, y, graphic, frame);
1650 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1651 int cut_mode, int mask_mode)
1653 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1654 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1655 cut_mode, mask_mode);
1658 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1661 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1664 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1667 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1670 void DrawLevelElementThruMask(int x, int y, int element)
1672 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1675 void DrawLevelFieldThruMask(int x, int y)
1677 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1680 /* !!! implementation of quicksand is totally broken !!! */
1681 #define IS_CRUMBLED_TILE(x, y, e) \
1682 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1683 !IS_MOVING(x, y) || \
1684 (e) == EL_QUICKSAND_EMPTYING || \
1685 (e) == EL_QUICKSAND_FAST_EMPTYING))
1687 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1692 int width, height, cx, cy;
1693 int sx = SCREENX(x), sy = SCREENY(y);
1694 int crumbled_border_size = graphic_info[graphic].border_size;
1697 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1699 for (i = 1; i < 4; i++)
1701 int dxx = (i & 1 ? dx : 0);
1702 int dyy = (i & 2 ? dy : 0);
1705 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1708 /* check if neighbour field is of same crumble type */
1709 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1710 graphic_info[graphic].class ==
1711 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1713 /* return if check prevents inner corner */
1714 if (same == (dxx == dx && dyy == dy))
1718 /* if we reach this point, we have an inner corner */
1720 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1722 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1723 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1724 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1725 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1727 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1728 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1731 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1736 int width, height, bx, by, cx, cy;
1737 int sx = SCREENX(x), sy = SCREENY(y);
1738 int crumbled_border_size = graphic_info[graphic].border_size;
1739 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1740 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1743 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1745 /* draw simple, sloppy, non-corner-accurate crumbled border */
1747 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1748 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1749 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1750 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1752 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1753 FX + sx * TILEX_VAR + cx,
1754 FY + sy * TILEY_VAR + cy);
1756 /* (remaining middle border part must be at least as big as corner part) */
1757 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1758 crumbled_border_size >= TILESIZE / 3)
1761 /* correct corners of crumbled border, if needed */
1763 for (i = -1; i <= 1; i += 2)
1765 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1766 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1767 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1770 /* check if neighbour field is of same crumble type */
1771 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1772 graphic_info[graphic].class ==
1773 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1775 /* no crumbled corner, but continued crumbled border */
1777 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1778 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1779 int b1 = (i == 1 ? crumbled_border_size_var :
1780 TILESIZE_VAR - 2 * crumbled_border_size_var);
1782 width = crumbled_border_size_var;
1783 height = crumbled_border_size_var;
1785 if (dir == 1 || dir == 2)
1800 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1802 FX + sx * TILEX_VAR + cx,
1803 FY + sy * TILEY_VAR + cy);
1808 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1810 int sx = SCREENX(x), sy = SCREENY(y);
1813 static int xy[4][2] =
1821 if (!IN_LEV_FIELD(x, y))
1824 element = TILE_GFX_ELEMENT(x, y);
1826 /* crumble field itself */
1827 if (IS_CRUMBLED_TILE(x, y, element))
1829 if (!IN_SCR_FIELD(sx, sy))
1832 for (i = 0; i < 4; i++)
1834 int xx = x + xy[i][0];
1835 int yy = y + xy[i][1];
1837 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1840 /* check if neighbour field is of same crumble type */
1841 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1842 graphic_info[graphic].class ==
1843 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1846 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1849 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1850 graphic_info[graphic].anim_frames == 2)
1852 for (i = 0; i < 4; i++)
1854 int dx = (i & 1 ? +1 : -1);
1855 int dy = (i & 2 ? +1 : -1);
1857 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1861 MarkTileDirty(sx, sy);
1863 else /* center field not crumbled -- crumble neighbour fields */
1865 for (i = 0; i < 4; i++)
1867 int xx = x + xy[i][0];
1868 int yy = y + xy[i][1];
1869 int sxx = sx + xy[i][0];
1870 int syy = sy + xy[i][1];
1872 if (!IN_LEV_FIELD(xx, yy) ||
1873 !IN_SCR_FIELD(sxx, syy))
1876 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1879 element = TILE_GFX_ELEMENT(xx, yy);
1881 if (!IS_CRUMBLED_TILE(xx, yy, element))
1884 graphic = el_act2crm(element, ACTION_DEFAULT);
1886 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1888 MarkTileDirty(sxx, syy);
1893 void DrawLevelFieldCrumbled(int x, int y)
1897 if (!IN_LEV_FIELD(x, y))
1900 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1901 GfxElement[x][y] != EL_UNDEFINED &&
1902 GFX_CRUMBLED(GfxElement[x][y]))
1904 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1909 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1911 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1914 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1917 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1918 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1919 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1920 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1921 int sx = SCREENX(x), sy = SCREENY(y);
1923 DrawGraphic(sx, sy, graphic1, frame1);
1924 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1927 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1929 int sx = SCREENX(x), sy = SCREENY(y);
1930 static int xy[4][2] =
1939 for (i = 0; i < 4; i++)
1941 int xx = x + xy[i][0];
1942 int yy = y + xy[i][1];
1943 int sxx = sx + xy[i][0];
1944 int syy = sy + xy[i][1];
1946 if (!IN_LEV_FIELD(xx, yy) ||
1947 !IN_SCR_FIELD(sxx, syy) ||
1948 !GFX_CRUMBLED(Feld[xx][yy]) ||
1952 DrawLevelField(xx, yy);
1956 static int getBorderElement(int x, int y)
1960 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1961 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1962 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1963 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1964 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1965 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1966 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1968 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1969 int steel_position = (x == -1 && y == -1 ? 0 :
1970 x == lev_fieldx && y == -1 ? 1 :
1971 x == -1 && y == lev_fieldy ? 2 :
1972 x == lev_fieldx && y == lev_fieldy ? 3 :
1973 x == -1 || x == lev_fieldx ? 4 :
1974 y == -1 || y == lev_fieldy ? 5 : 6);
1976 return border[steel_position][steel_type];
1979 void DrawScreenElement(int x, int y, int element)
1981 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1982 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1985 void DrawLevelElement(int x, int y, int element)
1987 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1988 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1991 void DrawScreenField(int x, int y)
1993 int lx = LEVELX(x), ly = LEVELY(y);
1994 int element, content;
1996 if (!IN_LEV_FIELD(lx, ly))
1998 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2001 element = getBorderElement(lx, ly);
2003 DrawScreenElement(x, y, element);
2008 element = Feld[lx][ly];
2009 content = Store[lx][ly];
2011 if (IS_MOVING(lx, ly))
2013 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2014 boolean cut_mode = NO_CUTTING;
2016 if (element == EL_QUICKSAND_EMPTYING ||
2017 element == EL_QUICKSAND_FAST_EMPTYING ||
2018 element == EL_MAGIC_WALL_EMPTYING ||
2019 element == EL_BD_MAGIC_WALL_EMPTYING ||
2020 element == EL_DC_MAGIC_WALL_EMPTYING ||
2021 element == EL_AMOEBA_DROPPING)
2022 cut_mode = CUT_ABOVE;
2023 else if (element == EL_QUICKSAND_FILLING ||
2024 element == EL_QUICKSAND_FAST_FILLING ||
2025 element == EL_MAGIC_WALL_FILLING ||
2026 element == EL_BD_MAGIC_WALL_FILLING ||
2027 element == EL_DC_MAGIC_WALL_FILLING)
2028 cut_mode = CUT_BELOW;
2030 if (cut_mode == CUT_ABOVE)
2031 DrawScreenElement(x, y, element);
2033 DrawScreenElement(x, y, EL_EMPTY);
2036 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2037 else if (cut_mode == NO_CUTTING)
2038 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2041 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2043 if (cut_mode == CUT_BELOW &&
2044 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2045 DrawLevelElement(lx, ly + 1, element);
2048 if (content == EL_ACID)
2050 int dir = MovDir[lx][ly];
2051 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2052 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2054 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2057 else if (IS_BLOCKED(lx, ly))
2062 boolean cut_mode = NO_CUTTING;
2063 int element_old, content_old;
2065 Blocked2Moving(lx, ly, &oldx, &oldy);
2068 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2069 MovDir[oldx][oldy] == MV_RIGHT);
2071 element_old = Feld[oldx][oldy];
2072 content_old = Store[oldx][oldy];
2074 if (element_old == EL_QUICKSAND_EMPTYING ||
2075 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2076 element_old == EL_MAGIC_WALL_EMPTYING ||
2077 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2078 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2079 element_old == EL_AMOEBA_DROPPING)
2080 cut_mode = CUT_ABOVE;
2082 DrawScreenElement(x, y, EL_EMPTY);
2085 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2087 else if (cut_mode == NO_CUTTING)
2088 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2091 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2094 else if (IS_DRAWABLE(element))
2095 DrawScreenElement(x, y, element);
2097 DrawScreenElement(x, y, EL_EMPTY);
2100 void DrawLevelField(int x, int y)
2102 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2103 DrawScreenField(SCREENX(x), SCREENY(y));
2104 else if (IS_MOVING(x, y))
2108 Moving2Blocked(x, y, &newx, &newy);
2109 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2110 DrawScreenField(SCREENX(newx), SCREENY(newy));
2112 else if (IS_BLOCKED(x, y))
2116 Blocked2Moving(x, y, &oldx, &oldy);
2117 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2118 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2122 void DrawSizedElement(int x, int y, int element, int tilesize)
2126 graphic = el2edimg(element);
2127 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2130 void DrawMiniElement(int x, int y, int element)
2134 graphic = el2edimg(element);
2135 DrawMiniGraphic(x, y, graphic);
2138 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2141 int x = sx + scroll_x, y = sy + scroll_y;
2143 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2144 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2145 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2146 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2148 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2151 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2153 int x = sx + scroll_x, y = sy + scroll_y;
2155 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2156 DrawMiniElement(sx, sy, EL_EMPTY);
2157 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2158 DrawMiniElement(sx, sy, Feld[x][y]);
2160 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2163 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2164 int x, int y, int xsize, int ysize,
2165 int tile_width, int tile_height)
2169 int dst_x = startx + x * tile_width;
2170 int dst_y = starty + y * tile_height;
2171 int width = graphic_info[graphic].width;
2172 int height = graphic_info[graphic].height;
2173 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2174 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2175 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2176 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2177 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2178 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2179 boolean draw_masked = graphic_info[graphic].draw_masked;
2181 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2183 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2185 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2189 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2190 inner_sx + (x - 1) * tile_width % inner_width);
2191 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2192 inner_sy + (y - 1) * tile_height % inner_height);
2195 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2198 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2202 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2203 int x, int y, int xsize, int ysize, int font_nr)
2205 int font_width = getFontWidth(font_nr);
2206 int font_height = getFontHeight(font_nr);
2208 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2209 font_width, font_height);
2212 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2214 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2215 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2216 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2217 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2218 boolean no_delay = (tape.warp_forward);
2219 unsigned int anim_delay = 0;
2220 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2221 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2222 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2223 int font_width = getFontWidth(font_nr);
2224 int font_height = getFontHeight(font_nr);
2225 int max_xsize = level.envelope[envelope_nr].xsize;
2226 int max_ysize = level.envelope[envelope_nr].ysize;
2227 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2228 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2229 int xend = max_xsize;
2230 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2231 int xstep = (xstart < xend ? 1 : 0);
2232 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2234 int end = MAX(xend - xstart, yend - ystart);
2237 for (i = start; i <= end; i++)
2239 int last_frame = end; // last frame of this "for" loop
2240 int x = xstart + i * xstep;
2241 int y = ystart + i * ystep;
2242 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2243 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2244 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2245 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2248 SetDrawtoField(DRAW_FIELDBUFFER);
2250 BlitScreenToBitmap(backbuffer);
2252 SetDrawtoField(DRAW_BACKBUFFER);
2254 for (yy = 0; yy < ysize; yy++)
2255 for (xx = 0; xx < xsize; xx++)
2256 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2258 DrawTextBuffer(sx + font_width, sy + font_height,
2259 level.envelope[envelope_nr].text, font_nr, max_xsize,
2260 xsize - 2, ysize - 2, 0, mask_mode,
2261 level.envelope[envelope_nr].autowrap,
2262 level.envelope[envelope_nr].centered, FALSE);
2264 redraw_mask |= REDRAW_FIELD;
2267 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2271 void ShowEnvelope(int envelope_nr)
2273 int element = EL_ENVELOPE_1 + envelope_nr;
2274 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2275 int sound_opening = element_info[element].sound[ACTION_OPENING];
2276 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2277 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2278 boolean no_delay = (tape.warp_forward);
2279 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2280 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2281 int anim_mode = graphic_info[graphic].anim_mode;
2282 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2283 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2285 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2287 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2289 if (anim_mode == ANIM_DEFAULT)
2290 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2292 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2295 Delay(wait_delay_value);
2297 WaitForEventToContinue();
2299 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2301 if (anim_mode != ANIM_NONE)
2302 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2304 if (anim_mode == ANIM_DEFAULT)
2305 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2307 game.envelope_active = FALSE;
2309 SetDrawtoField(DRAW_FIELDBUFFER);
2311 redraw_mask |= REDRAW_FIELD;
2315 static void setRequestBasePosition(int *x, int *y)
2317 int sx_base, sy_base;
2319 if (request.x != -1)
2320 sx_base = request.x;
2321 else if (request.align == ALIGN_LEFT)
2323 else if (request.align == ALIGN_RIGHT)
2324 sx_base = SX + SXSIZE;
2326 sx_base = SX + SXSIZE / 2;
2328 if (request.y != -1)
2329 sy_base = request.y;
2330 else if (request.valign == VALIGN_TOP)
2332 else if (request.valign == VALIGN_BOTTOM)
2333 sy_base = SY + SYSIZE;
2335 sy_base = SY + SYSIZE / 2;
2341 static void setRequestPositionExt(int *x, int *y, int width, int height,
2342 boolean add_border_size)
2344 int border_size = request.border_size;
2345 int sx_base, sy_base;
2348 setRequestBasePosition(&sx_base, &sy_base);
2350 if (request.align == ALIGN_LEFT)
2352 else if (request.align == ALIGN_RIGHT)
2353 sx = sx_base - width;
2355 sx = sx_base - width / 2;
2357 if (request.valign == VALIGN_TOP)
2359 else if (request.valign == VALIGN_BOTTOM)
2360 sy = sy_base - height;
2362 sy = sy_base - height / 2;
2364 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2365 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2367 if (add_border_size)
2377 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2379 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2382 void DrawEnvelopeRequest(char *text)
2384 int last_game_status = game_status; /* save current game status */
2385 char *text_final = text;
2386 char *text_door_style = NULL;
2387 int graphic = IMG_BACKGROUND_REQUEST;
2388 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2389 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2390 int font_nr = FONT_REQUEST;
2391 int font_width = getFontWidth(font_nr);
2392 int font_height = getFontHeight(font_nr);
2393 int border_size = request.border_size;
2394 int line_spacing = request.line_spacing;
2395 int line_height = font_height + line_spacing;
2396 int max_text_width = request.width - 2 * border_size;
2397 int max_text_height = request.height - 2 * border_size;
2398 int line_length = max_text_width / font_width;
2399 int max_lines = max_text_height / line_height;
2400 int text_width = line_length * font_width;
2401 int width = request.width;
2402 int height = request.height;
2403 int tile_size = MAX(request.step_offset, 1);
2404 int x_steps = width / tile_size;
2405 int y_steps = height / tile_size;
2406 int sx_offset = border_size;
2407 int sy_offset = border_size;
2411 if (request.centered)
2412 sx_offset = (request.width - text_width) / 2;
2414 if (request.wrap_single_words && !request.autowrap)
2416 char *src_text_ptr, *dst_text_ptr;
2418 text_door_style = checked_malloc(2 * strlen(text) + 1);
2420 src_text_ptr = text;
2421 dst_text_ptr = text_door_style;
2423 while (*src_text_ptr)
2425 if (*src_text_ptr == ' ' ||
2426 *src_text_ptr == '?' ||
2427 *src_text_ptr == '!')
2428 *dst_text_ptr++ = '\n';
2430 if (*src_text_ptr != ' ')
2431 *dst_text_ptr++ = *src_text_ptr;
2436 *dst_text_ptr = '\0';
2438 text_final = text_door_style;
2441 setRequestPosition(&sx, &sy, FALSE);
2443 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2445 for (y = 0; y < y_steps; y++)
2446 for (x = 0; x < x_steps; x++)
2447 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2448 x, y, x_steps, y_steps,
2449 tile_size, tile_size);
2451 /* force DOOR font inside door area */
2452 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
2454 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2455 line_length, -1, max_lines, line_spacing, mask_mode,
2456 request.autowrap, request.centered, FALSE);
2458 SetGameStatus(last_game_status); /* restore current game status */
2460 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2461 RedrawGadget(tool_gadget[i]);
2463 // store readily prepared envelope request for later use when animating
2464 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2466 if (text_door_style)
2467 free(text_door_style);
2470 void AnimateEnvelopeRequest(int anim_mode, int action)
2472 int graphic = IMG_BACKGROUND_REQUEST;
2473 boolean draw_masked = graphic_info[graphic].draw_masked;
2474 int delay_value_normal = request.step_delay;
2475 int delay_value_fast = delay_value_normal / 2;
2476 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2477 boolean no_delay = (tape.warp_forward);
2478 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2479 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2480 unsigned int anim_delay = 0;
2482 int tile_size = MAX(request.step_offset, 1);
2483 int max_xsize = request.width / tile_size;
2484 int max_ysize = request.height / tile_size;
2485 int max_xsize_inner = max_xsize - 2;
2486 int max_ysize_inner = max_ysize - 2;
2488 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2489 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2490 int xend = max_xsize_inner;
2491 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2492 int xstep = (xstart < xend ? 1 : 0);
2493 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2495 int end = MAX(xend - xstart, yend - ystart);
2498 if (setup.quick_doors)
2505 for (i = start; i <= end; i++)
2507 int last_frame = end; // last frame of this "for" loop
2508 int x = xstart + i * xstep;
2509 int y = ystart + i * ystep;
2510 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2511 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2512 int xsize_size_left = (xsize - 1) * tile_size;
2513 int ysize_size_top = (ysize - 1) * tile_size;
2514 int max_xsize_pos = (max_xsize - 1) * tile_size;
2515 int max_ysize_pos = (max_ysize - 1) * tile_size;
2516 int width = xsize * tile_size;
2517 int height = ysize * tile_size;
2522 setRequestPosition(&src_x, &src_y, FALSE);
2523 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2525 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2527 for (yy = 0; yy < 2; yy++)
2529 for (xx = 0; xx < 2; xx++)
2531 int src_xx = src_x + xx * max_xsize_pos;
2532 int src_yy = src_y + yy * max_ysize_pos;
2533 int dst_xx = dst_x + xx * xsize_size_left;
2534 int dst_yy = dst_y + yy * ysize_size_top;
2535 int xx_size = (xx ? tile_size : xsize_size_left);
2536 int yy_size = (yy ? tile_size : ysize_size_top);
2539 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2540 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2542 BlitBitmap(bitmap_db_cross, backbuffer,
2543 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2547 redraw_mask |= REDRAW_FIELD;
2552 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2556 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2558 int graphic = IMG_BACKGROUND_REQUEST;
2559 int sound_opening = SND_REQUEST_OPENING;
2560 int sound_closing = SND_REQUEST_CLOSING;
2561 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2562 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2563 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2564 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2565 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2567 if (game_status == GAME_MODE_PLAYING)
2568 BlitScreenToBitmap(backbuffer);
2570 SetDrawtoField(DRAW_BACKBUFFER);
2572 // SetDrawBackgroundMask(REDRAW_NONE);
2574 if (action == ACTION_OPENING)
2576 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2578 if (req_state & REQ_ASK)
2580 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2581 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2583 else if (req_state & REQ_CONFIRM)
2585 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2587 else if (req_state & REQ_PLAYER)
2589 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2590 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2591 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2592 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2595 DrawEnvelopeRequest(text);
2597 if (game_status != GAME_MODE_MAIN)
2601 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2603 if (action == ACTION_OPENING)
2605 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2607 if (anim_mode == ANIM_DEFAULT)
2608 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2610 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2614 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2616 if (anim_mode != ANIM_NONE)
2617 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2619 if (anim_mode == ANIM_DEFAULT)
2620 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2623 game.envelope_active = FALSE;
2625 if (action == ACTION_CLOSING)
2627 if (game_status != GAME_MODE_MAIN)
2630 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2633 // SetDrawBackgroundMask(last_draw_background_mask);
2635 redraw_mask |= REDRAW_FIELD;
2637 if (game_status == GAME_MODE_MAIN)
2642 if (action == ACTION_CLOSING &&
2643 game_status == GAME_MODE_PLAYING &&
2644 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2645 SetDrawtoField(DRAW_FIELDBUFFER);
2648 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2652 int graphic = el2preimg(element);
2654 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2655 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2658 void DrawLevel(int draw_background_mask)
2662 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2663 SetDrawBackgroundMask(draw_background_mask);
2667 for (x = BX1; x <= BX2; x++)
2668 for (y = BY1; y <= BY2; y++)
2669 DrawScreenField(x, y);
2671 redraw_mask |= REDRAW_FIELD;
2674 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2679 for (x = 0; x < size_x; x++)
2680 for (y = 0; y < size_y; y++)
2681 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2683 redraw_mask |= REDRAW_FIELD;
2686 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2690 for (x = 0; x < size_x; x++)
2691 for (y = 0; y < size_y; y++)
2692 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2694 redraw_mask |= REDRAW_FIELD;
2697 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2699 boolean show_level_border = (BorderElement != EL_EMPTY);
2700 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2701 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2702 int tile_size = preview.tile_size;
2703 int preview_width = preview.xsize * tile_size;
2704 int preview_height = preview.ysize * tile_size;
2705 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2706 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2707 int real_preview_width = real_preview_xsize * tile_size;
2708 int real_preview_height = real_preview_ysize * tile_size;
2709 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2710 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2713 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2716 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2718 dst_x += (preview_width - real_preview_width) / 2;
2719 dst_y += (preview_height - real_preview_height) / 2;
2721 for (x = 0; x < real_preview_xsize; x++)
2723 for (y = 0; y < real_preview_ysize; y++)
2725 int lx = from_x + x + (show_level_border ? -1 : 0);
2726 int ly = from_y + y + (show_level_border ? -1 : 0);
2727 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2728 getBorderElement(lx, ly));
2730 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2731 element, tile_size);
2735 redraw_mask |= REDRAW_FIELD;
2738 #define MICROLABEL_EMPTY 0
2739 #define MICROLABEL_LEVEL_NAME 1
2740 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2741 #define MICROLABEL_LEVEL_AUTHOR 3
2742 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2743 #define MICROLABEL_IMPORTED_FROM 5
2744 #define MICROLABEL_IMPORTED_BY_HEAD 6
2745 #define MICROLABEL_IMPORTED_BY 7
2747 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2749 int max_text_width = SXSIZE;
2750 int font_width = getFontWidth(font_nr);
2752 if (pos->align == ALIGN_CENTER)
2753 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2754 else if (pos->align == ALIGN_RIGHT)
2755 max_text_width = pos->x;
2757 max_text_width = SXSIZE - pos->x;
2759 return max_text_width / font_width;
2762 static void DrawPreviewLevelLabelExt(int mode)
2764 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2765 char label_text[MAX_OUTPUT_LINESIZE + 1];
2766 int max_len_label_text;
2767 int font_nr = pos->font;
2770 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2773 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2774 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2775 mode == MICROLABEL_IMPORTED_BY_HEAD)
2776 font_nr = pos->font_alt;
2778 max_len_label_text = getMaxTextLength(pos, font_nr);
2780 if (pos->size != -1)
2781 max_len_label_text = pos->size;
2783 for (i = 0; i < max_len_label_text; i++)
2784 label_text[i] = ' ';
2785 label_text[max_len_label_text] = '\0';
2787 if (strlen(label_text) > 0)
2788 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2791 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2792 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2793 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2794 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2795 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2796 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2797 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2798 max_len_label_text);
2799 label_text[max_len_label_text] = '\0';
2801 if (strlen(label_text) > 0)
2802 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2804 redraw_mask |= REDRAW_FIELD;
2807 static void DrawPreviewLevelExt(boolean restart)
2809 static unsigned int scroll_delay = 0;
2810 static unsigned int label_delay = 0;
2811 static int from_x, from_y, scroll_direction;
2812 static int label_state, label_counter;
2813 unsigned int scroll_delay_value = preview.step_delay;
2814 boolean show_level_border = (BorderElement != EL_EMPTY);
2815 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2816 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2817 int last_game_status = game_status; /* save current game status */
2824 if (preview.anim_mode == ANIM_CENTERED)
2826 if (level_xsize > preview.xsize)
2827 from_x = (level_xsize - preview.xsize) / 2;
2828 if (level_ysize > preview.ysize)
2829 from_y = (level_ysize - preview.ysize) / 2;
2832 from_x += preview.xoffset;
2833 from_y += preview.yoffset;
2835 scroll_direction = MV_RIGHT;
2839 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2840 DrawPreviewLevelLabelExt(label_state);
2842 /* initialize delay counters */
2843 DelayReached(&scroll_delay, 0);
2844 DelayReached(&label_delay, 0);
2846 if (leveldir_current->name)
2848 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2849 char label_text[MAX_OUTPUT_LINESIZE + 1];
2850 int font_nr = pos->font;
2851 int max_len_label_text = getMaxTextLength(pos, font_nr);
2853 if (pos->size != -1)
2854 max_len_label_text = pos->size;
2856 strncpy(label_text, leveldir_current->name, max_len_label_text);
2857 label_text[max_len_label_text] = '\0';
2859 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2860 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2863 SetGameStatus(last_game_status); /* restore current game status */
2868 /* scroll preview level, if needed */
2869 if (preview.anim_mode != ANIM_NONE &&
2870 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2871 DelayReached(&scroll_delay, scroll_delay_value))
2873 switch (scroll_direction)
2878 from_x -= preview.step_offset;
2879 from_x = (from_x < 0 ? 0 : from_x);
2882 scroll_direction = MV_UP;
2886 if (from_x < level_xsize - preview.xsize)
2888 from_x += preview.step_offset;
2889 from_x = (from_x > level_xsize - preview.xsize ?
2890 level_xsize - preview.xsize : from_x);
2893 scroll_direction = MV_DOWN;
2899 from_y -= preview.step_offset;
2900 from_y = (from_y < 0 ? 0 : from_y);
2903 scroll_direction = MV_RIGHT;
2907 if (from_y < level_ysize - preview.ysize)
2909 from_y += preview.step_offset;
2910 from_y = (from_y > level_ysize - preview.ysize ?
2911 level_ysize - preview.ysize : from_y);
2914 scroll_direction = MV_LEFT;
2921 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2924 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2925 /* redraw micro level label, if needed */
2926 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2927 !strEqual(level.author, ANONYMOUS_NAME) &&
2928 !strEqual(level.author, leveldir_current->name) &&
2929 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2931 int max_label_counter = 23;
2933 if (leveldir_current->imported_from != NULL &&
2934 strlen(leveldir_current->imported_from) > 0)
2935 max_label_counter += 14;
2936 if (leveldir_current->imported_by != NULL &&
2937 strlen(leveldir_current->imported_by) > 0)
2938 max_label_counter += 14;
2940 label_counter = (label_counter + 1) % max_label_counter;
2941 label_state = (label_counter >= 0 && label_counter <= 7 ?
2942 MICROLABEL_LEVEL_NAME :
2943 label_counter >= 9 && label_counter <= 12 ?
2944 MICROLABEL_LEVEL_AUTHOR_HEAD :
2945 label_counter >= 14 && label_counter <= 21 ?
2946 MICROLABEL_LEVEL_AUTHOR :
2947 label_counter >= 23 && label_counter <= 26 ?
2948 MICROLABEL_IMPORTED_FROM_HEAD :
2949 label_counter >= 28 && label_counter <= 35 ?
2950 MICROLABEL_IMPORTED_FROM :
2951 label_counter >= 37 && label_counter <= 40 ?
2952 MICROLABEL_IMPORTED_BY_HEAD :
2953 label_counter >= 42 && label_counter <= 49 ?
2954 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2956 if (leveldir_current->imported_from == NULL &&
2957 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2958 label_state == MICROLABEL_IMPORTED_FROM))
2959 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2960 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2962 DrawPreviewLevelLabelExt(label_state);
2965 SetGameStatus(last_game_status); /* restore current game status */
2968 void DrawPreviewLevelInitial()
2970 DrawPreviewLevelExt(TRUE);
2973 void DrawPreviewLevelAnimation()
2975 DrawPreviewLevelExt(FALSE);
2978 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2979 int graphic, int sync_frame,
2982 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2984 if (mask_mode == USE_MASKING)
2985 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2987 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2990 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2991 int graphic, int sync_frame, int mask_mode)
2993 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2995 if (mask_mode == USE_MASKING)
2996 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2998 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3001 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3003 int lx = LEVELX(x), ly = LEVELY(y);
3005 if (!IN_SCR_FIELD(x, y))
3008 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3009 graphic, GfxFrame[lx][ly], NO_MASKING);
3011 MarkTileDirty(x, y);
3014 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3016 int lx = LEVELX(x), ly = LEVELY(y);
3018 if (!IN_SCR_FIELD(x, y))
3021 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3022 graphic, GfxFrame[lx][ly], NO_MASKING);
3023 MarkTileDirty(x, y);
3026 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3028 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3031 void DrawLevelElementAnimation(int x, int y, int element)
3033 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3035 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3038 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3040 int sx = SCREENX(x), sy = SCREENY(y);
3042 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3045 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3048 DrawGraphicAnimation(sx, sy, graphic);
3051 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3052 DrawLevelFieldCrumbled(x, y);
3054 if (GFX_CRUMBLED(Feld[x][y]))
3055 DrawLevelFieldCrumbled(x, y);
3059 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3061 int sx = SCREENX(x), sy = SCREENY(y);
3064 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3067 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3069 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3072 DrawGraphicAnimation(sx, sy, graphic);
3074 if (GFX_CRUMBLED(element))
3075 DrawLevelFieldCrumbled(x, y);
3078 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3080 if (player->use_murphy)
3082 /* this works only because currently only one player can be "murphy" ... */
3083 static int last_horizontal_dir = MV_LEFT;
3084 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3086 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3087 last_horizontal_dir = move_dir;
3089 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3091 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3093 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3099 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3102 static boolean equalGraphics(int graphic1, int graphic2)
3104 struct GraphicInfo *g1 = &graphic_info[graphic1];
3105 struct GraphicInfo *g2 = &graphic_info[graphic2];
3107 return (g1->bitmap == g2->bitmap &&
3108 g1->src_x == g2->src_x &&
3109 g1->src_y == g2->src_y &&
3110 g1->anim_frames == g2->anim_frames &&
3111 g1->anim_delay == g2->anim_delay &&
3112 g1->anim_mode == g2->anim_mode);
3115 void DrawAllPlayers()
3119 for (i = 0; i < MAX_PLAYERS; i++)
3120 if (stored_player[i].active)
3121 DrawPlayer(&stored_player[i]);
3124 void DrawPlayerField(int x, int y)
3126 if (!IS_PLAYER(x, y))
3129 DrawPlayer(PLAYERINFO(x, y));
3132 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3134 void DrawPlayer(struct PlayerInfo *player)
3136 int jx = player->jx;
3137 int jy = player->jy;
3138 int move_dir = player->MovDir;
3139 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3140 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3141 int last_jx = (player->is_moving ? jx - dx : jx);
3142 int last_jy = (player->is_moving ? jy - dy : jy);
3143 int next_jx = jx + dx;
3144 int next_jy = jy + dy;
3145 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3146 boolean player_is_opaque = FALSE;
3147 int sx = SCREENX(jx), sy = SCREENY(jy);
3148 int sxx = 0, syy = 0;
3149 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3151 int action = ACTION_DEFAULT;
3152 int last_player_graphic = getPlayerGraphic(player, move_dir);
3153 int last_player_frame = player->Frame;
3156 /* GfxElement[][] is set to the element the player is digging or collecting;
3157 remove also for off-screen player if the player is not moving anymore */
3158 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3159 GfxElement[jx][jy] = EL_UNDEFINED;
3161 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3165 if (!IN_LEV_FIELD(jx, jy))
3167 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3168 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3169 printf("DrawPlayerField(): This should never happen!\n");
3174 if (element == EL_EXPLOSION)
3177 action = (player->is_pushing ? ACTION_PUSHING :
3178 player->is_digging ? ACTION_DIGGING :
3179 player->is_collecting ? ACTION_COLLECTING :
3180 player->is_moving ? ACTION_MOVING :
3181 player->is_snapping ? ACTION_SNAPPING :
3182 player->is_dropping ? ACTION_DROPPING :
3183 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3185 if (player->is_waiting)
3186 move_dir = player->dir_waiting;
3188 InitPlayerGfxAnimation(player, action, move_dir);
3190 /* ----------------------------------------------------------------------- */
3191 /* draw things in the field the player is leaving, if needed */
3192 /* ----------------------------------------------------------------------- */
3194 if (player->is_moving)
3196 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3198 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3200 if (last_element == EL_DYNAMITE_ACTIVE ||
3201 last_element == EL_EM_DYNAMITE_ACTIVE ||
3202 last_element == EL_SP_DISK_RED_ACTIVE)
3203 DrawDynamite(last_jx, last_jy);
3205 DrawLevelFieldThruMask(last_jx, last_jy);
3207 else if (last_element == EL_DYNAMITE_ACTIVE ||
3208 last_element == EL_EM_DYNAMITE_ACTIVE ||
3209 last_element == EL_SP_DISK_RED_ACTIVE)
3210 DrawDynamite(last_jx, last_jy);
3212 /* !!! this is not enough to prevent flickering of players which are
3213 moving next to each others without a free tile between them -- this
3214 can only be solved by drawing all players layer by layer (first the
3215 background, then the foreground etc.) !!! => TODO */
3216 else if (!IS_PLAYER(last_jx, last_jy))
3217 DrawLevelField(last_jx, last_jy);
3220 DrawLevelField(last_jx, last_jy);
3223 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3224 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3227 if (!IN_SCR_FIELD(sx, sy))
3230 /* ----------------------------------------------------------------------- */
3231 /* draw things behind the player, if needed */
3232 /* ----------------------------------------------------------------------- */
3235 DrawLevelElement(jx, jy, Back[jx][jy]);
3236 else if (IS_ACTIVE_BOMB(element))
3237 DrawLevelElement(jx, jy, EL_EMPTY);
3240 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3242 int old_element = GfxElement[jx][jy];
3243 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3244 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3246 if (GFX_CRUMBLED(old_element))
3247 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3249 DrawGraphic(sx, sy, old_graphic, frame);
3251 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3252 player_is_opaque = TRUE;
3256 GfxElement[jx][jy] = EL_UNDEFINED;
3258 /* make sure that pushed elements are drawn with correct frame rate */
3259 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3261 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3262 GfxFrame[jx][jy] = player->StepFrame;
3264 DrawLevelField(jx, jy);
3268 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3269 /* ----------------------------------------------------------------------- */
3270 /* draw player himself */
3271 /* ----------------------------------------------------------------------- */
3273 graphic = getPlayerGraphic(player, move_dir);
3275 /* in the case of changed player action or direction, prevent the current
3276 animation frame from being restarted for identical animations */
3277 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3278 player->Frame = last_player_frame;
3280 frame = getGraphicAnimationFrame(graphic, player->Frame);
3284 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3285 sxx = player->GfxPos;
3287 syy = player->GfxPos;
3290 if (player_is_opaque)
3291 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3293 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3295 if (SHIELD_ON(player))
3297 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3298 IMG_SHIELD_NORMAL_ACTIVE);
3299 int frame = getGraphicAnimationFrame(graphic, -1);
3301 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3305 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3308 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3309 sxx = player->GfxPos;
3311 syy = player->GfxPos;
3315 /* ----------------------------------------------------------------------- */
3316 /* draw things the player is pushing, if needed */
3317 /* ----------------------------------------------------------------------- */
3319 if (player->is_pushing && player->is_moving)
3321 int px = SCREENX(jx), py = SCREENY(jy);
3322 int pxx = (TILEX - ABS(sxx)) * dx;
3323 int pyy = (TILEY - ABS(syy)) * dy;
3324 int gfx_frame = GfxFrame[jx][jy];
3330 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3332 element = Feld[next_jx][next_jy];
3333 gfx_frame = GfxFrame[next_jx][next_jy];
3336 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3338 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3339 frame = getGraphicAnimationFrame(graphic, sync_frame);
3341 /* draw background element under pushed element (like the Sokoban field) */
3342 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3344 /* this allows transparent pushing animation over non-black background */
3347 DrawLevelElement(jx, jy, Back[jx][jy]);
3349 DrawLevelElement(jx, jy, EL_EMPTY);
3351 if (Back[next_jx][next_jy])
3352 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3354 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3356 else if (Back[next_jx][next_jy])
3357 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3360 /* do not draw (EM style) pushing animation when pushing is finished */
3361 /* (two-tile animations usually do not contain start and end frame) */
3362 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3363 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3365 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3367 /* masked drawing is needed for EMC style (double) movement graphics */
3368 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3369 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3373 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3374 /* ----------------------------------------------------------------------- */
3375 /* draw player himself */
3376 /* ----------------------------------------------------------------------- */
3378 graphic = getPlayerGraphic(player, move_dir);
3380 /* in the case of changed player action or direction, prevent the current
3381 animation frame from being restarted for identical animations */
3382 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3383 player->Frame = last_player_frame;
3385 frame = getGraphicAnimationFrame(graphic, player->Frame);
3389 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3390 sxx = player->GfxPos;
3392 syy = player->GfxPos;
3395 if (player_is_opaque)
3396 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3398 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3400 if (SHIELD_ON(player))
3402 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3403 IMG_SHIELD_NORMAL_ACTIVE);
3404 int frame = getGraphicAnimationFrame(graphic, -1);
3406 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3410 /* ----------------------------------------------------------------------- */
3411 /* draw things in front of player (active dynamite or dynabombs) */
3412 /* ----------------------------------------------------------------------- */
3414 if (IS_ACTIVE_BOMB(element))
3416 graphic = el2img(element);
3417 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3419 if (game.emulation == EMU_SUPAPLEX)
3420 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3422 DrawGraphicThruMask(sx, sy, graphic, frame);
3425 if (player_is_moving && last_element == EL_EXPLOSION)
3427 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3428 GfxElement[last_jx][last_jy] : EL_EMPTY);
3429 int graphic = el_act2img(element, ACTION_EXPLODING);
3430 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3431 int phase = ExplodePhase[last_jx][last_jy] - 1;
3432 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3435 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3438 /* ----------------------------------------------------------------------- */
3439 /* draw elements the player is just walking/passing through/under */
3440 /* ----------------------------------------------------------------------- */
3442 if (player_is_moving)
3444 /* handle the field the player is leaving ... */
3445 if (IS_ACCESSIBLE_INSIDE(last_element))
3446 DrawLevelField(last_jx, last_jy);
3447 else if (IS_ACCESSIBLE_UNDER(last_element))
3448 DrawLevelFieldThruMask(last_jx, last_jy);
3451 /* do not redraw accessible elements if the player is just pushing them */
3452 if (!player_is_moving || !player->is_pushing)
3454 /* ... and the field the player is entering */
3455 if (IS_ACCESSIBLE_INSIDE(element))
3456 DrawLevelField(jx, jy);
3457 else if (IS_ACCESSIBLE_UNDER(element))
3458 DrawLevelFieldThruMask(jx, jy);
3461 MarkTileDirty(sx, sy);
3464 /* ------------------------------------------------------------------------- */
3466 void WaitForEventToContinue()
3468 boolean still_wait = TRUE;
3470 /* simulate releasing mouse button over last gadget, if still pressed */
3472 HandleGadgets(-1, -1, 0);
3474 button_status = MB_RELEASED;
3488 case EVENT_BUTTONPRESS:
3489 case EVENT_KEYPRESS:
3493 case EVENT_KEYRELEASE:
3494 ClearPlayerAction();
3498 HandleOtherEvents(&event);
3502 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3509 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3513 #define MAX_REQUEST_LINES 13
3514 #define MAX_REQUEST_LINE_FONT1_LEN 7
3515 #define MAX_REQUEST_LINE_FONT2_LEN 10
3517 static int RequestHandleEvents(unsigned int req_state)
3519 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3520 local_player->LevelSolved_GameEnd);
3521 int width = request.width;
3522 int height = request.height;
3526 setRequestPosition(&sx, &sy, FALSE);
3528 button_status = MB_RELEASED;
3530 request_gadget_id = -1;
3537 SetDrawtoField(DRAW_FIELDBUFFER);
3539 HandleGameActions();
3541 SetDrawtoField(DRAW_BACKBUFFER);
3543 if (global.use_envelope_request)
3545 /* copy current state of request area to middle of playfield area */
3546 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3554 while (NextValidEvent(&event))
3558 case EVENT_BUTTONPRESS:
3559 case EVENT_BUTTONRELEASE:
3560 case EVENT_MOTIONNOTIFY:
3564 if (event.type == EVENT_MOTIONNOTIFY)
3569 motion_status = TRUE;
3570 mx = ((MotionEvent *) &event)->x;
3571 my = ((MotionEvent *) &event)->y;
3575 motion_status = FALSE;
3576 mx = ((ButtonEvent *) &event)->x;
3577 my = ((ButtonEvent *) &event)->y;
3578 if (event.type == EVENT_BUTTONPRESS)
3579 button_status = ((ButtonEvent *) &event)->button;
3581 button_status = MB_RELEASED;
3584 /* this sets 'request_gadget_id' */
3585 HandleGadgets(mx, my, button_status);
3587 switch (request_gadget_id)
3589 case TOOL_CTRL_ID_YES:
3592 case TOOL_CTRL_ID_NO:
3595 case TOOL_CTRL_ID_CONFIRM:
3596 result = TRUE | FALSE;
3599 case TOOL_CTRL_ID_PLAYER_1:
3602 case TOOL_CTRL_ID_PLAYER_2:
3605 case TOOL_CTRL_ID_PLAYER_3:
3608 case TOOL_CTRL_ID_PLAYER_4:
3619 case EVENT_KEYPRESS:
3620 switch (GetEventKey((KeyEvent *)&event, TRUE))
3623 if (req_state & REQ_CONFIRM)
3628 #if defined(TARGET_SDL2)
3635 #if defined(TARGET_SDL2)
3645 if (req_state & REQ_PLAYER)
3649 case EVENT_KEYRELEASE:
3650 ClearPlayerAction();
3654 HandleOtherEvents(&event);
3659 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3661 int joy = AnyJoystick();
3663 if (joy & JOY_BUTTON_1)
3665 else if (joy & JOY_BUTTON_2)
3671 if (global.use_envelope_request)
3673 /* copy back current state of pressed buttons inside request area */
3674 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3684 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3690 static boolean RequestDoor(char *text, unsigned int req_state)
3692 unsigned int old_door_state;
3693 int last_game_status = game_status; /* save current game status */
3694 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3695 int font_nr = FONT_TEXT_2;
3700 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3702 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3703 font_nr = FONT_TEXT_1;
3706 if (game_status == GAME_MODE_PLAYING)
3707 BlitScreenToBitmap(backbuffer);
3709 /* disable deactivated drawing when quick-loading level tape recording */
3710 if (tape.playing && tape.deactivate_display)
3711 TapeDeactivateDisplayOff(TRUE);
3713 SetMouseCursor(CURSOR_DEFAULT);
3715 #if defined(NETWORK_AVALIABLE)
3716 /* pause network game while waiting for request to answer */
3717 if (options.network &&
3718 game_status == GAME_MODE_PLAYING &&
3719 req_state & REQUEST_WAIT_FOR_INPUT)
3720 SendToServer_PausePlaying();
3723 old_door_state = GetDoorState();
3725 /* simulate releasing mouse button over last gadget, if still pressed */
3727 HandleGadgets(-1, -1, 0);
3731 /* draw released gadget before proceeding */
3734 if (old_door_state & DOOR_OPEN_1)
3736 CloseDoor(DOOR_CLOSE_1);
3738 /* save old door content */
3739 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3740 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3743 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3744 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3746 /* clear door drawing field */
3747 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3749 /* force DOOR font inside door area */
3750 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
3752 /* write text for request */
3753 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3755 char text_line[max_request_line_len + 1];
3761 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3763 tc = *(text_ptr + tx);
3764 // if (!tc || tc == ' ')
3765 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3769 if ((tc == '?' || tc == '!') && tl == 0)
3779 strncpy(text_line, text_ptr, tl);
3782 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3783 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3784 text_line, font_nr);
3786 text_ptr += tl + (tc == ' ' ? 1 : 0);
3787 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3790 SetGameStatus(last_game_status); /* restore current game status */
3792 if (req_state & REQ_ASK)
3794 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3795 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3797 else if (req_state & REQ_CONFIRM)
3799 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3801 else if (req_state & REQ_PLAYER)
3803 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3804 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3805 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3806 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3809 /* copy request gadgets to door backbuffer */
3810 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3812 OpenDoor(DOOR_OPEN_1);
3814 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3816 if (game_status == GAME_MODE_PLAYING)
3818 SetPanelBackground();
3819 SetDrawBackgroundMask(REDRAW_DOOR_1);
3823 SetDrawBackgroundMask(REDRAW_FIELD);
3829 if (game_status != GAME_MODE_MAIN)
3832 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3834 // ---------- handle request buttons ----------
3835 result = RequestHandleEvents(req_state);
3837 if (game_status != GAME_MODE_MAIN)
3842 if (!(req_state & REQ_STAY_OPEN))
3844 CloseDoor(DOOR_CLOSE_1);
3846 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3847 (req_state & REQ_REOPEN))
3848 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3853 if (game_status == GAME_MODE_PLAYING)
3855 SetPanelBackground();
3856 SetDrawBackgroundMask(REDRAW_DOOR_1);
3860 SetDrawBackgroundMask(REDRAW_FIELD);
3863 #if defined(NETWORK_AVALIABLE)
3864 /* continue network game after request */
3865 if (options.network &&
3866 game_status == GAME_MODE_PLAYING &&
3867 req_state & REQUEST_WAIT_FOR_INPUT)
3868 SendToServer_ContinuePlaying();
3871 /* restore deactivated drawing when quick-loading level tape recording */
3872 if (tape.playing && tape.deactivate_display)
3873 TapeDeactivateDisplayOn();
3878 static boolean RequestEnvelope(char *text, unsigned int req_state)
3882 if (game_status == GAME_MODE_PLAYING)
3883 BlitScreenToBitmap(backbuffer);
3885 /* disable deactivated drawing when quick-loading level tape recording */
3886 if (tape.playing && tape.deactivate_display)
3887 TapeDeactivateDisplayOff(TRUE);
3889 SetMouseCursor(CURSOR_DEFAULT);
3891 #if defined(NETWORK_AVALIABLE)
3892 /* pause network game while waiting for request to answer */
3893 if (options.network &&
3894 game_status == GAME_MODE_PLAYING &&
3895 req_state & REQUEST_WAIT_FOR_INPUT)
3896 SendToServer_PausePlaying();
3899 /* simulate releasing mouse button over last gadget, if still pressed */
3901 HandleGadgets(-1, -1, 0);
3905 // (replace with setting corresponding request background)
3906 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3907 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3909 /* clear door drawing field */
3910 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3912 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3914 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3916 if (game_status == GAME_MODE_PLAYING)
3918 SetPanelBackground();
3919 SetDrawBackgroundMask(REDRAW_DOOR_1);
3923 SetDrawBackgroundMask(REDRAW_FIELD);
3929 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3931 // ---------- handle request buttons ----------
3932 result = RequestHandleEvents(req_state);
3934 if (game_status != GAME_MODE_MAIN)
3939 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3943 if (game_status == GAME_MODE_PLAYING)
3945 SetPanelBackground();
3946 SetDrawBackgroundMask(REDRAW_DOOR_1);
3950 SetDrawBackgroundMask(REDRAW_FIELD);
3953 #if defined(NETWORK_AVALIABLE)
3954 /* continue network game after request */
3955 if (options.network &&
3956 game_status == GAME_MODE_PLAYING &&
3957 req_state & REQUEST_WAIT_FOR_INPUT)
3958 SendToServer_ContinuePlaying();
3961 /* restore deactivated drawing when quick-loading level tape recording */
3962 if (tape.playing && tape.deactivate_display)
3963 TapeDeactivateDisplayOn();
3968 boolean Request(char *text, unsigned int req_state)
3970 if (global.use_envelope_request)
3971 return RequestEnvelope(text, req_state);
3973 return RequestDoor(text, req_state);
3976 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3978 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3979 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3982 if (dpo1->sort_priority != dpo2->sort_priority)
3983 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3985 compare_result = dpo1->nr - dpo2->nr;
3987 return compare_result;
3990 void InitGraphicCompatibilityInfo_Doors()
3996 struct DoorInfo *door;
4000 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
4001 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
4003 { -1, -1, -1, NULL }
4005 struct Rect door_rect_list[] =
4007 { DX, DY, DXSIZE, DYSIZE },
4008 { VX, VY, VXSIZE, VYSIZE }
4012 for (i = 0; doors[i].door_token != -1; i++)
4014 int door_token = doors[i].door_token;
4015 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4016 int part_1 = doors[i].part_1;
4017 int part_8 = doors[i].part_8;
4018 int part_2 = part_1 + 1;
4019 int part_3 = part_1 + 2;
4020 struct DoorInfo *door = doors[i].door;
4021 struct Rect *door_rect = &door_rect_list[door_index];
4022 boolean door_gfx_redefined = FALSE;
4024 /* check if any door part graphic definitions have been redefined */
4026 for (j = 0; door_part_controls[j].door_token != -1; j++)
4028 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4029 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4031 if (dpc->door_token == door_token && fi->redefined)
4032 door_gfx_redefined = TRUE;
4035 /* check for old-style door graphic/animation modifications */
4037 if (!door_gfx_redefined)
4039 if (door->anim_mode & ANIM_STATIC_PANEL)
4041 door->panel.step_xoffset = 0;
4042 door->panel.step_yoffset = 0;
4045 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4047 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4048 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4049 int num_door_steps, num_panel_steps;
4051 /* remove door part graphics other than the two default wings */
4053 for (j = 0; door_part_controls[j].door_token != -1; j++)
4055 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4056 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4058 if (dpc->graphic >= part_3 &&
4059 dpc->graphic <= part_8)
4063 /* set graphics and screen positions of the default wings */
4065 g_part_1->width = door_rect->width;
4066 g_part_1->height = door_rect->height;
4067 g_part_2->width = door_rect->width;
4068 g_part_2->height = door_rect->height;
4069 g_part_2->src_x = door_rect->width;
4070 g_part_2->src_y = g_part_1->src_y;
4072 door->part_2.x = door->part_1.x;
4073 door->part_2.y = door->part_1.y;
4075 if (door->width != -1)
4077 g_part_1->width = door->width;
4078 g_part_2->width = door->width;
4080 // special treatment for graphics and screen position of right wing
4081 g_part_2->src_x += door_rect->width - door->width;
4082 door->part_2.x += door_rect->width - door->width;
4085 if (door->height != -1)
4087 g_part_1->height = door->height;
4088 g_part_2->height = door->height;
4090 // special treatment for graphics and screen position of bottom wing
4091 g_part_2->src_y += door_rect->height - door->height;
4092 door->part_2.y += door_rect->height - door->height;
4095 /* set animation delays for the default wings and panels */
4097 door->part_1.step_delay = door->step_delay;
4098 door->part_2.step_delay = door->step_delay;
4099 door->panel.step_delay = door->step_delay;
4101 /* set animation draw order for the default wings */
4103 door->part_1.sort_priority = 2; /* draw left wing over ... */
4104 door->part_2.sort_priority = 1; /* ... right wing */
4106 /* set animation draw offset for the default wings */
4108 if (door->anim_mode & ANIM_HORIZONTAL)
4110 door->part_1.step_xoffset = door->step_offset;
4111 door->part_1.step_yoffset = 0;
4112 door->part_2.step_xoffset = door->step_offset * -1;
4113 door->part_2.step_yoffset = 0;
4115 num_door_steps = g_part_1->width / door->step_offset;
4117 else // ANIM_VERTICAL
4119 door->part_1.step_xoffset = 0;
4120 door->part_1.step_yoffset = door->step_offset;
4121 door->part_2.step_xoffset = 0;
4122 door->part_2.step_yoffset = door->step_offset * -1;
4124 num_door_steps = g_part_1->height / door->step_offset;
4127 /* set animation draw offset for the default panels */
4129 if (door->step_offset > 1)
4131 num_panel_steps = 2 * door_rect->height / door->step_offset;
4132 door->panel.start_step = num_panel_steps - num_door_steps;
4133 door->panel.start_step_closing = door->panel.start_step;
4137 num_panel_steps = door_rect->height / door->step_offset;
4138 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4139 door->panel.start_step_closing = door->panel.start_step;
4140 door->panel.step_delay *= 2;
4151 for (i = 0; door_part_controls[i].door_token != -1; i++)
4153 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4154 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4156 /* initialize "start_step_opening" and "start_step_closing", if needed */
4157 if (dpc->pos->start_step_opening == 0 &&
4158 dpc->pos->start_step_closing == 0)
4160 // dpc->pos->start_step_opening = dpc->pos->start_step;
4161 dpc->pos->start_step_closing = dpc->pos->start_step;
4164 /* fill structure for door part draw order (sorted below) */
4166 dpo->sort_priority = dpc->pos->sort_priority;
4169 /* sort door part controls according to sort_priority and graphic number */
4170 qsort(door_part_order, MAX_DOOR_PARTS,
4171 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4174 unsigned int OpenDoor(unsigned int door_state)
4176 if (door_state & DOOR_COPY_BACK)
4178 if (door_state & DOOR_OPEN_1)
4179 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4180 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4182 if (door_state & DOOR_OPEN_2)
4183 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4184 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4186 door_state &= ~DOOR_COPY_BACK;
4189 return MoveDoor(door_state);
4192 unsigned int CloseDoor(unsigned int door_state)
4194 unsigned int old_door_state = GetDoorState();
4196 if (!(door_state & DOOR_NO_COPY_BACK))
4198 if (old_door_state & DOOR_OPEN_1)
4199 BlitBitmap(backbuffer, bitmap_db_door_1,
4200 DX, DY, DXSIZE, DYSIZE, 0, 0);
4202 if (old_door_state & DOOR_OPEN_2)
4203 BlitBitmap(backbuffer, bitmap_db_door_2,
4204 VX, VY, VXSIZE, VYSIZE, 0, 0);
4206 door_state &= ~DOOR_NO_COPY_BACK;
4209 return MoveDoor(door_state);
4212 unsigned int GetDoorState()
4214 return MoveDoor(DOOR_GET_STATE);
4217 unsigned int SetDoorState(unsigned int door_state)
4219 return MoveDoor(door_state | DOOR_SET_STATE);
4222 int euclid(int a, int b)
4224 return (b ? euclid(b, a % b) : a);
4227 unsigned int MoveDoor(unsigned int door_state)
4229 struct Rect door_rect_list[] =
4231 { DX, DY, DXSIZE, DYSIZE },
4232 { VX, VY, VXSIZE, VYSIZE }
4234 static int door1 = DOOR_OPEN_1;
4235 static int door2 = DOOR_CLOSE_2;
4236 unsigned int door_delay = 0;
4237 unsigned int door_delay_value;
4240 if (door_state == DOOR_GET_STATE)
4241 return (door1 | door2);
4243 if (door_state & DOOR_SET_STATE)
4245 if (door_state & DOOR_ACTION_1)
4246 door1 = door_state & DOOR_ACTION_1;
4247 if (door_state & DOOR_ACTION_2)
4248 door2 = door_state & DOOR_ACTION_2;
4250 return (door1 | door2);
4253 if (!(door_state & DOOR_FORCE_REDRAW))
4255 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4256 door_state &= ~DOOR_OPEN_1;
4257 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4258 door_state &= ~DOOR_CLOSE_1;
4259 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4260 door_state &= ~DOOR_OPEN_2;
4261 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4262 door_state &= ~DOOR_CLOSE_2;
4265 if (global.autoplay_leveldir)
4267 door_state |= DOOR_NO_DELAY;
4268 door_state &= ~DOOR_CLOSE_ALL;
4271 if (game_status == GAME_MODE_EDITOR)
4272 door_state |= DOOR_NO_DELAY;
4274 if (door_state & DOOR_ACTION)
4276 boolean door_panel_drawn[NUM_DOORS];
4277 boolean panel_has_doors[NUM_DOORS];
4278 boolean door_part_skip[MAX_DOOR_PARTS];
4279 boolean door_part_done[MAX_DOOR_PARTS];
4280 boolean door_part_done_all;
4281 int num_steps[MAX_DOOR_PARTS];
4282 int max_move_delay = 0; // delay for complete animations of all doors
4283 int max_step_delay = 0; // delay (ms) between two animation frames
4284 int num_move_steps = 0; // number of animation steps for all doors
4285 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4286 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4287 int current_move_delay = 0;
4291 for (i = 0; i < NUM_DOORS; i++)
4292 panel_has_doors[i] = FALSE;
4294 for (i = 0; i < MAX_DOOR_PARTS; i++)
4296 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4297 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4298 int door_token = dpc->door_token;
4300 door_part_done[i] = FALSE;
4301 door_part_skip[i] = (!(door_state & door_token) ||
4305 for (i = 0; i < MAX_DOOR_PARTS; i++)
4307 int nr = door_part_order[i].nr;
4308 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4309 struct DoorPartPosInfo *pos = dpc->pos;
4310 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4311 int door_token = dpc->door_token;
4312 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4313 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4314 int step_xoffset = ABS(pos->step_xoffset);
4315 int step_yoffset = ABS(pos->step_yoffset);
4316 int step_delay = pos->step_delay;
4317 int current_door_state = door_state & door_token;
4318 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4319 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4320 boolean part_opening = (is_panel ? door_closing : door_opening);
4321 int start_step = (part_opening ? pos->start_step_opening :
4322 pos->start_step_closing);
4323 float move_xsize = (step_xoffset ? g->width : 0);
4324 float move_ysize = (step_yoffset ? g->height : 0);
4325 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4326 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4327 int move_steps = (move_xsteps && move_ysteps ?
4328 MIN(move_xsteps, move_ysteps) :
4329 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4330 int move_delay = move_steps * step_delay;
4332 if (door_part_skip[nr])
4335 max_move_delay = MAX(max_move_delay, move_delay);
4336 max_step_delay = (max_step_delay == 0 ? step_delay :
4337 euclid(max_step_delay, step_delay));
4338 num_steps[nr] = move_steps;
4342 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4344 panel_has_doors[door_index] = TRUE;
4348 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4350 num_move_steps = max_move_delay / max_step_delay;
4351 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4353 door_delay_value = max_step_delay;
4355 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4357 start = num_move_steps - 1;
4361 /* opening door sound has priority over simultaneously closing door */
4362 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4363 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4364 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4365 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4368 for (k = start; k < num_move_steps; k++)
4370 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4372 door_part_done_all = TRUE;
4374 for (i = 0; i < NUM_DOORS; i++)
4375 door_panel_drawn[i] = FALSE;
4377 for (i = 0; i < MAX_DOOR_PARTS; i++)
4379 int nr = door_part_order[i].nr;
4380 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4381 struct DoorPartPosInfo *pos = dpc->pos;
4382 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4383 int door_token = dpc->door_token;
4384 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4385 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4386 boolean is_panel_and_door_has_closed = FALSE;
4387 struct Rect *door_rect = &door_rect_list[door_index];
4388 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4390 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4391 int current_door_state = door_state & door_token;
4392 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4393 boolean door_closing = !door_opening;
4394 boolean part_opening = (is_panel ? door_closing : door_opening);
4395 boolean part_closing = !part_opening;
4396 int start_step = (part_opening ? pos->start_step_opening :
4397 pos->start_step_closing);
4398 int step_delay = pos->step_delay;
4399 int step_factor = step_delay / max_step_delay;
4400 int k1 = (step_factor ? k / step_factor + 1 : k);
4401 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4402 int kk = MAX(0, k2);
4405 int src_x, src_y, src_xx, src_yy;
4406 int dst_x, dst_y, dst_xx, dst_yy;
4409 if (door_part_skip[nr])
4412 if (!(door_state & door_token))
4420 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4421 int kk_door = MAX(0, k2_door);
4422 int sync_frame = kk_door * door_delay_value;
4423 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4425 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4430 if (!door_panel_drawn[door_index])
4432 ClearRectangle(drawto, door_rect->x, door_rect->y,
4433 door_rect->width, door_rect->height);
4435 door_panel_drawn[door_index] = TRUE;
4438 // draw opening or closing door parts
4440 if (pos->step_xoffset < 0) // door part on right side
4443 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4446 if (dst_xx + width > door_rect->width)
4447 width = door_rect->width - dst_xx;
4449 else // door part on left side
4452 dst_xx = pos->x - kk * pos->step_xoffset;
4456 src_xx = ABS(dst_xx);
4460 width = g->width - src_xx;
4462 if (width > door_rect->width)
4463 width = door_rect->width;
4465 // printf("::: k == %d [%d] \n", k, start_step);
4468 if (pos->step_yoffset < 0) // door part on bottom side
4471 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4474 if (dst_yy + height > door_rect->height)
4475 height = door_rect->height - dst_yy;
4477 else // door part on top side
4480 dst_yy = pos->y - kk * pos->step_yoffset;
4484 src_yy = ABS(dst_yy);
4488 height = g->height - src_yy;
4491 src_x = g_src_x + src_xx;
4492 src_y = g_src_y + src_yy;
4494 dst_x = door_rect->x + dst_xx;
4495 dst_y = door_rect->y + dst_yy;
4497 is_panel_and_door_has_closed =
4500 panel_has_doors[door_index] &&
4501 k >= num_move_steps_doors_only - 1);
4503 if (width >= 0 && width <= g->width &&
4504 height >= 0 && height <= g->height &&
4505 !is_panel_and_door_has_closed)
4507 if (is_panel || !pos->draw_masked)
4508 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4511 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4515 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4517 if ((part_opening && (width < 0 || height < 0)) ||
4518 (part_closing && (width >= g->width && height >= g->height)))
4519 door_part_done[nr] = TRUE;
4521 // continue door part animations, but not panel after door has closed
4522 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4523 door_part_done_all = FALSE;
4526 if (!(door_state & DOOR_NO_DELAY))
4530 if (game_status == GAME_MODE_MAIN)
4533 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4535 current_move_delay += max_step_delay;
4538 if (door_part_done_all)
4543 if (door_state & DOOR_ACTION_1)
4544 door1 = door_state & DOOR_ACTION_1;
4545 if (door_state & DOOR_ACTION_2)
4546 door2 = door_state & DOOR_ACTION_2;
4548 // draw masked border over door area
4549 DrawMaskedBorder(REDRAW_DOOR_1);
4550 DrawMaskedBorder(REDRAW_DOOR_2);
4552 return (door1 | door2);
4555 static boolean useSpecialEditorDoor()
4557 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4558 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4560 // do not draw special editor door if editor border defined or redefined
4561 if (graphic_info[graphic].bitmap != NULL || redefined)
4564 // do not draw special editor door if global border defined to be empty
4565 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4568 // do not draw special editor door if viewport definitions do not match
4572 EY + EYSIZE != VY + VYSIZE)
4578 void DrawSpecialEditorDoor()
4580 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4581 int top_border_width = gfx1->width;
4582 int top_border_height = gfx1->height;
4583 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4584 int ex = EX - outer_border;
4585 int ey = EY - outer_border;
4586 int vy = VY - outer_border;
4587 int exsize = EXSIZE + 2 * outer_border;
4589 if (!useSpecialEditorDoor())
4592 /* draw bigger level editor toolbox window */
4593 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4594 top_border_width, top_border_height, ex, ey - top_border_height);
4595 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4596 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4598 redraw_mask |= REDRAW_ALL;
4601 void UndrawSpecialEditorDoor()
4603 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4604 int top_border_width = gfx1->width;
4605 int top_border_height = gfx1->height;
4606 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4607 int ex = EX - outer_border;
4608 int ey = EY - outer_border;
4609 int ey_top = ey - top_border_height;
4610 int exsize = EXSIZE + 2 * outer_border;
4611 int eysize = EYSIZE + 2 * outer_border;
4613 if (!useSpecialEditorDoor())
4616 /* draw normal tape recorder window */
4617 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4619 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4620 ex, ey_top, top_border_width, top_border_height,
4622 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4623 ex, ey, exsize, eysize, ex, ey);
4627 // if screen background is set to "[NONE]", clear editor toolbox window
4628 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4629 ClearRectangle(drawto, ex, ey, exsize, eysize);
4632 redraw_mask |= REDRAW_ALL;
4636 /* ---------- new tool button stuff ---------------------------------------- */
4641 struct TextPosInfo *pos;
4644 } toolbutton_info[NUM_TOOL_BUTTONS] =
4647 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4648 TOOL_CTRL_ID_YES, "yes"
4651 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4652 TOOL_CTRL_ID_NO, "no"
4655 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4656 TOOL_CTRL_ID_CONFIRM, "confirm"
4659 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4660 TOOL_CTRL_ID_PLAYER_1, "player 1"
4663 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4664 TOOL_CTRL_ID_PLAYER_2, "player 2"
4667 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4668 TOOL_CTRL_ID_PLAYER_3, "player 3"
4671 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4672 TOOL_CTRL_ID_PLAYER_4, "player 4"
4676 void CreateToolButtons()
4680 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4682 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4683 struct TextPosInfo *pos = toolbutton_info[i].pos;
4684 struct GadgetInfo *gi;
4685 Bitmap *deco_bitmap = None;
4686 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4687 unsigned int event_mask = GD_EVENT_RELEASED;
4690 int gd_x = gfx->src_x;
4691 int gd_y = gfx->src_y;
4692 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4693 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4696 if (global.use_envelope_request)
4697 setRequestPosition(&dx, &dy, TRUE);
4699 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4701 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4703 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4704 pos->size, &deco_bitmap, &deco_x, &deco_y);
4705 deco_xpos = (gfx->width - pos->size) / 2;
4706 deco_ypos = (gfx->height - pos->size) / 2;
4709 gi = CreateGadget(GDI_CUSTOM_ID, id,
4710 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4711 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4712 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4713 GDI_WIDTH, gfx->width,
4714 GDI_HEIGHT, gfx->height,
4715 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4716 GDI_STATE, GD_BUTTON_UNPRESSED,
4717 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4718 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4719 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4720 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4721 GDI_DECORATION_SIZE, pos->size, pos->size,
4722 GDI_DECORATION_SHIFTING, 1, 1,
4723 GDI_DIRECT_DRAW, FALSE,
4724 GDI_EVENT_MASK, event_mask,
4725 GDI_CALLBACK_ACTION, HandleToolButtons,
4729 Error(ERR_EXIT, "cannot create gadget");
4731 tool_gadget[id] = gi;
4735 void FreeToolButtons()
4739 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4740 FreeGadget(tool_gadget[i]);
4743 static void UnmapToolButtons()
4747 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4748 UnmapGadget(tool_gadget[i]);
4751 static void HandleToolButtons(struct GadgetInfo *gi)
4753 request_gadget_id = gi->custom_id;
4756 static struct Mapping_EM_to_RND_object
4759 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4760 boolean is_backside; /* backside of moving element */
4766 em_object_mapping_list[] =
4769 Xblank, TRUE, FALSE,
4773 Yacid_splash_eB, FALSE, FALSE,
4774 EL_ACID_SPLASH_RIGHT, -1, -1
4777 Yacid_splash_wB, FALSE, FALSE,
4778 EL_ACID_SPLASH_LEFT, -1, -1
4781 #ifdef EM_ENGINE_BAD_ROLL
4783 Xstone_force_e, FALSE, FALSE,
4784 EL_ROCK, -1, MV_BIT_RIGHT
4787 Xstone_force_w, FALSE, FALSE,
4788 EL_ROCK, -1, MV_BIT_LEFT
4791 Xnut_force_e, FALSE, FALSE,
4792 EL_NUT, -1, MV_BIT_RIGHT
4795 Xnut_force_w, FALSE, FALSE,
4796 EL_NUT, -1, MV_BIT_LEFT
4799 Xspring_force_e, FALSE, FALSE,
4800 EL_SPRING, -1, MV_BIT_RIGHT
4803 Xspring_force_w, FALSE, FALSE,
4804 EL_SPRING, -1, MV_BIT_LEFT
4807 Xemerald_force_e, FALSE, FALSE,
4808 EL_EMERALD, -1, MV_BIT_RIGHT
4811 Xemerald_force_w, FALSE, FALSE,
4812 EL_EMERALD, -1, MV_BIT_LEFT
4815 Xdiamond_force_e, FALSE, FALSE,
4816 EL_DIAMOND, -1, MV_BIT_RIGHT
4819 Xdiamond_force_w, FALSE, FALSE,
4820 EL_DIAMOND, -1, MV_BIT_LEFT
4823 Xbomb_force_e, FALSE, FALSE,
4824 EL_BOMB, -1, MV_BIT_RIGHT
4827 Xbomb_force_w, FALSE, FALSE,
4828 EL_BOMB, -1, MV_BIT_LEFT
4830 #endif /* EM_ENGINE_BAD_ROLL */
4833 Xstone, TRUE, FALSE,
4837 Xstone_pause, FALSE, FALSE,
4841 Xstone_fall, FALSE, FALSE,
4845 Ystone_s, FALSE, FALSE,
4846 EL_ROCK, ACTION_FALLING, -1
4849 Ystone_sB, FALSE, TRUE,
4850 EL_ROCK, ACTION_FALLING, -1
4853 Ystone_e, FALSE, FALSE,
4854 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4857 Ystone_eB, FALSE, TRUE,
4858 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4861 Ystone_w, FALSE, FALSE,
4862 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4865 Ystone_wB, FALSE, TRUE,
4866 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4873 Xnut_pause, FALSE, FALSE,
4877 Xnut_fall, FALSE, FALSE,
4881 Ynut_s, FALSE, FALSE,
4882 EL_NUT, ACTION_FALLING, -1
4885 Ynut_sB, FALSE, TRUE,
4886 EL_NUT, ACTION_FALLING, -1
4889 Ynut_e, FALSE, FALSE,
4890 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4893 Ynut_eB, FALSE, TRUE,
4894 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4897 Ynut_w, FALSE, FALSE,
4898 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4901 Ynut_wB, FALSE, TRUE,
4902 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4905 Xbug_n, TRUE, FALSE,
4909 Xbug_e, TRUE, FALSE,
4910 EL_BUG_RIGHT, -1, -1
4913 Xbug_s, TRUE, FALSE,
4917 Xbug_w, TRUE, FALSE,
4921 Xbug_gon, FALSE, FALSE,
4925 Xbug_goe, FALSE, FALSE,
4926 EL_BUG_RIGHT, -1, -1
4929 Xbug_gos, FALSE, FALSE,
4933 Xbug_gow, FALSE, FALSE,
4937 Ybug_n, FALSE, FALSE,
4938 EL_BUG, ACTION_MOVING, MV_BIT_UP
4941 Ybug_nB, FALSE, TRUE,
4942 EL_BUG, ACTION_MOVING, MV_BIT_UP
4945 Ybug_e, FALSE, FALSE,
4946 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4949 Ybug_eB, FALSE, TRUE,
4950 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4953 Ybug_s, FALSE, FALSE,
4954 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4957 Ybug_sB, FALSE, TRUE,
4958 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4961 Ybug_w, FALSE, FALSE,
4962 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4965 Ybug_wB, FALSE, TRUE,
4966 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4969 Ybug_w_n, FALSE, FALSE,
4970 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4973 Ybug_n_e, FALSE, FALSE,
4974 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4977 Ybug_e_s, FALSE, FALSE,
4978 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4981 Ybug_s_w, FALSE, FALSE,
4982 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4985 Ybug_e_n, FALSE, FALSE,
4986 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4989 Ybug_s_e, FALSE, FALSE,
4990 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4993 Ybug_w_s, FALSE, FALSE,
4994 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4997 Ybug_n_w, FALSE, FALSE,
4998 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5001 Ybug_stone, FALSE, FALSE,
5002 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5005 Ybug_spring, FALSE, FALSE,
5006 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5009 Xtank_n, TRUE, FALSE,
5010 EL_SPACESHIP_UP, -1, -1
5013 Xtank_e, TRUE, FALSE,
5014 EL_SPACESHIP_RIGHT, -1, -1
5017 Xtank_s, TRUE, FALSE,
5018 EL_SPACESHIP_DOWN, -1, -1
5021 Xtank_w, TRUE, FALSE,
5022 EL_SPACESHIP_LEFT, -1, -1
5025 Xtank_gon, FALSE, FALSE,
5026 EL_SPACESHIP_UP, -1, -1
5029 Xtank_goe, FALSE, FALSE,
5030 EL_SPACESHIP_RIGHT, -1, -1
5033 Xtank_gos, FALSE, FALSE,
5034 EL_SPACESHIP_DOWN, -1, -1
5037 Xtank_gow, FALSE, FALSE,
5038 EL_SPACESHIP_LEFT, -1, -1
5041 Ytank_n, FALSE, FALSE,
5042 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5045 Ytank_nB, FALSE, TRUE,
5046 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5049 Ytank_e, FALSE, FALSE,
5050 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5053 Ytank_eB, FALSE, TRUE,
5054 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5057 Ytank_s, FALSE, FALSE,
5058 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5061 Ytank_sB, FALSE, TRUE,
5062 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5065 Ytank_w, FALSE, FALSE,
5066 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5069 Ytank_wB, FALSE, TRUE,
5070 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5073 Ytank_w_n, FALSE, FALSE,
5074 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5077 Ytank_n_e, FALSE, FALSE,
5078 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5081 Ytank_e_s, FALSE, FALSE,
5082 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5085 Ytank_s_w, FALSE, FALSE,
5086 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5089 Ytank_e_n, FALSE, FALSE,
5090 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5093 Ytank_s_e, FALSE, FALSE,
5094 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5097 Ytank_w_s, FALSE, FALSE,
5098 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5101 Ytank_n_w, FALSE, FALSE,
5102 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5105 Ytank_stone, FALSE, FALSE,
5106 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5109 Ytank_spring, FALSE, FALSE,
5110 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5113 Xandroid, TRUE, FALSE,
5114 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5117 Xandroid_1_n, FALSE, FALSE,
5118 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5121 Xandroid_2_n, FALSE, FALSE,
5122 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5125 Xandroid_1_e, FALSE, FALSE,
5126 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5129 Xandroid_2_e, FALSE, FALSE,
5130 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5133 Xandroid_1_w, FALSE, FALSE,
5134 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5137 Xandroid_2_w, FALSE, FALSE,
5138 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5141 Xandroid_1_s, FALSE, FALSE,
5142 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5145 Xandroid_2_s, FALSE, FALSE,
5146 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5149 Yandroid_n, FALSE, FALSE,
5150 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5153 Yandroid_nB, FALSE, TRUE,
5154 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5157 Yandroid_ne, FALSE, FALSE,
5158 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5161 Yandroid_neB, FALSE, TRUE,
5162 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5165 Yandroid_e, FALSE, FALSE,
5166 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5169 Yandroid_eB, FALSE, TRUE,
5170 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5173 Yandroid_se, FALSE, FALSE,
5174 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5177 Yandroid_seB, FALSE, TRUE,
5178 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5181 Yandroid_s, FALSE, FALSE,
5182 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5185 Yandroid_sB, FALSE, TRUE,
5186 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5189 Yandroid_sw, FALSE, FALSE,
5190 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5193 Yandroid_swB, FALSE, TRUE,
5194 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5197 Yandroid_w, FALSE, FALSE,
5198 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5201 Yandroid_wB, FALSE, TRUE,
5202 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5205 Yandroid_nw, FALSE, FALSE,
5206 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5209 Yandroid_nwB, FALSE, TRUE,
5210 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5213 Xspring, TRUE, FALSE,
5217 Xspring_pause, FALSE, FALSE,
5221 Xspring_e, FALSE, FALSE,
5225 Xspring_w, FALSE, FALSE,
5229 Xspring_fall, FALSE, FALSE,
5233 Yspring_s, FALSE, FALSE,
5234 EL_SPRING, ACTION_FALLING, -1
5237 Yspring_sB, FALSE, TRUE,
5238 EL_SPRING, ACTION_FALLING, -1
5241 Yspring_e, FALSE, FALSE,
5242 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5245 Yspring_eB, FALSE, TRUE,
5246 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5249 Yspring_w, FALSE, FALSE,
5250 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5253 Yspring_wB, FALSE, TRUE,
5254 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5257 Yspring_kill_e, FALSE, FALSE,
5258 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5261 Yspring_kill_eB, FALSE, TRUE,
5262 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5265 Yspring_kill_w, FALSE, FALSE,
5266 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5269 Yspring_kill_wB, FALSE, TRUE,
5270 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5273 Xeater_n, TRUE, FALSE,
5274 EL_YAMYAM_UP, -1, -1
5277 Xeater_e, TRUE, FALSE,
5278 EL_YAMYAM_RIGHT, -1, -1
5281 Xeater_w, TRUE, FALSE,
5282 EL_YAMYAM_LEFT, -1, -1
5285 Xeater_s, TRUE, FALSE,
5286 EL_YAMYAM_DOWN, -1, -1
5289 Yeater_n, FALSE, FALSE,
5290 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5293 Yeater_nB, FALSE, TRUE,
5294 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5297 Yeater_e, FALSE, FALSE,
5298 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5301 Yeater_eB, FALSE, TRUE,
5302 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5305 Yeater_s, FALSE, FALSE,
5306 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5309 Yeater_sB, FALSE, TRUE,
5310 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5313 Yeater_w, FALSE, FALSE,
5314 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5317 Yeater_wB, FALSE, TRUE,
5318 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5321 Yeater_stone, FALSE, FALSE,
5322 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5325 Yeater_spring, FALSE, FALSE,
5326 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5329 Xalien, TRUE, FALSE,
5333 Xalien_pause, FALSE, FALSE,
5337 Yalien_n, FALSE, FALSE,
5338 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5341 Yalien_nB, FALSE, TRUE,
5342 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5345 Yalien_e, FALSE, FALSE,
5346 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5349 Yalien_eB, FALSE, TRUE,
5350 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5353 Yalien_s, FALSE, FALSE,
5354 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5357 Yalien_sB, FALSE, TRUE,
5358 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5361 Yalien_w, FALSE, FALSE,
5362 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5365 Yalien_wB, FALSE, TRUE,
5366 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5369 Yalien_stone, FALSE, FALSE,
5370 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5373 Yalien_spring, FALSE, FALSE,
5374 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5377 Xemerald, TRUE, FALSE,
5381 Xemerald_pause, FALSE, FALSE,
5385 Xemerald_fall, FALSE, FALSE,
5389 Xemerald_shine, FALSE, FALSE,
5390 EL_EMERALD, ACTION_TWINKLING, -1
5393 Yemerald_s, FALSE, FALSE,
5394 EL_EMERALD, ACTION_FALLING, -1
5397 Yemerald_sB, FALSE, TRUE,
5398 EL_EMERALD, ACTION_FALLING, -1
5401 Yemerald_e, FALSE, FALSE,
5402 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5405 Yemerald_eB, FALSE, TRUE,
5406 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5409 Yemerald_w, FALSE, FALSE,
5410 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5413 Yemerald_wB, FALSE, TRUE,
5414 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5417 Yemerald_eat, FALSE, FALSE,
5418 EL_EMERALD, ACTION_COLLECTING, -1
5421 Yemerald_stone, FALSE, FALSE,
5422 EL_NUT, ACTION_BREAKING, -1
5425 Xdiamond, TRUE, FALSE,
5429 Xdiamond_pause, FALSE, FALSE,
5433 Xdiamond_fall, FALSE, FALSE,
5437 Xdiamond_shine, FALSE, FALSE,
5438 EL_DIAMOND, ACTION_TWINKLING, -1
5441 Ydiamond_s, FALSE, FALSE,
5442 EL_DIAMOND, ACTION_FALLING, -1
5445 Ydiamond_sB, FALSE, TRUE,
5446 EL_DIAMOND, ACTION_FALLING, -1
5449 Ydiamond_e, FALSE, FALSE,
5450 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5453 Ydiamond_eB, FALSE, TRUE,
5454 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5457 Ydiamond_w, FALSE, FALSE,
5458 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5461 Ydiamond_wB, FALSE, TRUE,
5462 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5465 Ydiamond_eat, FALSE, FALSE,
5466 EL_DIAMOND, ACTION_COLLECTING, -1
5469 Ydiamond_stone, FALSE, FALSE,
5470 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5473 Xdrip_fall, TRUE, FALSE,
5474 EL_AMOEBA_DROP, -1, -1
5477 Xdrip_stretch, FALSE, FALSE,
5478 EL_AMOEBA_DROP, ACTION_FALLING, -1
5481 Xdrip_stretchB, FALSE, TRUE,
5482 EL_AMOEBA_DROP, ACTION_FALLING, -1
5485 Xdrip_eat, FALSE, FALSE,
5486 EL_AMOEBA_DROP, ACTION_GROWING, -1
5489 Ydrip_s1, FALSE, FALSE,
5490 EL_AMOEBA_DROP, ACTION_FALLING, -1
5493 Ydrip_s1B, FALSE, TRUE,
5494 EL_AMOEBA_DROP, ACTION_FALLING, -1
5497 Ydrip_s2, FALSE, FALSE,
5498 EL_AMOEBA_DROP, ACTION_FALLING, -1
5501 Ydrip_s2B, FALSE, TRUE,
5502 EL_AMOEBA_DROP, ACTION_FALLING, -1
5509 Xbomb_pause, FALSE, FALSE,
5513 Xbomb_fall, FALSE, FALSE,
5517 Ybomb_s, FALSE, FALSE,
5518 EL_BOMB, ACTION_FALLING, -1
5521 Ybomb_sB, FALSE, TRUE,
5522 EL_BOMB, ACTION_FALLING, -1
5525 Ybomb_e, FALSE, FALSE,
5526 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5529 Ybomb_eB, FALSE, TRUE,
5530 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5533 Ybomb_w, FALSE, FALSE,
5534 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5537 Ybomb_wB, FALSE, TRUE,
5538 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5541 Ybomb_eat, FALSE, FALSE,
5542 EL_BOMB, ACTION_ACTIVATING, -1
5545 Xballoon, TRUE, FALSE,
5549 Yballoon_n, FALSE, FALSE,
5550 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5553 Yballoon_nB, FALSE, TRUE,
5554 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5557 Yballoon_e, FALSE, FALSE,
5558 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5561 Yballoon_eB, FALSE, TRUE,
5562 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5565 Yballoon_s, FALSE, FALSE,
5566 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5569 Yballoon_sB, FALSE, TRUE,
5570 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5573 Yballoon_w, FALSE, FALSE,
5574 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5577 Yballoon_wB, FALSE, TRUE,
5578 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5581 Xgrass, TRUE, FALSE,
5582 EL_EMC_GRASS, -1, -1
5585 Ygrass_nB, FALSE, FALSE,
5586 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5589 Ygrass_eB, FALSE, FALSE,
5590 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5593 Ygrass_sB, FALSE, FALSE,
5594 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5597 Ygrass_wB, FALSE, FALSE,
5598 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5605 Ydirt_nB, FALSE, FALSE,
5606 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5609 Ydirt_eB, FALSE, FALSE,
5610 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5613 Ydirt_sB, FALSE, FALSE,
5614 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5617 Ydirt_wB, FALSE, FALSE,
5618 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5621 Xacid_ne, TRUE, FALSE,
5622 EL_ACID_POOL_TOPRIGHT, -1, -1
5625 Xacid_se, TRUE, FALSE,
5626 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5629 Xacid_s, TRUE, FALSE,
5630 EL_ACID_POOL_BOTTOM, -1, -1
5633 Xacid_sw, TRUE, FALSE,
5634 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5637 Xacid_nw, TRUE, FALSE,
5638 EL_ACID_POOL_TOPLEFT, -1, -1
5641 Xacid_1, TRUE, FALSE,
5645 Xacid_2, FALSE, FALSE,
5649 Xacid_3, FALSE, FALSE,
5653 Xacid_4, FALSE, FALSE,
5657 Xacid_5, FALSE, FALSE,
5661 Xacid_6, FALSE, FALSE,
5665 Xacid_7, FALSE, FALSE,
5669 Xacid_8, FALSE, FALSE,
5673 Xball_1, TRUE, FALSE,
5674 EL_EMC_MAGIC_BALL, -1, -1
5677 Xball_1B, FALSE, FALSE,
5678 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5681 Xball_2, FALSE, FALSE,
5682 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5685 Xball_2B, FALSE, FALSE,
5686 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5689 Yball_eat, FALSE, FALSE,
5690 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5693 Ykey_1_eat, FALSE, FALSE,
5694 EL_EM_KEY_1, ACTION_COLLECTING, -1
5697 Ykey_2_eat, FALSE, FALSE,
5698 EL_EM_KEY_2, ACTION_COLLECTING, -1
5701 Ykey_3_eat, FALSE, FALSE,
5702 EL_EM_KEY_3, ACTION_COLLECTING, -1
5705 Ykey_4_eat, FALSE, FALSE,
5706 EL_EM_KEY_4, ACTION_COLLECTING, -1
5709 Ykey_5_eat, FALSE, FALSE,
5710 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5713 Ykey_6_eat, FALSE, FALSE,
5714 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5717 Ykey_7_eat, FALSE, FALSE,
5718 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5721 Ykey_8_eat, FALSE, FALSE,
5722 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5725 Ylenses_eat, FALSE, FALSE,
5726 EL_EMC_LENSES, ACTION_COLLECTING, -1
5729 Ymagnify_eat, FALSE, FALSE,
5730 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5733 Ygrass_eat, FALSE, FALSE,
5734 EL_EMC_GRASS, ACTION_SNAPPING, -1
5737 Ydirt_eat, FALSE, FALSE,
5738 EL_SAND, ACTION_SNAPPING, -1
5741 Xgrow_ns, TRUE, FALSE,
5742 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5745 Ygrow_ns_eat, FALSE, FALSE,
5746 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5749 Xgrow_ew, TRUE, FALSE,
5750 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5753 Ygrow_ew_eat, FALSE, FALSE,
5754 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5757 Xwonderwall, TRUE, FALSE,
5758 EL_MAGIC_WALL, -1, -1
5761 XwonderwallB, FALSE, FALSE,
5762 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5765 Xamoeba_1, TRUE, FALSE,
5766 EL_AMOEBA_DRY, ACTION_OTHER, -1
5769 Xamoeba_2, FALSE, FALSE,
5770 EL_AMOEBA_DRY, ACTION_OTHER, -1
5773 Xamoeba_3, FALSE, FALSE,
5774 EL_AMOEBA_DRY, ACTION_OTHER, -1
5777 Xamoeba_4, FALSE, FALSE,
5778 EL_AMOEBA_DRY, ACTION_OTHER, -1
5781 Xamoeba_5, TRUE, FALSE,
5782 EL_AMOEBA_WET, ACTION_OTHER, -1
5785 Xamoeba_6, FALSE, FALSE,
5786 EL_AMOEBA_WET, ACTION_OTHER, -1
5789 Xamoeba_7, FALSE, FALSE,
5790 EL_AMOEBA_WET, ACTION_OTHER, -1
5793 Xamoeba_8, FALSE, FALSE,
5794 EL_AMOEBA_WET, ACTION_OTHER, -1
5797 Xdoor_1, TRUE, FALSE,
5798 EL_EM_GATE_1, -1, -1
5801 Xdoor_2, TRUE, FALSE,
5802 EL_EM_GATE_2, -1, -1
5805 Xdoor_3, TRUE, FALSE,
5806 EL_EM_GATE_3, -1, -1
5809 Xdoor_4, TRUE, FALSE,
5810 EL_EM_GATE_4, -1, -1
5813 Xdoor_5, TRUE, FALSE,
5814 EL_EMC_GATE_5, -1, -1
5817 Xdoor_6, TRUE, FALSE,
5818 EL_EMC_GATE_6, -1, -1
5821 Xdoor_7, TRUE, FALSE,
5822 EL_EMC_GATE_7, -1, -1
5825 Xdoor_8, TRUE, FALSE,
5826 EL_EMC_GATE_8, -1, -1
5829 Xkey_1, TRUE, FALSE,
5833 Xkey_2, TRUE, FALSE,
5837 Xkey_3, TRUE, FALSE,
5841 Xkey_4, TRUE, FALSE,
5845 Xkey_5, TRUE, FALSE,
5846 EL_EMC_KEY_5, -1, -1
5849 Xkey_6, TRUE, FALSE,
5850 EL_EMC_KEY_6, -1, -1
5853 Xkey_7, TRUE, FALSE,
5854 EL_EMC_KEY_7, -1, -1
5857 Xkey_8, TRUE, FALSE,
5858 EL_EMC_KEY_8, -1, -1
5861 Xwind_n, TRUE, FALSE,
5862 EL_BALLOON_SWITCH_UP, -1, -1
5865 Xwind_e, TRUE, FALSE,
5866 EL_BALLOON_SWITCH_RIGHT, -1, -1
5869 Xwind_s, TRUE, FALSE,
5870 EL_BALLOON_SWITCH_DOWN, -1, -1
5873 Xwind_w, TRUE, FALSE,
5874 EL_BALLOON_SWITCH_LEFT, -1, -1
5877 Xwind_nesw, TRUE, FALSE,
5878 EL_BALLOON_SWITCH_ANY, -1, -1
5881 Xwind_stop, TRUE, FALSE,
5882 EL_BALLOON_SWITCH_NONE, -1, -1
5886 EL_EM_EXIT_CLOSED, -1, -1
5889 Xexit_1, TRUE, FALSE,
5890 EL_EM_EXIT_OPEN, -1, -1
5893 Xexit_2, FALSE, FALSE,
5894 EL_EM_EXIT_OPEN, -1, -1
5897 Xexit_3, FALSE, FALSE,
5898 EL_EM_EXIT_OPEN, -1, -1
5901 Xdynamite, TRUE, FALSE,
5902 EL_EM_DYNAMITE, -1, -1
5905 Ydynamite_eat, FALSE, FALSE,
5906 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5909 Xdynamite_1, TRUE, FALSE,
5910 EL_EM_DYNAMITE_ACTIVE, -1, -1
5913 Xdynamite_2, FALSE, FALSE,
5914 EL_EM_DYNAMITE_ACTIVE, -1, -1
5917 Xdynamite_3, FALSE, FALSE,
5918 EL_EM_DYNAMITE_ACTIVE, -1, -1
5921 Xdynamite_4, FALSE, FALSE,
5922 EL_EM_DYNAMITE_ACTIVE, -1, -1
5925 Xbumper, TRUE, FALSE,
5926 EL_EMC_SPRING_BUMPER, -1, -1
5929 XbumperB, FALSE, FALSE,
5930 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5933 Xwheel, TRUE, FALSE,
5934 EL_ROBOT_WHEEL, -1, -1
5937 XwheelB, FALSE, FALSE,
5938 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5941 Xswitch, TRUE, FALSE,
5942 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5945 XswitchB, FALSE, FALSE,
5946 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5950 EL_QUICKSAND_EMPTY, -1, -1
5953 Xsand_stone, TRUE, FALSE,
5954 EL_QUICKSAND_FULL, -1, -1
5957 Xsand_stonein_1, FALSE, TRUE,
5958 EL_ROCK, ACTION_FILLING, -1
5961 Xsand_stonein_2, FALSE, TRUE,
5962 EL_ROCK, ACTION_FILLING, -1
5965 Xsand_stonein_3, FALSE, TRUE,
5966 EL_ROCK, ACTION_FILLING, -1
5969 Xsand_stonein_4, FALSE, TRUE,
5970 EL_ROCK, ACTION_FILLING, -1
5973 Xsand_stonesand_1, FALSE, FALSE,
5974 EL_QUICKSAND_EMPTYING, -1, -1
5977 Xsand_stonesand_2, FALSE, FALSE,
5978 EL_QUICKSAND_EMPTYING, -1, -1
5981 Xsand_stonesand_3, FALSE, FALSE,
5982 EL_QUICKSAND_EMPTYING, -1, -1
5985 Xsand_stonesand_4, FALSE, FALSE,
5986 EL_QUICKSAND_EMPTYING, -1, -1
5989 Xsand_stonesand_quickout_1, FALSE, FALSE,
5990 EL_QUICKSAND_EMPTYING, -1, -1
5993 Xsand_stonesand_quickout_2, FALSE, FALSE,
5994 EL_QUICKSAND_EMPTYING, -1, -1
5997 Xsand_stoneout_1, FALSE, FALSE,
5998 EL_ROCK, ACTION_EMPTYING, -1
6001 Xsand_stoneout_2, FALSE, FALSE,
6002 EL_ROCK, ACTION_EMPTYING, -1
6005 Xsand_sandstone_1, FALSE, FALSE,
6006 EL_QUICKSAND_FILLING, -1, -1
6009 Xsand_sandstone_2, FALSE, FALSE,
6010 EL_QUICKSAND_FILLING, -1, -1
6013 Xsand_sandstone_3, FALSE, FALSE,
6014 EL_QUICKSAND_FILLING, -1, -1
6017 Xsand_sandstone_4, FALSE, FALSE,
6018 EL_QUICKSAND_FILLING, -1, -1
6021 Xplant, TRUE, FALSE,
6022 EL_EMC_PLANT, -1, -1
6025 Yplant, FALSE, FALSE,
6026 EL_EMC_PLANT, -1, -1
6029 Xlenses, TRUE, FALSE,
6030 EL_EMC_LENSES, -1, -1
6033 Xmagnify, TRUE, FALSE,
6034 EL_EMC_MAGNIFIER, -1, -1
6037 Xdripper, TRUE, FALSE,
6038 EL_EMC_DRIPPER, -1, -1
6041 XdripperB, FALSE, FALSE,
6042 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6045 Xfake_blank, TRUE, FALSE,
6046 EL_INVISIBLE_WALL, -1, -1
6049 Xfake_blankB, FALSE, FALSE,
6050 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6053 Xfake_grass, TRUE, FALSE,
6054 EL_EMC_FAKE_GRASS, -1, -1
6057 Xfake_grassB, FALSE, FALSE,
6058 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6061 Xfake_door_1, TRUE, FALSE,
6062 EL_EM_GATE_1_GRAY, -1, -1
6065 Xfake_door_2, TRUE, FALSE,
6066 EL_EM_GATE_2_GRAY, -1, -1
6069 Xfake_door_3, TRUE, FALSE,
6070 EL_EM_GATE_3_GRAY, -1, -1
6073 Xfake_door_4, TRUE, FALSE,
6074 EL_EM_GATE_4_GRAY, -1, -1
6077 Xfake_door_5, TRUE, FALSE,
6078 EL_EMC_GATE_5_GRAY, -1, -1
6081 Xfake_door_6, TRUE, FALSE,
6082 EL_EMC_GATE_6_GRAY, -1, -1
6085 Xfake_door_7, TRUE, FALSE,
6086 EL_EMC_GATE_7_GRAY, -1, -1
6089 Xfake_door_8, TRUE, FALSE,
6090 EL_EMC_GATE_8_GRAY, -1, -1
6093 Xfake_acid_1, TRUE, FALSE,
6094 EL_EMC_FAKE_ACID, -1, -1
6097 Xfake_acid_2, FALSE, FALSE,
6098 EL_EMC_FAKE_ACID, -1, -1
6101 Xfake_acid_3, FALSE, FALSE,
6102 EL_EMC_FAKE_ACID, -1, -1
6105 Xfake_acid_4, FALSE, FALSE,
6106 EL_EMC_FAKE_ACID, -1, -1
6109 Xfake_acid_5, FALSE, FALSE,
6110 EL_EMC_FAKE_ACID, -1, -1
6113 Xfake_acid_6, FALSE, FALSE,
6114 EL_EMC_FAKE_ACID, -1, -1
6117 Xfake_acid_7, FALSE, FALSE,
6118 EL_EMC_FAKE_ACID, -1, -1
6121 Xfake_acid_8, FALSE, FALSE,
6122 EL_EMC_FAKE_ACID, -1, -1
6125 Xsteel_1, TRUE, FALSE,
6126 EL_STEELWALL, -1, -1
6129 Xsteel_2, TRUE, FALSE,
6130 EL_EMC_STEELWALL_2, -1, -1
6133 Xsteel_3, TRUE, FALSE,
6134 EL_EMC_STEELWALL_3, -1, -1
6137 Xsteel_4, TRUE, FALSE,
6138 EL_EMC_STEELWALL_4, -1, -1
6141 Xwall_1, TRUE, FALSE,
6145 Xwall_2, TRUE, FALSE,
6146 EL_EMC_WALL_14, -1, -1
6149 Xwall_3, TRUE, FALSE,
6150 EL_EMC_WALL_15, -1, -1
6153 Xwall_4, TRUE, FALSE,
6154 EL_EMC_WALL_16, -1, -1
6157 Xround_wall_1, TRUE, FALSE,
6158 EL_WALL_SLIPPERY, -1, -1
6161 Xround_wall_2, TRUE, FALSE,
6162 EL_EMC_WALL_SLIPPERY_2, -1, -1
6165 Xround_wall_3, TRUE, FALSE,
6166 EL_EMC_WALL_SLIPPERY_3, -1, -1
6169 Xround_wall_4, TRUE, FALSE,
6170 EL_EMC_WALL_SLIPPERY_4, -1, -1
6173 Xdecor_1, TRUE, FALSE,
6174 EL_EMC_WALL_8, -1, -1
6177 Xdecor_2, TRUE, FALSE,
6178 EL_EMC_WALL_6, -1, -1
6181 Xdecor_3, TRUE, FALSE,
6182 EL_EMC_WALL_4, -1, -1
6185 Xdecor_4, TRUE, FALSE,
6186 EL_EMC_WALL_7, -1, -1
6189 Xdecor_5, TRUE, FALSE,
6190 EL_EMC_WALL_5, -1, -1
6193 Xdecor_6, TRUE, FALSE,
6194 EL_EMC_WALL_9, -1, -1
6197 Xdecor_7, TRUE, FALSE,
6198 EL_EMC_WALL_10, -1, -1
6201 Xdecor_8, TRUE, FALSE,
6202 EL_EMC_WALL_1, -1, -1
6205 Xdecor_9, TRUE, FALSE,
6206 EL_EMC_WALL_2, -1, -1
6209 Xdecor_10, TRUE, FALSE,
6210 EL_EMC_WALL_3, -1, -1
6213 Xdecor_11, TRUE, FALSE,
6214 EL_EMC_WALL_11, -1, -1
6217 Xdecor_12, TRUE, FALSE,
6218 EL_EMC_WALL_12, -1, -1
6221 Xalpha_0, TRUE, FALSE,
6222 EL_CHAR('0'), -1, -1
6225 Xalpha_1, TRUE, FALSE,
6226 EL_CHAR('1'), -1, -1
6229 Xalpha_2, TRUE, FALSE,
6230 EL_CHAR('2'), -1, -1
6233 Xalpha_3, TRUE, FALSE,
6234 EL_CHAR('3'), -1, -1
6237 Xalpha_4, TRUE, FALSE,
6238 EL_CHAR('4'), -1, -1
6241 Xalpha_5, TRUE, FALSE,
6242 EL_CHAR('5'), -1, -1
6245 Xalpha_6, TRUE, FALSE,
6246 EL_CHAR('6'), -1, -1
6249 Xalpha_7, TRUE, FALSE,
6250 EL_CHAR('7'), -1, -1
6253 Xalpha_8, TRUE, FALSE,
6254 EL_CHAR('8'), -1, -1
6257 Xalpha_9, TRUE, FALSE,
6258 EL_CHAR('9'), -1, -1
6261 Xalpha_excla, TRUE, FALSE,
6262 EL_CHAR('!'), -1, -1
6265 Xalpha_quote, TRUE, FALSE,
6266 EL_CHAR('"'), -1, -1
6269 Xalpha_comma, TRUE, FALSE,
6270 EL_CHAR(','), -1, -1
6273 Xalpha_minus, TRUE, FALSE,
6274 EL_CHAR('-'), -1, -1
6277 Xalpha_perio, TRUE, FALSE,
6278 EL_CHAR('.'), -1, -1
6281 Xalpha_colon, TRUE, FALSE,
6282 EL_CHAR(':'), -1, -1
6285 Xalpha_quest, TRUE, FALSE,
6286 EL_CHAR('?'), -1, -1
6289 Xalpha_a, TRUE, FALSE,
6290 EL_CHAR('A'), -1, -1
6293 Xalpha_b, TRUE, FALSE,
6294 EL_CHAR('B'), -1, -1
6297 Xalpha_c, TRUE, FALSE,
6298 EL_CHAR('C'), -1, -1
6301 Xalpha_d, TRUE, FALSE,
6302 EL_CHAR('D'), -1, -1
6305 Xalpha_e, TRUE, FALSE,
6306 EL_CHAR('E'), -1, -1
6309 Xalpha_f, TRUE, FALSE,
6310 EL_CHAR('F'), -1, -1
6313 Xalpha_g, TRUE, FALSE,
6314 EL_CHAR('G'), -1, -1
6317 Xalpha_h, TRUE, FALSE,
6318 EL_CHAR('H'), -1, -1
6321 Xalpha_i, TRUE, FALSE,
6322 EL_CHAR('I'), -1, -1
6325 Xalpha_j, TRUE, FALSE,
6326 EL_CHAR('J'), -1, -1
6329 Xalpha_k, TRUE, FALSE,
6330 EL_CHAR('K'), -1, -1
6333 Xalpha_l, TRUE, FALSE,
6334 EL_CHAR('L'), -1, -1
6337 Xalpha_m, TRUE, FALSE,
6338 EL_CHAR('M'), -1, -1
6341 Xalpha_n, TRUE, FALSE,
6342 EL_CHAR('N'), -1, -1
6345 Xalpha_o, TRUE, FALSE,
6346 EL_CHAR('O'), -1, -1
6349 Xalpha_p, TRUE, FALSE,
6350 EL_CHAR('P'), -1, -1
6353 Xalpha_q, TRUE, FALSE,
6354 EL_CHAR('Q'), -1, -1
6357 Xalpha_r, TRUE, FALSE,
6358 EL_CHAR('R'), -1, -1
6361 Xalpha_s, TRUE, FALSE,
6362 EL_CHAR('S'), -1, -1
6365 Xalpha_t, TRUE, FALSE,
6366 EL_CHAR('T'), -1, -1
6369 Xalpha_u, TRUE, FALSE,
6370 EL_CHAR('U'), -1, -1
6373 Xalpha_v, TRUE, FALSE,
6374 EL_CHAR('V'), -1, -1
6377 Xalpha_w, TRUE, FALSE,
6378 EL_CHAR('W'), -1, -1
6381 Xalpha_x, TRUE, FALSE,
6382 EL_CHAR('X'), -1, -1
6385 Xalpha_y, TRUE, FALSE,
6386 EL_CHAR('Y'), -1, -1
6389 Xalpha_z, TRUE, FALSE,
6390 EL_CHAR('Z'), -1, -1
6393 Xalpha_arrow_e, TRUE, FALSE,
6394 EL_CHAR('>'), -1, -1
6397 Xalpha_arrow_w, TRUE, FALSE,
6398 EL_CHAR('<'), -1, -1
6401 Xalpha_copyr, TRUE, FALSE,
6402 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6406 Xboom_bug, FALSE, FALSE,
6407 EL_BUG, ACTION_EXPLODING, -1
6410 Xboom_bomb, FALSE, FALSE,
6411 EL_BOMB, ACTION_EXPLODING, -1
6414 Xboom_android, FALSE, FALSE,
6415 EL_EMC_ANDROID, ACTION_OTHER, -1
6418 Xboom_1, FALSE, FALSE,
6419 EL_DEFAULT, ACTION_EXPLODING, -1
6422 Xboom_2, FALSE, FALSE,
6423 EL_DEFAULT, ACTION_EXPLODING, -1
6426 Znormal, FALSE, FALSE,
6430 Zdynamite, FALSE, FALSE,
6434 Zplayer, FALSE, FALSE,
6438 ZBORDER, FALSE, FALSE,
6448 static struct Mapping_EM_to_RND_player
6457 em_player_mapping_list[] =
6461 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6465 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6469 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6473 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6477 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6481 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6485 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6489 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6493 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6497 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6501 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6505 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6509 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6513 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6517 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6521 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6525 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6529 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6533 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6537 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6541 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6545 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6549 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6553 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6557 EL_PLAYER_1, ACTION_DEFAULT, -1,
6561 EL_PLAYER_2, ACTION_DEFAULT, -1,
6565 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6569 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6573 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6577 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6581 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6585 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6589 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6593 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6597 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6601 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6605 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6609 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6613 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6617 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6621 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6625 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6629 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6633 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6637 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6641 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6645 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6649 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6653 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6657 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6661 EL_PLAYER_3, ACTION_DEFAULT, -1,
6665 EL_PLAYER_4, ACTION_DEFAULT, -1,
6674 int map_element_RND_to_EM(int element_rnd)
6676 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6677 static boolean mapping_initialized = FALSE;
6679 if (!mapping_initialized)
6683 /* return "Xalpha_quest" for all undefined elements in mapping array */
6684 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6685 mapping_RND_to_EM[i] = Xalpha_quest;
6687 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6688 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6689 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6690 em_object_mapping_list[i].element_em;
6692 mapping_initialized = TRUE;
6695 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6696 return mapping_RND_to_EM[element_rnd];
6698 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6703 int map_element_EM_to_RND(int element_em)
6705 static unsigned short mapping_EM_to_RND[TILE_MAX];
6706 static boolean mapping_initialized = FALSE;
6708 if (!mapping_initialized)
6712 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6713 for (i = 0; i < TILE_MAX; i++)
6714 mapping_EM_to_RND[i] = EL_UNKNOWN;
6716 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6717 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6718 em_object_mapping_list[i].element_rnd;
6720 mapping_initialized = TRUE;
6723 if (element_em >= 0 && element_em < TILE_MAX)
6724 return mapping_EM_to_RND[element_em];
6726 Error(ERR_WARN, "invalid EM level element %d", element_em);
6731 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6733 struct LevelInfo_EM *level_em = level->native_em_level;
6734 struct LEVEL *lev = level_em->lev;
6737 for (i = 0; i < TILE_MAX; i++)
6738 lev->android_array[i] = Xblank;
6740 for (i = 0; i < level->num_android_clone_elements; i++)
6742 int element_rnd = level->android_clone_element[i];
6743 int element_em = map_element_RND_to_EM(element_rnd);
6745 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6746 if (em_object_mapping_list[j].element_rnd == element_rnd)
6747 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6751 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6753 struct LevelInfo_EM *level_em = level->native_em_level;
6754 struct LEVEL *lev = level_em->lev;
6757 level->num_android_clone_elements = 0;
6759 for (i = 0; i < TILE_MAX; i++)
6761 int element_em = lev->android_array[i];
6763 boolean element_found = FALSE;
6765 if (element_em == Xblank)
6768 element_rnd = map_element_EM_to_RND(element_em);
6770 for (j = 0; j < level->num_android_clone_elements; j++)
6771 if (level->android_clone_element[j] == element_rnd)
6772 element_found = TRUE;
6776 level->android_clone_element[level->num_android_clone_elements++] =
6779 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6784 if (level->num_android_clone_elements == 0)
6786 level->num_android_clone_elements = 1;
6787 level->android_clone_element[0] = EL_EMPTY;
6791 int map_direction_RND_to_EM(int direction)
6793 return (direction == MV_UP ? 0 :
6794 direction == MV_RIGHT ? 1 :
6795 direction == MV_DOWN ? 2 :
6796 direction == MV_LEFT ? 3 :
6800 int map_direction_EM_to_RND(int direction)
6802 return (direction == 0 ? MV_UP :
6803 direction == 1 ? MV_RIGHT :
6804 direction == 2 ? MV_DOWN :
6805 direction == 3 ? MV_LEFT :
6809 int map_element_RND_to_SP(int element_rnd)
6811 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6813 if (element_rnd >= EL_SP_START &&
6814 element_rnd <= EL_SP_END)
6815 element_sp = element_rnd - EL_SP_START;
6816 else if (element_rnd == EL_EMPTY_SPACE)
6818 else if (element_rnd == EL_INVISIBLE_WALL)
6824 int map_element_SP_to_RND(int element_sp)
6826 int element_rnd = EL_UNKNOWN;
6828 if (element_sp >= 0x00 &&
6830 element_rnd = EL_SP_START + element_sp;
6831 else if (element_sp == 0x28)
6832 element_rnd = EL_INVISIBLE_WALL;
6837 int map_action_SP_to_RND(int action_sp)
6841 case actActive: return ACTION_ACTIVE;
6842 case actImpact: return ACTION_IMPACT;
6843 case actExploding: return ACTION_EXPLODING;
6844 case actDigging: return ACTION_DIGGING;
6845 case actSnapping: return ACTION_SNAPPING;
6846 case actCollecting: return ACTION_COLLECTING;
6847 case actPassing: return ACTION_PASSING;
6848 case actPushing: return ACTION_PUSHING;
6849 case actDropping: return ACTION_DROPPING;
6851 default: return ACTION_DEFAULT;
6855 int get_next_element(int element)
6859 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6860 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6861 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6862 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6863 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6864 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6865 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6866 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6867 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6868 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6869 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6871 default: return element;
6875 int el_act_dir2img(int element, int action, int direction)
6877 element = GFX_ELEMENT(element);
6878 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6880 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6881 return element_info[element].direction_graphic[action][direction];
6884 static int el_act_dir2crm(int element, int action, int direction)
6886 element = GFX_ELEMENT(element);
6887 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6889 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6890 return element_info[element].direction_crumbled[action][direction];
6893 int el_act2img(int element, int action)
6895 element = GFX_ELEMENT(element);
6897 return element_info[element].graphic[action];
6900 int el_act2crm(int element, int action)
6902 element = GFX_ELEMENT(element);
6904 return element_info[element].crumbled[action];
6907 int el_dir2img(int element, int direction)
6909 element = GFX_ELEMENT(element);
6911 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6914 int el2baseimg(int element)
6916 return element_info[element].graphic[ACTION_DEFAULT];
6919 int el2img(int element)
6921 element = GFX_ELEMENT(element);
6923 return element_info[element].graphic[ACTION_DEFAULT];
6926 int el2edimg(int element)
6928 element = GFX_ELEMENT(element);
6930 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6933 int el2preimg(int element)
6935 element = GFX_ELEMENT(element);
6937 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6940 int el2panelimg(int element)
6942 element = GFX_ELEMENT(element);
6944 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6947 int font2baseimg(int font_nr)
6949 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6952 int getBeltNrFromBeltElement(int element)
6954 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6955 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6956 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6959 int getBeltNrFromBeltActiveElement(int element)
6961 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6962 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6963 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6966 int getBeltNrFromBeltSwitchElement(int element)
6968 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6969 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6970 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6973 int getBeltDirNrFromBeltElement(int element)
6975 static int belt_base_element[4] =
6977 EL_CONVEYOR_BELT_1_LEFT,
6978 EL_CONVEYOR_BELT_2_LEFT,
6979 EL_CONVEYOR_BELT_3_LEFT,
6980 EL_CONVEYOR_BELT_4_LEFT
6983 int belt_nr = getBeltNrFromBeltElement(element);
6984 int belt_dir_nr = element - belt_base_element[belt_nr];
6986 return (belt_dir_nr % 3);
6989 int getBeltDirNrFromBeltSwitchElement(int element)
6991 static int belt_base_element[4] =
6993 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6994 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6995 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6996 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6999 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7000 int belt_dir_nr = element - belt_base_element[belt_nr];
7002 return (belt_dir_nr % 3);
7005 int getBeltDirFromBeltElement(int element)
7007 static int belt_move_dir[3] =
7014 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7016 return belt_move_dir[belt_dir_nr];
7019 int getBeltDirFromBeltSwitchElement(int element)
7021 static int belt_move_dir[3] =
7028 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7030 return belt_move_dir[belt_dir_nr];
7033 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7035 static int belt_base_element[4] =
7037 EL_CONVEYOR_BELT_1_LEFT,
7038 EL_CONVEYOR_BELT_2_LEFT,
7039 EL_CONVEYOR_BELT_3_LEFT,
7040 EL_CONVEYOR_BELT_4_LEFT
7043 return belt_base_element[belt_nr] + belt_dir_nr;
7046 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7048 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7050 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7053 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7055 static int belt_base_element[4] =
7057 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7058 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7059 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7060 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7063 return belt_base_element[belt_nr] + belt_dir_nr;
7066 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7068 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7070 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7073 boolean getTeamMode_EM()
7075 return game.team_mode;
7078 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7080 int game_frame_delay_value;
7082 game_frame_delay_value =
7083 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7084 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7087 if (tape.playing && tape.warp_forward && !tape.pausing)
7088 game_frame_delay_value = 0;
7090 return game_frame_delay_value;
7093 unsigned int InitRND(int seed)
7095 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7096 return InitEngineRandom_EM(seed);
7097 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7098 return InitEngineRandom_SP(seed);
7100 return InitEngineRandom_RND(seed);
7103 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7104 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7106 inline static int get_effective_element_EM(int tile, int frame_em)
7108 int element = object_mapping[tile].element_rnd;
7109 int action = object_mapping[tile].action;
7110 boolean is_backside = object_mapping[tile].is_backside;
7111 boolean action_removing = (action == ACTION_DIGGING ||
7112 action == ACTION_SNAPPING ||
7113 action == ACTION_COLLECTING);
7119 case Yacid_splash_eB:
7120 case Yacid_splash_wB:
7121 return (frame_em > 5 ? EL_EMPTY : element);
7127 else /* frame_em == 7 */
7131 case Yacid_splash_eB:
7132 case Yacid_splash_wB:
7135 case Yemerald_stone:
7138 case Ydiamond_stone:
7142 case Xdrip_stretchB:
7161 case Xsand_stonein_1:
7162 case Xsand_stonein_2:
7163 case Xsand_stonein_3:
7164 case Xsand_stonein_4:
7168 return (is_backside || action_removing ? EL_EMPTY : element);
7173 inline static boolean check_linear_animation_EM(int tile)
7177 case Xsand_stonesand_1:
7178 case Xsand_stonesand_quickout_1:
7179 case Xsand_sandstone_1:
7180 case Xsand_stonein_1:
7181 case Xsand_stoneout_1:
7200 case Yacid_splash_eB:
7201 case Yacid_splash_wB:
7202 case Yemerald_stone:
7209 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7210 boolean has_crumbled_graphics,
7211 int crumbled, int sync_frame)
7213 /* if element can be crumbled, but certain action graphics are just empty
7214 space (like instantly snapping sand to empty space in 1 frame), do not
7215 treat these empty space graphics as crumbled graphics in EMC engine */
7216 if (crumbled == IMG_EMPTY_SPACE)
7217 has_crumbled_graphics = FALSE;
7219 if (has_crumbled_graphics)
7221 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7222 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7223 g_crumbled->anim_delay,
7224 g_crumbled->anim_mode,
7225 g_crumbled->anim_start_frame,
7228 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7229 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7231 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7233 g_em->has_crumbled_graphics = TRUE;
7237 g_em->crumbled_bitmap = NULL;
7238 g_em->crumbled_src_x = 0;
7239 g_em->crumbled_src_y = 0;
7240 g_em->crumbled_border_size = 0;
7242 g_em->has_crumbled_graphics = FALSE;
7246 void ResetGfxAnimation_EM(int x, int y, int tile)
7251 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7252 int tile, int frame_em, int x, int y)
7254 int action = object_mapping[tile].action;
7255 int direction = object_mapping[tile].direction;
7256 int effective_element = get_effective_element_EM(tile, frame_em);
7257 int graphic = (direction == MV_NONE ?
7258 el_act2img(effective_element, action) :
7259 el_act_dir2img(effective_element, action, direction));
7260 struct GraphicInfo *g = &graphic_info[graphic];
7262 boolean action_removing = (action == ACTION_DIGGING ||
7263 action == ACTION_SNAPPING ||
7264 action == ACTION_COLLECTING);
7265 boolean action_moving = (action == ACTION_FALLING ||
7266 action == ACTION_MOVING ||
7267 action == ACTION_PUSHING ||
7268 action == ACTION_EATING ||
7269 action == ACTION_FILLING ||
7270 action == ACTION_EMPTYING);
7271 boolean action_falling = (action == ACTION_FALLING ||
7272 action == ACTION_FILLING ||
7273 action == ACTION_EMPTYING);
7275 /* special case: graphic uses "2nd movement tile" and has defined
7276 7 frames for movement animation (or less) => use default graphic
7277 for last (8th) frame which ends the movement animation */
7278 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7280 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7281 graphic = (direction == MV_NONE ?
7282 el_act2img(effective_element, action) :
7283 el_act_dir2img(effective_element, action, direction));
7285 g = &graphic_info[graphic];
7288 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7292 else if (action_moving)
7294 boolean is_backside = object_mapping[tile].is_backside;
7298 int direction = object_mapping[tile].direction;
7299 int move_dir = (action_falling ? MV_DOWN : direction);
7304 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7305 if (g->double_movement && frame_em == 0)
7309 if (move_dir == MV_LEFT)
7310 GfxFrame[x - 1][y] = GfxFrame[x][y];
7311 else if (move_dir == MV_RIGHT)
7312 GfxFrame[x + 1][y] = GfxFrame[x][y];
7313 else if (move_dir == MV_UP)
7314 GfxFrame[x][y - 1] = GfxFrame[x][y];
7315 else if (move_dir == MV_DOWN)
7316 GfxFrame[x][y + 1] = GfxFrame[x][y];
7323 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7324 if (tile == Xsand_stonesand_quickout_1 ||
7325 tile == Xsand_stonesand_quickout_2)
7329 if (graphic_info[graphic].anim_global_sync)
7330 sync_frame = FrameCounter;
7331 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7332 sync_frame = GfxFrame[x][y];
7334 sync_frame = 0; /* playfield border (pseudo steel) */
7336 SetRandomAnimationValue(x, y);
7338 int frame = getAnimationFrame(g->anim_frames,
7341 g->anim_start_frame,
7344 g_em->unique_identifier =
7345 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7348 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7349 int tile, int frame_em, int x, int y)
7351 int action = object_mapping[tile].action;
7352 int direction = object_mapping[tile].direction;
7353 boolean is_backside = object_mapping[tile].is_backside;
7354 int effective_element = get_effective_element_EM(tile, frame_em);
7355 int effective_action = action;
7356 int graphic = (direction == MV_NONE ?
7357 el_act2img(effective_element, effective_action) :
7358 el_act_dir2img(effective_element, effective_action,
7360 int crumbled = (direction == MV_NONE ?
7361 el_act2crm(effective_element, effective_action) :
7362 el_act_dir2crm(effective_element, effective_action,
7364 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7365 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7366 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7367 struct GraphicInfo *g = &graphic_info[graphic];
7370 /* special case: graphic uses "2nd movement tile" and has defined
7371 7 frames for movement animation (or less) => use default graphic
7372 for last (8th) frame which ends the movement animation */
7373 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7375 effective_action = ACTION_DEFAULT;
7376 graphic = (direction == MV_NONE ?
7377 el_act2img(effective_element, effective_action) :
7378 el_act_dir2img(effective_element, effective_action,
7380 crumbled = (direction == MV_NONE ?
7381 el_act2crm(effective_element, effective_action) :
7382 el_act_dir2crm(effective_element, effective_action,
7385 g = &graphic_info[graphic];
7388 if (graphic_info[graphic].anim_global_sync)
7389 sync_frame = FrameCounter;
7390 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7391 sync_frame = GfxFrame[x][y];
7393 sync_frame = 0; /* playfield border (pseudo steel) */
7395 SetRandomAnimationValue(x, y);
7397 int frame = getAnimationFrame(g->anim_frames,
7400 g->anim_start_frame,
7403 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7404 g->double_movement && is_backside);
7406 /* (updating the "crumbled" graphic definitions is probably not really needed,
7407 as animations for crumbled graphics can't be longer than one EMC cycle) */
7408 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7412 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7413 int player_nr, int anim, int frame_em)
7415 int element = player_mapping[player_nr][anim].element_rnd;
7416 int action = player_mapping[player_nr][anim].action;
7417 int direction = player_mapping[player_nr][anim].direction;
7418 int graphic = (direction == MV_NONE ?
7419 el_act2img(element, action) :
7420 el_act_dir2img(element, action, direction));
7421 struct GraphicInfo *g = &graphic_info[graphic];
7424 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7426 stored_player[player_nr].StepFrame = frame_em;
7428 sync_frame = stored_player[player_nr].Frame;
7430 int frame = getAnimationFrame(g->anim_frames,
7433 g->anim_start_frame,
7436 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7437 &g_em->src_x, &g_em->src_y, FALSE);
7440 void InitGraphicInfo_EM(void)
7445 int num_em_gfx_errors = 0;
7447 if (graphic_info_em_object[0][0].bitmap == NULL)
7449 /* EM graphics not yet initialized in em_open_all() */
7454 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7457 /* always start with reliable default values */
7458 for (i = 0; i < TILE_MAX; i++)
7460 object_mapping[i].element_rnd = EL_UNKNOWN;
7461 object_mapping[i].is_backside = FALSE;
7462 object_mapping[i].action = ACTION_DEFAULT;
7463 object_mapping[i].direction = MV_NONE;
7466 /* always start with reliable default values */
7467 for (p = 0; p < MAX_PLAYERS; p++)
7469 for (i = 0; i < SPR_MAX; i++)
7471 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7472 player_mapping[p][i].action = ACTION_DEFAULT;
7473 player_mapping[p][i].direction = MV_NONE;
7477 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7479 int e = em_object_mapping_list[i].element_em;
7481 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7482 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7484 if (em_object_mapping_list[i].action != -1)
7485 object_mapping[e].action = em_object_mapping_list[i].action;
7487 if (em_object_mapping_list[i].direction != -1)
7488 object_mapping[e].direction =
7489 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7492 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7494 int a = em_player_mapping_list[i].action_em;
7495 int p = em_player_mapping_list[i].player_nr;
7497 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7499 if (em_player_mapping_list[i].action != -1)
7500 player_mapping[p][a].action = em_player_mapping_list[i].action;
7502 if (em_player_mapping_list[i].direction != -1)
7503 player_mapping[p][a].direction =
7504 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7507 for (i = 0; i < TILE_MAX; i++)
7509 int element = object_mapping[i].element_rnd;
7510 int action = object_mapping[i].action;
7511 int direction = object_mapping[i].direction;
7512 boolean is_backside = object_mapping[i].is_backside;
7513 boolean action_exploding = ((action == ACTION_EXPLODING ||
7514 action == ACTION_SMASHED_BY_ROCK ||
7515 action == ACTION_SMASHED_BY_SPRING) &&
7516 element != EL_DIAMOND);
7517 boolean action_active = (action == ACTION_ACTIVE);
7518 boolean action_other = (action == ACTION_OTHER);
7520 for (j = 0; j < 8; j++)
7522 int effective_element = get_effective_element_EM(i, j);
7523 int effective_action = (j < 7 ? action :
7524 i == Xdrip_stretch ? action :
7525 i == Xdrip_stretchB ? action :
7526 i == Ydrip_s1 ? action :
7527 i == Ydrip_s1B ? action :
7528 i == Xball_1B ? action :
7529 i == Xball_2 ? action :
7530 i == Xball_2B ? action :
7531 i == Yball_eat ? action :
7532 i == Ykey_1_eat ? action :
7533 i == Ykey_2_eat ? action :
7534 i == Ykey_3_eat ? action :
7535 i == Ykey_4_eat ? action :
7536 i == Ykey_5_eat ? action :
7537 i == Ykey_6_eat ? action :
7538 i == Ykey_7_eat ? action :
7539 i == Ykey_8_eat ? action :
7540 i == Ylenses_eat ? action :
7541 i == Ymagnify_eat ? action :
7542 i == Ygrass_eat ? action :
7543 i == Ydirt_eat ? action :
7544 i == Xsand_stonein_1 ? action :
7545 i == Xsand_stonein_2 ? action :
7546 i == Xsand_stonein_3 ? action :
7547 i == Xsand_stonein_4 ? action :
7548 i == Xsand_stoneout_1 ? action :
7549 i == Xsand_stoneout_2 ? action :
7550 i == Xboom_android ? ACTION_EXPLODING :
7551 action_exploding ? ACTION_EXPLODING :
7552 action_active ? action :
7553 action_other ? action :
7555 int graphic = (el_act_dir2img(effective_element, effective_action,
7557 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7559 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7560 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7561 boolean has_action_graphics = (graphic != base_graphic);
7562 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7563 struct GraphicInfo *g = &graphic_info[graphic];
7564 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7567 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7568 boolean special_animation = (action != ACTION_DEFAULT &&
7569 g->anim_frames == 3 &&
7570 g->anim_delay == 2 &&
7571 g->anim_mode & ANIM_LINEAR);
7572 int sync_frame = (i == Xdrip_stretch ? 7 :
7573 i == Xdrip_stretchB ? 7 :
7574 i == Ydrip_s2 ? j + 8 :
7575 i == Ydrip_s2B ? j + 8 :
7584 i == Xfake_acid_1 ? 0 :
7585 i == Xfake_acid_2 ? 10 :
7586 i == Xfake_acid_3 ? 20 :
7587 i == Xfake_acid_4 ? 30 :
7588 i == Xfake_acid_5 ? 40 :
7589 i == Xfake_acid_6 ? 50 :
7590 i == Xfake_acid_7 ? 60 :
7591 i == Xfake_acid_8 ? 70 :
7593 i == Xball_2B ? j + 8 :
7594 i == Yball_eat ? j + 1 :
7595 i == Ykey_1_eat ? j + 1 :
7596 i == Ykey_2_eat ? j + 1 :
7597 i == Ykey_3_eat ? j + 1 :
7598 i == Ykey_4_eat ? j + 1 :
7599 i == Ykey_5_eat ? j + 1 :
7600 i == Ykey_6_eat ? j + 1 :
7601 i == Ykey_7_eat ? j + 1 :
7602 i == Ykey_8_eat ? j + 1 :
7603 i == Ylenses_eat ? j + 1 :
7604 i == Ymagnify_eat ? j + 1 :
7605 i == Ygrass_eat ? j + 1 :
7606 i == Ydirt_eat ? j + 1 :
7607 i == Xamoeba_1 ? 0 :
7608 i == Xamoeba_2 ? 1 :
7609 i == Xamoeba_3 ? 2 :
7610 i == Xamoeba_4 ? 3 :
7611 i == Xamoeba_5 ? 0 :
7612 i == Xamoeba_6 ? 1 :
7613 i == Xamoeba_7 ? 2 :
7614 i == Xamoeba_8 ? 3 :
7615 i == Xexit_2 ? j + 8 :
7616 i == Xexit_3 ? j + 16 :
7617 i == Xdynamite_1 ? 0 :
7618 i == Xdynamite_2 ? 8 :
7619 i == Xdynamite_3 ? 16 :
7620 i == Xdynamite_4 ? 24 :
7621 i == Xsand_stonein_1 ? j + 1 :
7622 i == Xsand_stonein_2 ? j + 9 :
7623 i == Xsand_stonein_3 ? j + 17 :
7624 i == Xsand_stonein_4 ? j + 25 :
7625 i == Xsand_stoneout_1 && j == 0 ? 0 :
7626 i == Xsand_stoneout_1 && j == 1 ? 0 :
7627 i == Xsand_stoneout_1 && j == 2 ? 1 :
7628 i == Xsand_stoneout_1 && j == 3 ? 2 :
7629 i == Xsand_stoneout_1 && j == 4 ? 2 :
7630 i == Xsand_stoneout_1 && j == 5 ? 3 :
7631 i == Xsand_stoneout_1 && j == 6 ? 4 :
7632 i == Xsand_stoneout_1 && j == 7 ? 4 :
7633 i == Xsand_stoneout_2 && j == 0 ? 5 :
7634 i == Xsand_stoneout_2 && j == 1 ? 6 :
7635 i == Xsand_stoneout_2 && j == 2 ? 7 :
7636 i == Xsand_stoneout_2 && j == 3 ? 8 :
7637 i == Xsand_stoneout_2 && j == 4 ? 9 :
7638 i == Xsand_stoneout_2 && j == 5 ? 11 :
7639 i == Xsand_stoneout_2 && j == 6 ? 13 :
7640 i == Xsand_stoneout_2 && j == 7 ? 15 :
7641 i == Xboom_bug && j == 1 ? 2 :
7642 i == Xboom_bug && j == 2 ? 2 :
7643 i == Xboom_bug && j == 3 ? 4 :
7644 i == Xboom_bug && j == 4 ? 4 :
7645 i == Xboom_bug && j == 5 ? 2 :
7646 i == Xboom_bug && j == 6 ? 2 :
7647 i == Xboom_bug && j == 7 ? 0 :
7648 i == Xboom_bomb && j == 1 ? 2 :
7649 i == Xboom_bomb && j == 2 ? 2 :
7650 i == Xboom_bomb && j == 3 ? 4 :
7651 i == Xboom_bomb && j == 4 ? 4 :
7652 i == Xboom_bomb && j == 5 ? 2 :
7653 i == Xboom_bomb && j == 6 ? 2 :
7654 i == Xboom_bomb && j == 7 ? 0 :
7655 i == Xboom_android && j == 7 ? 6 :
7656 i == Xboom_1 && j == 1 ? 2 :
7657 i == Xboom_1 && j == 2 ? 2 :
7658 i == Xboom_1 && j == 3 ? 4 :
7659 i == Xboom_1 && j == 4 ? 4 :
7660 i == Xboom_1 && j == 5 ? 6 :
7661 i == Xboom_1 && j == 6 ? 6 :
7662 i == Xboom_1 && j == 7 ? 8 :
7663 i == Xboom_2 && j == 0 ? 8 :
7664 i == Xboom_2 && j == 1 ? 8 :
7665 i == Xboom_2 && j == 2 ? 10 :
7666 i == Xboom_2 && j == 3 ? 10 :
7667 i == Xboom_2 && j == 4 ? 10 :
7668 i == Xboom_2 && j == 5 ? 12 :
7669 i == Xboom_2 && j == 6 ? 12 :
7670 i == Xboom_2 && j == 7 ? 12 :
7671 special_animation && j == 4 ? 3 :
7672 effective_action != action ? 0 :
7676 Bitmap *debug_bitmap = g_em->bitmap;
7677 int debug_src_x = g_em->src_x;
7678 int debug_src_y = g_em->src_y;
7681 int frame = getAnimationFrame(g->anim_frames,
7684 g->anim_start_frame,
7687 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7688 g->double_movement && is_backside);
7690 g_em->bitmap = src_bitmap;
7691 g_em->src_x = src_x;
7692 g_em->src_y = src_y;
7693 g_em->src_offset_x = 0;
7694 g_em->src_offset_y = 0;
7695 g_em->dst_offset_x = 0;
7696 g_em->dst_offset_y = 0;
7697 g_em->width = TILEX;
7698 g_em->height = TILEY;
7700 g_em->preserve_background = FALSE;
7702 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7705 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7706 effective_action == ACTION_MOVING ||
7707 effective_action == ACTION_PUSHING ||
7708 effective_action == ACTION_EATING)) ||
7709 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7710 effective_action == ACTION_EMPTYING)))
7713 (effective_action == ACTION_FALLING ||
7714 effective_action == ACTION_FILLING ||
7715 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7716 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7717 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7718 int num_steps = (i == Ydrip_s1 ? 16 :
7719 i == Ydrip_s1B ? 16 :
7720 i == Ydrip_s2 ? 16 :
7721 i == Ydrip_s2B ? 16 :
7722 i == Xsand_stonein_1 ? 32 :
7723 i == Xsand_stonein_2 ? 32 :
7724 i == Xsand_stonein_3 ? 32 :
7725 i == Xsand_stonein_4 ? 32 :
7726 i == Xsand_stoneout_1 ? 16 :
7727 i == Xsand_stoneout_2 ? 16 : 8);
7728 int cx = ABS(dx) * (TILEX / num_steps);
7729 int cy = ABS(dy) * (TILEY / num_steps);
7730 int step_frame = (i == Ydrip_s2 ? j + 8 :
7731 i == Ydrip_s2B ? j + 8 :
7732 i == Xsand_stonein_2 ? j + 8 :
7733 i == Xsand_stonein_3 ? j + 16 :
7734 i == Xsand_stonein_4 ? j + 24 :
7735 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7736 int step = (is_backside ? step_frame : num_steps - step_frame);
7738 if (is_backside) /* tile where movement starts */
7740 if (dx < 0 || dy < 0)
7742 g_em->src_offset_x = cx * step;
7743 g_em->src_offset_y = cy * step;
7747 g_em->dst_offset_x = cx * step;
7748 g_em->dst_offset_y = cy * step;
7751 else /* tile where movement ends */
7753 if (dx < 0 || dy < 0)
7755 g_em->dst_offset_x = cx * step;
7756 g_em->dst_offset_y = cy * step;
7760 g_em->src_offset_x = cx * step;
7761 g_em->src_offset_y = cy * step;
7765 g_em->width = TILEX - cx * step;
7766 g_em->height = TILEY - cy * step;
7769 /* create unique graphic identifier to decide if tile must be redrawn */
7770 /* bit 31 - 16 (16 bit): EM style graphic
7771 bit 15 - 12 ( 4 bit): EM style frame
7772 bit 11 - 6 ( 6 bit): graphic width
7773 bit 5 - 0 ( 6 bit): graphic height */
7774 g_em->unique_identifier =
7775 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7779 /* skip check for EMC elements not contained in original EMC artwork */
7780 if (element == EL_EMC_FAKE_ACID)
7783 if (g_em->bitmap != debug_bitmap ||
7784 g_em->src_x != debug_src_x ||
7785 g_em->src_y != debug_src_y ||
7786 g_em->src_offset_x != 0 ||
7787 g_em->src_offset_y != 0 ||
7788 g_em->dst_offset_x != 0 ||
7789 g_em->dst_offset_y != 0 ||
7790 g_em->width != TILEX ||
7791 g_em->height != TILEY)
7793 static int last_i = -1;
7801 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7802 i, element, element_info[element].token_name,
7803 element_action_info[effective_action].suffix, direction);
7805 if (element != effective_element)
7806 printf(" [%d ('%s')]",
7808 element_info[effective_element].token_name);
7812 if (g_em->bitmap != debug_bitmap)
7813 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7814 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7816 if (g_em->src_x != debug_src_x ||
7817 g_em->src_y != debug_src_y)
7818 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7819 j, (is_backside ? 'B' : 'F'),
7820 g_em->src_x, g_em->src_y,
7821 g_em->src_x / 32, g_em->src_y / 32,
7822 debug_src_x, debug_src_y,
7823 debug_src_x / 32, debug_src_y / 32);
7825 if (g_em->src_offset_x != 0 ||
7826 g_em->src_offset_y != 0 ||
7827 g_em->dst_offset_x != 0 ||
7828 g_em->dst_offset_y != 0)
7829 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7831 g_em->src_offset_x, g_em->src_offset_y,
7832 g_em->dst_offset_x, g_em->dst_offset_y);
7834 if (g_em->width != TILEX ||
7835 g_em->height != TILEY)
7836 printf(" %d (%d): size %d,%d should be %d,%d\n",
7838 g_em->width, g_em->height, TILEX, TILEY);
7840 num_em_gfx_errors++;
7847 for (i = 0; i < TILE_MAX; i++)
7849 for (j = 0; j < 8; j++)
7851 int element = object_mapping[i].element_rnd;
7852 int action = object_mapping[i].action;
7853 int direction = object_mapping[i].direction;
7854 boolean is_backside = object_mapping[i].is_backside;
7855 int graphic_action = el_act_dir2img(element, action, direction);
7856 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7858 if ((action == ACTION_SMASHED_BY_ROCK ||
7859 action == ACTION_SMASHED_BY_SPRING ||
7860 action == ACTION_EATING) &&
7861 graphic_action == graphic_default)
7863 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7864 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7865 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7866 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7869 /* no separate animation for "smashed by rock" -- use rock instead */
7870 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7871 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7873 g_em->bitmap = g_xx->bitmap;
7874 g_em->src_x = g_xx->src_x;
7875 g_em->src_y = g_xx->src_y;
7876 g_em->src_offset_x = g_xx->src_offset_x;
7877 g_em->src_offset_y = g_xx->src_offset_y;
7878 g_em->dst_offset_x = g_xx->dst_offset_x;
7879 g_em->dst_offset_y = g_xx->dst_offset_y;
7880 g_em->width = g_xx->width;
7881 g_em->height = g_xx->height;
7882 g_em->unique_identifier = g_xx->unique_identifier;
7885 g_em->preserve_background = TRUE;
7890 for (p = 0; p < MAX_PLAYERS; p++)
7892 for (i = 0; i < SPR_MAX; i++)
7894 int element = player_mapping[p][i].element_rnd;
7895 int action = player_mapping[p][i].action;
7896 int direction = player_mapping[p][i].direction;
7898 for (j = 0; j < 8; j++)
7900 int effective_element = element;
7901 int effective_action = action;
7902 int graphic = (direction == MV_NONE ?
7903 el_act2img(effective_element, effective_action) :
7904 el_act_dir2img(effective_element, effective_action,
7906 struct GraphicInfo *g = &graphic_info[graphic];
7907 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7913 Bitmap *debug_bitmap = g_em->bitmap;
7914 int debug_src_x = g_em->src_x;
7915 int debug_src_y = g_em->src_y;
7918 int frame = getAnimationFrame(g->anim_frames,
7921 g->anim_start_frame,
7924 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7926 g_em->bitmap = src_bitmap;
7927 g_em->src_x = src_x;
7928 g_em->src_y = src_y;
7929 g_em->src_offset_x = 0;
7930 g_em->src_offset_y = 0;
7931 g_em->dst_offset_x = 0;
7932 g_em->dst_offset_y = 0;
7933 g_em->width = TILEX;
7934 g_em->height = TILEY;
7938 /* skip check for EMC elements not contained in original EMC artwork */
7939 if (element == EL_PLAYER_3 ||
7940 element == EL_PLAYER_4)
7943 if (g_em->bitmap != debug_bitmap ||
7944 g_em->src_x != debug_src_x ||
7945 g_em->src_y != debug_src_y)
7947 static int last_i = -1;
7955 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7956 p, i, element, element_info[element].token_name,
7957 element_action_info[effective_action].suffix, direction);
7959 if (element != effective_element)
7960 printf(" [%d ('%s')]",
7962 element_info[effective_element].token_name);
7966 if (g_em->bitmap != debug_bitmap)
7967 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7968 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7970 if (g_em->src_x != debug_src_x ||
7971 g_em->src_y != debug_src_y)
7972 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7974 g_em->src_x, g_em->src_y,
7975 g_em->src_x / 32, g_em->src_y / 32,
7976 debug_src_x, debug_src_y,
7977 debug_src_x / 32, debug_src_y / 32);
7979 num_em_gfx_errors++;
7989 printf("::: [%d errors found]\n", num_em_gfx_errors);
7995 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7996 boolean any_player_moving,
7997 boolean any_player_snapping,
7998 boolean any_player_dropping)
8000 static boolean player_was_waiting = TRUE;
8002 if (frame == 0 && !any_player_dropping)
8004 if (!player_was_waiting)
8006 if (!SaveEngineSnapshotToList())
8009 player_was_waiting = TRUE;
8012 else if (any_player_moving || any_player_snapping || any_player_dropping)
8014 player_was_waiting = FALSE;
8018 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8019 boolean murphy_is_dropping)
8021 static boolean player_was_waiting = TRUE;
8023 if (murphy_is_waiting)
8025 if (!player_was_waiting)
8027 if (!SaveEngineSnapshotToList())
8030 player_was_waiting = TRUE;
8035 player_was_waiting = FALSE;
8039 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8040 boolean any_player_moving,
8041 boolean any_player_snapping,
8042 boolean any_player_dropping)
8044 if (tape.single_step && tape.recording && !tape.pausing)
8045 if (frame == 0 && !any_player_dropping)
8046 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8048 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8049 any_player_snapping, any_player_dropping);
8052 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8053 boolean murphy_is_dropping)
8055 if (tape.single_step && tape.recording && !tape.pausing)
8056 if (murphy_is_waiting)
8057 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8059 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8062 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8063 int graphic, int sync_frame, int x, int y)
8065 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8067 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8070 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8072 return (IS_NEXT_FRAME(sync_frame, graphic));
8075 int getGraphicInfo_Delay(int graphic)
8077 return graphic_info[graphic].anim_delay;
8080 void PlayMenuSoundExt(int sound)
8082 if (sound == SND_UNDEFINED)
8085 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8086 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8089 if (IS_LOOP_SOUND(sound))
8090 PlaySoundLoop(sound);
8095 void PlayMenuSound()
8097 PlayMenuSoundExt(menu.sound[game_status]);
8100 void PlayMenuSoundStereo(int sound, int stereo_position)
8102 if (sound == SND_UNDEFINED)
8105 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8106 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8109 if (IS_LOOP_SOUND(sound))
8110 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8112 PlaySoundStereo(sound, stereo_position);
8115 void PlayMenuSoundIfLoopExt(int sound)
8117 if (sound == SND_UNDEFINED)
8120 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8121 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8124 if (IS_LOOP_SOUND(sound))
8125 PlaySoundLoop(sound);
8128 void PlayMenuSoundIfLoop()
8130 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8133 void PlayMenuMusicExt(int music)
8135 if (music == MUS_UNDEFINED)
8138 if (!setup.sound_music)
8144 void PlayMenuMusic()
8146 PlayMenuMusicExt(menu.music[game_status]);
8149 void PlaySoundActivating()
8152 PlaySound(SND_MENU_ITEM_ACTIVATING);
8156 void PlaySoundSelecting()
8159 PlaySound(SND_MENU_ITEM_SELECTING);
8163 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8165 boolean change_fullscreen = (setup.fullscreen !=
8166 video.fullscreen_enabled);
8167 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8168 !strEqual(setup.fullscreen_mode,
8169 video.fullscreen_mode_current));
8170 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8171 setup.window_scaling_percent !=
8172 video.window_scaling_percent);
8174 if (change_window_scaling_percent && video.fullscreen_enabled)
8177 if (!change_window_scaling_percent && !video.fullscreen_available)
8180 #if defined(TARGET_SDL2)
8181 if (change_window_scaling_percent)
8183 SDLSetWindowScaling(setup.window_scaling_percent);
8187 else if (change_fullscreen)
8189 SDLSetWindowFullscreen(setup.fullscreen);
8191 /* set setup value according to successfully changed fullscreen mode */
8192 setup.fullscreen = video.fullscreen_enabled;
8198 if (change_fullscreen ||
8199 change_fullscreen_mode ||
8200 change_window_scaling_percent)
8202 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8204 /* save backbuffer content which gets lost when toggling fullscreen mode */
8205 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8207 if (change_fullscreen_mode)
8209 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8210 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8213 if (change_window_scaling_percent)
8215 /* keep window mode, but change window scaling */
8216 video.fullscreen_enabled = TRUE; /* force new window scaling */
8219 /* toggle fullscreen */
8220 ChangeVideoModeIfNeeded(setup.fullscreen);
8222 /* set setup value according to successfully changed fullscreen mode */
8223 setup.fullscreen = video.fullscreen_enabled;
8225 /* restore backbuffer content from temporary backbuffer backup bitmap */
8226 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8228 FreeBitmap(tmp_backbuffer);
8230 /* update visible window/screen */
8231 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8235 void JoinRectangles(int *x, int *y, int *width, int *height,
8236 int x2, int y2, int width2, int height2)
8238 // do not join with "off-screen" rectangle
8239 if (x2 == -1 || y2 == -1)
8244 *width = MAX(*width, width2);
8245 *height = MAX(*height, height2);
8248 void SetGameStatus(int game_status_new)
8250 game_status = game_status_new;
8252 global.anim_status_next = game_status;
8255 void ChangeViewportPropertiesIfNeeded()
8257 int gfx_game_mode = game_status;
8258 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8260 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8261 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8262 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8263 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8264 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8265 int new_win_xsize = vp_window->width;
8266 int new_win_ysize = vp_window->height;
8267 int border_size = vp_playfield->border_size;
8268 int new_sx = vp_playfield->x + border_size;
8269 int new_sy = vp_playfield->y + border_size;
8270 int new_sxsize = vp_playfield->width - 2 * border_size;
8271 int new_sysize = vp_playfield->height - 2 * border_size;
8272 int new_real_sx = vp_playfield->x;
8273 int new_real_sy = vp_playfield->y;
8274 int new_full_sxsize = vp_playfield->width;
8275 int new_full_sysize = vp_playfield->height;
8276 int new_dx = vp_door_1->x;
8277 int new_dy = vp_door_1->y;
8278 int new_dxsize = vp_door_1->width;
8279 int new_dysize = vp_door_1->height;
8280 int new_vx = vp_door_2->x;
8281 int new_vy = vp_door_2->y;
8282 int new_vxsize = vp_door_2->width;
8283 int new_vysize = vp_door_2->height;
8284 int new_ex = vp_door_3->x;
8285 int new_ey = vp_door_3->y;
8286 int new_exsize = vp_door_3->width;
8287 int new_eysize = vp_door_3->height;
8288 int new_tilesize_var =
8289 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8291 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8292 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8293 int new_scr_fieldx = new_sxsize / tilesize;
8294 int new_scr_fieldy = new_sysize / tilesize;
8295 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8296 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8297 boolean init_gfx_buffers = FALSE;
8298 boolean init_video_buffer = FALSE;
8299 boolean init_gadgets_and_toons = FALSE;
8300 boolean init_em_graphics = FALSE;
8302 if (new_win_xsize != WIN_XSIZE ||
8303 new_win_ysize != WIN_YSIZE)
8305 WIN_XSIZE = new_win_xsize;
8306 WIN_YSIZE = new_win_ysize;
8308 init_video_buffer = TRUE;
8309 init_gfx_buffers = TRUE;
8310 init_gadgets_and_toons = TRUE;
8312 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8315 if (new_scr_fieldx != SCR_FIELDX ||
8316 new_scr_fieldy != SCR_FIELDY)
8318 /* this always toggles between MAIN and GAME when using small tile size */
8320 SCR_FIELDX = new_scr_fieldx;
8321 SCR_FIELDY = new_scr_fieldy;
8323 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8334 new_sxsize != SXSIZE ||
8335 new_sysize != SYSIZE ||
8336 new_dxsize != DXSIZE ||
8337 new_dysize != DYSIZE ||
8338 new_vxsize != VXSIZE ||
8339 new_vysize != VYSIZE ||
8340 new_exsize != EXSIZE ||
8341 new_eysize != EYSIZE ||
8342 new_real_sx != REAL_SX ||
8343 new_real_sy != REAL_SY ||
8344 new_full_sxsize != FULL_SXSIZE ||
8345 new_full_sysize != FULL_SYSIZE ||
8346 new_tilesize_var != TILESIZE_VAR
8349 // ------------------------------------------------------------------------
8350 // determine next fading area for changed viewport definitions
8351 // ------------------------------------------------------------------------
8353 // start with current playfield area (default fading area)
8356 FADE_SXSIZE = FULL_SXSIZE;
8357 FADE_SYSIZE = FULL_SYSIZE;
8359 // add new playfield area if position or size has changed
8360 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8361 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8363 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8364 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8367 // add current and new door 1 area if position or size has changed
8368 if (new_dx != DX || new_dy != DY ||
8369 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8371 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8372 DX, DY, DXSIZE, DYSIZE);
8373 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8374 new_dx, new_dy, new_dxsize, new_dysize);
8377 // add current and new door 2 area if position or size has changed
8378 if (new_dx != VX || new_dy != VY ||
8379 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8381 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8382 VX, VY, VXSIZE, VYSIZE);
8383 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8384 new_vx, new_vy, new_vxsize, new_vysize);
8387 // ------------------------------------------------------------------------
8388 // handle changed tile size
8389 // ------------------------------------------------------------------------
8391 if (new_tilesize_var != TILESIZE_VAR)
8393 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8395 // changing tile size invalidates scroll values of engine snapshots
8396 FreeEngineSnapshotSingle();
8398 // changing tile size requires update of graphic mapping for EM engine
8399 init_em_graphics = TRUE;
8410 SXSIZE = new_sxsize;
8411 SYSIZE = new_sysize;
8412 DXSIZE = new_dxsize;
8413 DYSIZE = new_dysize;
8414 VXSIZE = new_vxsize;
8415 VYSIZE = new_vysize;
8416 EXSIZE = new_exsize;
8417 EYSIZE = new_eysize;
8418 REAL_SX = new_real_sx;
8419 REAL_SY = new_real_sy;
8420 FULL_SXSIZE = new_full_sxsize;
8421 FULL_SYSIZE = new_full_sysize;
8422 TILESIZE_VAR = new_tilesize_var;
8424 init_gfx_buffers = TRUE;
8425 init_gadgets_and_toons = TRUE;
8427 // printf("::: viewports: init_gfx_buffers\n");
8428 // printf("::: viewports: init_gadgets_and_toons\n");
8431 if (init_gfx_buffers)
8433 // printf("::: init_gfx_buffers\n");
8435 SCR_FIELDX = new_scr_fieldx_buffers;
8436 SCR_FIELDY = new_scr_fieldy_buffers;
8440 SCR_FIELDX = new_scr_fieldx;
8441 SCR_FIELDY = new_scr_fieldy;
8443 SetDrawDeactivationMask(REDRAW_NONE);
8444 SetDrawBackgroundMask(REDRAW_FIELD);
8447 if (init_video_buffer)
8449 // printf("::: init_video_buffer\n");
8451 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8452 InitImageTextures();
8455 if (init_gadgets_and_toons)
8457 // printf("::: init_gadgets_and_toons\n");
8461 InitGlobalAnimations();
8464 if (init_em_graphics)
8466 InitGraphicInfo_EM();