1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static unsigned int sync_frame_delay = 0;
177 static unsigned int sync_frame_delay_value = GAME_FRAME_DELAY;
179 static char *print_if_not_empty(int element)
181 static char *s = NULL;
182 char *token_name = element_info[element].token_name;
187 s = checked_malloc(strlen(token_name) + 10 + 1);
189 if (element != EL_EMPTY)
190 sprintf(s, "%d\t['%s']", element, token_name);
192 sprintf(s, "%d", element);
197 void DumpTile(int x, int y)
202 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
208 printf_line("-", 79);
209 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
210 printf_line("-", 79);
212 if (!IN_LEV_FIELD(x, y))
214 printf("(not in level field)\n");
220 printf(" Feld: %d\t['%s']\n", Feld[x][y],
221 element_info[Feld[x][y]].token_name);
222 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
223 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
224 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
225 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
226 printf(" MovPos: %d\n", MovPos[x][y]);
227 printf(" MovDir: %d\n", MovDir[x][y]);
228 printf(" MovDelay: %d\n", MovDelay[x][y]);
229 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
230 printf(" CustomValue: %d\n", CustomValue[x][y]);
231 printf(" GfxElement: %d\n", GfxElement[x][y]);
232 printf(" GfxAction: %d\n", GfxAction[x][y]);
233 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
237 void SetDrawtoField(int mode)
239 if (mode == DRAW_FIELDBUFFER)
245 BX2 = SCR_FIELDX + 1;
246 BY2 = SCR_FIELDY + 1;
248 drawto_field = fieldbuffer;
250 else /* DRAW_BACKBUFFER */
256 BX2 = SCR_FIELDX - 1;
257 BY2 = SCR_FIELDY - 1;
259 drawto_field = backbuffer;
263 static void RedrawPlayfield_RND()
265 if (game.envelope_active)
268 DrawLevel(REDRAW_ALL);
272 void RedrawPlayfield()
274 if (game_status != GAME_MODE_PLAYING)
277 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
278 RedrawPlayfield_EM(TRUE);
279 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
280 RedrawPlayfield_SP(TRUE);
281 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
282 RedrawPlayfield_RND();
284 BlitScreenToBitmap(backbuffer);
286 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
290 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
291 boolean blit_to_screen)
293 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
296 BlitToScreenMasked(bitmap, x, y, width, height, x, y);
298 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
301 static void DrawMaskedBorderExt_FIELD(boolean blit_to_screen)
303 if (global.border_status >= GAME_MODE_TITLE &&
304 global.border_status <= GAME_MODE_PLAYING &&
305 border.draw_masked[global.border_status])
306 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
310 static void DrawMaskedBorderExt_DOOR_1(boolean blit_to_screen)
312 // only draw border over closed doors when drawing to backbuffer
313 if (!blit_to_screen && (GetDoorState() & DOOR_OPEN_1))
316 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
317 (global.border_status != GAME_MODE_EDITOR ||
318 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
319 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, blit_to_screen);
322 static void DrawMaskedBorderExt_DOOR_2(boolean blit_to_screen)
324 // only draw border over closed doors when drawing to backbuffer
325 if (!blit_to_screen && (GetDoorState() & DOOR_OPEN_2))
328 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
329 global.border_status != GAME_MODE_EDITOR)
330 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, blit_to_screen);
333 static void DrawMaskedBorderExt_DOOR_3(boolean blit_to_screen)
335 /* currently not available */
338 static void DrawMaskedBorderExt_ALL(boolean blit_to_screen)
340 DrawMaskedBorderExt_FIELD(blit_to_screen);
341 DrawMaskedBorderExt_DOOR_1(blit_to_screen);
342 DrawMaskedBorderExt_DOOR_2(blit_to_screen);
343 DrawMaskedBorderExt_DOOR_3(blit_to_screen);
346 static void DrawMaskedBorderExt(int redraw_mask, boolean blit_to_screen)
348 /* never draw masked screen borders on borderless screens */
349 if (game_status == GAME_MODE_LOADING ||
350 game_status == GAME_MODE_TITLE)
353 if (redraw_mask & REDRAW_ALL)
354 DrawMaskedBorderExt_ALL(blit_to_screen);
357 if (redraw_mask & REDRAW_FIELD)
358 DrawMaskedBorderExt_FIELD(blit_to_screen);
359 if (redraw_mask & REDRAW_DOOR_1)
360 DrawMaskedBorderExt_DOOR_1(blit_to_screen);
361 if (redraw_mask & REDRAW_DOOR_2)
362 DrawMaskedBorderExt_DOOR_2(blit_to_screen);
363 if (redraw_mask & REDRAW_DOOR_3)
364 DrawMaskedBorderExt_DOOR_3(blit_to_screen);
368 void DrawMaskedBorder_FIELD()
370 DrawMaskedBorderExt_FIELD(FALSE);
373 void DrawMaskedBorder(int redraw_mask)
375 DrawMaskedBorderExt(redraw_mask, FALSE);
378 void DrawMaskedBorderToScreen(int redraw_mask)
380 DrawMaskedBorderExt(redraw_mask, TRUE);
383 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
385 int fx = FX, fy = FY;
386 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
387 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
389 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
390 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
391 int dx_var = dx * TILESIZE_VAR / TILESIZE;
392 int dy_var = dy * TILESIZE_VAR / TILESIZE;
395 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
396 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
398 if (EVEN(SCR_FIELDX))
400 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
401 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
403 fx += (dx_var > 0 ? TILEX_VAR : 0);
410 if (EVEN(SCR_FIELDY))
412 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
413 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
415 fy += (dy_var > 0 ? TILEY_VAR : 0);
422 if (full_lev_fieldx <= SCR_FIELDX)
424 if (EVEN(SCR_FIELDX))
425 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
427 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
430 if (full_lev_fieldy <= SCR_FIELDY)
432 if (EVEN(SCR_FIELDY))
433 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
435 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
438 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
441 void BlitScreenToBitmap(Bitmap *target_bitmap)
443 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
444 BlitScreenToBitmap_EM(target_bitmap);
445 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
446 BlitScreenToBitmap_SP(target_bitmap);
447 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
448 BlitScreenToBitmap_RND(target_bitmap);
450 redraw_mask |= REDRAW_FIELD;
453 void DrawFramesPerSecond()
456 int font_nr = FONT_TEXT_2;
457 int font_width = getFontWidth(font_nr);
459 sprintf(text, "%04.1f fps", global.frames_per_second);
461 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
462 font_nr, BLIT_OPAQUE);
467 if (redraw_mask == REDRAW_NONE)
471 // masked border now drawn immediately when blitting backbuffer to window
473 // draw masked border to all viewports, if defined
474 DrawMaskedBorder(redraw_mask);
477 // draw frames per second (only if debug mode is enabled)
478 if (redraw_mask & REDRAW_FPS)
479 DrawFramesPerSecond();
481 // redraw complete window if both playfield and (some) doors need redraw
482 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
483 redraw_mask = REDRAW_ALL;
485 /* although redrawing the whole window would be fine for normal gameplay,
486 being able to only redraw the playfield is required for deactivating
487 certain drawing areas (mainly playfield) to work, which is needed for
488 warp-forward to be fast enough (by skipping redraw of most frames) */
490 if (redraw_mask & REDRAW_ALL)
492 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
494 else if (redraw_mask & REDRAW_FIELD)
496 BlitBitmap(backbuffer, window,
497 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
499 else if (redraw_mask & REDRAW_DOORS)
501 if (redraw_mask & REDRAW_DOOR_1)
502 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
504 if (redraw_mask & REDRAW_DOOR_2)
505 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
507 if (redraw_mask & REDRAW_DOOR_3)
508 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
511 redraw_mask = REDRAW_NONE;
513 // force screen redraw in every frame to continue drawing global animations
514 redraw_mask = REDRAW_ALL;
517 static void FadeCrossSaveBackbuffer()
519 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
522 static void FadeCrossRestoreBackbuffer()
524 int redraw_mask_last = redraw_mask;
526 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
528 // do not change redraw mask when restoring backbuffer after cross-fading
529 redraw_mask = redraw_mask_last;
532 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
534 static int fade_type_skip = FADE_TYPE_NONE;
535 void (*draw_border_function)(void) = NULL;
536 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
537 int x, y, width, height;
538 int fade_delay, post_delay;
540 if (fade_type == FADE_TYPE_FADE_OUT)
542 if (fade_type_skip != FADE_TYPE_NONE)
544 /* skip all fade operations until specified fade operation */
545 if (fade_type & fade_type_skip)
546 fade_type_skip = FADE_TYPE_NONE;
552 FadeCrossSaveBackbuffer();
555 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
558 FadeCrossSaveBackbuffer();
565 redraw_mask |= fade_mask;
567 if (fade_type == FADE_TYPE_SKIP)
569 fade_type_skip = fade_mode;
574 fade_delay = fading.fade_delay;
575 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
577 if (fade_type_skip != FADE_TYPE_NONE)
579 /* skip all fade operations until specified fade operation */
580 if (fade_type & fade_type_skip)
581 fade_type_skip = FADE_TYPE_NONE;
586 if (global.autoplay_leveldir)
591 if (fade_mask == REDRAW_FIELD)
596 height = FADE_SYSIZE;
598 if (border.draw_masked_when_fading)
599 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
601 DrawMaskedBorder_FIELD(); /* draw once */
603 else /* REDRAW_ALL */
611 if (!setup.fade_screens ||
613 fading.fade_mode == FADE_MODE_NONE)
615 if (fade_mode == FADE_MODE_FADE_OUT)
618 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
620 redraw_mask &= ~fade_mask;
625 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
626 draw_border_function);
628 if (fade_type == FADE_TYPE_FADE_OUT)
629 FadeCrossRestoreBackbuffer();
631 redraw_mask &= ~fade_mask;
634 static void SetAnimStatus_BeforeFadingOut()
636 global.anim_status = GAME_MODE_PSEUDO_FADING;
639 static void SetAnimStatus_AfterFadingIn()
641 global.anim_status = global.anim_status_next;
643 // force update of global animation status in case of rapid screen changes
644 redraw_mask = REDRAW_ALL;
648 void FadeIn(int fade_mask)
651 DrawMaskedBorder(REDRAW_ALL);
654 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
655 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
657 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
661 FADE_SXSIZE = FULL_SXSIZE;
662 FADE_SYSIZE = FULL_SYSIZE;
664 SetAnimStatus_AfterFadingIn();
667 void FadeOut(int fade_mask)
669 SetAnimStatus_BeforeFadingOut();
672 DrawMaskedBorder(REDRAW_ALL);
675 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
676 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
678 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
680 global.border_status = game_status;
683 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
685 static struct TitleFadingInfo fading_leave_stored;
688 fading_leave_stored = fading_leave;
690 fading = fading_leave_stored;
693 void FadeSetEnterMenu()
695 fading = menu.enter_menu;
697 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
700 void FadeSetLeaveMenu()
702 fading = menu.leave_menu;
704 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
707 void FadeSetEnterScreen()
709 fading = menu.enter_screen[game_status];
711 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
714 void FadeSetNextScreen()
716 fading = menu.next_screen[game_status];
718 // (do not overwrite fade mode set by FadeSetEnterScreen)
719 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
722 void FadeSetLeaveScreen()
724 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
727 void FadeSetFromType(int type)
729 if (type & TYPE_ENTER_SCREEN)
730 FadeSetEnterScreen();
731 else if (type & TYPE_ENTER)
733 else if (type & TYPE_LEAVE)
737 void FadeSetDisabled()
739 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
741 fading = fading_none;
744 void FadeSkipNextFadeIn()
746 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
749 void FadeSkipNextFadeOut()
751 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
754 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
756 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
758 return (graphic == IMG_UNDEFINED ? NULL :
759 graphic_info[graphic].bitmap != NULL || redefined ?
760 graphic_info[graphic].bitmap :
761 graphic_info[default_graphic].bitmap);
764 Bitmap *getBackgroundBitmap(int graphic)
766 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
769 Bitmap *getGlobalBorderBitmap(int graphic)
771 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
774 Bitmap *getGlobalBorderBitmapFromGameStatus()
777 (game_status == GAME_MODE_MAIN ||
778 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
779 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
780 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
781 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
784 return getGlobalBorderBitmap(graphic);
787 void SetWindowBackgroundImageIfDefined(int graphic)
789 if (graphic_info[graphic].bitmap)
790 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
793 void SetMainBackgroundImageIfDefined(int graphic)
795 if (graphic_info[graphic].bitmap)
796 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
799 void SetDoorBackgroundImageIfDefined(int graphic)
801 if (graphic_info[graphic].bitmap)
802 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
805 void SetWindowBackgroundImage(int graphic)
807 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
810 void SetMainBackgroundImage(int graphic)
812 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
815 void SetDoorBackgroundImage(int graphic)
817 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
820 void SetPanelBackground()
822 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
824 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
825 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
827 SetDoorBackgroundBitmap(bitmap_db_panel);
830 void DrawBackground(int x, int y, int width, int height)
832 /* "drawto" might still point to playfield buffer here (hall of fame) */
833 ClearRectangleOnBackground(backbuffer, x, y, width, height);
835 if (IN_GFX_FIELD_FULL(x, y))
836 redraw_mask |= REDRAW_FIELD;
837 else if (IN_GFX_DOOR_1(x, y))
838 redraw_mask |= REDRAW_DOOR_1;
839 else if (IN_GFX_DOOR_2(x, y))
840 redraw_mask |= REDRAW_DOOR_2;
841 else if (IN_GFX_DOOR_3(x, y))
842 redraw_mask |= REDRAW_DOOR_3;
845 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
847 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
849 if (font->bitmap == NULL)
852 DrawBackground(x, y, width, height);
855 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
857 struct GraphicInfo *g = &graphic_info[graphic];
859 if (g->bitmap == NULL)
862 DrawBackground(x, y, width, height);
865 static int game_status_last = -1;
866 static Bitmap *global_border_bitmap_last = NULL;
867 static Bitmap *global_border_bitmap = NULL;
868 static int real_sx_last = -1, real_sy_last = -1;
869 static int full_sxsize_last = -1, full_sysize_last = -1;
870 static int dx_last = -1, dy_last = -1;
871 static int dxsize_last = -1, dysize_last = -1;
872 static int vx_last = -1, vy_last = -1;
873 static int vxsize_last = -1, vysize_last = -1;
875 boolean CheckIfGlobalBorderHasChanged()
877 // if game status has not changed, global border has not changed either
878 if (game_status == game_status_last)
881 // determine and store new global border bitmap for current game status
882 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
884 return (global_border_bitmap_last != global_border_bitmap);
887 boolean CheckIfGlobalBorderRedrawIsNeeded()
889 // if game status has not changed, nothing has to be redrawn
890 if (game_status == game_status_last)
893 // redraw if last screen was title screen
894 if (game_status_last == GAME_MODE_TITLE)
897 // redraw if global screen border has changed
898 if (CheckIfGlobalBorderHasChanged())
901 // redraw if position or size of playfield area has changed
902 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
903 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
906 // redraw if position or size of door area has changed
907 if (dx_last != DX || dy_last != DY ||
908 dxsize_last != DXSIZE || dysize_last != DYSIZE)
911 // redraw if position or size of tape area has changed
912 if (vx_last != VX || vy_last != VY ||
913 vxsize_last != VXSIZE || vysize_last != VYSIZE)
919 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
922 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
924 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
927 void RedrawGlobalBorder()
929 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
931 RedrawGlobalBorderFromBitmap(bitmap);
933 redraw_mask = REDRAW_ALL;
936 static void RedrawGlobalBorderIfNeeded()
938 if (game_status == game_status_last)
941 // copy current draw buffer to later copy back areas that have not changed
942 if (game_status_last != GAME_MODE_TITLE)
943 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
945 if (CheckIfGlobalBorderRedrawIsNeeded())
947 // redraw global screen border (or clear, if defined to be empty)
948 RedrawGlobalBorderFromBitmap(global_border_bitmap);
950 // copy previous playfield and door areas, if they are defined on both
951 // previous and current screen and if they still have the same size
953 if (real_sx_last != -1 && real_sy_last != -1 &&
954 REAL_SX != -1 && REAL_SY != -1 &&
955 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
956 BlitBitmap(bitmap_db_store, backbuffer,
957 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
960 if (dx_last != -1 && dy_last != -1 &&
961 DX != -1 && DY != -1 &&
962 dxsize_last == DXSIZE && dysize_last == DYSIZE)
963 BlitBitmap(bitmap_db_store, backbuffer,
964 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
966 if (vx_last != -1 && vy_last != -1 &&
967 VX != -1 && VY != -1 &&
968 vxsize_last == VXSIZE && vysize_last == VYSIZE)
969 BlitBitmap(bitmap_db_store, backbuffer,
970 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
972 redraw_mask = REDRAW_ALL;
975 game_status_last = game_status;
977 global_border_bitmap_last = global_border_bitmap;
979 real_sx_last = REAL_SX;
980 real_sy_last = REAL_SY;
981 full_sxsize_last = FULL_SXSIZE;
982 full_sysize_last = FULL_SYSIZE;
985 dxsize_last = DXSIZE;
986 dysize_last = DYSIZE;
989 vxsize_last = VXSIZE;
990 vysize_last = VYSIZE;
995 RedrawGlobalBorderIfNeeded();
997 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
998 /* (when entering hall of fame after playing) */
999 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1001 /* !!! maybe this should be done before clearing the background !!! */
1002 if (game_status == GAME_MODE_PLAYING)
1004 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1005 SetDrawtoField(DRAW_FIELDBUFFER);
1009 SetDrawtoField(DRAW_BACKBUFFER);
1013 void MarkTileDirty(int x, int y)
1015 redraw_mask |= REDRAW_FIELD;
1018 void SetBorderElement()
1022 BorderElement = EL_EMPTY;
1024 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1026 for (x = 0; x < lev_fieldx; x++)
1028 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1029 BorderElement = EL_STEELWALL;
1031 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1037 void FloodFillLevel(int from_x, int from_y, int fill_element,
1038 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1039 int max_fieldx, int max_fieldy)
1043 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1044 static int safety = 0;
1046 /* check if starting field still has the desired content */
1047 if (field[from_x][from_y] == fill_element)
1052 if (safety > max_fieldx * max_fieldy)
1053 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1055 old_element = field[from_x][from_y];
1056 field[from_x][from_y] = fill_element;
1058 for (i = 0; i < 4; i++)
1060 x = from_x + check[i][0];
1061 y = from_y + check[i][1];
1063 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1064 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1070 void SetRandomAnimationValue(int x, int y)
1072 gfx.anim_random_frame = GfxRandom[x][y];
1075 int getGraphicAnimationFrame(int graphic, int sync_frame)
1077 /* animation synchronized with global frame counter, not move position */
1078 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1079 sync_frame = FrameCounter;
1081 return getAnimationFrame(graphic_info[graphic].anim_frames,
1082 graphic_info[graphic].anim_delay,
1083 graphic_info[graphic].anim_mode,
1084 graphic_info[graphic].anim_start_frame,
1088 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1089 Bitmap **bitmap, int *x, int *y,
1090 boolean get_backside)
1092 struct GraphicInfo *g = &graphic_info[graphic];
1093 Bitmap *src_bitmap = g->bitmap;
1094 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1095 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1096 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1098 // if no in-game graphics defined, always use standard graphic size
1099 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1100 tilesize = TILESIZE;
1102 if (tilesize == gfx.standard_tile_size)
1103 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1104 else if (tilesize == game.tile_size)
1105 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1107 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1109 if (g->offset_y == 0) /* frames are ordered horizontally */
1111 int max_width = g->anim_frames_per_line * g->width;
1112 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1114 src_x = pos % max_width;
1115 src_y = src_y % g->height + pos / max_width * g->height;
1117 else if (g->offset_x == 0) /* frames are ordered vertically */
1119 int max_height = g->anim_frames_per_line * g->height;
1120 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1122 src_x = src_x % g->width + pos / max_height * g->width;
1123 src_y = pos % max_height;
1125 else /* frames are ordered diagonally */
1127 src_x = src_x + frame * g->offset_x;
1128 src_y = src_y + frame * g->offset_y;
1131 *bitmap = src_bitmap;
1132 *x = src_x * tilesize / g->tile_size;
1133 *y = src_y * tilesize / g->tile_size;
1136 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1137 int *x, int *y, boolean get_backside)
1139 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1143 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1144 Bitmap **bitmap, int *x, int *y)
1146 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1149 void getFixedGraphicSource(int graphic, int frame,
1150 Bitmap **bitmap, int *x, int *y)
1152 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1155 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1157 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1160 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1161 int *x, int *y, boolean get_backside)
1163 struct GraphicInfo *g = &graphic_info[graphic];
1164 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1165 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1167 if (TILESIZE_VAR != TILESIZE)
1168 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1171 *bitmap = g->bitmap;
1173 if (g->offset_y == 0) /* frames are ordered horizontally */
1175 int max_width = g->anim_frames_per_line * g->width;
1176 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1178 *x = pos % max_width;
1179 *y = src_y % g->height + pos / max_width * g->height;
1181 else if (g->offset_x == 0) /* frames are ordered vertically */
1183 int max_height = g->anim_frames_per_line * g->height;
1184 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1186 *x = src_x % g->width + pos / max_height * g->width;
1187 *y = pos % max_height;
1189 else /* frames are ordered diagonally */
1191 *x = src_x + frame * g->offset_x;
1192 *y = src_y + frame * g->offset_y;
1195 *x = *x * TILESIZE_VAR / g->tile_size;
1196 *y = *y * TILESIZE_VAR / g->tile_size;
1199 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1201 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1204 void DrawGraphic(int x, int y, int graphic, int frame)
1207 if (!IN_SCR_FIELD(x, y))
1209 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1210 printf("DrawGraphic(): This should never happen!\n");
1215 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1218 MarkTileDirty(x, y);
1221 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1224 if (!IN_SCR_FIELD(x, y))
1226 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1227 printf("DrawGraphic(): This should never happen!\n");
1232 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1234 MarkTileDirty(x, y);
1237 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1243 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1245 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1248 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1254 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1255 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1258 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1261 if (!IN_SCR_FIELD(x, y))
1263 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1264 printf("DrawGraphicThruMask(): This should never happen!\n");
1269 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1272 MarkTileDirty(x, y);
1275 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1278 if (!IN_SCR_FIELD(x, y))
1280 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1281 printf("DrawGraphicThruMask(): This should never happen!\n");
1286 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1288 MarkTileDirty(x, y);
1291 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1297 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1299 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1303 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1304 int graphic, int frame)
1309 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1311 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1315 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1317 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1319 MarkTileDirty(x / tilesize, y / tilesize);
1322 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1328 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1329 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1332 void DrawMiniGraphic(int x, int y, int graphic)
1334 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1335 MarkTileDirty(x / 2, y / 2);
1338 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1343 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1344 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1347 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1348 int graphic, int frame,
1349 int cut_mode, int mask_mode)
1354 int width = TILEX, height = TILEY;
1357 if (dx || dy) /* shifted graphic */
1359 if (x < BX1) /* object enters playfield from the left */
1366 else if (x > BX2) /* object enters playfield from the right */
1372 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1378 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1380 else if (dx) /* general horizontal movement */
1381 MarkTileDirty(x + SIGN(dx), y);
1383 if (y < BY1) /* object enters playfield from the top */
1385 if (cut_mode == CUT_BELOW) /* object completely above top border */
1393 else if (y > BY2) /* object enters playfield from the bottom */
1399 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1405 else if (dy > 0 && cut_mode == CUT_ABOVE)
1407 if (y == BY2) /* object completely above bottom border */
1413 MarkTileDirty(x, y + 1);
1414 } /* object leaves playfield to the bottom */
1415 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1417 else if (dy) /* general vertical movement */
1418 MarkTileDirty(x, y + SIGN(dy));
1422 if (!IN_SCR_FIELD(x, y))
1424 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1425 printf("DrawGraphicShifted(): This should never happen!\n");
1430 width = width * TILESIZE_VAR / TILESIZE;
1431 height = height * TILESIZE_VAR / TILESIZE;
1432 cx = cx * TILESIZE_VAR / TILESIZE;
1433 cy = cy * TILESIZE_VAR / TILESIZE;
1434 dx = dx * TILESIZE_VAR / TILESIZE;
1435 dy = dy * TILESIZE_VAR / TILESIZE;
1437 if (width > 0 && height > 0)
1439 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1444 dst_x = FX + x * TILEX_VAR + dx;
1445 dst_y = FY + y * TILEY_VAR + dy;
1447 if (mask_mode == USE_MASKING)
1448 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1451 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1454 MarkTileDirty(x, y);
1458 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1459 int graphic, int frame,
1460 int cut_mode, int mask_mode)
1465 int width = TILEX_VAR, height = TILEY_VAR;
1468 int x2 = x + SIGN(dx);
1469 int y2 = y + SIGN(dy);
1471 /* movement with two-tile animations must be sync'ed with movement position,
1472 not with current GfxFrame (which can be higher when using slow movement) */
1473 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1474 int anim_frames = graphic_info[graphic].anim_frames;
1476 /* (we also need anim_delay here for movement animations with less frames) */
1477 int anim_delay = graphic_info[graphic].anim_delay;
1478 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1480 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1481 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1483 /* re-calculate animation frame for two-tile movement animation */
1484 frame = getGraphicAnimationFrame(graphic, sync_frame);
1486 /* check if movement start graphic inside screen area and should be drawn */
1487 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1489 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1491 dst_x = FX + x1 * TILEX_VAR;
1492 dst_y = FY + y1 * TILEY_VAR;
1494 if (mask_mode == USE_MASKING)
1495 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1498 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1501 MarkTileDirty(x1, y1);
1504 /* check if movement end graphic inside screen area and should be drawn */
1505 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1507 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1509 dst_x = FX + x2 * TILEX_VAR;
1510 dst_y = FY + y2 * TILEY_VAR;
1512 if (mask_mode == USE_MASKING)
1513 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1516 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1519 MarkTileDirty(x2, y2);
1523 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1524 int graphic, int frame,
1525 int cut_mode, int mask_mode)
1529 DrawGraphic(x, y, graphic, frame);
1534 if (graphic_info[graphic].double_movement) /* EM style movement images */
1535 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1537 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1540 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1541 int frame, int cut_mode)
1543 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1546 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1547 int cut_mode, int mask_mode)
1549 int lx = LEVELX(x), ly = LEVELY(y);
1553 if (IN_LEV_FIELD(lx, ly))
1555 SetRandomAnimationValue(lx, ly);
1557 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1558 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1560 /* do not use double (EM style) movement graphic when not moving */
1561 if (graphic_info[graphic].double_movement && !dx && !dy)
1563 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1564 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1567 else /* border element */
1569 graphic = el2img(element);
1570 frame = getGraphicAnimationFrame(graphic, -1);
1573 if (element == EL_EXPANDABLE_WALL)
1575 boolean left_stopped = FALSE, right_stopped = FALSE;
1577 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1578 left_stopped = TRUE;
1579 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1580 right_stopped = TRUE;
1582 if (left_stopped && right_stopped)
1584 else if (left_stopped)
1586 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1587 frame = graphic_info[graphic].anim_frames - 1;
1589 else if (right_stopped)
1591 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1592 frame = graphic_info[graphic].anim_frames - 1;
1597 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1598 else if (mask_mode == USE_MASKING)
1599 DrawGraphicThruMask(x, y, graphic, frame);
1601 DrawGraphic(x, y, graphic, frame);
1604 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1605 int cut_mode, int mask_mode)
1607 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1608 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1609 cut_mode, mask_mode);
1612 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1615 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1618 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1621 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1624 void DrawLevelElementThruMask(int x, int y, int element)
1626 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1629 void DrawLevelFieldThruMask(int x, int y)
1631 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1634 /* !!! implementation of quicksand is totally broken !!! */
1635 #define IS_CRUMBLED_TILE(x, y, e) \
1636 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1637 !IS_MOVING(x, y) || \
1638 (e) == EL_QUICKSAND_EMPTYING || \
1639 (e) == EL_QUICKSAND_FAST_EMPTYING))
1641 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1646 int width, height, cx, cy;
1647 int sx = SCREENX(x), sy = SCREENY(y);
1648 int crumbled_border_size = graphic_info[graphic].border_size;
1651 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1653 for (i = 1; i < 4; i++)
1655 int dxx = (i & 1 ? dx : 0);
1656 int dyy = (i & 2 ? dy : 0);
1659 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1662 /* check if neighbour field is of same crumble type */
1663 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1664 graphic_info[graphic].class ==
1665 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1667 /* return if check prevents inner corner */
1668 if (same == (dxx == dx && dyy == dy))
1672 /* if we reach this point, we have an inner corner */
1674 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1676 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1677 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1678 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1679 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1681 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1682 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1685 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1690 int width, height, bx, by, cx, cy;
1691 int sx = SCREENX(x), sy = SCREENY(y);
1692 int crumbled_border_size = graphic_info[graphic].border_size;
1693 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1694 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1697 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1699 /* draw simple, sloppy, non-corner-accurate crumbled border */
1701 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1702 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1703 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1704 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1706 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1707 FX + sx * TILEX_VAR + cx,
1708 FY + sy * TILEY_VAR + cy);
1710 /* (remaining middle border part must be at least as big as corner part) */
1711 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1712 crumbled_border_size >= TILESIZE / 3)
1715 /* correct corners of crumbled border, if needed */
1717 for (i = -1; i <= 1; i += 2)
1719 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1720 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1721 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1724 /* check if neighbour field is of same crumble type */
1725 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1726 graphic_info[graphic].class ==
1727 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1729 /* no crumbled corner, but continued crumbled border */
1731 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1732 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1733 int b1 = (i == 1 ? crumbled_border_size_var :
1734 TILESIZE_VAR - 2 * crumbled_border_size_var);
1736 width = crumbled_border_size_var;
1737 height = crumbled_border_size_var;
1739 if (dir == 1 || dir == 2)
1754 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1756 FX + sx * TILEX_VAR + cx,
1757 FY + sy * TILEY_VAR + cy);
1762 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1764 int sx = SCREENX(x), sy = SCREENY(y);
1767 static int xy[4][2] =
1775 if (!IN_LEV_FIELD(x, y))
1778 element = TILE_GFX_ELEMENT(x, y);
1780 /* crumble field itself */
1781 if (IS_CRUMBLED_TILE(x, y, element))
1783 if (!IN_SCR_FIELD(sx, sy))
1786 for (i = 0; i < 4; i++)
1788 int xx = x + xy[i][0];
1789 int yy = y + xy[i][1];
1791 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1794 /* check if neighbour field is of same crumble type */
1795 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1796 graphic_info[graphic].class ==
1797 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1800 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1803 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1804 graphic_info[graphic].anim_frames == 2)
1806 for (i = 0; i < 4; i++)
1808 int dx = (i & 1 ? +1 : -1);
1809 int dy = (i & 2 ? +1 : -1);
1811 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1815 MarkTileDirty(sx, sy);
1817 else /* center field not crumbled -- crumble neighbour fields */
1819 for (i = 0; i < 4; i++)
1821 int xx = x + xy[i][0];
1822 int yy = y + xy[i][1];
1823 int sxx = sx + xy[i][0];
1824 int syy = sy + xy[i][1];
1826 if (!IN_LEV_FIELD(xx, yy) ||
1827 !IN_SCR_FIELD(sxx, syy))
1830 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1833 element = TILE_GFX_ELEMENT(xx, yy);
1835 if (!IS_CRUMBLED_TILE(xx, yy, element))
1838 graphic = el_act2crm(element, ACTION_DEFAULT);
1840 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1842 MarkTileDirty(sxx, syy);
1847 void DrawLevelFieldCrumbled(int x, int y)
1851 if (!IN_LEV_FIELD(x, y))
1854 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1855 GfxElement[x][y] != EL_UNDEFINED &&
1856 GFX_CRUMBLED(GfxElement[x][y]))
1858 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1863 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1865 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1868 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1871 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1872 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1873 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1874 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1875 int sx = SCREENX(x), sy = SCREENY(y);
1877 DrawGraphic(sx, sy, graphic1, frame1);
1878 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1881 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1883 int sx = SCREENX(x), sy = SCREENY(y);
1884 static int xy[4][2] =
1893 for (i = 0; i < 4; i++)
1895 int xx = x + xy[i][0];
1896 int yy = y + xy[i][1];
1897 int sxx = sx + xy[i][0];
1898 int syy = sy + xy[i][1];
1900 if (!IN_LEV_FIELD(xx, yy) ||
1901 !IN_SCR_FIELD(sxx, syy) ||
1902 !GFX_CRUMBLED(Feld[xx][yy]) ||
1906 DrawLevelField(xx, yy);
1910 static int getBorderElement(int x, int y)
1914 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1915 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1916 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1917 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1918 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1919 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1920 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1922 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1923 int steel_position = (x == -1 && y == -1 ? 0 :
1924 x == lev_fieldx && y == -1 ? 1 :
1925 x == -1 && y == lev_fieldy ? 2 :
1926 x == lev_fieldx && y == lev_fieldy ? 3 :
1927 x == -1 || x == lev_fieldx ? 4 :
1928 y == -1 || y == lev_fieldy ? 5 : 6);
1930 return border[steel_position][steel_type];
1933 void DrawScreenElement(int x, int y, int element)
1935 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1936 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1939 void DrawLevelElement(int x, int y, int element)
1941 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1942 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1945 void DrawScreenField(int x, int y)
1947 int lx = LEVELX(x), ly = LEVELY(y);
1948 int element, content;
1950 if (!IN_LEV_FIELD(lx, ly))
1952 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1955 element = getBorderElement(lx, ly);
1957 DrawScreenElement(x, y, element);
1962 element = Feld[lx][ly];
1963 content = Store[lx][ly];
1965 if (IS_MOVING(lx, ly))
1967 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1968 boolean cut_mode = NO_CUTTING;
1970 if (element == EL_QUICKSAND_EMPTYING ||
1971 element == EL_QUICKSAND_FAST_EMPTYING ||
1972 element == EL_MAGIC_WALL_EMPTYING ||
1973 element == EL_BD_MAGIC_WALL_EMPTYING ||
1974 element == EL_DC_MAGIC_WALL_EMPTYING ||
1975 element == EL_AMOEBA_DROPPING)
1976 cut_mode = CUT_ABOVE;
1977 else if (element == EL_QUICKSAND_FILLING ||
1978 element == EL_QUICKSAND_FAST_FILLING ||
1979 element == EL_MAGIC_WALL_FILLING ||
1980 element == EL_BD_MAGIC_WALL_FILLING ||
1981 element == EL_DC_MAGIC_WALL_FILLING)
1982 cut_mode = CUT_BELOW;
1984 if (cut_mode == CUT_ABOVE)
1985 DrawScreenElement(x, y, element);
1987 DrawScreenElement(x, y, EL_EMPTY);
1990 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1991 else if (cut_mode == NO_CUTTING)
1992 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1995 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1997 if (cut_mode == CUT_BELOW &&
1998 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1999 DrawLevelElement(lx, ly + 1, element);
2002 if (content == EL_ACID)
2004 int dir = MovDir[lx][ly];
2005 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2006 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2008 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2011 else if (IS_BLOCKED(lx, ly))
2016 boolean cut_mode = NO_CUTTING;
2017 int element_old, content_old;
2019 Blocked2Moving(lx, ly, &oldx, &oldy);
2022 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2023 MovDir[oldx][oldy] == MV_RIGHT);
2025 element_old = Feld[oldx][oldy];
2026 content_old = Store[oldx][oldy];
2028 if (element_old == EL_QUICKSAND_EMPTYING ||
2029 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2030 element_old == EL_MAGIC_WALL_EMPTYING ||
2031 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2032 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2033 element_old == EL_AMOEBA_DROPPING)
2034 cut_mode = CUT_ABOVE;
2036 DrawScreenElement(x, y, EL_EMPTY);
2039 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2041 else if (cut_mode == NO_CUTTING)
2042 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2045 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2048 else if (IS_DRAWABLE(element))
2049 DrawScreenElement(x, y, element);
2051 DrawScreenElement(x, y, EL_EMPTY);
2054 void DrawLevelField(int x, int y)
2056 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2057 DrawScreenField(SCREENX(x), SCREENY(y));
2058 else if (IS_MOVING(x, y))
2062 Moving2Blocked(x, y, &newx, &newy);
2063 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2064 DrawScreenField(SCREENX(newx), SCREENY(newy));
2066 else if (IS_BLOCKED(x, y))
2070 Blocked2Moving(x, y, &oldx, &oldy);
2071 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2072 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2076 void DrawSizedElement(int x, int y, int element, int tilesize)
2080 graphic = el2edimg(element);
2081 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2084 void DrawMiniElement(int x, int y, int element)
2088 graphic = el2edimg(element);
2089 DrawMiniGraphic(x, y, graphic);
2092 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2095 int x = sx + scroll_x, y = sy + scroll_y;
2097 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2098 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2099 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2100 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2102 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2105 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2107 int x = sx + scroll_x, y = sy + scroll_y;
2109 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2110 DrawMiniElement(sx, sy, EL_EMPTY);
2111 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2112 DrawMiniElement(sx, sy, Feld[x][y]);
2114 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2117 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2118 int x, int y, int xsize, int ysize,
2119 int tile_width, int tile_height)
2123 int dst_x = startx + x * tile_width;
2124 int dst_y = starty + y * tile_height;
2125 int width = graphic_info[graphic].width;
2126 int height = graphic_info[graphic].height;
2127 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2128 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2129 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2130 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2131 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2132 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2133 boolean draw_masked = graphic_info[graphic].draw_masked;
2135 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2137 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2139 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2143 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2144 inner_sx + (x - 1) * tile_width % inner_width);
2145 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2146 inner_sy + (y - 1) * tile_height % inner_height);
2149 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2152 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2156 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2157 int x, int y, int xsize, int ysize, int font_nr)
2159 int font_width = getFontWidth(font_nr);
2160 int font_height = getFontHeight(font_nr);
2162 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2163 font_width, font_height);
2166 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2168 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2169 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2170 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2171 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2172 boolean no_delay = (tape.warp_forward);
2173 unsigned int anim_delay = 0;
2174 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2175 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2176 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2177 int font_width = getFontWidth(font_nr);
2178 int font_height = getFontHeight(font_nr);
2179 int max_xsize = level.envelope[envelope_nr].xsize;
2180 int max_ysize = level.envelope[envelope_nr].ysize;
2181 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2182 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2183 int xend = max_xsize;
2184 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2185 int xstep = (xstart < xend ? 1 : 0);
2186 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2188 int end = MAX(xend - xstart, yend - ystart);
2191 for (i = start; i <= end; i++)
2193 int last_frame = end; // last frame of this "for" loop
2194 int x = xstart + i * xstep;
2195 int y = ystart + i * ystep;
2196 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2197 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2198 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2199 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2202 SetDrawtoField(DRAW_FIELDBUFFER);
2204 BlitScreenToBitmap(backbuffer);
2206 SetDrawtoField(DRAW_BACKBUFFER);
2208 for (yy = 0; yy < ysize; yy++)
2209 for (xx = 0; xx < xsize; xx++)
2210 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2212 DrawTextBuffer(sx + font_width, sy + font_height,
2213 level.envelope[envelope_nr].text, font_nr, max_xsize,
2214 xsize - 2, ysize - 2, 0, mask_mode,
2215 level.envelope[envelope_nr].autowrap,
2216 level.envelope[envelope_nr].centered, FALSE);
2218 redraw_mask |= REDRAW_FIELD;
2221 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2225 void ShowEnvelope(int envelope_nr)
2227 int element = EL_ENVELOPE_1 + envelope_nr;
2228 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2229 int sound_opening = element_info[element].sound[ACTION_OPENING];
2230 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2231 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2232 boolean no_delay = (tape.warp_forward);
2233 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2234 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2235 int anim_mode = graphic_info[graphic].anim_mode;
2236 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2237 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2239 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2241 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2243 if (anim_mode == ANIM_DEFAULT)
2244 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2246 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2249 Delay(wait_delay_value);
2251 WaitForEventToContinue();
2253 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2255 if (anim_mode != ANIM_NONE)
2256 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2258 if (anim_mode == ANIM_DEFAULT)
2259 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2261 game.envelope_active = FALSE;
2263 SetDrawtoField(DRAW_FIELDBUFFER);
2265 redraw_mask |= REDRAW_FIELD;
2269 static void setRequestBasePosition(int *x, int *y)
2271 int sx_base, sy_base;
2273 if (request.x != -1)
2274 sx_base = request.x;
2275 else if (request.align == ALIGN_LEFT)
2277 else if (request.align == ALIGN_RIGHT)
2278 sx_base = SX + SXSIZE;
2280 sx_base = SX + SXSIZE / 2;
2282 if (request.y != -1)
2283 sy_base = request.y;
2284 else if (request.valign == VALIGN_TOP)
2286 else if (request.valign == VALIGN_BOTTOM)
2287 sy_base = SY + SYSIZE;
2289 sy_base = SY + SYSIZE / 2;
2295 static void setRequestPositionExt(int *x, int *y, int width, int height,
2296 boolean add_border_size)
2298 int border_size = request.border_size;
2299 int sx_base, sy_base;
2302 setRequestBasePosition(&sx_base, &sy_base);
2304 if (request.align == ALIGN_LEFT)
2306 else if (request.align == ALIGN_RIGHT)
2307 sx = sx_base - width;
2309 sx = sx_base - width / 2;
2311 if (request.valign == VALIGN_TOP)
2313 else if (request.valign == VALIGN_BOTTOM)
2314 sy = sy_base - height;
2316 sy = sy_base - height / 2;
2318 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2319 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2321 if (add_border_size)
2331 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2333 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2336 void DrawEnvelopeRequest(char *text)
2338 int last_game_status = game_status; /* save current game status */
2339 char *text_final = text;
2340 char *text_door_style = NULL;
2341 int graphic = IMG_BACKGROUND_REQUEST;
2342 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2343 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2344 int font_nr = FONT_REQUEST;
2345 int font_width = getFontWidth(font_nr);
2346 int font_height = getFontHeight(font_nr);
2347 int border_size = request.border_size;
2348 int line_spacing = request.line_spacing;
2349 int line_height = font_height + line_spacing;
2350 int max_text_width = request.width - 2 * border_size;
2351 int max_text_height = request.height - 2 * border_size;
2352 int line_length = max_text_width / font_width;
2353 int max_lines = max_text_height / line_height;
2354 int text_width = line_length * font_width;
2355 int width = request.width;
2356 int height = request.height;
2357 int tile_size = MAX(request.step_offset, 1);
2358 int x_steps = width / tile_size;
2359 int y_steps = height / tile_size;
2360 int sx_offset = border_size;
2361 int sy_offset = border_size;
2365 if (request.centered)
2366 sx_offset = (request.width - text_width) / 2;
2368 if (request.wrap_single_words && !request.autowrap)
2370 char *src_text_ptr, *dst_text_ptr;
2372 text_door_style = checked_malloc(2 * strlen(text) + 1);
2374 src_text_ptr = text;
2375 dst_text_ptr = text_door_style;
2377 while (*src_text_ptr)
2379 if (*src_text_ptr == ' ' ||
2380 *src_text_ptr == '?' ||
2381 *src_text_ptr == '!')
2382 *dst_text_ptr++ = '\n';
2384 if (*src_text_ptr != ' ')
2385 *dst_text_ptr++ = *src_text_ptr;
2390 *dst_text_ptr = '\0';
2392 text_final = text_door_style;
2395 setRequestPosition(&sx, &sy, FALSE);
2397 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2399 for (y = 0; y < y_steps; y++)
2400 for (x = 0; x < x_steps; x++)
2401 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2402 x, y, x_steps, y_steps,
2403 tile_size, tile_size);
2405 /* force DOOR font inside door area */
2406 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
2408 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2409 line_length, -1, max_lines, line_spacing, mask_mode,
2410 request.autowrap, request.centered, FALSE);
2412 SetGameStatus(last_game_status); /* restore current game status */
2414 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2415 RedrawGadget(tool_gadget[i]);
2417 // store readily prepared envelope request for later use when animating
2418 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2420 if (text_door_style)
2421 free(text_door_style);
2424 void AnimateEnvelopeRequest(int anim_mode, int action)
2426 int graphic = IMG_BACKGROUND_REQUEST;
2427 boolean draw_masked = graphic_info[graphic].draw_masked;
2428 int delay_value_normal = request.step_delay;
2429 int delay_value_fast = delay_value_normal / 2;
2430 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2431 boolean no_delay = (tape.warp_forward);
2432 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2433 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2434 unsigned int anim_delay = 0;
2436 int tile_size = MAX(request.step_offset, 1);
2437 int max_xsize = request.width / tile_size;
2438 int max_ysize = request.height / tile_size;
2439 int max_xsize_inner = max_xsize - 2;
2440 int max_ysize_inner = max_ysize - 2;
2442 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2443 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2444 int xend = max_xsize_inner;
2445 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2446 int xstep = (xstart < xend ? 1 : 0);
2447 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2449 int end = MAX(xend - xstart, yend - ystart);
2452 if (setup.quick_doors)
2459 for (i = start; i <= end; i++)
2461 int last_frame = end; // last frame of this "for" loop
2462 int x = xstart + i * xstep;
2463 int y = ystart + i * ystep;
2464 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2465 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2466 int xsize_size_left = (xsize - 1) * tile_size;
2467 int ysize_size_top = (ysize - 1) * tile_size;
2468 int max_xsize_pos = (max_xsize - 1) * tile_size;
2469 int max_ysize_pos = (max_ysize - 1) * tile_size;
2470 int width = xsize * tile_size;
2471 int height = ysize * tile_size;
2476 setRequestPosition(&src_x, &src_y, FALSE);
2477 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2479 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2481 for (yy = 0; yy < 2; yy++)
2483 for (xx = 0; xx < 2; xx++)
2485 int src_xx = src_x + xx * max_xsize_pos;
2486 int src_yy = src_y + yy * max_ysize_pos;
2487 int dst_xx = dst_x + xx * xsize_size_left;
2488 int dst_yy = dst_y + yy * ysize_size_top;
2489 int xx_size = (xx ? tile_size : xsize_size_left);
2490 int yy_size = (yy ? tile_size : ysize_size_top);
2493 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2494 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2496 BlitBitmap(bitmap_db_cross, backbuffer,
2497 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2501 redraw_mask |= REDRAW_FIELD;
2506 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2510 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2512 int graphic = IMG_BACKGROUND_REQUEST;
2513 int sound_opening = SND_REQUEST_OPENING;
2514 int sound_closing = SND_REQUEST_CLOSING;
2515 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2516 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2517 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2518 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2519 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2521 if (game_status == GAME_MODE_PLAYING)
2522 BlitScreenToBitmap(backbuffer);
2524 SetDrawtoField(DRAW_BACKBUFFER);
2526 // SetDrawBackgroundMask(REDRAW_NONE);
2528 if (action == ACTION_OPENING)
2530 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2532 if (req_state & REQ_ASK)
2534 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2535 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2537 else if (req_state & REQ_CONFIRM)
2539 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2541 else if (req_state & REQ_PLAYER)
2543 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2544 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2545 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2546 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2549 DrawEnvelopeRequest(text);
2551 if (game_status != GAME_MODE_MAIN)
2555 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2557 if (action == ACTION_OPENING)
2559 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2561 if (anim_mode == ANIM_DEFAULT)
2562 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2564 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2568 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2570 if (anim_mode != ANIM_NONE)
2571 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2573 if (anim_mode == ANIM_DEFAULT)
2574 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2577 game.envelope_active = FALSE;
2579 if (action == ACTION_CLOSING)
2581 if (game_status != GAME_MODE_MAIN)
2584 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2587 // SetDrawBackgroundMask(last_draw_background_mask);
2589 redraw_mask |= REDRAW_FIELD;
2591 if (game_status == GAME_MODE_MAIN)
2596 if (action == ACTION_CLOSING &&
2597 game_status == GAME_MODE_PLAYING &&
2598 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2599 SetDrawtoField(DRAW_FIELDBUFFER);
2602 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2606 int graphic = el2preimg(element);
2608 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2609 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2612 void DrawLevel(int draw_background_mask)
2616 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2617 SetDrawBackgroundMask(draw_background_mask);
2621 for (x = BX1; x <= BX2; x++)
2622 for (y = BY1; y <= BY2; y++)
2623 DrawScreenField(x, y);
2625 redraw_mask |= REDRAW_FIELD;
2628 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2633 for (x = 0; x < size_x; x++)
2634 for (y = 0; y < size_y; y++)
2635 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2637 redraw_mask |= REDRAW_FIELD;
2640 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2644 for (x = 0; x < size_x; x++)
2645 for (y = 0; y < size_y; y++)
2646 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2648 redraw_mask |= REDRAW_FIELD;
2651 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2653 boolean show_level_border = (BorderElement != EL_EMPTY);
2654 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2655 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2656 int tile_size = preview.tile_size;
2657 int preview_width = preview.xsize * tile_size;
2658 int preview_height = preview.ysize * tile_size;
2659 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2660 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2661 int real_preview_width = real_preview_xsize * tile_size;
2662 int real_preview_height = real_preview_ysize * tile_size;
2663 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2664 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2667 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2670 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2672 dst_x += (preview_width - real_preview_width) / 2;
2673 dst_y += (preview_height - real_preview_height) / 2;
2675 for (x = 0; x < real_preview_xsize; x++)
2677 for (y = 0; y < real_preview_ysize; y++)
2679 int lx = from_x + x + (show_level_border ? -1 : 0);
2680 int ly = from_y + y + (show_level_border ? -1 : 0);
2681 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2682 getBorderElement(lx, ly));
2684 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2685 element, tile_size);
2689 redraw_mask |= REDRAW_FIELD;
2692 #define MICROLABEL_EMPTY 0
2693 #define MICROLABEL_LEVEL_NAME 1
2694 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2695 #define MICROLABEL_LEVEL_AUTHOR 3
2696 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2697 #define MICROLABEL_IMPORTED_FROM 5
2698 #define MICROLABEL_IMPORTED_BY_HEAD 6
2699 #define MICROLABEL_IMPORTED_BY 7
2701 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2703 int max_text_width = SXSIZE;
2704 int font_width = getFontWidth(font_nr);
2706 if (pos->align == ALIGN_CENTER)
2707 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2708 else if (pos->align == ALIGN_RIGHT)
2709 max_text_width = pos->x;
2711 max_text_width = SXSIZE - pos->x;
2713 return max_text_width / font_width;
2716 static void DrawPreviewLevelLabelExt(int mode)
2718 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2719 char label_text[MAX_OUTPUT_LINESIZE + 1];
2720 int max_len_label_text;
2721 int font_nr = pos->font;
2724 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2727 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2728 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2729 mode == MICROLABEL_IMPORTED_BY_HEAD)
2730 font_nr = pos->font_alt;
2732 max_len_label_text = getMaxTextLength(pos, font_nr);
2734 if (pos->size != -1)
2735 max_len_label_text = pos->size;
2737 for (i = 0; i < max_len_label_text; i++)
2738 label_text[i] = ' ';
2739 label_text[max_len_label_text] = '\0';
2741 if (strlen(label_text) > 0)
2742 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2745 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2746 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2747 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2748 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2749 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2750 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2751 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2752 max_len_label_text);
2753 label_text[max_len_label_text] = '\0';
2755 if (strlen(label_text) > 0)
2756 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2758 redraw_mask |= REDRAW_FIELD;
2761 static void DrawPreviewLevelExt(boolean restart)
2763 static unsigned int scroll_delay = 0;
2764 static unsigned int label_delay = 0;
2765 static int from_x, from_y, scroll_direction;
2766 static int label_state, label_counter;
2767 unsigned int scroll_delay_value = preview.step_delay;
2768 boolean show_level_border = (BorderElement != EL_EMPTY);
2769 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2770 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2771 int last_game_status = game_status; /* save current game status */
2778 if (preview.anim_mode == ANIM_CENTERED)
2780 if (level_xsize > preview.xsize)
2781 from_x = (level_xsize - preview.xsize) / 2;
2782 if (level_ysize > preview.ysize)
2783 from_y = (level_ysize - preview.ysize) / 2;
2786 from_x += preview.xoffset;
2787 from_y += preview.yoffset;
2789 scroll_direction = MV_RIGHT;
2793 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2794 DrawPreviewLevelLabelExt(label_state);
2796 /* initialize delay counters */
2797 DelayReached(&scroll_delay, 0);
2798 DelayReached(&label_delay, 0);
2800 if (leveldir_current->name)
2802 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2803 char label_text[MAX_OUTPUT_LINESIZE + 1];
2804 int font_nr = pos->font;
2805 int max_len_label_text = getMaxTextLength(pos, font_nr);
2807 if (pos->size != -1)
2808 max_len_label_text = pos->size;
2810 strncpy(label_text, leveldir_current->name, max_len_label_text);
2811 label_text[max_len_label_text] = '\0';
2813 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2814 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2817 SetGameStatus(last_game_status); /* restore current game status */
2822 /* scroll preview level, if needed */
2823 if (preview.anim_mode != ANIM_NONE &&
2824 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2825 DelayReached(&scroll_delay, scroll_delay_value))
2827 switch (scroll_direction)
2832 from_x -= preview.step_offset;
2833 from_x = (from_x < 0 ? 0 : from_x);
2836 scroll_direction = MV_UP;
2840 if (from_x < level_xsize - preview.xsize)
2842 from_x += preview.step_offset;
2843 from_x = (from_x > level_xsize - preview.xsize ?
2844 level_xsize - preview.xsize : from_x);
2847 scroll_direction = MV_DOWN;
2853 from_y -= preview.step_offset;
2854 from_y = (from_y < 0 ? 0 : from_y);
2857 scroll_direction = MV_RIGHT;
2861 if (from_y < level_ysize - preview.ysize)
2863 from_y += preview.step_offset;
2864 from_y = (from_y > level_ysize - preview.ysize ?
2865 level_ysize - preview.ysize : from_y);
2868 scroll_direction = MV_LEFT;
2875 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2878 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2879 /* redraw micro level label, if needed */
2880 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2881 !strEqual(level.author, ANONYMOUS_NAME) &&
2882 !strEqual(level.author, leveldir_current->name) &&
2883 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2885 int max_label_counter = 23;
2887 if (leveldir_current->imported_from != NULL &&
2888 strlen(leveldir_current->imported_from) > 0)
2889 max_label_counter += 14;
2890 if (leveldir_current->imported_by != NULL &&
2891 strlen(leveldir_current->imported_by) > 0)
2892 max_label_counter += 14;
2894 label_counter = (label_counter + 1) % max_label_counter;
2895 label_state = (label_counter >= 0 && label_counter <= 7 ?
2896 MICROLABEL_LEVEL_NAME :
2897 label_counter >= 9 && label_counter <= 12 ?
2898 MICROLABEL_LEVEL_AUTHOR_HEAD :
2899 label_counter >= 14 && label_counter <= 21 ?
2900 MICROLABEL_LEVEL_AUTHOR :
2901 label_counter >= 23 && label_counter <= 26 ?
2902 MICROLABEL_IMPORTED_FROM_HEAD :
2903 label_counter >= 28 && label_counter <= 35 ?
2904 MICROLABEL_IMPORTED_FROM :
2905 label_counter >= 37 && label_counter <= 40 ?
2906 MICROLABEL_IMPORTED_BY_HEAD :
2907 label_counter >= 42 && label_counter <= 49 ?
2908 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2910 if (leveldir_current->imported_from == NULL &&
2911 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2912 label_state == MICROLABEL_IMPORTED_FROM))
2913 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2914 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2916 DrawPreviewLevelLabelExt(label_state);
2919 SetGameStatus(last_game_status); /* restore current game status */
2922 void DrawPreviewLevelInitial()
2924 DrawPreviewLevelExt(TRUE);
2927 void DrawPreviewLevelAnimation()
2929 DrawPreviewLevelExt(FALSE);
2932 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2933 int graphic, int sync_frame,
2936 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2938 if (mask_mode == USE_MASKING)
2939 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2941 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2944 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2945 int graphic, int sync_frame, int mask_mode)
2947 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2949 if (mask_mode == USE_MASKING)
2950 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2952 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2955 inline static void DrawGraphicAnimation(int x, int y, int graphic)
2957 int lx = LEVELX(x), ly = LEVELY(y);
2959 if (!IN_SCR_FIELD(x, y))
2962 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2963 graphic, GfxFrame[lx][ly], NO_MASKING);
2965 MarkTileDirty(x, y);
2968 void DrawFixedGraphicAnimation(int x, int y, int graphic)
2970 int lx = LEVELX(x), ly = LEVELY(y);
2972 if (!IN_SCR_FIELD(x, y))
2975 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2976 graphic, GfxFrame[lx][ly], NO_MASKING);
2977 MarkTileDirty(x, y);
2980 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2982 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2985 void DrawLevelElementAnimation(int x, int y, int element)
2987 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2989 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2992 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2994 int sx = SCREENX(x), sy = SCREENY(y);
2996 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2999 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3002 DrawGraphicAnimation(sx, sy, graphic);
3005 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3006 DrawLevelFieldCrumbled(x, y);
3008 if (GFX_CRUMBLED(Feld[x][y]))
3009 DrawLevelFieldCrumbled(x, y);
3013 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3015 int sx = SCREENX(x), sy = SCREENY(y);
3018 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3021 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3023 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3026 DrawGraphicAnimation(sx, sy, graphic);
3028 if (GFX_CRUMBLED(element))
3029 DrawLevelFieldCrumbled(x, y);
3032 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3034 if (player->use_murphy)
3036 /* this works only because currently only one player can be "murphy" ... */
3037 static int last_horizontal_dir = MV_LEFT;
3038 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3040 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3041 last_horizontal_dir = move_dir;
3043 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3045 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3047 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3053 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3056 static boolean equalGraphics(int graphic1, int graphic2)
3058 struct GraphicInfo *g1 = &graphic_info[graphic1];
3059 struct GraphicInfo *g2 = &graphic_info[graphic2];
3061 return (g1->bitmap == g2->bitmap &&
3062 g1->src_x == g2->src_x &&
3063 g1->src_y == g2->src_y &&
3064 g1->anim_frames == g2->anim_frames &&
3065 g1->anim_delay == g2->anim_delay &&
3066 g1->anim_mode == g2->anim_mode);
3069 void DrawAllPlayers()
3073 for (i = 0; i < MAX_PLAYERS; i++)
3074 if (stored_player[i].active)
3075 DrawPlayer(&stored_player[i]);
3078 void DrawPlayerField(int x, int y)
3080 if (!IS_PLAYER(x, y))
3083 DrawPlayer(PLAYERINFO(x, y));
3086 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3088 void DrawPlayer(struct PlayerInfo *player)
3090 int jx = player->jx;
3091 int jy = player->jy;
3092 int move_dir = player->MovDir;
3093 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3094 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3095 int last_jx = (player->is_moving ? jx - dx : jx);
3096 int last_jy = (player->is_moving ? jy - dy : jy);
3097 int next_jx = jx + dx;
3098 int next_jy = jy + dy;
3099 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3100 boolean player_is_opaque = FALSE;
3101 int sx = SCREENX(jx), sy = SCREENY(jy);
3102 int sxx = 0, syy = 0;
3103 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3105 int action = ACTION_DEFAULT;
3106 int last_player_graphic = getPlayerGraphic(player, move_dir);
3107 int last_player_frame = player->Frame;
3110 /* GfxElement[][] is set to the element the player is digging or collecting;
3111 remove also for off-screen player if the player is not moving anymore */
3112 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3113 GfxElement[jx][jy] = EL_UNDEFINED;
3115 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3119 if (!IN_LEV_FIELD(jx, jy))
3121 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3122 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3123 printf("DrawPlayerField(): This should never happen!\n");
3128 if (element == EL_EXPLOSION)
3131 action = (player->is_pushing ? ACTION_PUSHING :
3132 player->is_digging ? ACTION_DIGGING :
3133 player->is_collecting ? ACTION_COLLECTING :
3134 player->is_moving ? ACTION_MOVING :
3135 player->is_snapping ? ACTION_SNAPPING :
3136 player->is_dropping ? ACTION_DROPPING :
3137 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3139 if (player->is_waiting)
3140 move_dir = player->dir_waiting;
3142 InitPlayerGfxAnimation(player, action, move_dir);
3144 /* ----------------------------------------------------------------------- */
3145 /* draw things in the field the player is leaving, if needed */
3146 /* ----------------------------------------------------------------------- */
3148 if (player->is_moving)
3150 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3152 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3154 if (last_element == EL_DYNAMITE_ACTIVE ||
3155 last_element == EL_EM_DYNAMITE_ACTIVE ||
3156 last_element == EL_SP_DISK_RED_ACTIVE)
3157 DrawDynamite(last_jx, last_jy);
3159 DrawLevelFieldThruMask(last_jx, last_jy);
3161 else if (last_element == EL_DYNAMITE_ACTIVE ||
3162 last_element == EL_EM_DYNAMITE_ACTIVE ||
3163 last_element == EL_SP_DISK_RED_ACTIVE)
3164 DrawDynamite(last_jx, last_jy);
3166 /* !!! this is not enough to prevent flickering of players which are
3167 moving next to each others without a free tile between them -- this
3168 can only be solved by drawing all players layer by layer (first the
3169 background, then the foreground etc.) !!! => TODO */
3170 else if (!IS_PLAYER(last_jx, last_jy))
3171 DrawLevelField(last_jx, last_jy);
3174 DrawLevelField(last_jx, last_jy);
3177 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3178 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3181 if (!IN_SCR_FIELD(sx, sy))
3184 /* ----------------------------------------------------------------------- */
3185 /* draw things behind the player, if needed */
3186 /* ----------------------------------------------------------------------- */
3189 DrawLevelElement(jx, jy, Back[jx][jy]);
3190 else if (IS_ACTIVE_BOMB(element))
3191 DrawLevelElement(jx, jy, EL_EMPTY);
3194 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3196 int old_element = GfxElement[jx][jy];
3197 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3198 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3200 if (GFX_CRUMBLED(old_element))
3201 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3203 DrawGraphic(sx, sy, old_graphic, frame);
3205 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3206 player_is_opaque = TRUE;
3210 GfxElement[jx][jy] = EL_UNDEFINED;
3212 /* make sure that pushed elements are drawn with correct frame rate */
3213 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3215 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3216 GfxFrame[jx][jy] = player->StepFrame;
3218 DrawLevelField(jx, jy);
3222 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3223 /* ----------------------------------------------------------------------- */
3224 /* draw player himself */
3225 /* ----------------------------------------------------------------------- */
3227 graphic = getPlayerGraphic(player, move_dir);
3229 /* in the case of changed player action or direction, prevent the current
3230 animation frame from being restarted for identical animations */
3231 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3232 player->Frame = last_player_frame;
3234 frame = getGraphicAnimationFrame(graphic, player->Frame);
3238 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3239 sxx = player->GfxPos;
3241 syy = player->GfxPos;
3244 if (player_is_opaque)
3245 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3247 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3249 if (SHIELD_ON(player))
3251 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3252 IMG_SHIELD_NORMAL_ACTIVE);
3253 int frame = getGraphicAnimationFrame(graphic, -1);
3255 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3259 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3262 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3263 sxx = player->GfxPos;
3265 syy = player->GfxPos;
3269 /* ----------------------------------------------------------------------- */
3270 /* draw things the player is pushing, if needed */
3271 /* ----------------------------------------------------------------------- */
3273 if (player->is_pushing && player->is_moving)
3275 int px = SCREENX(jx), py = SCREENY(jy);
3276 int pxx = (TILEX - ABS(sxx)) * dx;
3277 int pyy = (TILEY - ABS(syy)) * dy;
3278 int gfx_frame = GfxFrame[jx][jy];
3284 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3286 element = Feld[next_jx][next_jy];
3287 gfx_frame = GfxFrame[next_jx][next_jy];
3290 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3292 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3293 frame = getGraphicAnimationFrame(graphic, sync_frame);
3295 /* draw background element under pushed element (like the Sokoban field) */
3296 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3298 /* this allows transparent pushing animation over non-black background */
3301 DrawLevelElement(jx, jy, Back[jx][jy]);
3303 DrawLevelElement(jx, jy, EL_EMPTY);
3305 if (Back[next_jx][next_jy])
3306 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3308 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3310 else if (Back[next_jx][next_jy])
3311 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3314 /* do not draw (EM style) pushing animation when pushing is finished */
3315 /* (two-tile animations usually do not contain start and end frame) */
3316 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3317 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3319 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3321 /* masked drawing is needed for EMC style (double) movement graphics */
3322 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3323 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3327 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3328 /* ----------------------------------------------------------------------- */
3329 /* draw player himself */
3330 /* ----------------------------------------------------------------------- */
3332 graphic = getPlayerGraphic(player, move_dir);
3334 /* in the case of changed player action or direction, prevent the current
3335 animation frame from being restarted for identical animations */
3336 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3337 player->Frame = last_player_frame;
3339 frame = getGraphicAnimationFrame(graphic, player->Frame);
3343 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3344 sxx = player->GfxPos;
3346 syy = player->GfxPos;
3349 if (player_is_opaque)
3350 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3352 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3354 if (SHIELD_ON(player))
3356 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3357 IMG_SHIELD_NORMAL_ACTIVE);
3358 int frame = getGraphicAnimationFrame(graphic, -1);
3360 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3364 /* ----------------------------------------------------------------------- */
3365 /* draw things in front of player (active dynamite or dynabombs) */
3366 /* ----------------------------------------------------------------------- */
3368 if (IS_ACTIVE_BOMB(element))
3370 graphic = el2img(element);
3371 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3373 if (game.emulation == EMU_SUPAPLEX)
3374 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3376 DrawGraphicThruMask(sx, sy, graphic, frame);
3379 if (player_is_moving && last_element == EL_EXPLOSION)
3381 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3382 GfxElement[last_jx][last_jy] : EL_EMPTY);
3383 int graphic = el_act2img(element, ACTION_EXPLODING);
3384 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3385 int phase = ExplodePhase[last_jx][last_jy] - 1;
3386 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3389 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3392 /* ----------------------------------------------------------------------- */
3393 /* draw elements the player is just walking/passing through/under */
3394 /* ----------------------------------------------------------------------- */
3396 if (player_is_moving)
3398 /* handle the field the player is leaving ... */
3399 if (IS_ACCESSIBLE_INSIDE(last_element))
3400 DrawLevelField(last_jx, last_jy);
3401 else if (IS_ACCESSIBLE_UNDER(last_element))
3402 DrawLevelFieldThruMask(last_jx, last_jy);
3405 /* do not redraw accessible elements if the player is just pushing them */
3406 if (!player_is_moving || !player->is_pushing)
3408 /* ... and the field the player is entering */
3409 if (IS_ACCESSIBLE_INSIDE(element))
3410 DrawLevelField(jx, jy);
3411 else if (IS_ACCESSIBLE_UNDER(element))
3412 DrawLevelFieldThruMask(jx, jy);
3415 MarkTileDirty(sx, sy);
3418 /* ------------------------------------------------------------------------- */
3420 void WaitForEventToContinue()
3422 boolean still_wait = TRUE;
3424 /* simulate releasing mouse button over last gadget, if still pressed */
3426 HandleGadgets(-1, -1, 0);
3428 button_status = MB_RELEASED;
3442 case EVENT_BUTTONPRESS:
3443 case EVENT_KEYPRESS:
3447 case EVENT_KEYRELEASE:
3448 ClearPlayerAction();
3452 HandleOtherEvents(&event);
3456 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3463 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3467 #define MAX_REQUEST_LINES 13
3468 #define MAX_REQUEST_LINE_FONT1_LEN 7
3469 #define MAX_REQUEST_LINE_FONT2_LEN 10
3471 static int RequestHandleEvents(unsigned int req_state)
3473 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3474 local_player->LevelSolved_GameEnd);
3475 int width = request.width;
3476 int height = request.height;
3480 setRequestPosition(&sx, &sy, FALSE);
3482 button_status = MB_RELEASED;
3484 request_gadget_id = -1;
3491 SetDrawtoField(DRAW_FIELDBUFFER);
3493 HandleGameActions();
3495 SetDrawtoField(DRAW_BACKBUFFER);
3497 if (global.use_envelope_request)
3499 /* copy current state of request area to middle of playfield area */
3500 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3508 while (NextValidEvent(&event))
3512 case EVENT_BUTTONPRESS:
3513 case EVENT_BUTTONRELEASE:
3514 case EVENT_MOTIONNOTIFY:
3518 if (event.type == EVENT_MOTIONNOTIFY)
3523 motion_status = TRUE;
3524 mx = ((MotionEvent *) &event)->x;
3525 my = ((MotionEvent *) &event)->y;
3529 motion_status = FALSE;
3530 mx = ((ButtonEvent *) &event)->x;
3531 my = ((ButtonEvent *) &event)->y;
3532 if (event.type == EVENT_BUTTONPRESS)
3533 button_status = ((ButtonEvent *) &event)->button;
3535 button_status = MB_RELEASED;
3538 /* this sets 'request_gadget_id' */
3539 HandleGadgets(mx, my, button_status);
3541 switch (request_gadget_id)
3543 case TOOL_CTRL_ID_YES:
3546 case TOOL_CTRL_ID_NO:
3549 case TOOL_CTRL_ID_CONFIRM:
3550 result = TRUE | FALSE;
3553 case TOOL_CTRL_ID_PLAYER_1:
3556 case TOOL_CTRL_ID_PLAYER_2:
3559 case TOOL_CTRL_ID_PLAYER_3:
3562 case TOOL_CTRL_ID_PLAYER_4:
3573 case EVENT_KEYPRESS:
3574 switch (GetEventKey((KeyEvent *)&event, TRUE))
3577 if (req_state & REQ_CONFIRM)
3582 #if defined(TARGET_SDL2)
3589 #if defined(TARGET_SDL2)
3599 if (req_state & REQ_PLAYER)
3603 case EVENT_KEYRELEASE:
3604 ClearPlayerAction();
3608 HandleOtherEvents(&event);
3613 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3615 int joy = AnyJoystick();
3617 if (joy & JOY_BUTTON_1)
3619 else if (joy & JOY_BUTTON_2)
3625 if (global.use_envelope_request)
3627 /* copy back current state of pressed buttons inside request area */
3628 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3638 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3644 static boolean RequestDoor(char *text, unsigned int req_state)
3646 unsigned int old_door_state;
3647 int last_game_status = game_status; /* save current game status */
3648 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3649 int font_nr = FONT_TEXT_2;
3654 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3656 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3657 font_nr = FONT_TEXT_1;
3660 if (game_status == GAME_MODE_PLAYING)
3661 BlitScreenToBitmap(backbuffer);
3663 /* disable deactivated drawing when quick-loading level tape recording */
3664 if (tape.playing && tape.deactivate_display)
3665 TapeDeactivateDisplayOff(TRUE);
3667 SetMouseCursor(CURSOR_DEFAULT);
3669 #if defined(NETWORK_AVALIABLE)
3670 /* pause network game while waiting for request to answer */
3671 if (options.network &&
3672 game_status == GAME_MODE_PLAYING &&
3673 req_state & REQUEST_WAIT_FOR_INPUT)
3674 SendToServer_PausePlaying();
3677 old_door_state = GetDoorState();
3679 /* simulate releasing mouse button over last gadget, if still pressed */
3681 HandleGadgets(-1, -1, 0);
3685 /* draw released gadget before proceeding */
3688 if (old_door_state & DOOR_OPEN_1)
3690 CloseDoor(DOOR_CLOSE_1);
3692 /* save old door content */
3693 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3694 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3697 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3698 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3700 /* clear door drawing field */
3701 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3703 /* force DOOR font inside door area */
3704 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
3706 /* write text for request */
3707 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3709 char text_line[max_request_line_len + 1];
3715 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3717 tc = *(text_ptr + tx);
3718 // if (!tc || tc == ' ')
3719 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3723 if ((tc == '?' || tc == '!') && tl == 0)
3733 strncpy(text_line, text_ptr, tl);
3736 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3737 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3738 text_line, font_nr);
3740 text_ptr += tl + (tc == ' ' ? 1 : 0);
3741 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3744 SetGameStatus(last_game_status); /* restore current game status */
3746 if (req_state & REQ_ASK)
3748 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3749 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3751 else if (req_state & REQ_CONFIRM)
3753 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3755 else if (req_state & REQ_PLAYER)
3757 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3758 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3759 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3760 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3763 /* copy request gadgets to door backbuffer */
3764 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3766 OpenDoor(DOOR_OPEN_1);
3768 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3770 if (game_status == GAME_MODE_PLAYING)
3772 SetPanelBackground();
3773 SetDrawBackgroundMask(REDRAW_DOOR_1);
3777 SetDrawBackgroundMask(REDRAW_FIELD);
3783 if (game_status != GAME_MODE_MAIN)
3786 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3788 // ---------- handle request buttons ----------
3789 result = RequestHandleEvents(req_state);
3791 if (game_status != GAME_MODE_MAIN)
3796 if (!(req_state & REQ_STAY_OPEN))
3798 CloseDoor(DOOR_CLOSE_1);
3800 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3801 (req_state & REQ_REOPEN))
3802 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3807 if (game_status == GAME_MODE_PLAYING)
3809 SetPanelBackground();
3810 SetDrawBackgroundMask(REDRAW_DOOR_1);
3814 SetDrawBackgroundMask(REDRAW_FIELD);
3817 #if defined(NETWORK_AVALIABLE)
3818 /* continue network game after request */
3819 if (options.network &&
3820 game_status == GAME_MODE_PLAYING &&
3821 req_state & REQUEST_WAIT_FOR_INPUT)
3822 SendToServer_ContinuePlaying();
3825 /* restore deactivated drawing when quick-loading level tape recording */
3826 if (tape.playing && tape.deactivate_display)
3827 TapeDeactivateDisplayOn();
3832 static boolean RequestEnvelope(char *text, unsigned int req_state)
3836 if (game_status == GAME_MODE_PLAYING)
3837 BlitScreenToBitmap(backbuffer);
3839 /* disable deactivated drawing when quick-loading level tape recording */
3840 if (tape.playing && tape.deactivate_display)
3841 TapeDeactivateDisplayOff(TRUE);
3843 SetMouseCursor(CURSOR_DEFAULT);
3845 #if defined(NETWORK_AVALIABLE)
3846 /* pause network game while waiting for request to answer */
3847 if (options.network &&
3848 game_status == GAME_MODE_PLAYING &&
3849 req_state & REQUEST_WAIT_FOR_INPUT)
3850 SendToServer_PausePlaying();
3853 /* simulate releasing mouse button over last gadget, if still pressed */
3855 HandleGadgets(-1, -1, 0);
3859 // (replace with setting corresponding request background)
3860 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3861 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3863 /* clear door drawing field */
3864 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3866 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3868 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3870 if (game_status == GAME_MODE_PLAYING)
3872 SetPanelBackground();
3873 SetDrawBackgroundMask(REDRAW_DOOR_1);
3877 SetDrawBackgroundMask(REDRAW_FIELD);
3883 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3885 // ---------- handle request buttons ----------
3886 result = RequestHandleEvents(req_state);
3888 if (game_status != GAME_MODE_MAIN)
3893 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3897 if (game_status == GAME_MODE_PLAYING)
3899 SetPanelBackground();
3900 SetDrawBackgroundMask(REDRAW_DOOR_1);
3904 SetDrawBackgroundMask(REDRAW_FIELD);
3907 #if defined(NETWORK_AVALIABLE)
3908 /* continue network game after request */
3909 if (options.network &&
3910 game_status == GAME_MODE_PLAYING &&
3911 req_state & REQUEST_WAIT_FOR_INPUT)
3912 SendToServer_ContinuePlaying();
3915 /* restore deactivated drawing when quick-loading level tape recording */
3916 if (tape.playing && tape.deactivate_display)
3917 TapeDeactivateDisplayOn();
3922 boolean Request(char *text, unsigned int req_state)
3924 if (global.use_envelope_request)
3925 return RequestEnvelope(text, req_state);
3927 return RequestDoor(text, req_state);
3930 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3932 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3933 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3936 if (dpo1->sort_priority != dpo2->sort_priority)
3937 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3939 compare_result = dpo1->nr - dpo2->nr;
3941 return compare_result;
3944 void InitGraphicCompatibilityInfo_Doors()
3950 struct DoorInfo *door;
3954 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3955 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3957 { -1, -1, -1, NULL }
3959 struct Rect door_rect_list[] =
3961 { DX, DY, DXSIZE, DYSIZE },
3962 { VX, VY, VXSIZE, VYSIZE }
3966 for (i = 0; doors[i].door_token != -1; i++)
3968 int door_token = doors[i].door_token;
3969 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3970 int part_1 = doors[i].part_1;
3971 int part_8 = doors[i].part_8;
3972 int part_2 = part_1 + 1;
3973 int part_3 = part_1 + 2;
3974 struct DoorInfo *door = doors[i].door;
3975 struct Rect *door_rect = &door_rect_list[door_index];
3976 boolean door_gfx_redefined = FALSE;
3978 /* check if any door part graphic definitions have been redefined */
3980 for (j = 0; door_part_controls[j].door_token != -1; j++)
3982 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3983 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3985 if (dpc->door_token == door_token && fi->redefined)
3986 door_gfx_redefined = TRUE;
3989 /* check for old-style door graphic/animation modifications */
3991 if (!door_gfx_redefined)
3993 if (door->anim_mode & ANIM_STATIC_PANEL)
3995 door->panel.step_xoffset = 0;
3996 door->panel.step_yoffset = 0;
3999 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4001 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4002 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4003 int num_door_steps, num_panel_steps;
4005 /* remove door part graphics other than the two default wings */
4007 for (j = 0; door_part_controls[j].door_token != -1; j++)
4009 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4010 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4012 if (dpc->graphic >= part_3 &&
4013 dpc->graphic <= part_8)
4017 /* set graphics and screen positions of the default wings */
4019 g_part_1->width = door_rect->width;
4020 g_part_1->height = door_rect->height;
4021 g_part_2->width = door_rect->width;
4022 g_part_2->height = door_rect->height;
4023 g_part_2->src_x = door_rect->width;
4024 g_part_2->src_y = g_part_1->src_y;
4026 door->part_2.x = door->part_1.x;
4027 door->part_2.y = door->part_1.y;
4029 if (door->width != -1)
4031 g_part_1->width = door->width;
4032 g_part_2->width = door->width;
4034 // special treatment for graphics and screen position of right wing
4035 g_part_2->src_x += door_rect->width - door->width;
4036 door->part_2.x += door_rect->width - door->width;
4039 if (door->height != -1)
4041 g_part_1->height = door->height;
4042 g_part_2->height = door->height;
4044 // special treatment for graphics and screen position of bottom wing
4045 g_part_2->src_y += door_rect->height - door->height;
4046 door->part_2.y += door_rect->height - door->height;
4049 /* set animation delays for the default wings and panels */
4051 door->part_1.step_delay = door->step_delay;
4052 door->part_2.step_delay = door->step_delay;
4053 door->panel.step_delay = door->step_delay;
4055 /* set animation draw order for the default wings */
4057 door->part_1.sort_priority = 2; /* draw left wing over ... */
4058 door->part_2.sort_priority = 1; /* ... right wing */
4060 /* set animation draw offset for the default wings */
4062 if (door->anim_mode & ANIM_HORIZONTAL)
4064 door->part_1.step_xoffset = door->step_offset;
4065 door->part_1.step_yoffset = 0;
4066 door->part_2.step_xoffset = door->step_offset * -1;
4067 door->part_2.step_yoffset = 0;
4069 num_door_steps = g_part_1->width / door->step_offset;
4071 else // ANIM_VERTICAL
4073 door->part_1.step_xoffset = 0;
4074 door->part_1.step_yoffset = door->step_offset;
4075 door->part_2.step_xoffset = 0;
4076 door->part_2.step_yoffset = door->step_offset * -1;
4078 num_door_steps = g_part_1->height / door->step_offset;
4081 /* set animation draw offset for the default panels */
4083 if (door->step_offset > 1)
4085 num_panel_steps = 2 * door_rect->height / door->step_offset;
4086 door->panel.start_step = num_panel_steps - num_door_steps;
4087 door->panel.start_step_closing = door->panel.start_step;
4091 num_panel_steps = door_rect->height / door->step_offset;
4092 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4093 door->panel.start_step_closing = door->panel.start_step;
4094 door->panel.step_delay *= 2;
4105 for (i = 0; door_part_controls[i].door_token != -1; i++)
4107 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4108 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4110 /* initialize "start_step_opening" and "start_step_closing", if needed */
4111 if (dpc->pos->start_step_opening == 0 &&
4112 dpc->pos->start_step_closing == 0)
4114 // dpc->pos->start_step_opening = dpc->pos->start_step;
4115 dpc->pos->start_step_closing = dpc->pos->start_step;
4118 /* fill structure for door part draw order (sorted below) */
4120 dpo->sort_priority = dpc->pos->sort_priority;
4123 /* sort door part controls according to sort_priority and graphic number */
4124 qsort(door_part_order, MAX_DOOR_PARTS,
4125 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4128 unsigned int OpenDoor(unsigned int door_state)
4130 if (door_state & DOOR_COPY_BACK)
4132 if (door_state & DOOR_OPEN_1)
4133 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4134 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4136 if (door_state & DOOR_OPEN_2)
4137 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4138 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4140 door_state &= ~DOOR_COPY_BACK;
4143 return MoveDoor(door_state);
4146 unsigned int CloseDoor(unsigned int door_state)
4148 unsigned int old_door_state = GetDoorState();
4150 if (!(door_state & DOOR_NO_COPY_BACK))
4152 if (old_door_state & DOOR_OPEN_1)
4153 BlitBitmap(backbuffer, bitmap_db_door_1,
4154 DX, DY, DXSIZE, DYSIZE, 0, 0);
4156 if (old_door_state & DOOR_OPEN_2)
4157 BlitBitmap(backbuffer, bitmap_db_door_2,
4158 VX, VY, VXSIZE, VYSIZE, 0, 0);
4160 door_state &= ~DOOR_NO_COPY_BACK;
4163 return MoveDoor(door_state);
4166 unsigned int GetDoorState()
4168 return MoveDoor(DOOR_GET_STATE);
4171 unsigned int SetDoorState(unsigned int door_state)
4173 return MoveDoor(door_state | DOOR_SET_STATE);
4176 int euclid(int a, int b)
4178 return (b ? euclid(b, a % b) : a);
4181 unsigned int MoveDoor(unsigned int door_state)
4183 struct Rect door_rect_list[] =
4185 { DX, DY, DXSIZE, DYSIZE },
4186 { VX, VY, VXSIZE, VYSIZE }
4188 static int door1 = DOOR_OPEN_1;
4189 static int door2 = DOOR_CLOSE_2;
4190 unsigned int door_delay = 0;
4191 unsigned int door_delay_value;
4194 if (door_state == DOOR_GET_STATE)
4195 return (door1 | door2);
4197 if (door_state & DOOR_SET_STATE)
4199 if (door_state & DOOR_ACTION_1)
4200 door1 = door_state & DOOR_ACTION_1;
4201 if (door_state & DOOR_ACTION_2)
4202 door2 = door_state & DOOR_ACTION_2;
4204 return (door1 | door2);
4207 if (!(door_state & DOOR_FORCE_REDRAW))
4209 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4210 door_state &= ~DOOR_OPEN_1;
4211 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4212 door_state &= ~DOOR_CLOSE_1;
4213 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4214 door_state &= ~DOOR_OPEN_2;
4215 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4216 door_state &= ~DOOR_CLOSE_2;
4219 if (global.autoplay_leveldir)
4221 door_state |= DOOR_NO_DELAY;
4222 door_state &= ~DOOR_CLOSE_ALL;
4225 if (game_status == GAME_MODE_EDITOR)
4226 door_state |= DOOR_NO_DELAY;
4228 if (door_state & DOOR_ACTION)
4230 boolean door_panel_drawn[NUM_DOORS];
4231 boolean panel_has_doors[NUM_DOORS];
4232 boolean door_part_skip[MAX_DOOR_PARTS];
4233 boolean door_part_done[MAX_DOOR_PARTS];
4234 boolean door_part_done_all;
4235 int num_steps[MAX_DOOR_PARTS];
4236 int max_move_delay = 0; // delay for complete animations of all doors
4237 int max_step_delay = 0; // delay (ms) between two animation frames
4238 int num_move_steps = 0; // number of animation steps for all doors
4239 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4240 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4241 int current_move_delay = 0;
4245 for (i = 0; i < NUM_DOORS; i++)
4246 panel_has_doors[i] = FALSE;
4248 for (i = 0; i < MAX_DOOR_PARTS; i++)
4250 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4251 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4252 int door_token = dpc->door_token;
4254 door_part_done[i] = FALSE;
4255 door_part_skip[i] = (!(door_state & door_token) ||
4259 for (i = 0; i < MAX_DOOR_PARTS; i++)
4261 int nr = door_part_order[i].nr;
4262 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4263 struct DoorPartPosInfo *pos = dpc->pos;
4264 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4265 int door_token = dpc->door_token;
4266 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4267 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4268 int step_xoffset = ABS(pos->step_xoffset);
4269 int step_yoffset = ABS(pos->step_yoffset);
4270 int step_delay = pos->step_delay;
4271 int current_door_state = door_state & door_token;
4272 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4273 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4274 boolean part_opening = (is_panel ? door_closing : door_opening);
4275 int start_step = (part_opening ? pos->start_step_opening :
4276 pos->start_step_closing);
4277 float move_xsize = (step_xoffset ? g->width : 0);
4278 float move_ysize = (step_yoffset ? g->height : 0);
4279 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4280 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4281 int move_steps = (move_xsteps && move_ysteps ?
4282 MIN(move_xsteps, move_ysteps) :
4283 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4284 int move_delay = move_steps * step_delay;
4286 if (door_part_skip[nr])
4289 max_move_delay = MAX(max_move_delay, move_delay);
4290 max_step_delay = (max_step_delay == 0 ? step_delay :
4291 euclid(max_step_delay, step_delay));
4292 num_steps[nr] = move_steps;
4296 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4298 panel_has_doors[door_index] = TRUE;
4302 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4304 num_move_steps = max_move_delay / max_step_delay;
4305 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4307 door_delay_value = max_step_delay;
4309 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4311 start = num_move_steps - 1;
4315 /* opening door sound has priority over simultaneously closing door */
4316 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4317 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4318 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4319 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4322 for (k = start; k < num_move_steps; k++)
4324 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4326 door_part_done_all = TRUE;
4328 for (i = 0; i < NUM_DOORS; i++)
4329 door_panel_drawn[i] = FALSE;
4331 for (i = 0; i < MAX_DOOR_PARTS; i++)
4333 int nr = door_part_order[i].nr;
4334 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4335 struct DoorPartPosInfo *pos = dpc->pos;
4336 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4337 int door_token = dpc->door_token;
4338 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4339 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4340 boolean is_panel_and_door_has_closed = FALSE;
4341 struct Rect *door_rect = &door_rect_list[door_index];
4342 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4344 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4345 int current_door_state = door_state & door_token;
4346 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4347 boolean door_closing = !door_opening;
4348 boolean part_opening = (is_panel ? door_closing : door_opening);
4349 boolean part_closing = !part_opening;
4350 int start_step = (part_opening ? pos->start_step_opening :
4351 pos->start_step_closing);
4352 int step_delay = pos->step_delay;
4353 int step_factor = step_delay / max_step_delay;
4354 int k1 = (step_factor ? k / step_factor + 1 : k);
4355 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4356 int kk = MAX(0, k2);
4359 int src_x, src_y, src_xx, src_yy;
4360 int dst_x, dst_y, dst_xx, dst_yy;
4363 if (door_part_skip[nr])
4366 if (!(door_state & door_token))
4374 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4375 int kk_door = MAX(0, k2_door);
4376 int sync_frame = kk_door * door_delay_value;
4377 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4379 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4384 if (!door_panel_drawn[door_index])
4386 ClearRectangle(drawto, door_rect->x, door_rect->y,
4387 door_rect->width, door_rect->height);
4389 door_panel_drawn[door_index] = TRUE;
4392 // draw opening or closing door parts
4394 if (pos->step_xoffset < 0) // door part on right side
4397 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4400 if (dst_xx + width > door_rect->width)
4401 width = door_rect->width - dst_xx;
4403 else // door part on left side
4406 dst_xx = pos->x - kk * pos->step_xoffset;
4410 src_xx = ABS(dst_xx);
4414 width = g->width - src_xx;
4416 if (width > door_rect->width)
4417 width = door_rect->width;
4419 // printf("::: k == %d [%d] \n", k, start_step);
4422 if (pos->step_yoffset < 0) // door part on bottom side
4425 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4428 if (dst_yy + height > door_rect->height)
4429 height = door_rect->height - dst_yy;
4431 else // door part on top side
4434 dst_yy = pos->y - kk * pos->step_yoffset;
4438 src_yy = ABS(dst_yy);
4442 height = g->height - src_yy;
4445 src_x = g_src_x + src_xx;
4446 src_y = g_src_y + src_yy;
4448 dst_x = door_rect->x + dst_xx;
4449 dst_y = door_rect->y + dst_yy;
4451 is_panel_and_door_has_closed =
4454 panel_has_doors[door_index] &&
4455 k >= num_move_steps_doors_only - 1);
4457 if (width >= 0 && width <= g->width &&
4458 height >= 0 && height <= g->height &&
4459 !is_panel_and_door_has_closed)
4461 if (is_panel || !pos->draw_masked)
4462 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4465 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4469 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4471 if ((part_opening && (width < 0 || height < 0)) ||
4472 (part_closing && (width >= g->width && height >= g->height)))
4473 door_part_done[nr] = TRUE;
4475 // continue door part animations, but not panel after door has closed
4476 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4477 door_part_done_all = FALSE;
4480 if (!(door_state & DOOR_NO_DELAY))
4484 if (game_status == GAME_MODE_MAIN)
4487 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4489 current_move_delay += max_step_delay;
4492 if (door_part_done_all)
4497 if (door_state & DOOR_ACTION_1)
4498 door1 = door_state & DOOR_ACTION_1;
4499 if (door_state & DOOR_ACTION_2)
4500 door2 = door_state & DOOR_ACTION_2;
4502 // draw masked border over door area
4503 DrawMaskedBorder(REDRAW_DOOR_1);
4504 DrawMaskedBorder(REDRAW_DOOR_2);
4506 return (door1 | door2);
4509 static boolean useSpecialEditorDoor()
4511 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4512 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4514 // do not draw special editor door if editor border defined or redefined
4515 if (graphic_info[graphic].bitmap != NULL || redefined)
4518 // do not draw special editor door if global border defined to be empty
4519 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4522 // do not draw special editor door if viewport definitions do not match
4526 EY + EYSIZE != VY + VYSIZE)
4532 void DrawSpecialEditorDoor()
4534 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4535 int top_border_width = gfx1->width;
4536 int top_border_height = gfx1->height;
4537 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4538 int ex = EX - outer_border;
4539 int ey = EY - outer_border;
4540 int vy = VY - outer_border;
4541 int exsize = EXSIZE + 2 * outer_border;
4543 if (!useSpecialEditorDoor())
4546 /* draw bigger level editor toolbox window */
4547 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4548 top_border_width, top_border_height, ex, ey - top_border_height);
4549 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4550 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4552 redraw_mask |= REDRAW_ALL;
4555 void UndrawSpecialEditorDoor()
4557 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4558 int top_border_width = gfx1->width;
4559 int top_border_height = gfx1->height;
4560 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4561 int ex = EX - outer_border;
4562 int ey = EY - outer_border;
4563 int ey_top = ey - top_border_height;
4564 int exsize = EXSIZE + 2 * outer_border;
4565 int eysize = EYSIZE + 2 * outer_border;
4567 if (!useSpecialEditorDoor())
4570 /* draw normal tape recorder window */
4571 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4573 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4574 ex, ey_top, top_border_width, top_border_height,
4576 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4577 ex, ey, exsize, eysize, ex, ey);
4581 // if screen background is set to "[NONE]", clear editor toolbox window
4582 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4583 ClearRectangle(drawto, ex, ey, exsize, eysize);
4586 redraw_mask |= REDRAW_ALL;
4590 /* ---------- new tool button stuff ---------------------------------------- */
4595 struct TextPosInfo *pos;
4598 } toolbutton_info[NUM_TOOL_BUTTONS] =
4601 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4602 TOOL_CTRL_ID_YES, "yes"
4605 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4606 TOOL_CTRL_ID_NO, "no"
4609 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4610 TOOL_CTRL_ID_CONFIRM, "confirm"
4613 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4614 TOOL_CTRL_ID_PLAYER_1, "player 1"
4617 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4618 TOOL_CTRL_ID_PLAYER_2, "player 2"
4621 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4622 TOOL_CTRL_ID_PLAYER_3, "player 3"
4625 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4626 TOOL_CTRL_ID_PLAYER_4, "player 4"
4630 void CreateToolButtons()
4634 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4636 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4637 struct TextPosInfo *pos = toolbutton_info[i].pos;
4638 struct GadgetInfo *gi;
4639 Bitmap *deco_bitmap = None;
4640 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4641 unsigned int event_mask = GD_EVENT_RELEASED;
4644 int gd_x = gfx->src_x;
4645 int gd_y = gfx->src_y;
4646 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4647 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4650 if (global.use_envelope_request)
4651 setRequestPosition(&dx, &dy, TRUE);
4653 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4655 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4657 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4658 pos->size, &deco_bitmap, &deco_x, &deco_y);
4659 deco_xpos = (gfx->width - pos->size) / 2;
4660 deco_ypos = (gfx->height - pos->size) / 2;
4663 gi = CreateGadget(GDI_CUSTOM_ID, id,
4664 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4665 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4666 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4667 GDI_WIDTH, gfx->width,
4668 GDI_HEIGHT, gfx->height,
4669 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4670 GDI_STATE, GD_BUTTON_UNPRESSED,
4671 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4672 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4673 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4674 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4675 GDI_DECORATION_SIZE, pos->size, pos->size,
4676 GDI_DECORATION_SHIFTING, 1, 1,
4677 GDI_DIRECT_DRAW, FALSE,
4678 GDI_EVENT_MASK, event_mask,
4679 GDI_CALLBACK_ACTION, HandleToolButtons,
4683 Error(ERR_EXIT, "cannot create gadget");
4685 tool_gadget[id] = gi;
4689 void FreeToolButtons()
4693 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4694 FreeGadget(tool_gadget[i]);
4697 static void UnmapToolButtons()
4701 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4702 UnmapGadget(tool_gadget[i]);
4705 static void HandleToolButtons(struct GadgetInfo *gi)
4707 request_gadget_id = gi->custom_id;
4710 static struct Mapping_EM_to_RND_object
4713 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4714 boolean is_backside; /* backside of moving element */
4720 em_object_mapping_list[] =
4723 Xblank, TRUE, FALSE,
4727 Yacid_splash_eB, FALSE, FALSE,
4728 EL_ACID_SPLASH_RIGHT, -1, -1
4731 Yacid_splash_wB, FALSE, FALSE,
4732 EL_ACID_SPLASH_LEFT, -1, -1
4735 #ifdef EM_ENGINE_BAD_ROLL
4737 Xstone_force_e, FALSE, FALSE,
4738 EL_ROCK, -1, MV_BIT_RIGHT
4741 Xstone_force_w, FALSE, FALSE,
4742 EL_ROCK, -1, MV_BIT_LEFT
4745 Xnut_force_e, FALSE, FALSE,
4746 EL_NUT, -1, MV_BIT_RIGHT
4749 Xnut_force_w, FALSE, FALSE,
4750 EL_NUT, -1, MV_BIT_LEFT
4753 Xspring_force_e, FALSE, FALSE,
4754 EL_SPRING, -1, MV_BIT_RIGHT
4757 Xspring_force_w, FALSE, FALSE,
4758 EL_SPRING, -1, MV_BIT_LEFT
4761 Xemerald_force_e, FALSE, FALSE,
4762 EL_EMERALD, -1, MV_BIT_RIGHT
4765 Xemerald_force_w, FALSE, FALSE,
4766 EL_EMERALD, -1, MV_BIT_LEFT
4769 Xdiamond_force_e, FALSE, FALSE,
4770 EL_DIAMOND, -1, MV_BIT_RIGHT
4773 Xdiamond_force_w, FALSE, FALSE,
4774 EL_DIAMOND, -1, MV_BIT_LEFT
4777 Xbomb_force_e, FALSE, FALSE,
4778 EL_BOMB, -1, MV_BIT_RIGHT
4781 Xbomb_force_w, FALSE, FALSE,
4782 EL_BOMB, -1, MV_BIT_LEFT
4784 #endif /* EM_ENGINE_BAD_ROLL */
4787 Xstone, TRUE, FALSE,
4791 Xstone_pause, FALSE, FALSE,
4795 Xstone_fall, FALSE, FALSE,
4799 Ystone_s, FALSE, FALSE,
4800 EL_ROCK, ACTION_FALLING, -1
4803 Ystone_sB, FALSE, TRUE,
4804 EL_ROCK, ACTION_FALLING, -1
4807 Ystone_e, FALSE, FALSE,
4808 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4811 Ystone_eB, FALSE, TRUE,
4812 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4815 Ystone_w, FALSE, FALSE,
4816 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4819 Ystone_wB, FALSE, TRUE,
4820 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4827 Xnut_pause, FALSE, FALSE,
4831 Xnut_fall, FALSE, FALSE,
4835 Ynut_s, FALSE, FALSE,
4836 EL_NUT, ACTION_FALLING, -1
4839 Ynut_sB, FALSE, TRUE,
4840 EL_NUT, ACTION_FALLING, -1
4843 Ynut_e, FALSE, FALSE,
4844 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4847 Ynut_eB, FALSE, TRUE,
4848 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4851 Ynut_w, FALSE, FALSE,
4852 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4855 Ynut_wB, FALSE, TRUE,
4856 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4859 Xbug_n, TRUE, FALSE,
4863 Xbug_e, TRUE, FALSE,
4864 EL_BUG_RIGHT, -1, -1
4867 Xbug_s, TRUE, FALSE,
4871 Xbug_w, TRUE, FALSE,
4875 Xbug_gon, FALSE, FALSE,
4879 Xbug_goe, FALSE, FALSE,
4880 EL_BUG_RIGHT, -1, -1
4883 Xbug_gos, FALSE, FALSE,
4887 Xbug_gow, FALSE, FALSE,
4891 Ybug_n, FALSE, FALSE,
4892 EL_BUG, ACTION_MOVING, MV_BIT_UP
4895 Ybug_nB, FALSE, TRUE,
4896 EL_BUG, ACTION_MOVING, MV_BIT_UP
4899 Ybug_e, FALSE, FALSE,
4900 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4903 Ybug_eB, FALSE, TRUE,
4904 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4907 Ybug_s, FALSE, FALSE,
4908 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4911 Ybug_sB, FALSE, TRUE,
4912 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4915 Ybug_w, FALSE, FALSE,
4916 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4919 Ybug_wB, FALSE, TRUE,
4920 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4923 Ybug_w_n, FALSE, FALSE,
4924 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4927 Ybug_n_e, FALSE, FALSE,
4928 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4931 Ybug_e_s, FALSE, FALSE,
4932 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4935 Ybug_s_w, FALSE, FALSE,
4936 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4939 Ybug_e_n, FALSE, FALSE,
4940 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4943 Ybug_s_e, FALSE, FALSE,
4944 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4947 Ybug_w_s, FALSE, FALSE,
4948 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4951 Ybug_n_w, FALSE, FALSE,
4952 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4955 Ybug_stone, FALSE, FALSE,
4956 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4959 Ybug_spring, FALSE, FALSE,
4960 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4963 Xtank_n, TRUE, FALSE,
4964 EL_SPACESHIP_UP, -1, -1
4967 Xtank_e, TRUE, FALSE,
4968 EL_SPACESHIP_RIGHT, -1, -1
4971 Xtank_s, TRUE, FALSE,
4972 EL_SPACESHIP_DOWN, -1, -1
4975 Xtank_w, TRUE, FALSE,
4976 EL_SPACESHIP_LEFT, -1, -1
4979 Xtank_gon, FALSE, FALSE,
4980 EL_SPACESHIP_UP, -1, -1
4983 Xtank_goe, FALSE, FALSE,
4984 EL_SPACESHIP_RIGHT, -1, -1
4987 Xtank_gos, FALSE, FALSE,
4988 EL_SPACESHIP_DOWN, -1, -1
4991 Xtank_gow, FALSE, FALSE,
4992 EL_SPACESHIP_LEFT, -1, -1
4995 Ytank_n, FALSE, FALSE,
4996 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4999 Ytank_nB, FALSE, TRUE,
5000 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5003 Ytank_e, FALSE, FALSE,
5004 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5007 Ytank_eB, FALSE, TRUE,
5008 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5011 Ytank_s, FALSE, FALSE,
5012 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5015 Ytank_sB, FALSE, TRUE,
5016 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5019 Ytank_w, FALSE, FALSE,
5020 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5023 Ytank_wB, FALSE, TRUE,
5024 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5027 Ytank_w_n, FALSE, FALSE,
5028 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5031 Ytank_n_e, FALSE, FALSE,
5032 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5035 Ytank_e_s, FALSE, FALSE,
5036 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5039 Ytank_s_w, FALSE, FALSE,
5040 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5043 Ytank_e_n, FALSE, FALSE,
5044 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5047 Ytank_s_e, FALSE, FALSE,
5048 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5051 Ytank_w_s, FALSE, FALSE,
5052 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5055 Ytank_n_w, FALSE, FALSE,
5056 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5059 Ytank_stone, FALSE, FALSE,
5060 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5063 Ytank_spring, FALSE, FALSE,
5064 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5067 Xandroid, TRUE, FALSE,
5068 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5071 Xandroid_1_n, FALSE, FALSE,
5072 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5075 Xandroid_2_n, FALSE, FALSE,
5076 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5079 Xandroid_1_e, FALSE, FALSE,
5080 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5083 Xandroid_2_e, FALSE, FALSE,
5084 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5087 Xandroid_1_w, FALSE, FALSE,
5088 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5091 Xandroid_2_w, FALSE, FALSE,
5092 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5095 Xandroid_1_s, FALSE, FALSE,
5096 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5099 Xandroid_2_s, FALSE, FALSE,
5100 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5103 Yandroid_n, FALSE, FALSE,
5104 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5107 Yandroid_nB, FALSE, TRUE,
5108 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5111 Yandroid_ne, FALSE, FALSE,
5112 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5115 Yandroid_neB, FALSE, TRUE,
5116 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5119 Yandroid_e, FALSE, FALSE,
5120 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5123 Yandroid_eB, FALSE, TRUE,
5124 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5127 Yandroid_se, FALSE, FALSE,
5128 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5131 Yandroid_seB, FALSE, TRUE,
5132 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5135 Yandroid_s, FALSE, FALSE,
5136 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5139 Yandroid_sB, FALSE, TRUE,
5140 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5143 Yandroid_sw, FALSE, FALSE,
5144 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5147 Yandroid_swB, FALSE, TRUE,
5148 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5151 Yandroid_w, FALSE, FALSE,
5152 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5155 Yandroid_wB, FALSE, TRUE,
5156 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5159 Yandroid_nw, FALSE, FALSE,
5160 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5163 Yandroid_nwB, FALSE, TRUE,
5164 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5167 Xspring, TRUE, FALSE,
5171 Xspring_pause, FALSE, FALSE,
5175 Xspring_e, FALSE, FALSE,
5179 Xspring_w, FALSE, FALSE,
5183 Xspring_fall, FALSE, FALSE,
5187 Yspring_s, FALSE, FALSE,
5188 EL_SPRING, ACTION_FALLING, -1
5191 Yspring_sB, FALSE, TRUE,
5192 EL_SPRING, ACTION_FALLING, -1
5195 Yspring_e, FALSE, FALSE,
5196 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5199 Yspring_eB, FALSE, TRUE,
5200 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5203 Yspring_w, FALSE, FALSE,
5204 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5207 Yspring_wB, FALSE, TRUE,
5208 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5211 Yspring_kill_e, FALSE, FALSE,
5212 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5215 Yspring_kill_eB, FALSE, TRUE,
5216 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5219 Yspring_kill_w, FALSE, FALSE,
5220 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5223 Yspring_kill_wB, FALSE, TRUE,
5224 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5227 Xeater_n, TRUE, FALSE,
5228 EL_YAMYAM_UP, -1, -1
5231 Xeater_e, TRUE, FALSE,
5232 EL_YAMYAM_RIGHT, -1, -1
5235 Xeater_w, TRUE, FALSE,
5236 EL_YAMYAM_LEFT, -1, -1
5239 Xeater_s, TRUE, FALSE,
5240 EL_YAMYAM_DOWN, -1, -1
5243 Yeater_n, FALSE, FALSE,
5244 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5247 Yeater_nB, FALSE, TRUE,
5248 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5251 Yeater_e, FALSE, FALSE,
5252 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5255 Yeater_eB, FALSE, TRUE,
5256 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5259 Yeater_s, FALSE, FALSE,
5260 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5263 Yeater_sB, FALSE, TRUE,
5264 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5267 Yeater_w, FALSE, FALSE,
5268 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5271 Yeater_wB, FALSE, TRUE,
5272 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5275 Yeater_stone, FALSE, FALSE,
5276 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5279 Yeater_spring, FALSE, FALSE,
5280 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5283 Xalien, TRUE, FALSE,
5287 Xalien_pause, FALSE, FALSE,
5291 Yalien_n, FALSE, FALSE,
5292 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5295 Yalien_nB, FALSE, TRUE,
5296 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5299 Yalien_e, FALSE, FALSE,
5300 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5303 Yalien_eB, FALSE, TRUE,
5304 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5307 Yalien_s, FALSE, FALSE,
5308 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5311 Yalien_sB, FALSE, TRUE,
5312 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5315 Yalien_w, FALSE, FALSE,
5316 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5319 Yalien_wB, FALSE, TRUE,
5320 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5323 Yalien_stone, FALSE, FALSE,
5324 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5327 Yalien_spring, FALSE, FALSE,
5328 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5331 Xemerald, TRUE, FALSE,
5335 Xemerald_pause, FALSE, FALSE,
5339 Xemerald_fall, FALSE, FALSE,
5343 Xemerald_shine, FALSE, FALSE,
5344 EL_EMERALD, ACTION_TWINKLING, -1
5347 Yemerald_s, FALSE, FALSE,
5348 EL_EMERALD, ACTION_FALLING, -1
5351 Yemerald_sB, FALSE, TRUE,
5352 EL_EMERALD, ACTION_FALLING, -1
5355 Yemerald_e, FALSE, FALSE,
5356 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5359 Yemerald_eB, FALSE, TRUE,
5360 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5363 Yemerald_w, FALSE, FALSE,
5364 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5367 Yemerald_wB, FALSE, TRUE,
5368 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5371 Yemerald_eat, FALSE, FALSE,
5372 EL_EMERALD, ACTION_COLLECTING, -1
5375 Yemerald_stone, FALSE, FALSE,
5376 EL_NUT, ACTION_BREAKING, -1
5379 Xdiamond, TRUE, FALSE,
5383 Xdiamond_pause, FALSE, FALSE,
5387 Xdiamond_fall, FALSE, FALSE,
5391 Xdiamond_shine, FALSE, FALSE,
5392 EL_DIAMOND, ACTION_TWINKLING, -1
5395 Ydiamond_s, FALSE, FALSE,
5396 EL_DIAMOND, ACTION_FALLING, -1
5399 Ydiamond_sB, FALSE, TRUE,
5400 EL_DIAMOND, ACTION_FALLING, -1
5403 Ydiamond_e, FALSE, FALSE,
5404 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5407 Ydiamond_eB, FALSE, TRUE,
5408 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5411 Ydiamond_w, FALSE, FALSE,
5412 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5415 Ydiamond_wB, FALSE, TRUE,
5416 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5419 Ydiamond_eat, FALSE, FALSE,
5420 EL_DIAMOND, ACTION_COLLECTING, -1
5423 Ydiamond_stone, FALSE, FALSE,
5424 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5427 Xdrip_fall, TRUE, FALSE,
5428 EL_AMOEBA_DROP, -1, -1
5431 Xdrip_stretch, FALSE, FALSE,
5432 EL_AMOEBA_DROP, ACTION_FALLING, -1
5435 Xdrip_stretchB, FALSE, TRUE,
5436 EL_AMOEBA_DROP, ACTION_FALLING, -1
5439 Xdrip_eat, FALSE, FALSE,
5440 EL_AMOEBA_DROP, ACTION_GROWING, -1
5443 Ydrip_s1, FALSE, FALSE,
5444 EL_AMOEBA_DROP, ACTION_FALLING, -1
5447 Ydrip_s1B, FALSE, TRUE,
5448 EL_AMOEBA_DROP, ACTION_FALLING, -1
5451 Ydrip_s2, FALSE, FALSE,
5452 EL_AMOEBA_DROP, ACTION_FALLING, -1
5455 Ydrip_s2B, FALSE, TRUE,
5456 EL_AMOEBA_DROP, ACTION_FALLING, -1
5463 Xbomb_pause, FALSE, FALSE,
5467 Xbomb_fall, FALSE, FALSE,
5471 Ybomb_s, FALSE, FALSE,
5472 EL_BOMB, ACTION_FALLING, -1
5475 Ybomb_sB, FALSE, TRUE,
5476 EL_BOMB, ACTION_FALLING, -1
5479 Ybomb_e, FALSE, FALSE,
5480 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5483 Ybomb_eB, FALSE, TRUE,
5484 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5487 Ybomb_w, FALSE, FALSE,
5488 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5491 Ybomb_wB, FALSE, TRUE,
5492 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5495 Ybomb_eat, FALSE, FALSE,
5496 EL_BOMB, ACTION_ACTIVATING, -1
5499 Xballoon, TRUE, FALSE,
5503 Yballoon_n, FALSE, FALSE,
5504 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5507 Yballoon_nB, FALSE, TRUE,
5508 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5511 Yballoon_e, FALSE, FALSE,
5512 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5515 Yballoon_eB, FALSE, TRUE,
5516 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5519 Yballoon_s, FALSE, FALSE,
5520 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5523 Yballoon_sB, FALSE, TRUE,
5524 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5527 Yballoon_w, FALSE, FALSE,
5528 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5531 Yballoon_wB, FALSE, TRUE,
5532 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5535 Xgrass, TRUE, FALSE,
5536 EL_EMC_GRASS, -1, -1
5539 Ygrass_nB, FALSE, FALSE,
5540 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5543 Ygrass_eB, FALSE, FALSE,
5544 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5547 Ygrass_sB, FALSE, FALSE,
5548 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5551 Ygrass_wB, FALSE, FALSE,
5552 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5559 Ydirt_nB, FALSE, FALSE,
5560 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5563 Ydirt_eB, FALSE, FALSE,
5564 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5567 Ydirt_sB, FALSE, FALSE,
5568 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5571 Ydirt_wB, FALSE, FALSE,
5572 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5575 Xacid_ne, TRUE, FALSE,
5576 EL_ACID_POOL_TOPRIGHT, -1, -1
5579 Xacid_se, TRUE, FALSE,
5580 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5583 Xacid_s, TRUE, FALSE,
5584 EL_ACID_POOL_BOTTOM, -1, -1
5587 Xacid_sw, TRUE, FALSE,
5588 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5591 Xacid_nw, TRUE, FALSE,
5592 EL_ACID_POOL_TOPLEFT, -1, -1
5595 Xacid_1, TRUE, FALSE,
5599 Xacid_2, FALSE, FALSE,
5603 Xacid_3, FALSE, FALSE,
5607 Xacid_4, FALSE, FALSE,
5611 Xacid_5, FALSE, FALSE,
5615 Xacid_6, FALSE, FALSE,
5619 Xacid_7, FALSE, FALSE,
5623 Xacid_8, FALSE, FALSE,
5627 Xball_1, TRUE, FALSE,
5628 EL_EMC_MAGIC_BALL, -1, -1
5631 Xball_1B, FALSE, FALSE,
5632 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5635 Xball_2, FALSE, FALSE,
5636 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5639 Xball_2B, FALSE, FALSE,
5640 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5643 Yball_eat, FALSE, FALSE,
5644 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5647 Ykey_1_eat, FALSE, FALSE,
5648 EL_EM_KEY_1, ACTION_COLLECTING, -1
5651 Ykey_2_eat, FALSE, FALSE,
5652 EL_EM_KEY_2, ACTION_COLLECTING, -1
5655 Ykey_3_eat, FALSE, FALSE,
5656 EL_EM_KEY_3, ACTION_COLLECTING, -1
5659 Ykey_4_eat, FALSE, FALSE,
5660 EL_EM_KEY_4, ACTION_COLLECTING, -1
5663 Ykey_5_eat, FALSE, FALSE,
5664 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5667 Ykey_6_eat, FALSE, FALSE,
5668 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5671 Ykey_7_eat, FALSE, FALSE,
5672 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5675 Ykey_8_eat, FALSE, FALSE,
5676 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5679 Ylenses_eat, FALSE, FALSE,
5680 EL_EMC_LENSES, ACTION_COLLECTING, -1
5683 Ymagnify_eat, FALSE, FALSE,
5684 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5687 Ygrass_eat, FALSE, FALSE,
5688 EL_EMC_GRASS, ACTION_SNAPPING, -1
5691 Ydirt_eat, FALSE, FALSE,
5692 EL_SAND, ACTION_SNAPPING, -1
5695 Xgrow_ns, TRUE, FALSE,
5696 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5699 Ygrow_ns_eat, FALSE, FALSE,
5700 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5703 Xgrow_ew, TRUE, FALSE,
5704 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5707 Ygrow_ew_eat, FALSE, FALSE,
5708 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5711 Xwonderwall, TRUE, FALSE,
5712 EL_MAGIC_WALL, -1, -1
5715 XwonderwallB, FALSE, FALSE,
5716 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5719 Xamoeba_1, TRUE, FALSE,
5720 EL_AMOEBA_DRY, ACTION_OTHER, -1
5723 Xamoeba_2, FALSE, FALSE,
5724 EL_AMOEBA_DRY, ACTION_OTHER, -1
5727 Xamoeba_3, FALSE, FALSE,
5728 EL_AMOEBA_DRY, ACTION_OTHER, -1
5731 Xamoeba_4, FALSE, FALSE,
5732 EL_AMOEBA_DRY, ACTION_OTHER, -1
5735 Xamoeba_5, TRUE, FALSE,
5736 EL_AMOEBA_WET, ACTION_OTHER, -1
5739 Xamoeba_6, FALSE, FALSE,
5740 EL_AMOEBA_WET, ACTION_OTHER, -1
5743 Xamoeba_7, FALSE, FALSE,
5744 EL_AMOEBA_WET, ACTION_OTHER, -1
5747 Xamoeba_8, FALSE, FALSE,
5748 EL_AMOEBA_WET, ACTION_OTHER, -1
5751 Xdoor_1, TRUE, FALSE,
5752 EL_EM_GATE_1, -1, -1
5755 Xdoor_2, TRUE, FALSE,
5756 EL_EM_GATE_2, -1, -1
5759 Xdoor_3, TRUE, FALSE,
5760 EL_EM_GATE_3, -1, -1
5763 Xdoor_4, TRUE, FALSE,
5764 EL_EM_GATE_4, -1, -1
5767 Xdoor_5, TRUE, FALSE,
5768 EL_EMC_GATE_5, -1, -1
5771 Xdoor_6, TRUE, FALSE,
5772 EL_EMC_GATE_6, -1, -1
5775 Xdoor_7, TRUE, FALSE,
5776 EL_EMC_GATE_7, -1, -1
5779 Xdoor_8, TRUE, FALSE,
5780 EL_EMC_GATE_8, -1, -1
5783 Xkey_1, TRUE, FALSE,
5787 Xkey_2, TRUE, FALSE,
5791 Xkey_3, TRUE, FALSE,
5795 Xkey_4, TRUE, FALSE,
5799 Xkey_5, TRUE, FALSE,
5800 EL_EMC_KEY_5, -1, -1
5803 Xkey_6, TRUE, FALSE,
5804 EL_EMC_KEY_6, -1, -1
5807 Xkey_7, TRUE, FALSE,
5808 EL_EMC_KEY_7, -1, -1
5811 Xkey_8, TRUE, FALSE,
5812 EL_EMC_KEY_8, -1, -1
5815 Xwind_n, TRUE, FALSE,
5816 EL_BALLOON_SWITCH_UP, -1, -1
5819 Xwind_e, TRUE, FALSE,
5820 EL_BALLOON_SWITCH_RIGHT, -1, -1
5823 Xwind_s, TRUE, FALSE,
5824 EL_BALLOON_SWITCH_DOWN, -1, -1
5827 Xwind_w, TRUE, FALSE,
5828 EL_BALLOON_SWITCH_LEFT, -1, -1
5831 Xwind_nesw, TRUE, FALSE,
5832 EL_BALLOON_SWITCH_ANY, -1, -1
5835 Xwind_stop, TRUE, FALSE,
5836 EL_BALLOON_SWITCH_NONE, -1, -1
5840 EL_EM_EXIT_CLOSED, -1, -1
5843 Xexit_1, TRUE, FALSE,
5844 EL_EM_EXIT_OPEN, -1, -1
5847 Xexit_2, FALSE, FALSE,
5848 EL_EM_EXIT_OPEN, -1, -1
5851 Xexit_3, FALSE, FALSE,
5852 EL_EM_EXIT_OPEN, -1, -1
5855 Xdynamite, TRUE, FALSE,
5856 EL_EM_DYNAMITE, -1, -1
5859 Ydynamite_eat, FALSE, FALSE,
5860 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5863 Xdynamite_1, TRUE, FALSE,
5864 EL_EM_DYNAMITE_ACTIVE, -1, -1
5867 Xdynamite_2, FALSE, FALSE,
5868 EL_EM_DYNAMITE_ACTIVE, -1, -1
5871 Xdynamite_3, FALSE, FALSE,
5872 EL_EM_DYNAMITE_ACTIVE, -1, -1
5875 Xdynamite_4, FALSE, FALSE,
5876 EL_EM_DYNAMITE_ACTIVE, -1, -1
5879 Xbumper, TRUE, FALSE,
5880 EL_EMC_SPRING_BUMPER, -1, -1
5883 XbumperB, FALSE, FALSE,
5884 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5887 Xwheel, TRUE, FALSE,
5888 EL_ROBOT_WHEEL, -1, -1
5891 XwheelB, FALSE, FALSE,
5892 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5895 Xswitch, TRUE, FALSE,
5896 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5899 XswitchB, FALSE, FALSE,
5900 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5904 EL_QUICKSAND_EMPTY, -1, -1
5907 Xsand_stone, TRUE, FALSE,
5908 EL_QUICKSAND_FULL, -1, -1
5911 Xsand_stonein_1, FALSE, TRUE,
5912 EL_ROCK, ACTION_FILLING, -1
5915 Xsand_stonein_2, FALSE, TRUE,
5916 EL_ROCK, ACTION_FILLING, -1
5919 Xsand_stonein_3, FALSE, TRUE,
5920 EL_ROCK, ACTION_FILLING, -1
5923 Xsand_stonein_4, FALSE, TRUE,
5924 EL_ROCK, ACTION_FILLING, -1
5927 Xsand_stonesand_1, FALSE, FALSE,
5928 EL_QUICKSAND_EMPTYING, -1, -1
5931 Xsand_stonesand_2, FALSE, FALSE,
5932 EL_QUICKSAND_EMPTYING, -1, -1
5935 Xsand_stonesand_3, FALSE, FALSE,
5936 EL_QUICKSAND_EMPTYING, -1, -1
5939 Xsand_stonesand_4, FALSE, FALSE,
5940 EL_QUICKSAND_EMPTYING, -1, -1
5943 Xsand_stonesand_quickout_1, FALSE, FALSE,
5944 EL_QUICKSAND_EMPTYING, -1, -1
5947 Xsand_stonesand_quickout_2, FALSE, FALSE,
5948 EL_QUICKSAND_EMPTYING, -1, -1
5951 Xsand_stoneout_1, FALSE, FALSE,
5952 EL_ROCK, ACTION_EMPTYING, -1
5955 Xsand_stoneout_2, FALSE, FALSE,
5956 EL_ROCK, ACTION_EMPTYING, -1
5959 Xsand_sandstone_1, FALSE, FALSE,
5960 EL_QUICKSAND_FILLING, -1, -1
5963 Xsand_sandstone_2, FALSE, FALSE,
5964 EL_QUICKSAND_FILLING, -1, -1
5967 Xsand_sandstone_3, FALSE, FALSE,
5968 EL_QUICKSAND_FILLING, -1, -1
5971 Xsand_sandstone_4, FALSE, FALSE,
5972 EL_QUICKSAND_FILLING, -1, -1
5975 Xplant, TRUE, FALSE,
5976 EL_EMC_PLANT, -1, -1
5979 Yplant, FALSE, FALSE,
5980 EL_EMC_PLANT, -1, -1
5983 Xlenses, TRUE, FALSE,
5984 EL_EMC_LENSES, -1, -1
5987 Xmagnify, TRUE, FALSE,
5988 EL_EMC_MAGNIFIER, -1, -1
5991 Xdripper, TRUE, FALSE,
5992 EL_EMC_DRIPPER, -1, -1
5995 XdripperB, FALSE, FALSE,
5996 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5999 Xfake_blank, TRUE, FALSE,
6000 EL_INVISIBLE_WALL, -1, -1
6003 Xfake_blankB, FALSE, FALSE,
6004 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6007 Xfake_grass, TRUE, FALSE,
6008 EL_EMC_FAKE_GRASS, -1, -1
6011 Xfake_grassB, FALSE, FALSE,
6012 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6015 Xfake_door_1, TRUE, FALSE,
6016 EL_EM_GATE_1_GRAY, -1, -1
6019 Xfake_door_2, TRUE, FALSE,
6020 EL_EM_GATE_2_GRAY, -1, -1
6023 Xfake_door_3, TRUE, FALSE,
6024 EL_EM_GATE_3_GRAY, -1, -1
6027 Xfake_door_4, TRUE, FALSE,
6028 EL_EM_GATE_4_GRAY, -1, -1
6031 Xfake_door_5, TRUE, FALSE,
6032 EL_EMC_GATE_5_GRAY, -1, -1
6035 Xfake_door_6, TRUE, FALSE,
6036 EL_EMC_GATE_6_GRAY, -1, -1
6039 Xfake_door_7, TRUE, FALSE,
6040 EL_EMC_GATE_7_GRAY, -1, -1
6043 Xfake_door_8, TRUE, FALSE,
6044 EL_EMC_GATE_8_GRAY, -1, -1
6047 Xfake_acid_1, TRUE, FALSE,
6048 EL_EMC_FAKE_ACID, -1, -1
6051 Xfake_acid_2, FALSE, FALSE,
6052 EL_EMC_FAKE_ACID, -1, -1
6055 Xfake_acid_3, FALSE, FALSE,
6056 EL_EMC_FAKE_ACID, -1, -1
6059 Xfake_acid_4, FALSE, FALSE,
6060 EL_EMC_FAKE_ACID, -1, -1
6063 Xfake_acid_5, FALSE, FALSE,
6064 EL_EMC_FAKE_ACID, -1, -1
6067 Xfake_acid_6, FALSE, FALSE,
6068 EL_EMC_FAKE_ACID, -1, -1
6071 Xfake_acid_7, FALSE, FALSE,
6072 EL_EMC_FAKE_ACID, -1, -1
6075 Xfake_acid_8, FALSE, FALSE,
6076 EL_EMC_FAKE_ACID, -1, -1
6079 Xsteel_1, TRUE, FALSE,
6080 EL_STEELWALL, -1, -1
6083 Xsteel_2, TRUE, FALSE,
6084 EL_EMC_STEELWALL_2, -1, -1
6087 Xsteel_3, TRUE, FALSE,
6088 EL_EMC_STEELWALL_3, -1, -1
6091 Xsteel_4, TRUE, FALSE,
6092 EL_EMC_STEELWALL_4, -1, -1
6095 Xwall_1, TRUE, FALSE,
6099 Xwall_2, TRUE, FALSE,
6100 EL_EMC_WALL_14, -1, -1
6103 Xwall_3, TRUE, FALSE,
6104 EL_EMC_WALL_15, -1, -1
6107 Xwall_4, TRUE, FALSE,
6108 EL_EMC_WALL_16, -1, -1
6111 Xround_wall_1, TRUE, FALSE,
6112 EL_WALL_SLIPPERY, -1, -1
6115 Xround_wall_2, TRUE, FALSE,
6116 EL_EMC_WALL_SLIPPERY_2, -1, -1
6119 Xround_wall_3, TRUE, FALSE,
6120 EL_EMC_WALL_SLIPPERY_3, -1, -1
6123 Xround_wall_4, TRUE, FALSE,
6124 EL_EMC_WALL_SLIPPERY_4, -1, -1
6127 Xdecor_1, TRUE, FALSE,
6128 EL_EMC_WALL_8, -1, -1
6131 Xdecor_2, TRUE, FALSE,
6132 EL_EMC_WALL_6, -1, -1
6135 Xdecor_3, TRUE, FALSE,
6136 EL_EMC_WALL_4, -1, -1
6139 Xdecor_4, TRUE, FALSE,
6140 EL_EMC_WALL_7, -1, -1
6143 Xdecor_5, TRUE, FALSE,
6144 EL_EMC_WALL_5, -1, -1
6147 Xdecor_6, TRUE, FALSE,
6148 EL_EMC_WALL_9, -1, -1
6151 Xdecor_7, TRUE, FALSE,
6152 EL_EMC_WALL_10, -1, -1
6155 Xdecor_8, TRUE, FALSE,
6156 EL_EMC_WALL_1, -1, -1
6159 Xdecor_9, TRUE, FALSE,
6160 EL_EMC_WALL_2, -1, -1
6163 Xdecor_10, TRUE, FALSE,
6164 EL_EMC_WALL_3, -1, -1
6167 Xdecor_11, TRUE, FALSE,
6168 EL_EMC_WALL_11, -1, -1
6171 Xdecor_12, TRUE, FALSE,
6172 EL_EMC_WALL_12, -1, -1
6175 Xalpha_0, TRUE, FALSE,
6176 EL_CHAR('0'), -1, -1
6179 Xalpha_1, TRUE, FALSE,
6180 EL_CHAR('1'), -1, -1
6183 Xalpha_2, TRUE, FALSE,
6184 EL_CHAR('2'), -1, -1
6187 Xalpha_3, TRUE, FALSE,
6188 EL_CHAR('3'), -1, -1
6191 Xalpha_4, TRUE, FALSE,
6192 EL_CHAR('4'), -1, -1
6195 Xalpha_5, TRUE, FALSE,
6196 EL_CHAR('5'), -1, -1
6199 Xalpha_6, TRUE, FALSE,
6200 EL_CHAR('6'), -1, -1
6203 Xalpha_7, TRUE, FALSE,
6204 EL_CHAR('7'), -1, -1
6207 Xalpha_8, TRUE, FALSE,
6208 EL_CHAR('8'), -1, -1
6211 Xalpha_9, TRUE, FALSE,
6212 EL_CHAR('9'), -1, -1
6215 Xalpha_excla, TRUE, FALSE,
6216 EL_CHAR('!'), -1, -1
6219 Xalpha_quote, TRUE, FALSE,
6220 EL_CHAR('"'), -1, -1
6223 Xalpha_comma, TRUE, FALSE,
6224 EL_CHAR(','), -1, -1
6227 Xalpha_minus, TRUE, FALSE,
6228 EL_CHAR('-'), -1, -1
6231 Xalpha_perio, TRUE, FALSE,
6232 EL_CHAR('.'), -1, -1
6235 Xalpha_colon, TRUE, FALSE,
6236 EL_CHAR(':'), -1, -1
6239 Xalpha_quest, TRUE, FALSE,
6240 EL_CHAR('?'), -1, -1
6243 Xalpha_a, TRUE, FALSE,
6244 EL_CHAR('A'), -1, -1
6247 Xalpha_b, TRUE, FALSE,
6248 EL_CHAR('B'), -1, -1
6251 Xalpha_c, TRUE, FALSE,
6252 EL_CHAR('C'), -1, -1
6255 Xalpha_d, TRUE, FALSE,
6256 EL_CHAR('D'), -1, -1
6259 Xalpha_e, TRUE, FALSE,
6260 EL_CHAR('E'), -1, -1
6263 Xalpha_f, TRUE, FALSE,
6264 EL_CHAR('F'), -1, -1
6267 Xalpha_g, TRUE, FALSE,
6268 EL_CHAR('G'), -1, -1
6271 Xalpha_h, TRUE, FALSE,
6272 EL_CHAR('H'), -1, -1
6275 Xalpha_i, TRUE, FALSE,
6276 EL_CHAR('I'), -1, -1
6279 Xalpha_j, TRUE, FALSE,
6280 EL_CHAR('J'), -1, -1
6283 Xalpha_k, TRUE, FALSE,
6284 EL_CHAR('K'), -1, -1
6287 Xalpha_l, TRUE, FALSE,
6288 EL_CHAR('L'), -1, -1
6291 Xalpha_m, TRUE, FALSE,
6292 EL_CHAR('M'), -1, -1
6295 Xalpha_n, TRUE, FALSE,
6296 EL_CHAR('N'), -1, -1
6299 Xalpha_o, TRUE, FALSE,
6300 EL_CHAR('O'), -1, -1
6303 Xalpha_p, TRUE, FALSE,
6304 EL_CHAR('P'), -1, -1
6307 Xalpha_q, TRUE, FALSE,
6308 EL_CHAR('Q'), -1, -1
6311 Xalpha_r, TRUE, FALSE,
6312 EL_CHAR('R'), -1, -1
6315 Xalpha_s, TRUE, FALSE,
6316 EL_CHAR('S'), -1, -1
6319 Xalpha_t, TRUE, FALSE,
6320 EL_CHAR('T'), -1, -1
6323 Xalpha_u, TRUE, FALSE,
6324 EL_CHAR('U'), -1, -1
6327 Xalpha_v, TRUE, FALSE,
6328 EL_CHAR('V'), -1, -1
6331 Xalpha_w, TRUE, FALSE,
6332 EL_CHAR('W'), -1, -1
6335 Xalpha_x, TRUE, FALSE,
6336 EL_CHAR('X'), -1, -1
6339 Xalpha_y, TRUE, FALSE,
6340 EL_CHAR('Y'), -1, -1
6343 Xalpha_z, TRUE, FALSE,
6344 EL_CHAR('Z'), -1, -1
6347 Xalpha_arrow_e, TRUE, FALSE,
6348 EL_CHAR('>'), -1, -1
6351 Xalpha_arrow_w, TRUE, FALSE,
6352 EL_CHAR('<'), -1, -1
6355 Xalpha_copyr, TRUE, FALSE,
6356 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6360 Xboom_bug, FALSE, FALSE,
6361 EL_BUG, ACTION_EXPLODING, -1
6364 Xboom_bomb, FALSE, FALSE,
6365 EL_BOMB, ACTION_EXPLODING, -1
6368 Xboom_android, FALSE, FALSE,
6369 EL_EMC_ANDROID, ACTION_OTHER, -1
6372 Xboom_1, FALSE, FALSE,
6373 EL_DEFAULT, ACTION_EXPLODING, -1
6376 Xboom_2, FALSE, FALSE,
6377 EL_DEFAULT, ACTION_EXPLODING, -1
6380 Znormal, FALSE, FALSE,
6384 Zdynamite, FALSE, FALSE,
6388 Zplayer, FALSE, FALSE,
6392 ZBORDER, FALSE, FALSE,
6402 static struct Mapping_EM_to_RND_player
6411 em_player_mapping_list[] =
6415 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6419 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6423 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6427 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6431 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6435 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6439 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6443 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6447 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6451 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6455 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6459 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6463 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6467 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6471 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6475 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6479 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6483 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6487 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6491 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6495 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6499 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6503 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6507 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6511 EL_PLAYER_1, ACTION_DEFAULT, -1,
6515 EL_PLAYER_2, ACTION_DEFAULT, -1,
6519 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6523 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6527 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6531 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6535 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6539 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6543 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6547 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6551 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6555 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6559 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6563 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6567 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6571 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6575 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6579 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6583 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6587 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6591 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6595 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6599 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6603 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6607 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6611 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6615 EL_PLAYER_3, ACTION_DEFAULT, -1,
6619 EL_PLAYER_4, ACTION_DEFAULT, -1,
6628 int map_element_RND_to_EM(int element_rnd)
6630 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6631 static boolean mapping_initialized = FALSE;
6633 if (!mapping_initialized)
6637 /* return "Xalpha_quest" for all undefined elements in mapping array */
6638 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6639 mapping_RND_to_EM[i] = Xalpha_quest;
6641 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6642 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6643 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6644 em_object_mapping_list[i].element_em;
6646 mapping_initialized = TRUE;
6649 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6650 return mapping_RND_to_EM[element_rnd];
6652 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6657 int map_element_EM_to_RND(int element_em)
6659 static unsigned short mapping_EM_to_RND[TILE_MAX];
6660 static boolean mapping_initialized = FALSE;
6662 if (!mapping_initialized)
6666 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6667 for (i = 0; i < TILE_MAX; i++)
6668 mapping_EM_to_RND[i] = EL_UNKNOWN;
6670 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6671 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6672 em_object_mapping_list[i].element_rnd;
6674 mapping_initialized = TRUE;
6677 if (element_em >= 0 && element_em < TILE_MAX)
6678 return mapping_EM_to_RND[element_em];
6680 Error(ERR_WARN, "invalid EM level element %d", element_em);
6685 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6687 struct LevelInfo_EM *level_em = level->native_em_level;
6688 struct LEVEL *lev = level_em->lev;
6691 for (i = 0; i < TILE_MAX; i++)
6692 lev->android_array[i] = Xblank;
6694 for (i = 0; i < level->num_android_clone_elements; i++)
6696 int element_rnd = level->android_clone_element[i];
6697 int element_em = map_element_RND_to_EM(element_rnd);
6699 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6700 if (em_object_mapping_list[j].element_rnd == element_rnd)
6701 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6705 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6707 struct LevelInfo_EM *level_em = level->native_em_level;
6708 struct LEVEL *lev = level_em->lev;
6711 level->num_android_clone_elements = 0;
6713 for (i = 0; i < TILE_MAX; i++)
6715 int element_em = lev->android_array[i];
6717 boolean element_found = FALSE;
6719 if (element_em == Xblank)
6722 element_rnd = map_element_EM_to_RND(element_em);
6724 for (j = 0; j < level->num_android_clone_elements; j++)
6725 if (level->android_clone_element[j] == element_rnd)
6726 element_found = TRUE;
6730 level->android_clone_element[level->num_android_clone_elements++] =
6733 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6738 if (level->num_android_clone_elements == 0)
6740 level->num_android_clone_elements = 1;
6741 level->android_clone_element[0] = EL_EMPTY;
6745 int map_direction_RND_to_EM(int direction)
6747 return (direction == MV_UP ? 0 :
6748 direction == MV_RIGHT ? 1 :
6749 direction == MV_DOWN ? 2 :
6750 direction == MV_LEFT ? 3 :
6754 int map_direction_EM_to_RND(int direction)
6756 return (direction == 0 ? MV_UP :
6757 direction == 1 ? MV_RIGHT :
6758 direction == 2 ? MV_DOWN :
6759 direction == 3 ? MV_LEFT :
6763 int map_element_RND_to_SP(int element_rnd)
6765 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6767 if (element_rnd >= EL_SP_START &&
6768 element_rnd <= EL_SP_END)
6769 element_sp = element_rnd - EL_SP_START;
6770 else if (element_rnd == EL_EMPTY_SPACE)
6772 else if (element_rnd == EL_INVISIBLE_WALL)
6778 int map_element_SP_to_RND(int element_sp)
6780 int element_rnd = EL_UNKNOWN;
6782 if (element_sp >= 0x00 &&
6784 element_rnd = EL_SP_START + element_sp;
6785 else if (element_sp == 0x28)
6786 element_rnd = EL_INVISIBLE_WALL;
6791 int map_action_SP_to_RND(int action_sp)
6795 case actActive: return ACTION_ACTIVE;
6796 case actImpact: return ACTION_IMPACT;
6797 case actExploding: return ACTION_EXPLODING;
6798 case actDigging: return ACTION_DIGGING;
6799 case actSnapping: return ACTION_SNAPPING;
6800 case actCollecting: return ACTION_COLLECTING;
6801 case actPassing: return ACTION_PASSING;
6802 case actPushing: return ACTION_PUSHING;
6803 case actDropping: return ACTION_DROPPING;
6805 default: return ACTION_DEFAULT;
6809 int get_next_element(int element)
6813 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6814 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6815 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6816 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6817 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6818 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6819 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6820 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6821 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6822 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6823 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6825 default: return element;
6829 int el_act_dir2img(int element, int action, int direction)
6831 element = GFX_ELEMENT(element);
6832 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6834 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6835 return element_info[element].direction_graphic[action][direction];
6838 static int el_act_dir2crm(int element, int action, int direction)
6840 element = GFX_ELEMENT(element);
6841 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6843 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6844 return element_info[element].direction_crumbled[action][direction];
6847 int el_act2img(int element, int action)
6849 element = GFX_ELEMENT(element);
6851 return element_info[element].graphic[action];
6854 int el_act2crm(int element, int action)
6856 element = GFX_ELEMENT(element);
6858 return element_info[element].crumbled[action];
6861 int el_dir2img(int element, int direction)
6863 element = GFX_ELEMENT(element);
6865 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6868 int el2baseimg(int element)
6870 return element_info[element].graphic[ACTION_DEFAULT];
6873 int el2img(int element)
6875 element = GFX_ELEMENT(element);
6877 return element_info[element].graphic[ACTION_DEFAULT];
6880 int el2edimg(int element)
6882 element = GFX_ELEMENT(element);
6884 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6887 int el2preimg(int element)
6889 element = GFX_ELEMENT(element);
6891 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6894 int el2panelimg(int element)
6896 element = GFX_ELEMENT(element);
6898 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6901 int font2baseimg(int font_nr)
6903 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6906 int getBeltNrFromBeltElement(int element)
6908 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6909 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6910 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6913 int getBeltNrFromBeltActiveElement(int element)
6915 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6916 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6917 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6920 int getBeltNrFromBeltSwitchElement(int element)
6922 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6923 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6924 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6927 int getBeltDirNrFromBeltElement(int element)
6929 static int belt_base_element[4] =
6931 EL_CONVEYOR_BELT_1_LEFT,
6932 EL_CONVEYOR_BELT_2_LEFT,
6933 EL_CONVEYOR_BELT_3_LEFT,
6934 EL_CONVEYOR_BELT_4_LEFT
6937 int belt_nr = getBeltNrFromBeltElement(element);
6938 int belt_dir_nr = element - belt_base_element[belt_nr];
6940 return (belt_dir_nr % 3);
6943 int getBeltDirNrFromBeltSwitchElement(int element)
6945 static int belt_base_element[4] =
6947 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6948 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6949 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6950 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6953 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6954 int belt_dir_nr = element - belt_base_element[belt_nr];
6956 return (belt_dir_nr % 3);
6959 int getBeltDirFromBeltElement(int element)
6961 static int belt_move_dir[3] =
6968 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6970 return belt_move_dir[belt_dir_nr];
6973 int getBeltDirFromBeltSwitchElement(int element)
6975 static int belt_move_dir[3] =
6982 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6984 return belt_move_dir[belt_dir_nr];
6987 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6989 static int belt_base_element[4] =
6991 EL_CONVEYOR_BELT_1_LEFT,
6992 EL_CONVEYOR_BELT_2_LEFT,
6993 EL_CONVEYOR_BELT_3_LEFT,
6994 EL_CONVEYOR_BELT_4_LEFT
6997 return belt_base_element[belt_nr] + belt_dir_nr;
7000 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7002 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7004 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7007 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7009 static int belt_base_element[4] =
7011 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7012 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7013 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7014 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7017 return belt_base_element[belt_nr] + belt_dir_nr;
7020 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7022 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7024 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7027 boolean getTeamMode_EM()
7029 return game.team_mode;
7032 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7034 int game_frame_delay_value;
7036 game_frame_delay_value =
7037 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7038 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7041 if (tape.playing && tape.warp_forward && !tape.pausing)
7042 game_frame_delay_value = 0;
7044 return game_frame_delay_value;
7047 unsigned int InitRND(int seed)
7049 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7050 return InitEngineRandom_EM(seed);
7051 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7052 return InitEngineRandom_SP(seed);
7054 return InitEngineRandom_RND(seed);
7057 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7058 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7060 inline static int get_effective_element_EM(int tile, int frame_em)
7062 int element = object_mapping[tile].element_rnd;
7063 int action = object_mapping[tile].action;
7064 boolean is_backside = object_mapping[tile].is_backside;
7065 boolean action_removing = (action == ACTION_DIGGING ||
7066 action == ACTION_SNAPPING ||
7067 action == ACTION_COLLECTING);
7073 case Yacid_splash_eB:
7074 case Yacid_splash_wB:
7075 return (frame_em > 5 ? EL_EMPTY : element);
7081 else /* frame_em == 7 */
7085 case Yacid_splash_eB:
7086 case Yacid_splash_wB:
7089 case Yemerald_stone:
7092 case Ydiamond_stone:
7096 case Xdrip_stretchB:
7115 case Xsand_stonein_1:
7116 case Xsand_stonein_2:
7117 case Xsand_stonein_3:
7118 case Xsand_stonein_4:
7122 return (is_backside || action_removing ? EL_EMPTY : element);
7127 inline static boolean check_linear_animation_EM(int tile)
7131 case Xsand_stonesand_1:
7132 case Xsand_stonesand_quickout_1:
7133 case Xsand_sandstone_1:
7134 case Xsand_stonein_1:
7135 case Xsand_stoneout_1:
7154 case Yacid_splash_eB:
7155 case Yacid_splash_wB:
7156 case Yemerald_stone:
7163 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7164 boolean has_crumbled_graphics,
7165 int crumbled, int sync_frame)
7167 /* if element can be crumbled, but certain action graphics are just empty
7168 space (like instantly snapping sand to empty space in 1 frame), do not
7169 treat these empty space graphics as crumbled graphics in EMC engine */
7170 if (crumbled == IMG_EMPTY_SPACE)
7171 has_crumbled_graphics = FALSE;
7173 if (has_crumbled_graphics)
7175 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7176 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7177 g_crumbled->anim_delay,
7178 g_crumbled->anim_mode,
7179 g_crumbled->anim_start_frame,
7182 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7183 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7185 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7187 g_em->has_crumbled_graphics = TRUE;
7191 g_em->crumbled_bitmap = NULL;
7192 g_em->crumbled_src_x = 0;
7193 g_em->crumbled_src_y = 0;
7194 g_em->crumbled_border_size = 0;
7196 g_em->has_crumbled_graphics = FALSE;
7200 void ResetGfxAnimation_EM(int x, int y, int tile)
7205 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7206 int tile, int frame_em, int x, int y)
7208 int action = object_mapping[tile].action;
7209 int direction = object_mapping[tile].direction;
7210 int effective_element = get_effective_element_EM(tile, frame_em);
7211 int graphic = (direction == MV_NONE ?
7212 el_act2img(effective_element, action) :
7213 el_act_dir2img(effective_element, action, direction));
7214 struct GraphicInfo *g = &graphic_info[graphic];
7216 boolean action_removing = (action == ACTION_DIGGING ||
7217 action == ACTION_SNAPPING ||
7218 action == ACTION_COLLECTING);
7219 boolean action_moving = (action == ACTION_FALLING ||
7220 action == ACTION_MOVING ||
7221 action == ACTION_PUSHING ||
7222 action == ACTION_EATING ||
7223 action == ACTION_FILLING ||
7224 action == ACTION_EMPTYING);
7225 boolean action_falling = (action == ACTION_FALLING ||
7226 action == ACTION_FILLING ||
7227 action == ACTION_EMPTYING);
7229 /* special case: graphic uses "2nd movement tile" and has defined
7230 7 frames for movement animation (or less) => use default graphic
7231 for last (8th) frame which ends the movement animation */
7232 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7234 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7235 graphic = (direction == MV_NONE ?
7236 el_act2img(effective_element, action) :
7237 el_act_dir2img(effective_element, action, direction));
7239 g = &graphic_info[graphic];
7242 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7246 else if (action_moving)
7248 boolean is_backside = object_mapping[tile].is_backside;
7252 int direction = object_mapping[tile].direction;
7253 int move_dir = (action_falling ? MV_DOWN : direction);
7258 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7259 if (g->double_movement && frame_em == 0)
7263 if (move_dir == MV_LEFT)
7264 GfxFrame[x - 1][y] = GfxFrame[x][y];
7265 else if (move_dir == MV_RIGHT)
7266 GfxFrame[x + 1][y] = GfxFrame[x][y];
7267 else if (move_dir == MV_UP)
7268 GfxFrame[x][y - 1] = GfxFrame[x][y];
7269 else if (move_dir == MV_DOWN)
7270 GfxFrame[x][y + 1] = GfxFrame[x][y];
7277 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7278 if (tile == Xsand_stonesand_quickout_1 ||
7279 tile == Xsand_stonesand_quickout_2)
7283 if (graphic_info[graphic].anim_global_sync)
7284 sync_frame = FrameCounter;
7285 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7286 sync_frame = GfxFrame[x][y];
7288 sync_frame = 0; /* playfield border (pseudo steel) */
7290 SetRandomAnimationValue(x, y);
7292 int frame = getAnimationFrame(g->anim_frames,
7295 g->anim_start_frame,
7298 g_em->unique_identifier =
7299 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7302 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7303 int tile, int frame_em, int x, int y)
7305 int action = object_mapping[tile].action;
7306 int direction = object_mapping[tile].direction;
7307 boolean is_backside = object_mapping[tile].is_backside;
7308 int effective_element = get_effective_element_EM(tile, frame_em);
7309 int effective_action = action;
7310 int graphic = (direction == MV_NONE ?
7311 el_act2img(effective_element, effective_action) :
7312 el_act_dir2img(effective_element, effective_action,
7314 int crumbled = (direction == MV_NONE ?
7315 el_act2crm(effective_element, effective_action) :
7316 el_act_dir2crm(effective_element, effective_action,
7318 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7319 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7320 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7321 struct GraphicInfo *g = &graphic_info[graphic];
7324 /* special case: graphic uses "2nd movement tile" and has defined
7325 7 frames for movement animation (or less) => use default graphic
7326 for last (8th) frame which ends the movement animation */
7327 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7329 effective_action = ACTION_DEFAULT;
7330 graphic = (direction == MV_NONE ?
7331 el_act2img(effective_element, effective_action) :
7332 el_act_dir2img(effective_element, effective_action,
7334 crumbled = (direction == MV_NONE ?
7335 el_act2crm(effective_element, effective_action) :
7336 el_act_dir2crm(effective_element, effective_action,
7339 g = &graphic_info[graphic];
7342 if (graphic_info[graphic].anim_global_sync)
7343 sync_frame = FrameCounter;
7344 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7345 sync_frame = GfxFrame[x][y];
7347 sync_frame = 0; /* playfield border (pseudo steel) */
7349 SetRandomAnimationValue(x, y);
7351 int frame = getAnimationFrame(g->anim_frames,
7354 g->anim_start_frame,
7357 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7358 g->double_movement && is_backside);
7360 /* (updating the "crumbled" graphic definitions is probably not really needed,
7361 as animations for crumbled graphics can't be longer than one EMC cycle) */
7362 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7366 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7367 int player_nr, int anim, int frame_em)
7369 int element = player_mapping[player_nr][anim].element_rnd;
7370 int action = player_mapping[player_nr][anim].action;
7371 int direction = player_mapping[player_nr][anim].direction;
7372 int graphic = (direction == MV_NONE ?
7373 el_act2img(element, action) :
7374 el_act_dir2img(element, action, direction));
7375 struct GraphicInfo *g = &graphic_info[graphic];
7378 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7380 stored_player[player_nr].StepFrame = frame_em;
7382 sync_frame = stored_player[player_nr].Frame;
7384 int frame = getAnimationFrame(g->anim_frames,
7387 g->anim_start_frame,
7390 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7391 &g_em->src_x, &g_em->src_y, FALSE);
7394 void InitGraphicInfo_EM(void)
7399 int num_em_gfx_errors = 0;
7401 if (graphic_info_em_object[0][0].bitmap == NULL)
7403 /* EM graphics not yet initialized in em_open_all() */
7408 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7411 /* always start with reliable default values */
7412 for (i = 0; i < TILE_MAX; i++)
7414 object_mapping[i].element_rnd = EL_UNKNOWN;
7415 object_mapping[i].is_backside = FALSE;
7416 object_mapping[i].action = ACTION_DEFAULT;
7417 object_mapping[i].direction = MV_NONE;
7420 /* always start with reliable default values */
7421 for (p = 0; p < MAX_PLAYERS; p++)
7423 for (i = 0; i < SPR_MAX; i++)
7425 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7426 player_mapping[p][i].action = ACTION_DEFAULT;
7427 player_mapping[p][i].direction = MV_NONE;
7431 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7433 int e = em_object_mapping_list[i].element_em;
7435 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7436 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7438 if (em_object_mapping_list[i].action != -1)
7439 object_mapping[e].action = em_object_mapping_list[i].action;
7441 if (em_object_mapping_list[i].direction != -1)
7442 object_mapping[e].direction =
7443 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7446 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7448 int a = em_player_mapping_list[i].action_em;
7449 int p = em_player_mapping_list[i].player_nr;
7451 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7453 if (em_player_mapping_list[i].action != -1)
7454 player_mapping[p][a].action = em_player_mapping_list[i].action;
7456 if (em_player_mapping_list[i].direction != -1)
7457 player_mapping[p][a].direction =
7458 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7461 for (i = 0; i < TILE_MAX; i++)
7463 int element = object_mapping[i].element_rnd;
7464 int action = object_mapping[i].action;
7465 int direction = object_mapping[i].direction;
7466 boolean is_backside = object_mapping[i].is_backside;
7467 boolean action_exploding = ((action == ACTION_EXPLODING ||
7468 action == ACTION_SMASHED_BY_ROCK ||
7469 action == ACTION_SMASHED_BY_SPRING) &&
7470 element != EL_DIAMOND);
7471 boolean action_active = (action == ACTION_ACTIVE);
7472 boolean action_other = (action == ACTION_OTHER);
7474 for (j = 0; j < 8; j++)
7476 int effective_element = get_effective_element_EM(i, j);
7477 int effective_action = (j < 7 ? action :
7478 i == Xdrip_stretch ? action :
7479 i == Xdrip_stretchB ? action :
7480 i == Ydrip_s1 ? action :
7481 i == Ydrip_s1B ? action :
7482 i == Xball_1B ? action :
7483 i == Xball_2 ? action :
7484 i == Xball_2B ? action :
7485 i == Yball_eat ? action :
7486 i == Ykey_1_eat ? action :
7487 i == Ykey_2_eat ? action :
7488 i == Ykey_3_eat ? action :
7489 i == Ykey_4_eat ? action :
7490 i == Ykey_5_eat ? action :
7491 i == Ykey_6_eat ? action :
7492 i == Ykey_7_eat ? action :
7493 i == Ykey_8_eat ? action :
7494 i == Ylenses_eat ? action :
7495 i == Ymagnify_eat ? action :
7496 i == Ygrass_eat ? action :
7497 i == Ydirt_eat ? action :
7498 i == Xsand_stonein_1 ? action :
7499 i == Xsand_stonein_2 ? action :
7500 i == Xsand_stonein_3 ? action :
7501 i == Xsand_stonein_4 ? action :
7502 i == Xsand_stoneout_1 ? action :
7503 i == Xsand_stoneout_2 ? action :
7504 i == Xboom_android ? ACTION_EXPLODING :
7505 action_exploding ? ACTION_EXPLODING :
7506 action_active ? action :
7507 action_other ? action :
7509 int graphic = (el_act_dir2img(effective_element, effective_action,
7511 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7513 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7514 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7515 boolean has_action_graphics = (graphic != base_graphic);
7516 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7517 struct GraphicInfo *g = &graphic_info[graphic];
7518 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7521 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7522 boolean special_animation = (action != ACTION_DEFAULT &&
7523 g->anim_frames == 3 &&
7524 g->anim_delay == 2 &&
7525 g->anim_mode & ANIM_LINEAR);
7526 int sync_frame = (i == Xdrip_stretch ? 7 :
7527 i == Xdrip_stretchB ? 7 :
7528 i == Ydrip_s2 ? j + 8 :
7529 i == Ydrip_s2B ? j + 8 :
7538 i == Xfake_acid_1 ? 0 :
7539 i == Xfake_acid_2 ? 10 :
7540 i == Xfake_acid_3 ? 20 :
7541 i == Xfake_acid_4 ? 30 :
7542 i == Xfake_acid_5 ? 40 :
7543 i == Xfake_acid_6 ? 50 :
7544 i == Xfake_acid_7 ? 60 :
7545 i == Xfake_acid_8 ? 70 :
7547 i == Xball_2B ? j + 8 :
7548 i == Yball_eat ? j + 1 :
7549 i == Ykey_1_eat ? j + 1 :
7550 i == Ykey_2_eat ? j + 1 :
7551 i == Ykey_3_eat ? j + 1 :
7552 i == Ykey_4_eat ? j + 1 :
7553 i == Ykey_5_eat ? j + 1 :
7554 i == Ykey_6_eat ? j + 1 :
7555 i == Ykey_7_eat ? j + 1 :
7556 i == Ykey_8_eat ? j + 1 :
7557 i == Ylenses_eat ? j + 1 :
7558 i == Ymagnify_eat ? j + 1 :
7559 i == Ygrass_eat ? j + 1 :
7560 i == Ydirt_eat ? j + 1 :
7561 i == Xamoeba_1 ? 0 :
7562 i == Xamoeba_2 ? 1 :
7563 i == Xamoeba_3 ? 2 :
7564 i == Xamoeba_4 ? 3 :
7565 i == Xamoeba_5 ? 0 :
7566 i == Xamoeba_6 ? 1 :
7567 i == Xamoeba_7 ? 2 :
7568 i == Xamoeba_8 ? 3 :
7569 i == Xexit_2 ? j + 8 :
7570 i == Xexit_3 ? j + 16 :
7571 i == Xdynamite_1 ? 0 :
7572 i == Xdynamite_2 ? 8 :
7573 i == Xdynamite_3 ? 16 :
7574 i == Xdynamite_4 ? 24 :
7575 i == Xsand_stonein_1 ? j + 1 :
7576 i == Xsand_stonein_2 ? j + 9 :
7577 i == Xsand_stonein_3 ? j + 17 :
7578 i == Xsand_stonein_4 ? j + 25 :
7579 i == Xsand_stoneout_1 && j == 0 ? 0 :
7580 i == Xsand_stoneout_1 && j == 1 ? 0 :
7581 i == Xsand_stoneout_1 && j == 2 ? 1 :
7582 i == Xsand_stoneout_1 && j == 3 ? 2 :
7583 i == Xsand_stoneout_1 && j == 4 ? 2 :
7584 i == Xsand_stoneout_1 && j == 5 ? 3 :
7585 i == Xsand_stoneout_1 && j == 6 ? 4 :
7586 i == Xsand_stoneout_1 && j == 7 ? 4 :
7587 i == Xsand_stoneout_2 && j == 0 ? 5 :
7588 i == Xsand_stoneout_2 && j == 1 ? 6 :
7589 i == Xsand_stoneout_2 && j == 2 ? 7 :
7590 i == Xsand_stoneout_2 && j == 3 ? 8 :
7591 i == Xsand_stoneout_2 && j == 4 ? 9 :
7592 i == Xsand_stoneout_2 && j == 5 ? 11 :
7593 i == Xsand_stoneout_2 && j == 6 ? 13 :
7594 i == Xsand_stoneout_2 && j == 7 ? 15 :
7595 i == Xboom_bug && j == 1 ? 2 :
7596 i == Xboom_bug && j == 2 ? 2 :
7597 i == Xboom_bug && j == 3 ? 4 :
7598 i == Xboom_bug && j == 4 ? 4 :
7599 i == Xboom_bug && j == 5 ? 2 :
7600 i == Xboom_bug && j == 6 ? 2 :
7601 i == Xboom_bug && j == 7 ? 0 :
7602 i == Xboom_bomb && j == 1 ? 2 :
7603 i == Xboom_bomb && j == 2 ? 2 :
7604 i == Xboom_bomb && j == 3 ? 4 :
7605 i == Xboom_bomb && j == 4 ? 4 :
7606 i == Xboom_bomb && j == 5 ? 2 :
7607 i == Xboom_bomb && j == 6 ? 2 :
7608 i == Xboom_bomb && j == 7 ? 0 :
7609 i == Xboom_android && j == 7 ? 6 :
7610 i == Xboom_1 && j == 1 ? 2 :
7611 i == Xboom_1 && j == 2 ? 2 :
7612 i == Xboom_1 && j == 3 ? 4 :
7613 i == Xboom_1 && j == 4 ? 4 :
7614 i == Xboom_1 && j == 5 ? 6 :
7615 i == Xboom_1 && j == 6 ? 6 :
7616 i == Xboom_1 && j == 7 ? 8 :
7617 i == Xboom_2 && j == 0 ? 8 :
7618 i == Xboom_2 && j == 1 ? 8 :
7619 i == Xboom_2 && j == 2 ? 10 :
7620 i == Xboom_2 && j == 3 ? 10 :
7621 i == Xboom_2 && j == 4 ? 10 :
7622 i == Xboom_2 && j == 5 ? 12 :
7623 i == Xboom_2 && j == 6 ? 12 :
7624 i == Xboom_2 && j == 7 ? 12 :
7625 special_animation && j == 4 ? 3 :
7626 effective_action != action ? 0 :
7630 Bitmap *debug_bitmap = g_em->bitmap;
7631 int debug_src_x = g_em->src_x;
7632 int debug_src_y = g_em->src_y;
7635 int frame = getAnimationFrame(g->anim_frames,
7638 g->anim_start_frame,
7641 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7642 g->double_movement && is_backside);
7644 g_em->bitmap = src_bitmap;
7645 g_em->src_x = src_x;
7646 g_em->src_y = src_y;
7647 g_em->src_offset_x = 0;
7648 g_em->src_offset_y = 0;
7649 g_em->dst_offset_x = 0;
7650 g_em->dst_offset_y = 0;
7651 g_em->width = TILEX;
7652 g_em->height = TILEY;
7654 g_em->preserve_background = FALSE;
7656 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7659 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7660 effective_action == ACTION_MOVING ||
7661 effective_action == ACTION_PUSHING ||
7662 effective_action == ACTION_EATING)) ||
7663 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7664 effective_action == ACTION_EMPTYING)))
7667 (effective_action == ACTION_FALLING ||
7668 effective_action == ACTION_FILLING ||
7669 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7670 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7671 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7672 int num_steps = (i == Ydrip_s1 ? 16 :
7673 i == Ydrip_s1B ? 16 :
7674 i == Ydrip_s2 ? 16 :
7675 i == Ydrip_s2B ? 16 :
7676 i == Xsand_stonein_1 ? 32 :
7677 i == Xsand_stonein_2 ? 32 :
7678 i == Xsand_stonein_3 ? 32 :
7679 i == Xsand_stonein_4 ? 32 :
7680 i == Xsand_stoneout_1 ? 16 :
7681 i == Xsand_stoneout_2 ? 16 : 8);
7682 int cx = ABS(dx) * (TILEX / num_steps);
7683 int cy = ABS(dy) * (TILEY / num_steps);
7684 int step_frame = (i == Ydrip_s2 ? j + 8 :
7685 i == Ydrip_s2B ? j + 8 :
7686 i == Xsand_stonein_2 ? j + 8 :
7687 i == Xsand_stonein_3 ? j + 16 :
7688 i == Xsand_stonein_4 ? j + 24 :
7689 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7690 int step = (is_backside ? step_frame : num_steps - step_frame);
7692 if (is_backside) /* tile where movement starts */
7694 if (dx < 0 || dy < 0)
7696 g_em->src_offset_x = cx * step;
7697 g_em->src_offset_y = cy * step;
7701 g_em->dst_offset_x = cx * step;
7702 g_em->dst_offset_y = cy * step;
7705 else /* tile where movement ends */
7707 if (dx < 0 || dy < 0)
7709 g_em->dst_offset_x = cx * step;
7710 g_em->dst_offset_y = cy * step;
7714 g_em->src_offset_x = cx * step;
7715 g_em->src_offset_y = cy * step;
7719 g_em->width = TILEX - cx * step;
7720 g_em->height = TILEY - cy * step;
7723 /* create unique graphic identifier to decide if tile must be redrawn */
7724 /* bit 31 - 16 (16 bit): EM style graphic
7725 bit 15 - 12 ( 4 bit): EM style frame
7726 bit 11 - 6 ( 6 bit): graphic width
7727 bit 5 - 0 ( 6 bit): graphic height */
7728 g_em->unique_identifier =
7729 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7733 /* skip check for EMC elements not contained in original EMC artwork */
7734 if (element == EL_EMC_FAKE_ACID)
7737 if (g_em->bitmap != debug_bitmap ||
7738 g_em->src_x != debug_src_x ||
7739 g_em->src_y != debug_src_y ||
7740 g_em->src_offset_x != 0 ||
7741 g_em->src_offset_y != 0 ||
7742 g_em->dst_offset_x != 0 ||
7743 g_em->dst_offset_y != 0 ||
7744 g_em->width != TILEX ||
7745 g_em->height != TILEY)
7747 static int last_i = -1;
7755 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7756 i, element, element_info[element].token_name,
7757 element_action_info[effective_action].suffix, direction);
7759 if (element != effective_element)
7760 printf(" [%d ('%s')]",
7762 element_info[effective_element].token_name);
7766 if (g_em->bitmap != debug_bitmap)
7767 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7768 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7770 if (g_em->src_x != debug_src_x ||
7771 g_em->src_y != debug_src_y)
7772 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7773 j, (is_backside ? 'B' : 'F'),
7774 g_em->src_x, g_em->src_y,
7775 g_em->src_x / 32, g_em->src_y / 32,
7776 debug_src_x, debug_src_y,
7777 debug_src_x / 32, debug_src_y / 32);
7779 if (g_em->src_offset_x != 0 ||
7780 g_em->src_offset_y != 0 ||
7781 g_em->dst_offset_x != 0 ||
7782 g_em->dst_offset_y != 0)
7783 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7785 g_em->src_offset_x, g_em->src_offset_y,
7786 g_em->dst_offset_x, g_em->dst_offset_y);
7788 if (g_em->width != TILEX ||
7789 g_em->height != TILEY)
7790 printf(" %d (%d): size %d,%d should be %d,%d\n",
7792 g_em->width, g_em->height, TILEX, TILEY);
7794 num_em_gfx_errors++;
7801 for (i = 0; i < TILE_MAX; i++)
7803 for (j = 0; j < 8; j++)
7805 int element = object_mapping[i].element_rnd;
7806 int action = object_mapping[i].action;
7807 int direction = object_mapping[i].direction;
7808 boolean is_backside = object_mapping[i].is_backside;
7809 int graphic_action = el_act_dir2img(element, action, direction);
7810 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7812 if ((action == ACTION_SMASHED_BY_ROCK ||
7813 action == ACTION_SMASHED_BY_SPRING ||
7814 action == ACTION_EATING) &&
7815 graphic_action == graphic_default)
7817 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7818 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7819 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7820 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7823 /* no separate animation for "smashed by rock" -- use rock instead */
7824 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7825 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7827 g_em->bitmap = g_xx->bitmap;
7828 g_em->src_x = g_xx->src_x;
7829 g_em->src_y = g_xx->src_y;
7830 g_em->src_offset_x = g_xx->src_offset_x;
7831 g_em->src_offset_y = g_xx->src_offset_y;
7832 g_em->dst_offset_x = g_xx->dst_offset_x;
7833 g_em->dst_offset_y = g_xx->dst_offset_y;
7834 g_em->width = g_xx->width;
7835 g_em->height = g_xx->height;
7836 g_em->unique_identifier = g_xx->unique_identifier;
7839 g_em->preserve_background = TRUE;
7844 for (p = 0; p < MAX_PLAYERS; p++)
7846 for (i = 0; i < SPR_MAX; i++)
7848 int element = player_mapping[p][i].element_rnd;
7849 int action = player_mapping[p][i].action;
7850 int direction = player_mapping[p][i].direction;
7852 for (j = 0; j < 8; j++)
7854 int effective_element = element;
7855 int effective_action = action;
7856 int graphic = (direction == MV_NONE ?
7857 el_act2img(effective_element, effective_action) :
7858 el_act_dir2img(effective_element, effective_action,
7860 struct GraphicInfo *g = &graphic_info[graphic];
7861 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7867 Bitmap *debug_bitmap = g_em->bitmap;
7868 int debug_src_x = g_em->src_x;
7869 int debug_src_y = g_em->src_y;
7872 int frame = getAnimationFrame(g->anim_frames,
7875 g->anim_start_frame,
7878 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7880 g_em->bitmap = src_bitmap;
7881 g_em->src_x = src_x;
7882 g_em->src_y = src_y;
7883 g_em->src_offset_x = 0;
7884 g_em->src_offset_y = 0;
7885 g_em->dst_offset_x = 0;
7886 g_em->dst_offset_y = 0;
7887 g_em->width = TILEX;
7888 g_em->height = TILEY;
7892 /* skip check for EMC elements not contained in original EMC artwork */
7893 if (element == EL_PLAYER_3 ||
7894 element == EL_PLAYER_4)
7897 if (g_em->bitmap != debug_bitmap ||
7898 g_em->src_x != debug_src_x ||
7899 g_em->src_y != debug_src_y)
7901 static int last_i = -1;
7909 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7910 p, i, element, element_info[element].token_name,
7911 element_action_info[effective_action].suffix, direction);
7913 if (element != effective_element)
7914 printf(" [%d ('%s')]",
7916 element_info[effective_element].token_name);
7920 if (g_em->bitmap != debug_bitmap)
7921 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7922 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7924 if (g_em->src_x != debug_src_x ||
7925 g_em->src_y != debug_src_y)
7926 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7928 g_em->src_x, g_em->src_y,
7929 g_em->src_x / 32, g_em->src_y / 32,
7930 debug_src_x, debug_src_y,
7931 debug_src_x / 32, debug_src_y / 32);
7933 num_em_gfx_errors++;
7943 printf("::: [%d errors found]\n", num_em_gfx_errors);
7949 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7950 boolean any_player_moving,
7951 boolean any_player_snapping,
7952 boolean any_player_dropping)
7954 static boolean player_was_waiting = TRUE;
7956 if (frame == 0 && !any_player_dropping)
7958 if (!player_was_waiting)
7960 if (!SaveEngineSnapshotToList())
7963 player_was_waiting = TRUE;
7966 else if (any_player_moving || any_player_snapping || any_player_dropping)
7968 player_was_waiting = FALSE;
7972 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7973 boolean murphy_is_dropping)
7975 static boolean player_was_waiting = TRUE;
7977 if (murphy_is_waiting)
7979 if (!player_was_waiting)
7981 if (!SaveEngineSnapshotToList())
7984 player_was_waiting = TRUE;
7989 player_was_waiting = FALSE;
7993 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7994 boolean any_player_moving,
7995 boolean any_player_snapping,
7996 boolean any_player_dropping)
7998 if (tape.single_step && tape.recording && !tape.pausing)
7999 if (frame == 0 && !any_player_dropping)
8000 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8002 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8003 any_player_snapping, any_player_dropping);
8006 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8007 boolean murphy_is_dropping)
8009 if (tape.single_step && tape.recording && !tape.pausing)
8010 if (murphy_is_waiting)
8011 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8013 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8016 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8017 int graphic, int sync_frame, int x, int y)
8019 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8021 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8024 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8026 return (IS_NEXT_FRAME(sync_frame, graphic));
8029 int getGraphicInfo_Delay(int graphic)
8031 return graphic_info[graphic].anim_delay;
8034 void PlayMenuSoundExt(int sound)
8036 if (sound == SND_UNDEFINED)
8039 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8040 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8043 if (IS_LOOP_SOUND(sound))
8044 PlaySoundLoop(sound);
8049 void PlayMenuSound()
8051 PlayMenuSoundExt(menu.sound[game_status]);
8054 void PlayMenuSoundStereo(int sound, int stereo_position)
8056 if (sound == SND_UNDEFINED)
8059 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8060 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8063 if (IS_LOOP_SOUND(sound))
8064 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8066 PlaySoundStereo(sound, stereo_position);
8069 void PlayMenuSoundIfLoopExt(int sound)
8071 if (sound == SND_UNDEFINED)
8074 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8075 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8078 if (IS_LOOP_SOUND(sound))
8079 PlaySoundLoop(sound);
8082 void PlayMenuSoundIfLoop()
8084 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8087 void PlayMenuMusicExt(int music)
8089 if (music == MUS_UNDEFINED)
8092 if (!setup.sound_music)
8098 void PlayMenuMusic()
8100 PlayMenuMusicExt(menu.music[game_status]);
8103 void PlaySoundActivating()
8106 PlaySound(SND_MENU_ITEM_ACTIVATING);
8110 void PlaySoundSelecting()
8113 PlaySound(SND_MENU_ITEM_SELECTING);
8117 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8119 boolean change_fullscreen = (setup.fullscreen !=
8120 video.fullscreen_enabled);
8121 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8122 !strEqual(setup.fullscreen_mode,
8123 video.fullscreen_mode_current));
8124 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8125 setup.window_scaling_percent !=
8126 video.window_scaling_percent);
8128 if (change_window_scaling_percent && video.fullscreen_enabled)
8131 if (!change_window_scaling_percent && !video.fullscreen_available)
8134 #if defined(TARGET_SDL2)
8135 if (change_window_scaling_percent)
8137 SDLSetWindowScaling(setup.window_scaling_percent);
8141 else if (change_fullscreen)
8143 SDLSetWindowFullscreen(setup.fullscreen);
8145 /* set setup value according to successfully changed fullscreen mode */
8146 setup.fullscreen = video.fullscreen_enabled;
8152 if (change_fullscreen ||
8153 change_fullscreen_mode ||
8154 change_window_scaling_percent)
8156 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8158 /* save backbuffer content which gets lost when toggling fullscreen mode */
8159 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8161 if (change_fullscreen_mode)
8163 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8164 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8167 if (change_window_scaling_percent)
8169 /* keep window mode, but change window scaling */
8170 video.fullscreen_enabled = TRUE; /* force new window scaling */
8173 /* toggle fullscreen */
8174 ChangeVideoModeIfNeeded(setup.fullscreen);
8176 /* set setup value according to successfully changed fullscreen mode */
8177 setup.fullscreen = video.fullscreen_enabled;
8179 /* restore backbuffer content from temporary backbuffer backup bitmap */
8180 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8182 FreeBitmap(tmp_backbuffer);
8184 /* update visible window/screen */
8185 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8189 void JoinRectangles(int *x, int *y, int *width, int *height,
8190 int x2, int y2, int width2, int height2)
8192 // do not join with "off-screen" rectangle
8193 if (x2 == -1 || y2 == -1)
8198 *width = MAX(*width, width2);
8199 *height = MAX(*height, height2);
8202 void SetGameStatus(int game_status_new)
8204 game_status = game_status_new;
8206 global.anim_status_next = game_status;
8209 void ChangeViewportPropertiesIfNeeded()
8211 int gfx_game_mode = game_status;
8212 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8214 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8215 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8216 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8217 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8218 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8219 int new_win_xsize = vp_window->width;
8220 int new_win_ysize = vp_window->height;
8221 int border_size = vp_playfield->border_size;
8222 int new_sx = vp_playfield->x + border_size;
8223 int new_sy = vp_playfield->y + border_size;
8224 int new_sxsize = vp_playfield->width - 2 * border_size;
8225 int new_sysize = vp_playfield->height - 2 * border_size;
8226 int new_real_sx = vp_playfield->x;
8227 int new_real_sy = vp_playfield->y;
8228 int new_full_sxsize = vp_playfield->width;
8229 int new_full_sysize = vp_playfield->height;
8230 int new_dx = vp_door_1->x;
8231 int new_dy = vp_door_1->y;
8232 int new_dxsize = vp_door_1->width;
8233 int new_dysize = vp_door_1->height;
8234 int new_vx = vp_door_2->x;
8235 int new_vy = vp_door_2->y;
8236 int new_vxsize = vp_door_2->width;
8237 int new_vysize = vp_door_2->height;
8238 int new_ex = vp_door_3->x;
8239 int new_ey = vp_door_3->y;
8240 int new_exsize = vp_door_3->width;
8241 int new_eysize = vp_door_3->height;
8242 int new_tilesize_var =
8243 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8245 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8246 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8247 int new_scr_fieldx = new_sxsize / tilesize;
8248 int new_scr_fieldy = new_sysize / tilesize;
8249 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8250 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8251 boolean init_gfx_buffers = FALSE;
8252 boolean init_video_buffer = FALSE;
8253 boolean init_gadgets_and_toons = FALSE;
8254 boolean init_em_graphics = FALSE;
8256 if (new_win_xsize != WIN_XSIZE ||
8257 new_win_ysize != WIN_YSIZE)
8259 WIN_XSIZE = new_win_xsize;
8260 WIN_YSIZE = new_win_ysize;
8262 init_video_buffer = TRUE;
8263 init_gfx_buffers = TRUE;
8264 init_gadgets_and_toons = TRUE;
8266 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8269 if (new_scr_fieldx != SCR_FIELDX ||
8270 new_scr_fieldy != SCR_FIELDY)
8272 /* this always toggles between MAIN and GAME when using small tile size */
8274 SCR_FIELDX = new_scr_fieldx;
8275 SCR_FIELDY = new_scr_fieldy;
8277 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8288 new_sxsize != SXSIZE ||
8289 new_sysize != SYSIZE ||
8290 new_dxsize != DXSIZE ||
8291 new_dysize != DYSIZE ||
8292 new_vxsize != VXSIZE ||
8293 new_vysize != VYSIZE ||
8294 new_exsize != EXSIZE ||
8295 new_eysize != EYSIZE ||
8296 new_real_sx != REAL_SX ||
8297 new_real_sy != REAL_SY ||
8298 new_full_sxsize != FULL_SXSIZE ||
8299 new_full_sysize != FULL_SYSIZE ||
8300 new_tilesize_var != TILESIZE_VAR
8303 // ------------------------------------------------------------------------
8304 // determine next fading area for changed viewport definitions
8305 // ------------------------------------------------------------------------
8307 // start with current playfield area (default fading area)
8310 FADE_SXSIZE = FULL_SXSIZE;
8311 FADE_SYSIZE = FULL_SYSIZE;
8313 // add new playfield area if position or size has changed
8314 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8315 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8317 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8318 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8321 // add current and new door 1 area if position or size has changed
8322 if (new_dx != DX || new_dy != DY ||
8323 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8325 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8326 DX, DY, DXSIZE, DYSIZE);
8327 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8328 new_dx, new_dy, new_dxsize, new_dysize);
8331 // add current and new door 2 area if position or size has changed
8332 if (new_dx != VX || new_dy != VY ||
8333 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8335 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8336 VX, VY, VXSIZE, VYSIZE);
8337 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8338 new_vx, new_vy, new_vxsize, new_vysize);
8341 // ------------------------------------------------------------------------
8342 // handle changed tile size
8343 // ------------------------------------------------------------------------
8345 if (new_tilesize_var != TILESIZE_VAR)
8347 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8349 // changing tile size invalidates scroll values of engine snapshots
8350 FreeEngineSnapshotSingle();
8352 // changing tile size requires update of graphic mapping for EM engine
8353 init_em_graphics = TRUE;
8364 SXSIZE = new_sxsize;
8365 SYSIZE = new_sysize;
8366 DXSIZE = new_dxsize;
8367 DYSIZE = new_dysize;
8368 VXSIZE = new_vxsize;
8369 VYSIZE = new_vysize;
8370 EXSIZE = new_exsize;
8371 EYSIZE = new_eysize;
8372 REAL_SX = new_real_sx;
8373 REAL_SY = new_real_sy;
8374 FULL_SXSIZE = new_full_sxsize;
8375 FULL_SYSIZE = new_full_sysize;
8376 TILESIZE_VAR = new_tilesize_var;
8378 init_gfx_buffers = TRUE;
8379 init_gadgets_and_toons = TRUE;
8381 // printf("::: viewports: init_gfx_buffers\n");
8382 // printf("::: viewports: init_gadgets_and_toons\n");
8385 if (init_gfx_buffers)
8387 // printf("::: init_gfx_buffers\n");
8389 SCR_FIELDX = new_scr_fieldx_buffers;
8390 SCR_FIELDY = new_scr_fieldy_buffers;
8394 SCR_FIELDX = new_scr_fieldx;
8395 SCR_FIELDY = new_scr_fieldy;
8397 SetDrawDeactivationMask(REDRAW_NONE);
8398 SetDrawBackgroundMask(REDRAW_FIELD);
8401 if (init_video_buffer)
8403 // printf("::: init_video_buffer\n");
8405 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8406 InitImageTextures();
8409 if (init_gadgets_and_toons)
8411 // printf("::: init_gadgets_and_toons\n");
8415 InitGlobalAnimations();
8418 if (init_em_graphics)
8420 InitGraphicInfo_EM();