1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static char *print_if_not_empty(int element)
178 static char *s = NULL;
179 char *token_name = element_info[element].token_name;
184 s = checked_malloc(strlen(token_name) + 10 + 1);
186 if (element != EL_EMPTY)
187 sprintf(s, "%d\t['%s']", element, token_name);
189 sprintf(s, "%d", element);
194 void DumpTile(int x, int y)
199 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
205 printf_line("-", 79);
206 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
207 printf_line("-", 79);
209 if (!IN_LEV_FIELD(x, y))
211 printf("(not in level field)\n");
217 printf(" Feld: %d\t['%s']\n", Feld[x][y],
218 element_info[Feld[x][y]].token_name);
219 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
220 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
221 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
222 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
223 printf(" MovPos: %d\n", MovPos[x][y]);
224 printf(" MovDir: %d\n", MovDir[x][y]);
225 printf(" MovDelay: %d\n", MovDelay[x][y]);
226 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
227 printf(" CustomValue: %d\n", CustomValue[x][y]);
228 printf(" GfxElement: %d\n", GfxElement[x][y]);
229 printf(" GfxAction: %d\n", GfxAction[x][y]);
230 printf(" GfxFrame: %d\n", GfxFrame[x][y]);
234 void SetDrawtoField(int mode)
236 if (mode == DRAW_FIELDBUFFER)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
245 drawto_field = fieldbuffer;
247 else /* DRAW_BACKBUFFER */
253 BX2 = SCR_FIELDX - 1;
254 BY2 = SCR_FIELDY - 1;
256 drawto_field = backbuffer;
260 static void RedrawPlayfield_RND()
262 if (game.envelope_active)
265 DrawLevel(REDRAW_ALL);
269 void RedrawPlayfield()
271 if (game_status != GAME_MODE_PLAYING)
274 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
275 RedrawPlayfield_EM(TRUE);
276 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
277 RedrawPlayfield_SP(TRUE);
278 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
279 RedrawPlayfield_RND();
281 BlitScreenToBitmap(backbuffer);
283 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
287 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
289 Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
291 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
294 void DrawMaskedBorder_FIELD()
296 if (global.border_status >= GAME_MODE_TITLE &&
297 global.border_status <= GAME_MODE_PLAYING &&
298 border.draw_masked[global.border_status])
299 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
302 void DrawMaskedBorder_DOOR_1()
304 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
305 (global.border_status != GAME_MODE_EDITOR ||
306 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
307 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
310 void DrawMaskedBorder_DOOR_2()
312 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
313 global.border_status != GAME_MODE_EDITOR)
314 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
317 void DrawMaskedBorder_DOOR_3()
319 /* currently not available */
322 void DrawMaskedBorder_ALL()
324 DrawMaskedBorder_FIELD();
325 DrawMaskedBorder_DOOR_1();
326 DrawMaskedBorder_DOOR_2();
327 DrawMaskedBorder_DOOR_3();
330 void DrawMaskedBorder(int redraw_mask)
332 /* never draw masked screen borders on borderless screens */
333 if (effectiveGameStatus() == GAME_MODE_LOADING ||
334 effectiveGameStatus() == GAME_MODE_TITLE)
337 if (redraw_mask & REDRAW_ALL)
338 DrawMaskedBorder_ALL();
341 if (redraw_mask & REDRAW_FIELD)
342 DrawMaskedBorder_FIELD();
343 if (redraw_mask & REDRAW_DOOR_1)
344 DrawMaskedBorder_DOOR_1();
345 if (redraw_mask & REDRAW_DOOR_2)
346 DrawMaskedBorder_DOOR_2();
347 if (redraw_mask & REDRAW_DOOR_3)
348 DrawMaskedBorder_DOOR_3();
352 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
354 int fx = FX, fy = FY;
355 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
356 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
358 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
359 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
360 int dx_var = dx * TILESIZE_VAR / TILESIZE;
361 int dy_var = dy * TILESIZE_VAR / TILESIZE;
364 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
365 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
367 if (EVEN(SCR_FIELDX))
369 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
370 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
372 fx += (dx_var > 0 ? TILEX_VAR : 0);
379 if (EVEN(SCR_FIELDY))
381 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
382 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
384 fy += (dy_var > 0 ? TILEY_VAR : 0);
391 if (full_lev_fieldx <= SCR_FIELDX)
393 if (EVEN(SCR_FIELDX))
394 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
396 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
399 if (full_lev_fieldy <= SCR_FIELDY)
401 if (EVEN(SCR_FIELDY))
402 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
404 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
407 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
410 void BlitScreenToBitmap(Bitmap *target_bitmap)
412 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
413 BlitScreenToBitmap_EM(target_bitmap);
414 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
415 BlitScreenToBitmap_SP(target_bitmap);
416 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
417 BlitScreenToBitmap_RND(target_bitmap);
419 redraw_mask |= REDRAW_FIELD;
422 void DrawFramesPerSecond()
425 int font_nr = FONT_TEXT_2;
426 int font_width = getFontWidth(font_nr);
428 sprintf(text, "%04.1f fps", global.frames_per_second);
430 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
431 font_nr, BLIT_OPAQUE);
436 if (redraw_mask == REDRAW_NONE)
439 // draw masked border to all viewports, if defined
440 DrawMaskedBorder(redraw_mask);
442 // draw frames per second (only if debug mode is enabled)
443 if (redraw_mask & REDRAW_FPS)
444 DrawFramesPerSecond();
446 // redraw complete window if both playfield and (some) doors need redraw
447 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
448 redraw_mask = REDRAW_ALL;
450 /* although redrawing the whole window would be fine for normal gameplay,
451 being able to only redraw the playfield is required for deactivating
452 certain drawing areas (mainly playfield) to work, which is needed for
453 warp-forward to be fast enough (by skipping redraw of most frames) */
455 if (redraw_mask & REDRAW_ALL)
457 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
459 else if (redraw_mask & REDRAW_FIELD)
461 BlitBitmap(backbuffer, window,
462 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
464 else if (redraw_mask & REDRAW_DOORS)
466 if (redraw_mask & REDRAW_DOOR_1)
467 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
469 if (redraw_mask & REDRAW_DOOR_2)
470 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
472 if (redraw_mask & REDRAW_DOOR_3)
473 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
476 redraw_mask = REDRAW_NONE;
479 static void FadeCrossSaveBackbuffer()
481 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
484 static void FadeCrossRestoreBackbuffer()
486 int redraw_mask_last = redraw_mask;
488 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
490 // do not change redraw mask when restoring backbuffer after cross-fading
491 redraw_mask = redraw_mask_last;
494 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
496 static int fade_type_skip = FADE_TYPE_NONE;
497 void (*draw_border_function)(void) = NULL;
498 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
499 int x, y, width, height;
500 int fade_delay, post_delay;
502 if (fade_type == FADE_TYPE_FADE_OUT)
504 if (fade_type_skip != FADE_TYPE_NONE)
506 /* skip all fade operations until specified fade operation */
507 if (fade_type & fade_type_skip)
508 fade_type_skip = FADE_TYPE_NONE;
514 FadeCrossSaveBackbuffer();
517 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
520 FadeCrossSaveBackbuffer();
527 redraw_mask |= fade_mask;
529 if (fade_type == FADE_TYPE_SKIP)
531 fade_type_skip = fade_mode;
536 fade_delay = fading.fade_delay;
537 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
539 if (fade_type_skip != FADE_TYPE_NONE)
541 /* skip all fade operations until specified fade operation */
542 if (fade_type & fade_type_skip)
543 fade_type_skip = FADE_TYPE_NONE;
548 if (global.autoplay_leveldir)
553 if (fade_mask == REDRAW_FIELD)
558 height = FADE_SYSIZE;
560 if (border.draw_masked_when_fading)
561 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
563 DrawMaskedBorder_FIELD(); /* draw once */
565 else /* REDRAW_ALL */
573 if (!setup.fade_screens ||
575 fading.fade_mode == FADE_MODE_NONE)
577 if (fade_mode == FADE_MODE_FADE_OUT)
580 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
582 redraw_mask &= ~fade_mask;
587 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
588 draw_border_function);
590 if (fade_type == FADE_TYPE_FADE_OUT)
591 FadeCrossRestoreBackbuffer();
593 redraw_mask &= ~fade_mask;
596 void FadeIn(int fade_mask)
598 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
599 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
601 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
605 FADE_SXSIZE = FULL_SXSIZE;
606 FADE_SYSIZE = FULL_SYSIZE;
609 void FadeOut(int fade_mask)
611 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
612 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
614 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
616 global.border_status = game_status;
619 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
621 static struct TitleFadingInfo fading_leave_stored;
624 fading_leave_stored = fading_leave;
626 fading = fading_leave_stored;
629 void FadeSetEnterMenu()
631 fading = menu.enter_menu;
633 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
636 void FadeSetLeaveMenu()
638 fading = menu.leave_menu;
640 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
643 void FadeSetEnterScreen()
645 fading = menu.enter_screen[game_status];
647 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
650 void FadeSetNextScreen()
652 fading = menu.next_screen;
654 // (do not overwrite fade mode set by FadeSetEnterScreen)
655 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
658 void FadeSetLeaveScreen()
660 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
663 void FadeSetFromType(int type)
665 if (type & TYPE_ENTER_SCREEN)
666 FadeSetEnterScreen();
667 else if (type & TYPE_ENTER)
669 else if (type & TYPE_LEAVE)
673 void FadeSetDisabled()
675 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
677 fading = fading_none;
680 void FadeSkipNextFadeIn()
682 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
685 void FadeSkipNextFadeOut()
687 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
690 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
692 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
694 return (graphic == IMG_UNDEFINED ? NULL :
695 graphic_info[graphic].bitmap != NULL || redefined ?
696 graphic_info[graphic].bitmap :
697 graphic_info[default_graphic].bitmap);
700 Bitmap *getBackgroundBitmap(int graphic)
702 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
705 Bitmap *getGlobalBorderBitmap(int graphic)
707 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
710 Bitmap *getGlobalBorderBitmapFromGameStatus()
712 int graphic = (game_status == GAME_MODE_MAIN ? IMG_GLOBAL_BORDER_MAIN :
713 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
714 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
715 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
718 return getGlobalBorderBitmap(graphic);
721 void SetWindowBackgroundImageIfDefined(int graphic)
723 if (graphic_info[graphic].bitmap)
724 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
727 void SetMainBackgroundImageIfDefined(int graphic)
729 if (graphic_info[graphic].bitmap)
730 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
733 void SetDoorBackgroundImageIfDefined(int graphic)
735 if (graphic_info[graphic].bitmap)
736 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
739 void SetWindowBackgroundImage(int graphic)
741 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
744 void SetMainBackgroundImage(int graphic)
746 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
749 void SetDoorBackgroundImage(int graphic)
751 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
754 void SetPanelBackground()
756 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
758 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
759 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
761 SetDoorBackgroundBitmap(bitmap_db_panel);
764 void DrawBackground(int x, int y, int width, int height)
766 /* "drawto" might still point to playfield buffer here (hall of fame) */
767 ClearRectangleOnBackground(backbuffer, x, y, width, height);
769 if (IN_GFX_FIELD_FULL(x, y))
770 redraw_mask |= REDRAW_FIELD;
771 else if (IN_GFX_DOOR_1(x, y))
772 redraw_mask |= REDRAW_DOOR_1;
773 else if (IN_GFX_DOOR_2(x, y))
774 redraw_mask |= REDRAW_DOOR_2;
775 else if (IN_GFX_DOOR_3(x, y))
776 redraw_mask |= REDRAW_DOOR_3;
779 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
781 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
783 if (font->bitmap == NULL)
786 DrawBackground(x, y, width, height);
789 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
791 struct GraphicInfo *g = &graphic_info[graphic];
793 if (g->bitmap == NULL)
796 DrawBackground(x, y, width, height);
799 static int game_status_last = -1;
800 static Bitmap *global_border_bitmap_last = NULL;
801 static Bitmap *global_border_bitmap = NULL;
802 static int real_sx_last = -1, real_sy_last = -1;
803 static int full_sxsize_last = -1, full_sysize_last = -1;
804 static int dx_last = -1, dy_last = -1;
805 static int dxsize_last = -1, dysize_last = -1;
806 static int vx_last = -1, vy_last = -1;
807 static int vxsize_last = -1, vysize_last = -1;
809 boolean CheckIfGlobalBorderHasChanged()
811 // if game status has not changed, global border has not changed either
812 if (game_status == game_status_last)
815 // determine and store new global border bitmap for current game status
816 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
818 return (global_border_bitmap_last != global_border_bitmap);
821 boolean CheckIfGlobalBorderRedrawIsNeeded()
823 // if game status has not changed, nothing has to be redrawn
824 if (game_status == game_status_last)
827 // redraw if global screen border has changed
828 if (CheckIfGlobalBorderHasChanged())
831 // redraw if position or size of playfield area has changed
832 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
833 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
836 // redraw if position or size of door area has changed
837 if (dx_last != DX || dy_last != DY ||
838 dxsize_last != DXSIZE || dysize_last != DYSIZE)
841 // redraw if position or size of tape area has changed
842 if (vx_last != VX || vy_last != VY ||
843 vxsize_last != VXSIZE || vysize_last != VYSIZE)
849 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
852 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
854 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
857 void RedrawGlobalBorder()
859 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
861 RedrawGlobalBorderFromBitmap(bitmap);
863 redraw_mask = REDRAW_ALL;
866 static void RedrawGlobalBorderIfNeeded()
868 if (game_status == game_status_last)
871 // copy current draw buffer to later copy back areas that have not changed
872 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
874 if (CheckIfGlobalBorderRedrawIsNeeded())
876 // redraw global screen border (or clear, if defined to be empty)
877 RedrawGlobalBorderFromBitmap(global_border_bitmap);
879 // copy previous playfield and door areas, if they are defined on both
880 // previous and current screen and if they still have the same size
882 if (real_sx_last != -1 && real_sy_last != -1 &&
883 REAL_SX != -1 && REAL_SY != -1 &&
884 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
885 BlitBitmap(bitmap_db_store, backbuffer,
886 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
889 if (dx_last != -1 && dy_last != -1 &&
890 DX != -1 && DY != -1 &&
891 dxsize_last == DXSIZE && dysize_last == DYSIZE)
892 BlitBitmap(bitmap_db_store, backbuffer,
893 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
895 if (vx_last != -1 && vy_last != -1 &&
896 VX != -1 && VY != -1 &&
897 vxsize_last == VXSIZE && vysize_last == VYSIZE)
898 BlitBitmap(bitmap_db_store, backbuffer,
899 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
901 redraw_mask = REDRAW_ALL;
904 game_status_last = game_status;
906 global_border_bitmap_last = global_border_bitmap;
908 real_sx_last = REAL_SX;
909 real_sy_last = REAL_SY;
910 full_sxsize_last = FULL_SXSIZE;
911 full_sysize_last = FULL_SYSIZE;
914 dxsize_last = DXSIZE;
915 dysize_last = DYSIZE;
918 vxsize_last = VXSIZE;
919 vysize_last = VYSIZE;
924 RedrawGlobalBorderIfNeeded();
926 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
927 /* (when entering hall of fame after playing) */
928 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
930 /* !!! maybe this should be done before clearing the background !!! */
931 if (game_status == GAME_MODE_PLAYING)
933 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
934 SetDrawtoField(DRAW_FIELDBUFFER);
938 SetDrawtoField(DRAW_BACKBUFFER);
942 void MarkTileDirty(int x, int y)
944 redraw_mask |= REDRAW_FIELD;
947 void SetBorderElement()
951 BorderElement = EL_EMPTY;
953 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
955 for (x = 0; x < lev_fieldx; x++)
957 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
958 BorderElement = EL_STEELWALL;
960 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
966 void FloodFillLevel(int from_x, int from_y, int fill_element,
967 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
968 int max_fieldx, int max_fieldy)
972 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
973 static int safety = 0;
975 /* check if starting field still has the desired content */
976 if (field[from_x][from_y] == fill_element)
981 if (safety > max_fieldx * max_fieldy)
982 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
984 old_element = field[from_x][from_y];
985 field[from_x][from_y] = fill_element;
987 for (i = 0; i < 4; i++)
989 x = from_x + check[i][0];
990 y = from_y + check[i][1];
992 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
993 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
999 void SetRandomAnimationValue(int x, int y)
1001 gfx.anim_random_frame = GfxRandom[x][y];
1004 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
1006 /* animation synchronized with global frame counter, not move position */
1007 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1008 sync_frame = FrameCounter;
1010 return getAnimationFrame(graphic_info[graphic].anim_frames,
1011 graphic_info[graphic].anim_delay,
1012 graphic_info[graphic].anim_mode,
1013 graphic_info[graphic].anim_start_frame,
1017 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1018 Bitmap **bitmap, int *x, int *y,
1019 boolean get_backside)
1021 struct GraphicInfo *g = &graphic_info[graphic];
1022 Bitmap *src_bitmap = g->bitmap;
1023 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1024 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1025 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1027 // if no in-game graphics defined, always use standard graphic size
1028 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1029 tilesize = TILESIZE;
1031 if (tilesize == gfx.standard_tile_size)
1032 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1033 else if (tilesize == game.tile_size)
1034 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1036 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1038 if (g->offset_y == 0) /* frames are ordered horizontally */
1040 int max_width = g->anim_frames_per_line * g->width;
1041 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1043 src_x = pos % max_width;
1044 src_y = src_y % g->height + pos / max_width * g->height;
1046 else if (g->offset_x == 0) /* frames are ordered vertically */
1048 int max_height = g->anim_frames_per_line * g->height;
1049 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1051 src_x = src_x % g->width + pos / max_height * g->width;
1052 src_y = pos % max_height;
1054 else /* frames are ordered diagonally */
1056 src_x = src_x + frame * g->offset_x;
1057 src_y = src_y + frame * g->offset_y;
1060 *bitmap = src_bitmap;
1061 *x = src_x * tilesize / TILESIZE;
1062 *y = src_y * tilesize / TILESIZE;
1065 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1066 int *x, int *y, boolean get_backside)
1068 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1072 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1073 Bitmap **bitmap, int *x, int *y)
1075 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1078 void getFixedGraphicSource(int graphic, int frame,
1079 Bitmap **bitmap, int *x, int *y)
1081 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1084 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1086 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1089 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1090 int *x, int *y, boolean get_backside)
1092 struct GraphicInfo *g = &graphic_info[graphic];
1093 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1094 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1096 if (TILESIZE_VAR != TILESIZE)
1097 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1100 *bitmap = g->bitmap;
1102 if (g->offset_y == 0) /* frames are ordered horizontally */
1104 int max_width = g->anim_frames_per_line * g->width;
1105 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1107 *x = pos % max_width;
1108 *y = src_y % g->height + pos / max_width * g->height;
1110 else if (g->offset_x == 0) /* frames are ordered vertically */
1112 int max_height = g->anim_frames_per_line * g->height;
1113 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1115 *x = src_x % g->width + pos / max_height * g->width;
1116 *y = pos % max_height;
1118 else /* frames are ordered diagonally */
1120 *x = src_x + frame * g->offset_x;
1121 *y = src_y + frame * g->offset_y;
1125 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1127 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1130 void DrawGraphic(int x, int y, int graphic, int frame)
1133 if (!IN_SCR_FIELD(x, y))
1135 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1136 printf("DrawGraphic(): This should never happen!\n");
1141 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1144 MarkTileDirty(x, y);
1147 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1150 if (!IN_SCR_FIELD(x, y))
1152 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1153 printf("DrawGraphic(): This should never happen!\n");
1158 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1160 MarkTileDirty(x, y);
1163 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1169 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1171 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1174 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1180 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1181 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1184 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1187 if (!IN_SCR_FIELD(x, y))
1189 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1190 printf("DrawGraphicThruMask(): This should never happen!\n");
1195 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1198 MarkTileDirty(x, y);
1201 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1204 if (!IN_SCR_FIELD(x, y))
1206 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1207 printf("DrawGraphicThruMask(): This should never happen!\n");
1212 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1214 MarkTileDirty(x, y);
1217 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1223 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1225 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1229 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1230 int graphic, int frame)
1232 struct GraphicInfo *g = &graphic_info[graphic];
1236 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1238 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1242 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1244 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1246 MarkTileDirty(x / tilesize, y / tilesize);
1249 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1255 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1256 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1259 void DrawMiniGraphic(int x, int y, int graphic)
1261 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1262 MarkTileDirty(x / 2, y / 2);
1265 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1270 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1271 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1274 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1275 int graphic, int frame,
1276 int cut_mode, int mask_mode)
1281 int width = TILEX, height = TILEY;
1284 if (dx || dy) /* shifted graphic */
1286 if (x < BX1) /* object enters playfield from the left */
1293 else if (x > BX2) /* object enters playfield from the right */
1299 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1305 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1307 else if (dx) /* general horizontal movement */
1308 MarkTileDirty(x + SIGN(dx), y);
1310 if (y < BY1) /* object enters playfield from the top */
1312 if (cut_mode==CUT_BELOW) /* object completely above top border */
1320 else if (y > BY2) /* object enters playfield from the bottom */
1326 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1332 else if (dy > 0 && cut_mode == CUT_ABOVE)
1334 if (y == BY2) /* object completely above bottom border */
1340 MarkTileDirty(x, y + 1);
1341 } /* object leaves playfield to the bottom */
1342 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1344 else if (dy) /* general vertical movement */
1345 MarkTileDirty(x, y + SIGN(dy));
1349 if (!IN_SCR_FIELD(x, y))
1351 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1352 printf("DrawGraphicShifted(): This should never happen!\n");
1357 width = width * TILESIZE_VAR / TILESIZE;
1358 height = height * TILESIZE_VAR / TILESIZE;
1359 cx = cx * TILESIZE_VAR / TILESIZE;
1360 cy = cy * TILESIZE_VAR / TILESIZE;
1361 dx = dx * TILESIZE_VAR / TILESIZE;
1362 dy = dy * TILESIZE_VAR / TILESIZE;
1364 if (width > 0 && height > 0)
1366 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1371 dst_x = FX + x * TILEX_VAR + dx;
1372 dst_y = FY + y * TILEY_VAR + dy;
1374 if (mask_mode == USE_MASKING)
1375 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1378 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1381 MarkTileDirty(x, y);
1385 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1386 int graphic, int frame,
1387 int cut_mode, int mask_mode)
1392 int width = TILEX_VAR, height = TILEY_VAR;
1395 int x2 = x + SIGN(dx);
1396 int y2 = y + SIGN(dy);
1398 /* movement with two-tile animations must be sync'ed with movement position,
1399 not with current GfxFrame (which can be higher when using slow movement) */
1400 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1401 int anim_frames = graphic_info[graphic].anim_frames;
1403 /* (we also need anim_delay here for movement animations with less frames) */
1404 int anim_delay = graphic_info[graphic].anim_delay;
1405 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1407 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1408 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1410 /* re-calculate animation frame for two-tile movement animation */
1411 frame = getGraphicAnimationFrame(graphic, sync_frame);
1413 /* check if movement start graphic inside screen area and should be drawn */
1414 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1416 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1418 dst_x = FX + x1 * TILEX_VAR;
1419 dst_y = FY + y1 * TILEY_VAR;
1421 if (mask_mode == USE_MASKING)
1422 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1425 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1428 MarkTileDirty(x1, y1);
1431 /* check if movement end graphic inside screen area and should be drawn */
1432 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1434 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1436 dst_x = FX + x2 * TILEX_VAR;
1437 dst_y = FY + y2 * TILEY_VAR;
1439 if (mask_mode == USE_MASKING)
1440 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1443 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1446 MarkTileDirty(x2, y2);
1450 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1451 int graphic, int frame,
1452 int cut_mode, int mask_mode)
1456 DrawGraphic(x, y, graphic, frame);
1461 if (graphic_info[graphic].double_movement) /* EM style movement images */
1462 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1464 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1467 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1468 int frame, int cut_mode)
1470 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1473 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1474 int cut_mode, int mask_mode)
1476 int lx = LEVELX(x), ly = LEVELY(y);
1480 if (IN_LEV_FIELD(lx, ly))
1482 SetRandomAnimationValue(lx, ly);
1484 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1485 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1487 /* do not use double (EM style) movement graphic when not moving */
1488 if (graphic_info[graphic].double_movement && !dx && !dy)
1490 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1491 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1494 else /* border element */
1496 graphic = el2img(element);
1497 frame = getGraphicAnimationFrame(graphic, -1);
1500 if (element == EL_EXPANDABLE_WALL)
1502 boolean left_stopped = FALSE, right_stopped = FALSE;
1504 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1505 left_stopped = TRUE;
1506 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1507 right_stopped = TRUE;
1509 if (left_stopped && right_stopped)
1511 else if (left_stopped)
1513 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1514 frame = graphic_info[graphic].anim_frames - 1;
1516 else if (right_stopped)
1518 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1519 frame = graphic_info[graphic].anim_frames - 1;
1524 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1525 else if (mask_mode == USE_MASKING)
1526 DrawGraphicThruMask(x, y, graphic, frame);
1528 DrawGraphic(x, y, graphic, frame);
1531 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1532 int cut_mode, int mask_mode)
1534 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1535 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1536 cut_mode, mask_mode);
1539 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1542 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1545 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1548 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1551 void DrawLevelElementThruMask(int x, int y, int element)
1553 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1556 void DrawLevelFieldThruMask(int x, int y)
1558 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1561 /* !!! implementation of quicksand is totally broken !!! */
1562 #define IS_CRUMBLED_TILE(x, y, e) \
1563 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1564 !IS_MOVING(x, y) || \
1565 (e) == EL_QUICKSAND_EMPTYING || \
1566 (e) == EL_QUICKSAND_FAST_EMPTYING))
1568 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1573 int width, height, cx, cy;
1574 int sx = SCREENX(x), sy = SCREENY(y);
1575 int crumbled_border_size = graphic_info[graphic].border_size;
1578 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1580 for (i = 1; i < 4; i++)
1582 int dxx = (i & 1 ? dx : 0);
1583 int dyy = (i & 2 ? dy : 0);
1586 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1589 /* check if neighbour field is of same crumble type */
1590 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1591 graphic_info[graphic].class ==
1592 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1594 /* return if check prevents inner corner */
1595 if (same == (dxx == dx && dyy == dy))
1599 /* if we reach this point, we have an inner corner */
1601 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1603 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1604 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1605 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1606 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1608 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1609 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1612 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1617 int width, height, bx, by, cx, cy;
1618 int sx = SCREENX(x), sy = SCREENY(y);
1619 int crumbled_border_size = graphic_info[graphic].border_size;
1620 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1621 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1624 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1626 /* draw simple, sloppy, non-corner-accurate crumbled border */
1628 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1629 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1630 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1631 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1633 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1634 FX + sx * TILEX_VAR + cx,
1635 FY + sy * TILEY_VAR + cy);
1637 /* (remaining middle border part must be at least as big as corner part) */
1638 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1639 crumbled_border_size >= TILESIZE / 3)
1642 /* correct corners of crumbled border, if needed */
1644 for (i = -1; i <= 1; i += 2)
1646 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1647 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1648 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1651 /* check if neighbour field is of same crumble type */
1652 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1653 graphic_info[graphic].class ==
1654 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1656 /* no crumbled corner, but continued crumbled border */
1658 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1659 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1660 int b1 = (i == 1 ? crumbled_border_size_var :
1661 TILESIZE_VAR - 2 * crumbled_border_size_var);
1663 width = crumbled_border_size_var;
1664 height = crumbled_border_size_var;
1666 if (dir == 1 || dir == 2)
1681 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1683 FX + sx * TILEX_VAR + cx,
1684 FY + sy * TILEY_VAR + cy);
1689 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1691 int sx = SCREENX(x), sy = SCREENY(y);
1694 static int xy[4][2] =
1702 if (!IN_LEV_FIELD(x, y))
1705 element = TILE_GFX_ELEMENT(x, y);
1707 /* crumble field itself */
1708 if (IS_CRUMBLED_TILE(x, y, element))
1710 if (!IN_SCR_FIELD(sx, sy))
1713 for (i = 0; i < 4; i++)
1715 int xx = x + xy[i][0];
1716 int yy = y + xy[i][1];
1718 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1721 /* check if neighbour field is of same crumble type */
1722 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1723 graphic_info[graphic].class ==
1724 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1727 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1730 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1731 graphic_info[graphic].anim_frames == 2)
1733 for (i = 0; i < 4; i++)
1735 int dx = (i & 1 ? +1 : -1);
1736 int dy = (i & 2 ? +1 : -1);
1738 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1742 MarkTileDirty(sx, sy);
1744 else /* center field not crumbled -- crumble neighbour fields */
1746 for (i = 0; i < 4; i++)
1748 int xx = x + xy[i][0];
1749 int yy = y + xy[i][1];
1750 int sxx = sx + xy[i][0];
1751 int syy = sy + xy[i][1];
1753 if (!IN_LEV_FIELD(xx, yy) ||
1754 !IN_SCR_FIELD(sxx, syy))
1757 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1760 element = TILE_GFX_ELEMENT(xx, yy);
1762 if (!IS_CRUMBLED_TILE(xx, yy, element))
1765 graphic = el_act2crm(element, ACTION_DEFAULT);
1767 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1769 MarkTileDirty(sxx, syy);
1774 void DrawLevelFieldCrumbled(int x, int y)
1778 if (!IN_LEV_FIELD(x, y))
1781 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1782 GfxElement[x][y] != EL_UNDEFINED &&
1783 GFX_CRUMBLED(GfxElement[x][y]))
1785 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1790 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1792 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1795 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1798 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1799 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1800 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1801 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1802 int sx = SCREENX(x), sy = SCREENY(y);
1804 DrawGraphic(sx, sy, graphic1, frame1);
1805 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1808 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1810 int sx = SCREENX(x), sy = SCREENY(y);
1811 static int xy[4][2] =
1820 for (i = 0; i < 4; i++)
1822 int xx = x + xy[i][0];
1823 int yy = y + xy[i][1];
1824 int sxx = sx + xy[i][0];
1825 int syy = sy + xy[i][1];
1827 if (!IN_LEV_FIELD(xx, yy) ||
1828 !IN_SCR_FIELD(sxx, syy) ||
1829 !GFX_CRUMBLED(Feld[xx][yy]) ||
1833 DrawLevelField(xx, yy);
1837 static int getBorderElement(int x, int y)
1841 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1842 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1843 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1844 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1845 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1846 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1847 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1849 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1850 int steel_position = (x == -1 && y == -1 ? 0 :
1851 x == lev_fieldx && y == -1 ? 1 :
1852 x == -1 && y == lev_fieldy ? 2 :
1853 x == lev_fieldx && y == lev_fieldy ? 3 :
1854 x == -1 || x == lev_fieldx ? 4 :
1855 y == -1 || y == lev_fieldy ? 5 : 6);
1857 return border[steel_position][steel_type];
1860 void DrawScreenElement(int x, int y, int element)
1862 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1863 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1866 void DrawLevelElement(int x, int y, int element)
1868 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1869 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1872 void DrawScreenField(int x, int y)
1874 int lx = LEVELX(x), ly = LEVELY(y);
1875 int element, content;
1877 if (!IN_LEV_FIELD(lx, ly))
1879 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1882 element = getBorderElement(lx, ly);
1884 DrawScreenElement(x, y, element);
1889 element = Feld[lx][ly];
1890 content = Store[lx][ly];
1892 if (IS_MOVING(lx, ly))
1894 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1895 boolean cut_mode = NO_CUTTING;
1897 if (element == EL_QUICKSAND_EMPTYING ||
1898 element == EL_QUICKSAND_FAST_EMPTYING ||
1899 element == EL_MAGIC_WALL_EMPTYING ||
1900 element == EL_BD_MAGIC_WALL_EMPTYING ||
1901 element == EL_DC_MAGIC_WALL_EMPTYING ||
1902 element == EL_AMOEBA_DROPPING)
1903 cut_mode = CUT_ABOVE;
1904 else if (element == EL_QUICKSAND_FILLING ||
1905 element == EL_QUICKSAND_FAST_FILLING ||
1906 element == EL_MAGIC_WALL_FILLING ||
1907 element == EL_BD_MAGIC_WALL_FILLING ||
1908 element == EL_DC_MAGIC_WALL_FILLING)
1909 cut_mode = CUT_BELOW;
1911 if (cut_mode == CUT_ABOVE)
1912 DrawScreenElement(x, y, element);
1914 DrawScreenElement(x, y, EL_EMPTY);
1917 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1918 else if (cut_mode == NO_CUTTING)
1919 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1922 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1924 if (cut_mode == CUT_BELOW &&
1925 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1926 DrawLevelElement(lx, ly + 1, element);
1929 if (content == EL_ACID)
1931 int dir = MovDir[lx][ly];
1932 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1933 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1935 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1938 else if (IS_BLOCKED(lx, ly))
1943 boolean cut_mode = NO_CUTTING;
1944 int element_old, content_old;
1946 Blocked2Moving(lx, ly, &oldx, &oldy);
1949 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1950 MovDir[oldx][oldy] == MV_RIGHT);
1952 element_old = Feld[oldx][oldy];
1953 content_old = Store[oldx][oldy];
1955 if (element_old == EL_QUICKSAND_EMPTYING ||
1956 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1957 element_old == EL_MAGIC_WALL_EMPTYING ||
1958 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1959 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1960 element_old == EL_AMOEBA_DROPPING)
1961 cut_mode = CUT_ABOVE;
1963 DrawScreenElement(x, y, EL_EMPTY);
1966 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1968 else if (cut_mode == NO_CUTTING)
1969 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1972 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1975 else if (IS_DRAWABLE(element))
1976 DrawScreenElement(x, y, element);
1978 DrawScreenElement(x, y, EL_EMPTY);
1981 void DrawLevelField(int x, int y)
1983 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1984 DrawScreenField(SCREENX(x), SCREENY(y));
1985 else if (IS_MOVING(x, y))
1989 Moving2Blocked(x, y, &newx, &newy);
1990 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1991 DrawScreenField(SCREENX(newx), SCREENY(newy));
1993 else if (IS_BLOCKED(x, y))
1997 Blocked2Moving(x, y, &oldx, &oldy);
1998 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
1999 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2003 void DrawSizedElement(int x, int y, int element, int tilesize)
2007 graphic = el2edimg(element);
2008 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2011 void DrawMiniElement(int x, int y, int element)
2015 graphic = el2edimg(element);
2016 DrawMiniGraphic(x, y, graphic);
2019 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2022 int x = sx + scroll_x, y = sy + scroll_y;
2024 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2025 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2026 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2027 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2029 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2032 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2034 int x = sx + scroll_x, y = sy + scroll_y;
2036 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2037 DrawMiniElement(sx, sy, EL_EMPTY);
2038 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2039 DrawMiniElement(sx, sy, Feld[x][y]);
2041 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2044 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2045 int x, int y, int xsize, int ysize,
2046 int tile_width, int tile_height)
2050 int dst_x = startx + x * tile_width;
2051 int dst_y = starty + y * tile_height;
2052 int width = graphic_info[graphic].width;
2053 int height = graphic_info[graphic].height;
2054 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2055 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2056 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2057 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2058 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2059 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2060 boolean draw_masked = graphic_info[graphic].draw_masked;
2062 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2064 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2066 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2070 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2071 inner_sx + (x - 1) * tile_width % inner_width);
2072 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2073 inner_sy + (y - 1) * tile_height % inner_height);
2076 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2079 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2083 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2084 int x, int y, int xsize, int ysize, int font_nr)
2086 int font_width = getFontWidth(font_nr);
2087 int font_height = getFontHeight(font_nr);
2089 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2090 font_width, font_height);
2093 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2095 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2096 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2097 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2098 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2099 boolean no_delay = (tape.warp_forward);
2100 unsigned int anim_delay = 0;
2101 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2102 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2103 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2104 int font_width = getFontWidth(font_nr);
2105 int font_height = getFontHeight(font_nr);
2106 int max_xsize = level.envelope[envelope_nr].xsize;
2107 int max_ysize = level.envelope[envelope_nr].ysize;
2108 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2109 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2110 int xend = max_xsize;
2111 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2112 int xstep = (xstart < xend ? 1 : 0);
2113 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2115 int end = MAX(xend - xstart, yend - ystart);
2118 for (i = start; i <= end; i++)
2120 int last_frame = end; // last frame of this "for" loop
2121 int x = xstart + i * xstep;
2122 int y = ystart + i * ystep;
2123 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2124 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2125 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2126 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2129 SetDrawtoField(DRAW_FIELDBUFFER);
2131 BlitScreenToBitmap(backbuffer);
2133 SetDrawtoField(DRAW_BACKBUFFER);
2135 for (yy = 0; yy < ysize; yy++)
2136 for (xx = 0; xx < xsize; xx++)
2137 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2139 DrawTextBuffer(sx + font_width, sy + font_height,
2140 level.envelope[envelope_nr].text, font_nr, max_xsize,
2141 xsize - 2, ysize - 2, 0, mask_mode,
2142 level.envelope[envelope_nr].autowrap,
2143 level.envelope[envelope_nr].centered, FALSE);
2145 redraw_mask |= REDRAW_FIELD;
2148 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2152 void ShowEnvelope(int envelope_nr)
2154 int element = EL_ENVELOPE_1 + envelope_nr;
2155 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2156 int sound_opening = element_info[element].sound[ACTION_OPENING];
2157 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2158 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2159 boolean no_delay = (tape.warp_forward);
2160 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2161 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2162 int anim_mode = graphic_info[graphic].anim_mode;
2163 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2164 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2166 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2168 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2170 if (anim_mode == ANIM_DEFAULT)
2171 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2173 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2176 Delay(wait_delay_value);
2178 WaitForEventToContinue();
2180 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2182 if (anim_mode != ANIM_NONE)
2183 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2185 if (anim_mode == ANIM_DEFAULT)
2186 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2188 game.envelope_active = FALSE;
2190 SetDrawtoField(DRAW_FIELDBUFFER);
2192 redraw_mask |= REDRAW_FIELD;
2196 static void setRequestBasePosition(int *x, int *y)
2198 int sx_base, sy_base;
2200 if (request.x != -1)
2201 sx_base = request.x;
2202 else if (request.align == ALIGN_LEFT)
2204 else if (request.align == ALIGN_RIGHT)
2205 sx_base = SX + SXSIZE;
2207 sx_base = SX + SXSIZE / 2;
2209 if (request.y != -1)
2210 sy_base = request.y;
2211 else if (request.valign == VALIGN_TOP)
2213 else if (request.valign == VALIGN_BOTTOM)
2214 sy_base = SY + SYSIZE;
2216 sy_base = SY + SYSIZE / 2;
2222 static void setRequestPositionExt(int *x, int *y, int width, int height,
2223 boolean add_border_size)
2225 int border_size = request.border_size;
2226 int sx_base, sy_base;
2229 setRequestBasePosition(&sx_base, &sy_base);
2231 if (request.align == ALIGN_LEFT)
2233 else if (request.align == ALIGN_RIGHT)
2234 sx = sx_base - width;
2236 sx = sx_base - width / 2;
2238 if (request.valign == VALIGN_TOP)
2240 else if (request.valign == VALIGN_BOTTOM)
2241 sy = sy_base - height;
2243 sy = sy_base - height / 2;
2245 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2246 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2248 if (add_border_size)
2258 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2260 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2263 void DrawEnvelopeRequest(char *text)
2265 char *text_final = text;
2266 char *text_door_style = NULL;
2267 int graphic = IMG_BACKGROUND_REQUEST;
2268 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2269 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2270 int font_nr = FONT_REQUEST;
2271 int font_width = getFontWidth(font_nr);
2272 int font_height = getFontHeight(font_nr);
2273 int border_size = request.border_size;
2274 int line_spacing = request.line_spacing;
2275 int line_height = font_height + line_spacing;
2276 int text_width = request.width - 2 * border_size;
2277 int text_height = request.height - 2 * border_size;
2278 int line_length = text_width / font_width;
2279 int max_lines = text_height / line_height;
2280 int width = request.width;
2281 int height = request.height;
2282 int tile_size = request.step_offset;
2283 int x_steps = width / tile_size;
2284 int y_steps = height / tile_size;
2288 if (request.wrap_single_words)
2290 char *src_text_ptr, *dst_text_ptr;
2292 text_door_style = checked_malloc(2 * strlen(text) + 1);
2294 src_text_ptr = text;
2295 dst_text_ptr = text_door_style;
2297 while (*src_text_ptr)
2299 if (*src_text_ptr == ' ' ||
2300 *src_text_ptr == '?' ||
2301 *src_text_ptr == '!')
2302 *dst_text_ptr++ = '\n';
2304 if (*src_text_ptr != ' ')
2305 *dst_text_ptr++ = *src_text_ptr;
2310 *dst_text_ptr = '\0';
2312 text_final = text_door_style;
2315 setRequestPosition(&sx, &sy, FALSE);
2317 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2319 for (y = 0; y < y_steps; y++)
2320 for (x = 0; x < x_steps; x++)
2321 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2322 x, y, x_steps, y_steps,
2323 tile_size, tile_size);
2325 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2326 line_length, -1, max_lines, line_spacing, mask_mode,
2327 request.autowrap, request.centered, FALSE);
2329 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2330 RedrawGadget(tool_gadget[i]);
2332 // store readily prepared envelope request for later use when animating
2333 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2335 if (text_door_style)
2336 free(text_door_style);
2339 void AnimateEnvelopeRequest(int anim_mode, int action)
2341 int graphic = IMG_BACKGROUND_REQUEST;
2342 boolean draw_masked = graphic_info[graphic].draw_masked;
2343 int delay_value_normal = request.step_delay;
2344 int delay_value_fast = delay_value_normal / 2;
2345 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2346 boolean no_delay = (tape.warp_forward);
2347 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2348 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2349 unsigned int anim_delay = 0;
2351 int tile_size = request.step_offset;
2352 int max_xsize = request.width / tile_size;
2353 int max_ysize = request.height / tile_size;
2354 int max_xsize_inner = max_xsize - 2;
2355 int max_ysize_inner = max_ysize - 2;
2357 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2358 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2359 int xend = max_xsize_inner;
2360 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2361 int xstep = (xstart < xend ? 1 : 0);
2362 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2364 int end = MAX(xend - xstart, yend - ystart);
2367 if (setup.quick_doors)
2375 if (action == ACTION_OPENING)
2376 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2377 else if (action == ACTION_CLOSING)
2378 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2381 for (i = start; i <= end; i++)
2383 int last_frame = end; // last frame of this "for" loop
2384 int x = xstart + i * xstep;
2385 int y = ystart + i * ystep;
2386 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2387 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2388 int xsize_size_left = (xsize - 1) * tile_size;
2389 int ysize_size_top = (ysize - 1) * tile_size;
2390 int max_xsize_pos = (max_xsize - 1) * tile_size;
2391 int max_ysize_pos = (max_ysize - 1) * tile_size;
2392 int width = xsize * tile_size;
2393 int height = ysize * tile_size;
2398 setRequestPosition(&src_x, &src_y, FALSE);
2399 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2401 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2403 for (yy = 0; yy < 2; yy++)
2405 for (xx = 0; xx < 2; xx++)
2407 int src_xx = src_x + xx * max_xsize_pos;
2408 int src_yy = src_y + yy * max_ysize_pos;
2409 int dst_xx = dst_x + xx * xsize_size_left;
2410 int dst_yy = dst_y + yy * ysize_size_top;
2411 int xx_size = (xx ? tile_size : xsize_size_left);
2412 int yy_size = (yy ? tile_size : ysize_size_top);
2415 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2416 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2418 BlitBitmap(bitmap_db_cross, backbuffer,
2419 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2423 redraw_mask |= REDRAW_FIELD;
2428 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2432 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2434 int last_game_status = game_status; /* save current game status */
2435 int graphic = IMG_BACKGROUND_REQUEST;
2436 int sound_opening = SND_REQUEST_OPENING;
2437 int sound_closing = SND_REQUEST_CLOSING;
2438 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2439 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2440 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2441 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2442 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2444 if (game_status == GAME_MODE_PLAYING)
2445 BlitScreenToBitmap(backbuffer);
2447 SetDrawtoField(DRAW_BACKBUFFER);
2449 // SetDrawBackgroundMask(REDRAW_NONE);
2451 if (action == ACTION_OPENING)
2453 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2455 if (req_state & REQ_ASK)
2457 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2458 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2460 else if (req_state & REQ_CONFIRM)
2462 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2464 else if (req_state & REQ_PLAYER)
2466 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2467 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2468 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2469 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2472 DrawEnvelopeRequest(text);
2474 if (game_status != GAME_MODE_MAIN)
2478 /* force DOOR font inside door area */
2479 game_status = GAME_MODE_PSEUDO_DOOR;
2481 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2483 if (action == ACTION_OPENING)
2485 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2487 if (anim_mode == ANIM_DEFAULT)
2488 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2490 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2494 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2496 if (anim_mode != ANIM_NONE)
2497 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2499 if (anim_mode == ANIM_DEFAULT)
2500 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2503 game.envelope_active = FALSE;
2505 game_status = last_game_status; /* restore current game status */
2507 if (action == ACTION_CLOSING)
2509 if (game_status != GAME_MODE_MAIN)
2512 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2515 // SetDrawBackgroundMask(last_draw_background_mask);
2517 redraw_mask |= REDRAW_FIELD;
2519 if (game_status == GAME_MODE_MAIN)
2524 if (action == ACTION_CLOSING &&
2525 game_status == GAME_MODE_PLAYING &&
2526 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2527 SetDrawtoField(DRAW_FIELDBUFFER);
2530 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2534 int graphic = el2preimg(element);
2536 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2537 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2540 void DrawLevel(int draw_background_mask)
2544 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2545 SetDrawBackgroundMask(draw_background_mask);
2549 for (x = BX1; x <= BX2; x++)
2550 for (y = BY1; y <= BY2; y++)
2551 DrawScreenField(x, y);
2553 redraw_mask |= REDRAW_FIELD;
2556 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2561 for (x = 0; x < size_x; x++)
2562 for (y = 0; y < size_y; y++)
2563 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2565 redraw_mask |= REDRAW_FIELD;
2568 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2572 for (x = 0; x < size_x; x++)
2573 for (y = 0; y < size_y; y++)
2574 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2576 redraw_mask |= REDRAW_FIELD;
2579 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2581 boolean show_level_border = (BorderElement != EL_EMPTY);
2582 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2583 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2584 int tile_size = preview.tile_size;
2585 int preview_width = preview.xsize * tile_size;
2586 int preview_height = preview.ysize * tile_size;
2587 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2588 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2589 int real_preview_width = real_preview_xsize * tile_size;
2590 int real_preview_height = real_preview_ysize * tile_size;
2591 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2592 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2595 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2598 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2600 dst_x += (preview_width - real_preview_width) / 2;
2601 dst_y += (preview_height - real_preview_height) / 2;
2603 for (x = 0; x < real_preview_xsize; x++)
2605 for (y = 0; y < real_preview_ysize; y++)
2607 int lx = from_x + x + (show_level_border ? -1 : 0);
2608 int ly = from_y + y + (show_level_border ? -1 : 0);
2609 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2610 getBorderElement(lx, ly));
2612 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2613 element, tile_size);
2617 redraw_mask |= REDRAW_FIELD;
2620 #define MICROLABEL_EMPTY 0
2621 #define MICROLABEL_LEVEL_NAME 1
2622 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2623 #define MICROLABEL_LEVEL_AUTHOR 3
2624 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2625 #define MICROLABEL_IMPORTED_FROM 5
2626 #define MICROLABEL_IMPORTED_BY_HEAD 6
2627 #define MICROLABEL_IMPORTED_BY 7
2629 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2631 int max_text_width = SXSIZE;
2632 int font_width = getFontWidth(font_nr);
2634 if (pos->align == ALIGN_CENTER)
2635 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2636 else if (pos->align == ALIGN_RIGHT)
2637 max_text_width = pos->x;
2639 max_text_width = SXSIZE - pos->x;
2641 return max_text_width / font_width;
2644 static void DrawPreviewLevelLabelExt(int mode)
2646 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2647 char label_text[MAX_OUTPUT_LINESIZE + 1];
2648 int max_len_label_text;
2649 int font_nr = pos->font;
2652 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2655 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2656 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2657 mode == MICROLABEL_IMPORTED_BY_HEAD)
2658 font_nr = pos->font_alt;
2660 max_len_label_text = getMaxTextLength(pos, font_nr);
2662 if (pos->size != -1)
2663 max_len_label_text = pos->size;
2665 for (i = 0; i < max_len_label_text; i++)
2666 label_text[i] = ' ';
2667 label_text[max_len_label_text] = '\0';
2669 if (strlen(label_text) > 0)
2670 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2673 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2674 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2675 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2676 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2677 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2678 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2679 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2680 max_len_label_text);
2681 label_text[max_len_label_text] = '\0';
2683 if (strlen(label_text) > 0)
2684 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2686 redraw_mask |= REDRAW_FIELD;
2689 static void DrawPreviewLevelExt(boolean restart)
2691 static unsigned int scroll_delay = 0;
2692 static unsigned int label_delay = 0;
2693 static int from_x, from_y, scroll_direction;
2694 static int label_state, label_counter;
2695 unsigned int scroll_delay_value = preview.step_delay;
2696 boolean show_level_border = (BorderElement != EL_EMPTY);
2697 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2698 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2699 int last_game_status = game_status; /* save current game status */
2706 if (preview.anim_mode == ANIM_CENTERED)
2708 if (level_xsize > preview.xsize)
2709 from_x = (level_xsize - preview.xsize) / 2;
2710 if (level_ysize > preview.ysize)
2711 from_y = (level_ysize - preview.ysize) / 2;
2714 from_x += preview.xoffset;
2715 from_y += preview.yoffset;
2717 scroll_direction = MV_RIGHT;
2721 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2722 DrawPreviewLevelLabelExt(label_state);
2724 /* initialize delay counters */
2725 DelayReached(&scroll_delay, 0);
2726 DelayReached(&label_delay, 0);
2728 if (leveldir_current->name)
2730 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2731 char label_text[MAX_OUTPUT_LINESIZE + 1];
2732 int font_nr = pos->font;
2733 int max_len_label_text = getMaxTextLength(pos, font_nr);
2735 if (pos->size != -1)
2736 max_len_label_text = pos->size;
2738 strncpy(label_text, leveldir_current->name, max_len_label_text);
2739 label_text[max_len_label_text] = '\0';
2741 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2742 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2745 game_status = last_game_status; /* restore current game status */
2750 /* scroll preview level, if needed */
2751 if (preview.anim_mode != ANIM_NONE &&
2752 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2753 DelayReached(&scroll_delay, scroll_delay_value))
2755 switch (scroll_direction)
2760 from_x -= preview.step_offset;
2761 from_x = (from_x < 0 ? 0 : from_x);
2764 scroll_direction = MV_UP;
2768 if (from_x < level_xsize - preview.xsize)
2770 from_x += preview.step_offset;
2771 from_x = (from_x > level_xsize - preview.xsize ?
2772 level_xsize - preview.xsize : from_x);
2775 scroll_direction = MV_DOWN;
2781 from_y -= preview.step_offset;
2782 from_y = (from_y < 0 ? 0 : from_y);
2785 scroll_direction = MV_RIGHT;
2789 if (from_y < level_ysize - preview.ysize)
2791 from_y += preview.step_offset;
2792 from_y = (from_y > level_ysize - preview.ysize ?
2793 level_ysize - preview.ysize : from_y);
2796 scroll_direction = MV_LEFT;
2803 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2806 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2807 /* redraw micro level label, if needed */
2808 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2809 !strEqual(level.author, ANONYMOUS_NAME) &&
2810 !strEqual(level.author, leveldir_current->name) &&
2811 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2813 int max_label_counter = 23;
2815 if (leveldir_current->imported_from != NULL &&
2816 strlen(leveldir_current->imported_from) > 0)
2817 max_label_counter += 14;
2818 if (leveldir_current->imported_by != NULL &&
2819 strlen(leveldir_current->imported_by) > 0)
2820 max_label_counter += 14;
2822 label_counter = (label_counter + 1) % max_label_counter;
2823 label_state = (label_counter >= 0 && label_counter <= 7 ?
2824 MICROLABEL_LEVEL_NAME :
2825 label_counter >= 9 && label_counter <= 12 ?
2826 MICROLABEL_LEVEL_AUTHOR_HEAD :
2827 label_counter >= 14 && label_counter <= 21 ?
2828 MICROLABEL_LEVEL_AUTHOR :
2829 label_counter >= 23 && label_counter <= 26 ?
2830 MICROLABEL_IMPORTED_FROM_HEAD :
2831 label_counter >= 28 && label_counter <= 35 ?
2832 MICROLABEL_IMPORTED_FROM :
2833 label_counter >= 37 && label_counter <= 40 ?
2834 MICROLABEL_IMPORTED_BY_HEAD :
2835 label_counter >= 42 && label_counter <= 49 ?
2836 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2838 if (leveldir_current->imported_from == NULL &&
2839 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2840 label_state == MICROLABEL_IMPORTED_FROM))
2841 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2842 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2844 DrawPreviewLevelLabelExt(label_state);
2847 game_status = last_game_status; /* restore current game status */
2850 void DrawPreviewLevelInitial()
2852 DrawPreviewLevelExt(TRUE);
2855 void DrawPreviewLevelAnimation()
2857 DrawPreviewLevelExt(FALSE);
2860 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2861 int graphic, int sync_frame, int mask_mode)
2863 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2865 if (mask_mode == USE_MASKING)
2866 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2868 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2871 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2872 int graphic, int sync_frame,
2875 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2877 if (mask_mode == USE_MASKING)
2878 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2880 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2883 inline void DrawGraphicAnimation(int x, int y, int graphic)
2885 int lx = LEVELX(x), ly = LEVELY(y);
2887 if (!IN_SCR_FIELD(x, y))
2890 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2891 graphic, GfxFrame[lx][ly], NO_MASKING);
2893 MarkTileDirty(x, y);
2896 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2898 int lx = LEVELX(x), ly = LEVELY(y);
2900 if (!IN_SCR_FIELD(x, y))
2903 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2904 graphic, GfxFrame[lx][ly], NO_MASKING);
2905 MarkTileDirty(x, y);
2908 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2910 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2913 void DrawLevelElementAnimation(int x, int y, int element)
2915 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2917 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2920 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2922 int sx = SCREENX(x), sy = SCREENY(y);
2924 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2927 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2930 DrawGraphicAnimation(sx, sy, graphic);
2933 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2934 DrawLevelFieldCrumbled(x, y);
2936 if (GFX_CRUMBLED(Feld[x][y]))
2937 DrawLevelFieldCrumbled(x, y);
2941 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2943 int sx = SCREENX(x), sy = SCREENY(y);
2946 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2949 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2951 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2954 DrawGraphicAnimation(sx, sy, graphic);
2956 if (GFX_CRUMBLED(element))
2957 DrawLevelFieldCrumbled(x, y);
2960 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2962 if (player->use_murphy)
2964 /* this works only because currently only one player can be "murphy" ... */
2965 static int last_horizontal_dir = MV_LEFT;
2966 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2968 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2969 last_horizontal_dir = move_dir;
2971 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2973 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2975 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2981 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2984 static boolean equalGraphics(int graphic1, int graphic2)
2986 struct GraphicInfo *g1 = &graphic_info[graphic1];
2987 struct GraphicInfo *g2 = &graphic_info[graphic2];
2989 return (g1->bitmap == g2->bitmap &&
2990 g1->src_x == g2->src_x &&
2991 g1->src_y == g2->src_y &&
2992 g1->anim_frames == g2->anim_frames &&
2993 g1->anim_delay == g2->anim_delay &&
2994 g1->anim_mode == g2->anim_mode);
2997 void DrawAllPlayers()
3001 for (i = 0; i < MAX_PLAYERS; i++)
3002 if (stored_player[i].active)
3003 DrawPlayer(&stored_player[i]);
3006 void DrawPlayerField(int x, int y)
3008 if (!IS_PLAYER(x, y))
3011 DrawPlayer(PLAYERINFO(x, y));
3014 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3016 void DrawPlayer(struct PlayerInfo *player)
3018 int jx = player->jx;
3019 int jy = player->jy;
3020 int move_dir = player->MovDir;
3021 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3022 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3023 int last_jx = (player->is_moving ? jx - dx : jx);
3024 int last_jy = (player->is_moving ? jy - dy : jy);
3025 int next_jx = jx + dx;
3026 int next_jy = jy + dy;
3027 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3028 boolean player_is_opaque = FALSE;
3029 int sx = SCREENX(jx), sy = SCREENY(jy);
3030 int sxx = 0, syy = 0;
3031 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3033 int action = ACTION_DEFAULT;
3034 int last_player_graphic = getPlayerGraphic(player, move_dir);
3035 int last_player_frame = player->Frame;
3038 /* GfxElement[][] is set to the element the player is digging or collecting;
3039 remove also for off-screen player if the player is not moving anymore */
3040 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3041 GfxElement[jx][jy] = EL_UNDEFINED;
3043 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3047 if (!IN_LEV_FIELD(jx, jy))
3049 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3050 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3051 printf("DrawPlayerField(): This should never happen!\n");
3056 if (element == EL_EXPLOSION)
3059 action = (player->is_pushing ? ACTION_PUSHING :
3060 player->is_digging ? ACTION_DIGGING :
3061 player->is_collecting ? ACTION_COLLECTING :
3062 player->is_moving ? ACTION_MOVING :
3063 player->is_snapping ? ACTION_SNAPPING :
3064 player->is_dropping ? ACTION_DROPPING :
3065 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3067 if (player->is_waiting)
3068 move_dir = player->dir_waiting;
3070 InitPlayerGfxAnimation(player, action, move_dir);
3072 /* ----------------------------------------------------------------------- */
3073 /* draw things in the field the player is leaving, if needed */
3074 /* ----------------------------------------------------------------------- */
3076 if (player->is_moving)
3078 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3080 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3082 if (last_element == EL_DYNAMITE_ACTIVE ||
3083 last_element == EL_EM_DYNAMITE_ACTIVE ||
3084 last_element == EL_SP_DISK_RED_ACTIVE)
3085 DrawDynamite(last_jx, last_jy);
3087 DrawLevelFieldThruMask(last_jx, last_jy);
3089 else if (last_element == EL_DYNAMITE_ACTIVE ||
3090 last_element == EL_EM_DYNAMITE_ACTIVE ||
3091 last_element == EL_SP_DISK_RED_ACTIVE)
3092 DrawDynamite(last_jx, last_jy);
3094 /* !!! this is not enough to prevent flickering of players which are
3095 moving next to each others without a free tile between them -- this
3096 can only be solved by drawing all players layer by layer (first the
3097 background, then the foreground etc.) !!! => TODO */
3098 else if (!IS_PLAYER(last_jx, last_jy))
3099 DrawLevelField(last_jx, last_jy);
3102 DrawLevelField(last_jx, last_jy);
3105 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3106 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3109 if (!IN_SCR_FIELD(sx, sy))
3112 /* ----------------------------------------------------------------------- */
3113 /* draw things behind the player, if needed */
3114 /* ----------------------------------------------------------------------- */
3117 DrawLevelElement(jx, jy, Back[jx][jy]);
3118 else if (IS_ACTIVE_BOMB(element))
3119 DrawLevelElement(jx, jy, EL_EMPTY);
3122 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3124 int old_element = GfxElement[jx][jy];
3125 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3126 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3128 if (GFX_CRUMBLED(old_element))
3129 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3131 DrawGraphic(sx, sy, old_graphic, frame);
3133 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3134 player_is_opaque = TRUE;
3138 GfxElement[jx][jy] = EL_UNDEFINED;
3140 /* make sure that pushed elements are drawn with correct frame rate */
3141 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3143 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3144 GfxFrame[jx][jy] = player->StepFrame;
3146 DrawLevelField(jx, jy);
3150 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3151 /* ----------------------------------------------------------------------- */
3152 /* draw player himself */
3153 /* ----------------------------------------------------------------------- */
3155 graphic = getPlayerGraphic(player, move_dir);
3157 /* in the case of changed player action or direction, prevent the current
3158 animation frame from being restarted for identical animations */
3159 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3160 player->Frame = last_player_frame;
3162 frame = getGraphicAnimationFrame(graphic, player->Frame);
3166 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3167 sxx = player->GfxPos;
3169 syy = player->GfxPos;
3172 if (player_is_opaque)
3173 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3175 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3177 if (SHIELD_ON(player))
3179 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3180 IMG_SHIELD_NORMAL_ACTIVE);
3181 int frame = getGraphicAnimationFrame(graphic, -1);
3183 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3187 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3190 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3191 sxx = player->GfxPos;
3193 syy = player->GfxPos;
3197 /* ----------------------------------------------------------------------- */
3198 /* draw things the player is pushing, if needed */
3199 /* ----------------------------------------------------------------------- */
3201 if (player->is_pushing && player->is_moving)
3203 int px = SCREENX(jx), py = SCREENY(jy);
3204 int pxx = (TILEX - ABS(sxx)) * dx;
3205 int pyy = (TILEY - ABS(syy)) * dy;
3206 int gfx_frame = GfxFrame[jx][jy];
3212 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3214 element = Feld[next_jx][next_jy];
3215 gfx_frame = GfxFrame[next_jx][next_jy];
3218 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3220 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3221 frame = getGraphicAnimationFrame(graphic, sync_frame);
3223 /* draw background element under pushed element (like the Sokoban field) */
3224 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3226 /* this allows transparent pushing animation over non-black background */
3229 DrawLevelElement(jx, jy, Back[jx][jy]);
3231 DrawLevelElement(jx, jy, EL_EMPTY);
3233 if (Back[next_jx][next_jy])
3234 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3236 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3238 else if (Back[next_jx][next_jy])
3239 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3242 /* do not draw (EM style) pushing animation when pushing is finished */
3243 /* (two-tile animations usually do not contain start and end frame) */
3244 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3245 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3247 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3249 /* masked drawing is needed for EMC style (double) movement graphics */
3250 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3251 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3255 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3256 /* ----------------------------------------------------------------------- */
3257 /* draw player himself */
3258 /* ----------------------------------------------------------------------- */
3260 graphic = getPlayerGraphic(player, move_dir);
3262 /* in the case of changed player action or direction, prevent the current
3263 animation frame from being restarted for identical animations */
3264 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3265 player->Frame = last_player_frame;
3267 frame = getGraphicAnimationFrame(graphic, player->Frame);
3271 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3272 sxx = player->GfxPos;
3274 syy = player->GfxPos;
3277 if (player_is_opaque)
3278 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3280 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3282 if (SHIELD_ON(player))
3284 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3285 IMG_SHIELD_NORMAL_ACTIVE);
3286 int frame = getGraphicAnimationFrame(graphic, -1);
3288 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3292 /* ----------------------------------------------------------------------- */
3293 /* draw things in front of player (active dynamite or dynabombs) */
3294 /* ----------------------------------------------------------------------- */
3296 if (IS_ACTIVE_BOMB(element))
3298 graphic = el2img(element);
3299 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3301 if (game.emulation == EMU_SUPAPLEX)
3302 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3304 DrawGraphicThruMask(sx, sy, graphic, frame);
3307 if (player_is_moving && last_element == EL_EXPLOSION)
3309 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3310 GfxElement[last_jx][last_jy] : EL_EMPTY);
3311 int graphic = el_act2img(element, ACTION_EXPLODING);
3312 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3313 int phase = ExplodePhase[last_jx][last_jy] - 1;
3314 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3317 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3320 /* ----------------------------------------------------------------------- */
3321 /* draw elements the player is just walking/passing through/under */
3322 /* ----------------------------------------------------------------------- */
3324 if (player_is_moving)
3326 /* handle the field the player is leaving ... */
3327 if (IS_ACCESSIBLE_INSIDE(last_element))
3328 DrawLevelField(last_jx, last_jy);
3329 else if (IS_ACCESSIBLE_UNDER(last_element))
3330 DrawLevelFieldThruMask(last_jx, last_jy);
3333 /* do not redraw accessible elements if the player is just pushing them */
3334 if (!player_is_moving || !player->is_pushing)
3336 /* ... and the field the player is entering */
3337 if (IS_ACCESSIBLE_INSIDE(element))
3338 DrawLevelField(jx, jy);
3339 else if (IS_ACCESSIBLE_UNDER(element))
3340 DrawLevelFieldThruMask(jx, jy);
3343 MarkTileDirty(sx, sy);
3346 /* ------------------------------------------------------------------------- */
3348 void WaitForEventToContinue()
3350 boolean still_wait = TRUE;
3352 /* simulate releasing mouse button over last gadget, if still pressed */
3354 HandleGadgets(-1, -1, 0);
3356 button_status = MB_RELEASED;
3370 case EVENT_BUTTONPRESS:
3371 case EVENT_KEYPRESS:
3375 case EVENT_KEYRELEASE:
3376 ClearPlayerAction();
3380 HandleOtherEvents(&event);
3384 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3391 /* don't eat all CPU time */
3396 #define MAX_REQUEST_LINES 13
3397 #define MAX_REQUEST_LINE_FONT1_LEN 7
3398 #define MAX_REQUEST_LINE_FONT2_LEN 10
3400 static int RequestHandleEvents(unsigned int req_state)
3402 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3403 local_player->LevelSolved_GameEnd);
3404 int last_game_status = game_status; /* save current game status */
3405 int width = request.width;
3406 int height = request.height;
3410 setRequestPosition(&sx, &sy, FALSE);
3412 button_status = MB_RELEASED;
3414 request_gadget_id = -1;
3421 SetDrawtoField(DRAW_FIELDBUFFER);
3423 HandleGameActions();
3425 SetDrawtoField(DRAW_BACKBUFFER);
3427 if (global.use_envelope_request)
3429 /* copy current state of request area to middle of playfield area */
3430 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3438 while (NextValidEvent(&event))
3442 case EVENT_BUTTONPRESS:
3443 case EVENT_BUTTONRELEASE:
3444 case EVENT_MOTIONNOTIFY:
3448 if (event.type == EVENT_MOTIONNOTIFY)
3453 motion_status = TRUE;
3454 mx = ((MotionEvent *) &event)->x;
3455 my = ((MotionEvent *) &event)->y;
3459 motion_status = FALSE;
3460 mx = ((ButtonEvent *) &event)->x;
3461 my = ((ButtonEvent *) &event)->y;
3462 if (event.type == EVENT_BUTTONPRESS)
3463 button_status = ((ButtonEvent *) &event)->button;
3465 button_status = MB_RELEASED;
3468 /* this sets 'request_gadget_id' */
3469 HandleGadgets(mx, my, button_status);
3471 switch (request_gadget_id)
3473 case TOOL_CTRL_ID_YES:
3476 case TOOL_CTRL_ID_NO:
3479 case TOOL_CTRL_ID_CONFIRM:
3480 result = TRUE | FALSE;
3483 case TOOL_CTRL_ID_PLAYER_1:
3486 case TOOL_CTRL_ID_PLAYER_2:
3489 case TOOL_CTRL_ID_PLAYER_3:
3492 case TOOL_CTRL_ID_PLAYER_4:
3503 case EVENT_KEYPRESS:
3504 switch (GetEventKey((KeyEvent *)&event, TRUE))
3507 if (req_state & REQ_CONFIRM)
3512 #if defined(TARGET_SDL2)
3519 #if defined(TARGET_SDL2)
3529 if (req_state & REQ_PLAYER)
3533 case EVENT_KEYRELEASE:
3534 ClearPlayerAction();
3538 HandleOtherEvents(&event);
3543 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3545 int joy = AnyJoystick();
3547 if (joy & JOY_BUTTON_1)
3549 else if (joy & JOY_BUTTON_2)
3555 if (global.use_envelope_request)
3557 /* copy back current state of pressed buttons inside request area */
3558 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3565 if (!PendingEvent()) /* delay only if no pending events */
3569 game_status = GAME_MODE_PSEUDO_DOOR;
3573 game_status = last_game_status; /* restore current game status */
3579 static boolean RequestDoor(char *text, unsigned int req_state)
3581 unsigned int old_door_state;
3582 int last_game_status = game_status; /* save current game status */
3583 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3584 int font_nr = FONT_TEXT_2;
3589 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3591 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3592 font_nr = FONT_TEXT_1;
3595 if (game_status == GAME_MODE_PLAYING)
3596 BlitScreenToBitmap(backbuffer);
3598 /* disable deactivated drawing when quick-loading level tape recording */
3599 if (tape.playing && tape.deactivate_display)
3600 TapeDeactivateDisplayOff(TRUE);
3602 SetMouseCursor(CURSOR_DEFAULT);
3604 #if defined(NETWORK_AVALIABLE)
3605 /* pause network game while waiting for request to answer */
3606 if (options.network &&
3607 game_status == GAME_MODE_PLAYING &&
3608 req_state & REQUEST_WAIT_FOR_INPUT)
3609 SendToServer_PausePlaying();
3612 old_door_state = GetDoorState();
3614 /* simulate releasing mouse button over last gadget, if still pressed */
3616 HandleGadgets(-1, -1, 0);
3620 /* draw released gadget before proceeding */
3623 if (old_door_state & DOOR_OPEN_1)
3625 CloseDoor(DOOR_CLOSE_1);
3627 /* save old door content */
3628 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3629 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3632 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3633 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3635 /* clear door drawing field */
3636 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3638 /* force DOOR font inside door area */
3639 game_status = GAME_MODE_PSEUDO_DOOR;
3641 /* write text for request */
3642 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3644 char text_line[max_request_line_len + 1];
3650 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3652 tc = *(text_ptr + tx);
3653 // if (!tc || tc == ' ')
3654 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3658 if ((tc == '?' || tc == '!') && tl == 0)
3668 strncpy(text_line, text_ptr, tl);
3671 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3672 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3673 text_line, font_nr);
3675 text_ptr += tl + (tc == ' ' ? 1 : 0);
3676 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3679 game_status = last_game_status; /* restore current game status */
3681 if (req_state & REQ_ASK)
3683 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3684 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3686 else if (req_state & REQ_CONFIRM)
3688 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3690 else if (req_state & REQ_PLAYER)
3692 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3693 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3694 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3695 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3698 /* copy request gadgets to door backbuffer */
3699 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3701 OpenDoor(DOOR_OPEN_1);
3703 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3705 if (game_status == GAME_MODE_PLAYING)
3707 SetPanelBackground();
3708 SetDrawBackgroundMask(REDRAW_DOOR_1);
3712 SetDrawBackgroundMask(REDRAW_FIELD);
3718 if (game_status != GAME_MODE_MAIN)
3721 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3723 // ---------- handle request buttons ----------
3724 result = RequestHandleEvents(req_state);
3726 if (game_status != GAME_MODE_MAIN)
3731 if (!(req_state & REQ_STAY_OPEN))
3733 CloseDoor(DOOR_CLOSE_1);
3735 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3736 (req_state & REQ_REOPEN))
3737 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3742 if (game_status == GAME_MODE_PLAYING)
3744 SetPanelBackground();
3745 SetDrawBackgroundMask(REDRAW_DOOR_1);
3749 SetDrawBackgroundMask(REDRAW_FIELD);
3752 #if defined(NETWORK_AVALIABLE)
3753 /* continue network game after request */
3754 if (options.network &&
3755 game_status == GAME_MODE_PLAYING &&
3756 req_state & REQUEST_WAIT_FOR_INPUT)
3757 SendToServer_ContinuePlaying();
3760 /* restore deactivated drawing when quick-loading level tape recording */
3761 if (tape.playing && tape.deactivate_display)
3762 TapeDeactivateDisplayOn();
3767 static boolean RequestEnvelope(char *text, unsigned int req_state)
3771 if (game_status == GAME_MODE_PLAYING)
3772 BlitScreenToBitmap(backbuffer);
3774 /* disable deactivated drawing when quick-loading level tape recording */
3775 if (tape.playing && tape.deactivate_display)
3776 TapeDeactivateDisplayOff(TRUE);
3778 SetMouseCursor(CURSOR_DEFAULT);
3780 #if defined(NETWORK_AVALIABLE)
3781 /* pause network game while waiting for request to answer */
3782 if (options.network &&
3783 game_status == GAME_MODE_PLAYING &&
3784 req_state & REQUEST_WAIT_FOR_INPUT)
3785 SendToServer_PausePlaying();
3788 /* simulate releasing mouse button over last gadget, if still pressed */
3790 HandleGadgets(-1, -1, 0);
3794 // (replace with setting corresponding request background)
3795 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3796 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3798 /* clear door drawing field */
3799 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3801 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3803 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3805 if (game_status == GAME_MODE_PLAYING)
3807 SetPanelBackground();
3808 SetDrawBackgroundMask(REDRAW_DOOR_1);
3812 SetDrawBackgroundMask(REDRAW_FIELD);
3818 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3820 // ---------- handle request buttons ----------
3821 result = RequestHandleEvents(req_state);
3823 if (game_status != GAME_MODE_MAIN)
3828 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3832 if (game_status == GAME_MODE_PLAYING)
3834 SetPanelBackground();
3835 SetDrawBackgroundMask(REDRAW_DOOR_1);
3839 SetDrawBackgroundMask(REDRAW_FIELD);
3842 #if defined(NETWORK_AVALIABLE)
3843 /* continue network game after request */
3844 if (options.network &&
3845 game_status == GAME_MODE_PLAYING &&
3846 req_state & REQUEST_WAIT_FOR_INPUT)
3847 SendToServer_ContinuePlaying();
3850 /* restore deactivated drawing when quick-loading level tape recording */
3851 if (tape.playing && tape.deactivate_display)
3852 TapeDeactivateDisplayOn();
3857 boolean Request(char *text, unsigned int req_state)
3859 if (global.use_envelope_request)
3860 return RequestEnvelope(text, req_state);
3862 return RequestDoor(text, req_state);
3865 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3867 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3868 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3871 if (dpo1->sort_priority != dpo2->sort_priority)
3872 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3874 compare_result = dpo1->nr - dpo2->nr;
3876 return compare_result;
3879 void InitGraphicCompatibilityInfo_Doors()
3885 struct DoorInfo *door;
3889 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3890 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3892 { -1, -1, -1, NULL }
3894 struct Rect door_rect_list[] =
3896 { DX, DY, DXSIZE, DYSIZE },
3897 { VX, VY, VXSIZE, VYSIZE }
3901 for (i = 0; doors[i].door_token != -1; i++)
3903 int door_token = doors[i].door_token;
3904 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3905 int part_1 = doors[i].part_1;
3906 int part_8 = doors[i].part_8;
3907 int part_2 = part_1 + 1;
3908 int part_3 = part_1 + 2;
3909 struct DoorInfo *door = doors[i].door;
3910 struct Rect *door_rect = &door_rect_list[door_index];
3911 boolean door_gfx_redefined = FALSE;
3913 /* check if any door part graphic definitions have been redefined */
3915 for (j = 0; door_part_controls[j].door_token != -1; j++)
3917 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3918 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3920 if (dpc->door_token == door_token && fi->redefined)
3921 door_gfx_redefined = TRUE;
3924 /* check for old-style door graphic/animation modifications */
3926 if (!door_gfx_redefined)
3928 if (door->anim_mode & ANIM_STATIC_PANEL)
3930 door->panel.step_xoffset = 0;
3931 door->panel.step_yoffset = 0;
3934 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3936 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3937 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3938 int num_door_steps, num_panel_steps;
3940 /* remove door part graphics other than the two default wings */
3942 for (j = 0; door_part_controls[j].door_token != -1; j++)
3944 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3945 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3947 if (dpc->graphic >= part_3 &&
3948 dpc->graphic <= part_8)
3952 /* set graphics and screen positions of the default wings */
3954 g_part_1->width = door_rect->width;
3955 g_part_1->height = door_rect->height;
3956 g_part_2->width = door_rect->width;
3957 g_part_2->height = door_rect->height;
3958 g_part_2->src_x = door_rect->width;
3959 g_part_2->src_y = g_part_1->src_y;
3961 door->part_2.x = door->part_1.x;
3962 door->part_2.y = door->part_1.y;
3964 if (door->width != -1)
3966 g_part_1->width = door->width;
3967 g_part_2->width = door->width;
3969 // special treatment for graphics and screen position of right wing
3970 g_part_2->src_x += door_rect->width - door->width;
3971 door->part_2.x += door_rect->width - door->width;
3974 if (door->height != -1)
3976 g_part_1->height = door->height;
3977 g_part_2->height = door->height;
3979 // special treatment for graphics and screen position of bottom wing
3980 g_part_2->src_y += door_rect->height - door->height;
3981 door->part_2.y += door_rect->height - door->height;
3984 /* set animation delays for the default wings and panels */
3986 door->part_1.step_delay = door->step_delay;
3987 door->part_2.step_delay = door->step_delay;
3988 door->panel.step_delay = door->step_delay;
3990 /* set animation draw order for the default wings */
3992 door->part_1.sort_priority = 2; /* draw left wing over ... */
3993 door->part_2.sort_priority = 1; /* ... right wing */
3995 /* set animation draw offset for the default wings */
3997 if (door->anim_mode & ANIM_HORIZONTAL)
3999 door->part_1.step_xoffset = door->step_offset;
4000 door->part_1.step_yoffset = 0;
4001 door->part_2.step_xoffset = door->step_offset * -1;
4002 door->part_2.step_yoffset = 0;
4004 num_door_steps = g_part_1->width / door->step_offset;
4006 else // ANIM_VERTICAL
4008 door->part_1.step_xoffset = 0;
4009 door->part_1.step_yoffset = door->step_offset;
4010 door->part_2.step_xoffset = 0;
4011 door->part_2.step_yoffset = door->step_offset * -1;
4013 num_door_steps = g_part_1->height / door->step_offset;
4016 /* set animation draw offset for the default panels */
4018 if (door->step_offset > 1)
4020 num_panel_steps = 2 * door_rect->height / door->step_offset;
4021 door->panel.start_step = num_panel_steps - num_door_steps;
4022 door->panel.start_step_closing = door->panel.start_step;
4026 num_panel_steps = door_rect->height / door->step_offset;
4027 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4028 door->panel.start_step_closing = door->panel.start_step;
4029 door->panel.step_delay *= 2;
4040 for (i = 0; door_part_controls[i].door_token != -1; i++)
4042 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4043 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4045 /* initialize "start_step_opening" and "start_step_closing", if needed */
4046 if (dpc->pos->start_step_opening == 0 &&
4047 dpc->pos->start_step_closing == 0)
4049 // dpc->pos->start_step_opening = dpc->pos->start_step;
4050 dpc->pos->start_step_closing = dpc->pos->start_step;
4053 /* fill structure for door part draw order (sorted below) */
4055 dpo->sort_priority = dpc->pos->sort_priority;
4058 /* sort door part controls according to sort_priority and graphic number */
4059 qsort(door_part_order, MAX_DOOR_PARTS,
4060 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4063 unsigned int OpenDoor(unsigned int door_state)
4065 if (door_state & DOOR_COPY_BACK)
4067 if (door_state & DOOR_OPEN_1)
4068 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4069 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4071 if (door_state & DOOR_OPEN_2)
4072 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4073 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4075 door_state &= ~DOOR_COPY_BACK;
4078 return MoveDoor(door_state);
4081 unsigned int CloseDoor(unsigned int door_state)
4083 unsigned int old_door_state = GetDoorState();
4085 if (!(door_state & DOOR_NO_COPY_BACK))
4087 if (old_door_state & DOOR_OPEN_1)
4088 BlitBitmap(backbuffer, bitmap_db_door_1,
4089 DX, DY, DXSIZE, DYSIZE, 0, 0);
4091 if (old_door_state & DOOR_OPEN_2)
4092 BlitBitmap(backbuffer, bitmap_db_door_2,
4093 VX, VY, VXSIZE, VYSIZE, 0, 0);
4095 door_state &= ~DOOR_NO_COPY_BACK;
4098 return MoveDoor(door_state);
4101 unsigned int GetDoorState()
4103 return MoveDoor(DOOR_GET_STATE);
4106 unsigned int SetDoorState(unsigned int door_state)
4108 return MoveDoor(door_state | DOOR_SET_STATE);
4111 int euclid(int a, int b)
4113 return (b ? euclid(b, a % b) : a);
4116 unsigned int MoveDoor(unsigned int door_state)
4118 struct Rect door_rect_list[] =
4120 { DX, DY, DXSIZE, DYSIZE },
4121 { VX, VY, VXSIZE, VYSIZE }
4123 static int door1 = DOOR_OPEN_1;
4124 static int door2 = DOOR_CLOSE_2;
4125 unsigned int door_delay = 0;
4126 unsigned int door_delay_value;
4129 if (door_state == DOOR_GET_STATE)
4130 return (door1 | door2);
4132 if (door_state & DOOR_SET_STATE)
4134 if (door_state & DOOR_ACTION_1)
4135 door1 = door_state & DOOR_ACTION_1;
4136 if (door_state & DOOR_ACTION_2)
4137 door2 = door_state & DOOR_ACTION_2;
4139 return (door1 | door2);
4142 if (!(door_state & DOOR_FORCE_REDRAW))
4144 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4145 door_state &= ~DOOR_OPEN_1;
4146 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4147 door_state &= ~DOOR_CLOSE_1;
4148 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4149 door_state &= ~DOOR_OPEN_2;
4150 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4151 door_state &= ~DOOR_CLOSE_2;
4154 if (global.autoplay_leveldir)
4156 door_state |= DOOR_NO_DELAY;
4157 door_state &= ~DOOR_CLOSE_ALL;
4160 if (game_status == GAME_MODE_EDITOR)
4161 door_state |= DOOR_NO_DELAY;
4163 if (door_state & DOOR_ACTION)
4165 boolean door_panel_drawn[NUM_DOORS];
4166 boolean panel_has_doors[NUM_DOORS];
4167 boolean door_part_skip[MAX_DOOR_PARTS];
4168 boolean door_part_done[MAX_DOOR_PARTS];
4169 boolean door_part_done_all;
4170 int num_steps[MAX_DOOR_PARTS];
4171 int max_move_delay = 0; // delay for complete animations of all doors
4172 int max_step_delay = 0; // delay (ms) between two animation frames
4173 int num_move_steps = 0; // number of animation steps for all doors
4174 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4175 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4176 int current_move_delay = 0;
4180 for (i = 0; i < NUM_DOORS; i++)
4181 panel_has_doors[i] = FALSE;
4183 for (i = 0; i < MAX_DOOR_PARTS; i++)
4185 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4186 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4187 int door_token = dpc->door_token;
4189 door_part_done[i] = FALSE;
4190 door_part_skip[i] = (!(door_state & door_token) ||
4194 for (i = 0; i < MAX_DOOR_PARTS; i++)
4196 int nr = door_part_order[i].nr;
4197 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4198 struct DoorPartPosInfo *pos = dpc->pos;
4199 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4200 int door_token = dpc->door_token;
4201 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4202 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4203 int step_xoffset = ABS(pos->step_xoffset);
4204 int step_yoffset = ABS(pos->step_yoffset);
4205 int step_delay = pos->step_delay;
4206 int current_door_state = door_state & door_token;
4207 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4208 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4209 boolean part_opening = (is_panel ? door_closing : door_opening);
4210 int start_step = (part_opening ? pos->start_step_opening :
4211 pos->start_step_closing);
4212 float move_xsize = (step_xoffset ? g->width : 0);
4213 float move_ysize = (step_yoffset ? g->height : 0);
4214 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4215 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4216 int move_steps = (move_xsteps && move_ysteps ?
4217 MIN(move_xsteps, move_ysteps) :
4218 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4219 int move_delay = move_steps * step_delay;
4221 if (door_part_skip[nr])
4224 max_move_delay = MAX(max_move_delay, move_delay);
4225 max_step_delay = (max_step_delay == 0 ? step_delay :
4226 euclid(max_step_delay, step_delay));
4227 num_steps[nr] = move_steps;
4231 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4233 panel_has_doors[door_index] = TRUE;
4237 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4239 num_move_steps = max_move_delay / max_step_delay;
4240 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4242 door_delay_value = max_step_delay;
4244 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4246 start = num_move_steps - 1;
4250 /* opening door sound has priority over simultaneously closing door */
4251 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4252 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4253 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4254 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4257 for (k = start; k < num_move_steps; k++)
4259 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4261 door_part_done_all = TRUE;
4263 for (i = 0; i < NUM_DOORS; i++)
4264 door_panel_drawn[i] = FALSE;
4266 for (i = 0; i < MAX_DOOR_PARTS; i++)
4268 int nr = door_part_order[i].nr;
4269 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4270 struct DoorPartPosInfo *pos = dpc->pos;
4271 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4272 int door_token = dpc->door_token;
4273 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4274 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4275 boolean is_panel_and_door_has_closed = FALSE;
4276 struct Rect *door_rect = &door_rect_list[door_index];
4277 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4279 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4280 int current_door_state = door_state & door_token;
4281 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4282 boolean door_closing = !door_opening;
4283 boolean part_opening = (is_panel ? door_closing : door_opening);
4284 boolean part_closing = !part_opening;
4285 int start_step = (part_opening ? pos->start_step_opening :
4286 pos->start_step_closing);
4287 int step_delay = pos->step_delay;
4288 int step_factor = step_delay / max_step_delay;
4289 int k1 = (step_factor ? k / step_factor + 1 : k);
4290 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4291 int kk = MAX(0, k2);
4294 int src_x, src_y, src_xx, src_yy;
4295 int dst_x, dst_y, dst_xx, dst_yy;
4298 if (door_part_skip[nr])
4301 if (!(door_state & door_token))
4309 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4310 int kk_door = MAX(0, k2_door);
4311 int sync_frame = kk_door * door_delay_value;
4312 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4314 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4319 if (!door_panel_drawn[door_index])
4321 ClearRectangle(drawto, door_rect->x, door_rect->y,
4322 door_rect->width, door_rect->height);
4324 door_panel_drawn[door_index] = TRUE;
4327 // draw opening or closing door parts
4329 if (pos->step_xoffset < 0) // door part on right side
4332 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4335 if (dst_xx + width > door_rect->width)
4336 width = door_rect->width - dst_xx;
4338 else // door part on left side
4341 dst_xx = pos->x - kk * pos->step_xoffset;
4345 src_xx = ABS(dst_xx);
4349 width = g->width - src_xx;
4351 if (width > door_rect->width)
4352 width = door_rect->width;
4354 // printf("::: k == %d [%d] \n", k, start_step);
4357 if (pos->step_yoffset < 0) // door part on bottom side
4360 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4363 if (dst_yy + height > door_rect->height)
4364 height = door_rect->height - dst_yy;
4366 else // door part on top side
4369 dst_yy = pos->y - kk * pos->step_yoffset;
4373 src_yy = ABS(dst_yy);
4377 height = g->height - src_yy;
4380 src_x = g_src_x + src_xx;
4381 src_y = g_src_y + src_yy;
4383 dst_x = door_rect->x + dst_xx;
4384 dst_y = door_rect->y + dst_yy;
4386 is_panel_and_door_has_closed =
4389 panel_has_doors[door_index] &&
4390 k >= num_move_steps_doors_only - 1);
4392 if (width >= 0 && width <= g->width &&
4393 height >= 0 && height <= g->height &&
4394 !is_panel_and_door_has_closed)
4396 if (is_panel || !pos->draw_masked)
4397 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4400 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4404 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4406 if ((part_opening && (width < 0 || height < 0)) ||
4407 (part_closing && (width >= g->width && height >= g->height)))
4408 door_part_done[nr] = TRUE;
4410 // continue door part animations, but not panel after door has closed
4411 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4412 door_part_done_all = FALSE;
4415 if (!(door_state & DOOR_NO_DELAY))
4419 if (game_status == GAME_MODE_MAIN)
4422 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4424 current_move_delay += max_step_delay;
4427 if (door_part_done_all)
4432 if (door_state & DOOR_ACTION_1)
4433 door1 = door_state & DOOR_ACTION_1;
4434 if (door_state & DOOR_ACTION_2)
4435 door2 = door_state & DOOR_ACTION_2;
4437 return (door1 | door2);
4440 void DrawSpecialEditorDoor()
4442 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4443 int top_border_width = gfx1->width;
4444 int top_border_height = gfx1->height;
4445 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4446 int ex = EX - outer_border;
4447 int ey = EY - outer_border;
4448 int vy = VY - outer_border;
4449 int exsize = EXSIZE + 2 * outer_border;
4451 /* draw bigger level editor toolbox window */
4452 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4453 top_border_width, top_border_height, ex, ey - top_border_height);
4454 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4455 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4457 redraw_mask |= REDRAW_ALL;
4460 void UndrawSpecialEditorDoor()
4462 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4463 int top_border_width = gfx1->width;
4464 int top_border_height = gfx1->height;
4465 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4466 int ex = EX - outer_border;
4467 int ey = EY - outer_border;
4468 int ey_top = ey - top_border_height;
4469 int exsize = EXSIZE + 2 * outer_border;
4470 int eysize = EYSIZE + 2 * outer_border;
4472 /* draw normal tape recorder window */
4473 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4475 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4476 ex, ey_top, top_border_width, top_border_height,
4478 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4479 ex, ey, exsize, eysize, ex, ey);
4483 // if screen background is set to "[NONE]", clear editor toolbox window
4484 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4485 ClearRectangle(drawto, ex, ey, exsize, eysize);
4488 redraw_mask |= REDRAW_ALL;
4492 /* ---------- new tool button stuff ---------------------------------------- */
4497 struct TextPosInfo *pos;
4500 } toolbutton_info[NUM_TOOL_BUTTONS] =
4503 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4504 TOOL_CTRL_ID_YES, "yes"
4507 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4508 TOOL_CTRL_ID_NO, "no"
4511 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4512 TOOL_CTRL_ID_CONFIRM, "confirm"
4515 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4516 TOOL_CTRL_ID_PLAYER_1, "player 1"
4519 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4520 TOOL_CTRL_ID_PLAYER_2, "player 2"
4523 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4524 TOOL_CTRL_ID_PLAYER_3, "player 3"
4527 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4528 TOOL_CTRL_ID_PLAYER_4, "player 4"
4532 void CreateToolButtons()
4536 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4538 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4539 struct TextPosInfo *pos = toolbutton_info[i].pos;
4540 struct GadgetInfo *gi;
4541 Bitmap *deco_bitmap = None;
4542 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4543 unsigned int event_mask = GD_EVENT_RELEASED;
4546 int gd_x = gfx->src_x;
4547 int gd_y = gfx->src_y;
4548 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4549 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4552 if (global.use_envelope_request)
4553 setRequestPosition(&dx, &dy, TRUE);
4555 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4557 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4559 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4560 pos->size, &deco_bitmap, &deco_x, &deco_y);
4561 deco_xpos = (gfx->width - pos->size) / 2;
4562 deco_ypos = (gfx->height - pos->size) / 2;
4565 gi = CreateGadget(GDI_CUSTOM_ID, id,
4566 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4567 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4568 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4569 GDI_WIDTH, gfx->width,
4570 GDI_HEIGHT, gfx->height,
4571 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4572 GDI_STATE, GD_BUTTON_UNPRESSED,
4573 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4574 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4575 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4576 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4577 GDI_DECORATION_SIZE, pos->size, pos->size,
4578 GDI_DECORATION_SHIFTING, 1, 1,
4579 GDI_DIRECT_DRAW, FALSE,
4580 GDI_EVENT_MASK, event_mask,
4581 GDI_CALLBACK_ACTION, HandleToolButtons,
4585 Error(ERR_EXIT, "cannot create gadget");
4587 tool_gadget[id] = gi;
4591 void FreeToolButtons()
4595 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4596 FreeGadget(tool_gadget[i]);
4599 static void UnmapToolButtons()
4603 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4604 UnmapGadget(tool_gadget[i]);
4607 static void HandleToolButtons(struct GadgetInfo *gi)
4609 request_gadget_id = gi->custom_id;
4612 static struct Mapping_EM_to_RND_object
4615 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4616 boolean is_backside; /* backside of moving element */
4622 em_object_mapping_list[] =
4625 Xblank, TRUE, FALSE,
4629 Yacid_splash_eB, FALSE, FALSE,
4630 EL_ACID_SPLASH_RIGHT, -1, -1
4633 Yacid_splash_wB, FALSE, FALSE,
4634 EL_ACID_SPLASH_LEFT, -1, -1
4637 #ifdef EM_ENGINE_BAD_ROLL
4639 Xstone_force_e, FALSE, FALSE,
4640 EL_ROCK, -1, MV_BIT_RIGHT
4643 Xstone_force_w, FALSE, FALSE,
4644 EL_ROCK, -1, MV_BIT_LEFT
4647 Xnut_force_e, FALSE, FALSE,
4648 EL_NUT, -1, MV_BIT_RIGHT
4651 Xnut_force_w, FALSE, FALSE,
4652 EL_NUT, -1, MV_BIT_LEFT
4655 Xspring_force_e, FALSE, FALSE,
4656 EL_SPRING, -1, MV_BIT_RIGHT
4659 Xspring_force_w, FALSE, FALSE,
4660 EL_SPRING, -1, MV_BIT_LEFT
4663 Xemerald_force_e, FALSE, FALSE,
4664 EL_EMERALD, -1, MV_BIT_RIGHT
4667 Xemerald_force_w, FALSE, FALSE,
4668 EL_EMERALD, -1, MV_BIT_LEFT
4671 Xdiamond_force_e, FALSE, FALSE,
4672 EL_DIAMOND, -1, MV_BIT_RIGHT
4675 Xdiamond_force_w, FALSE, FALSE,
4676 EL_DIAMOND, -1, MV_BIT_LEFT
4679 Xbomb_force_e, FALSE, FALSE,
4680 EL_BOMB, -1, MV_BIT_RIGHT
4683 Xbomb_force_w, FALSE, FALSE,
4684 EL_BOMB, -1, MV_BIT_LEFT
4686 #endif /* EM_ENGINE_BAD_ROLL */
4689 Xstone, TRUE, FALSE,
4693 Xstone_pause, FALSE, FALSE,
4697 Xstone_fall, FALSE, FALSE,
4701 Ystone_s, FALSE, FALSE,
4702 EL_ROCK, ACTION_FALLING, -1
4705 Ystone_sB, FALSE, TRUE,
4706 EL_ROCK, ACTION_FALLING, -1
4709 Ystone_e, FALSE, FALSE,
4710 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4713 Ystone_eB, FALSE, TRUE,
4714 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4717 Ystone_w, FALSE, FALSE,
4718 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4721 Ystone_wB, FALSE, TRUE,
4722 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4729 Xnut_pause, FALSE, FALSE,
4733 Xnut_fall, FALSE, FALSE,
4737 Ynut_s, FALSE, FALSE,
4738 EL_NUT, ACTION_FALLING, -1
4741 Ynut_sB, FALSE, TRUE,
4742 EL_NUT, ACTION_FALLING, -1
4745 Ynut_e, FALSE, FALSE,
4746 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4749 Ynut_eB, FALSE, TRUE,
4750 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4753 Ynut_w, FALSE, FALSE,
4754 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4757 Ynut_wB, FALSE, TRUE,
4758 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4761 Xbug_n, TRUE, FALSE,
4765 Xbug_e, TRUE, FALSE,
4766 EL_BUG_RIGHT, -1, -1
4769 Xbug_s, TRUE, FALSE,
4773 Xbug_w, TRUE, FALSE,
4777 Xbug_gon, FALSE, FALSE,
4781 Xbug_goe, FALSE, FALSE,
4782 EL_BUG_RIGHT, -1, -1
4785 Xbug_gos, FALSE, FALSE,
4789 Xbug_gow, FALSE, FALSE,
4793 Ybug_n, FALSE, FALSE,
4794 EL_BUG, ACTION_MOVING, MV_BIT_UP
4797 Ybug_nB, FALSE, TRUE,
4798 EL_BUG, ACTION_MOVING, MV_BIT_UP
4801 Ybug_e, FALSE, FALSE,
4802 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4805 Ybug_eB, FALSE, TRUE,
4806 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4809 Ybug_s, FALSE, FALSE,
4810 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4813 Ybug_sB, FALSE, TRUE,
4814 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4817 Ybug_w, FALSE, FALSE,
4818 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4821 Ybug_wB, FALSE, TRUE,
4822 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4825 Ybug_w_n, FALSE, FALSE,
4826 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4829 Ybug_n_e, FALSE, FALSE,
4830 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4833 Ybug_e_s, FALSE, FALSE,
4834 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4837 Ybug_s_w, FALSE, FALSE,
4838 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4841 Ybug_e_n, FALSE, FALSE,
4842 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4845 Ybug_s_e, FALSE, FALSE,
4846 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4849 Ybug_w_s, FALSE, FALSE,
4850 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4853 Ybug_n_w, FALSE, FALSE,
4854 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4857 Ybug_stone, FALSE, FALSE,
4858 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4861 Ybug_spring, FALSE, FALSE,
4862 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4865 Xtank_n, TRUE, FALSE,
4866 EL_SPACESHIP_UP, -1, -1
4869 Xtank_e, TRUE, FALSE,
4870 EL_SPACESHIP_RIGHT, -1, -1
4873 Xtank_s, TRUE, FALSE,
4874 EL_SPACESHIP_DOWN, -1, -1
4877 Xtank_w, TRUE, FALSE,
4878 EL_SPACESHIP_LEFT, -1, -1
4881 Xtank_gon, FALSE, FALSE,
4882 EL_SPACESHIP_UP, -1, -1
4885 Xtank_goe, FALSE, FALSE,
4886 EL_SPACESHIP_RIGHT, -1, -1
4889 Xtank_gos, FALSE, FALSE,
4890 EL_SPACESHIP_DOWN, -1, -1
4893 Xtank_gow, FALSE, FALSE,
4894 EL_SPACESHIP_LEFT, -1, -1
4897 Ytank_n, FALSE, FALSE,
4898 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4901 Ytank_nB, FALSE, TRUE,
4902 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4905 Ytank_e, FALSE, FALSE,
4906 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4909 Ytank_eB, FALSE, TRUE,
4910 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4913 Ytank_s, FALSE, FALSE,
4914 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4917 Ytank_sB, FALSE, TRUE,
4918 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4921 Ytank_w, FALSE, FALSE,
4922 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4925 Ytank_wB, FALSE, TRUE,
4926 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4929 Ytank_w_n, FALSE, FALSE,
4930 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4933 Ytank_n_e, FALSE, FALSE,
4934 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4937 Ytank_e_s, FALSE, FALSE,
4938 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4941 Ytank_s_w, FALSE, FALSE,
4942 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4945 Ytank_e_n, FALSE, FALSE,
4946 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4949 Ytank_s_e, FALSE, FALSE,
4950 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4953 Ytank_w_s, FALSE, FALSE,
4954 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4957 Ytank_n_w, FALSE, FALSE,
4958 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4961 Ytank_stone, FALSE, FALSE,
4962 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4965 Ytank_spring, FALSE, FALSE,
4966 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4969 Xandroid, TRUE, FALSE,
4970 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4973 Xandroid_1_n, FALSE, FALSE,
4974 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4977 Xandroid_2_n, FALSE, FALSE,
4978 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4981 Xandroid_1_e, FALSE, FALSE,
4982 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4985 Xandroid_2_e, FALSE, FALSE,
4986 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4989 Xandroid_1_w, FALSE, FALSE,
4990 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4993 Xandroid_2_w, FALSE, FALSE,
4994 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4997 Xandroid_1_s, FALSE, FALSE,
4998 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5001 Xandroid_2_s, FALSE, FALSE,
5002 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5005 Yandroid_n, FALSE, FALSE,
5006 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5009 Yandroid_nB, FALSE, TRUE,
5010 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5013 Yandroid_ne, FALSE, FALSE,
5014 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5017 Yandroid_neB, FALSE, TRUE,
5018 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5021 Yandroid_e, FALSE, FALSE,
5022 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5025 Yandroid_eB, FALSE, TRUE,
5026 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5029 Yandroid_se, FALSE, FALSE,
5030 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5033 Yandroid_seB, FALSE, TRUE,
5034 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5037 Yandroid_s, FALSE, FALSE,
5038 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5041 Yandroid_sB, FALSE, TRUE,
5042 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5045 Yandroid_sw, FALSE, FALSE,
5046 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5049 Yandroid_swB, FALSE, TRUE,
5050 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5053 Yandroid_w, FALSE, FALSE,
5054 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5057 Yandroid_wB, FALSE, TRUE,
5058 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5061 Yandroid_nw, FALSE, FALSE,
5062 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5065 Yandroid_nwB, FALSE, TRUE,
5066 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5069 Xspring, TRUE, FALSE,
5073 Xspring_pause, FALSE, FALSE,
5077 Xspring_e, FALSE, FALSE,
5081 Xspring_w, FALSE, FALSE,
5085 Xspring_fall, FALSE, FALSE,
5089 Yspring_s, FALSE, FALSE,
5090 EL_SPRING, ACTION_FALLING, -1
5093 Yspring_sB, FALSE, TRUE,
5094 EL_SPRING, ACTION_FALLING, -1
5097 Yspring_e, FALSE, FALSE,
5098 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5101 Yspring_eB, FALSE, TRUE,
5102 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5105 Yspring_w, FALSE, FALSE,
5106 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5109 Yspring_wB, FALSE, TRUE,
5110 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5113 Yspring_kill_e, FALSE, FALSE,
5114 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5117 Yspring_kill_eB, FALSE, TRUE,
5118 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5121 Yspring_kill_w, FALSE, FALSE,
5122 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5125 Yspring_kill_wB, FALSE, TRUE,
5126 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5129 Xeater_n, TRUE, FALSE,
5130 EL_YAMYAM_UP, -1, -1
5133 Xeater_e, TRUE, FALSE,
5134 EL_YAMYAM_RIGHT, -1, -1
5137 Xeater_w, TRUE, FALSE,
5138 EL_YAMYAM_LEFT, -1, -1
5141 Xeater_s, TRUE, FALSE,
5142 EL_YAMYAM_DOWN, -1, -1
5145 Yeater_n, FALSE, FALSE,
5146 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5149 Yeater_nB, FALSE, TRUE,
5150 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5153 Yeater_e, FALSE, FALSE,
5154 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5157 Yeater_eB, FALSE, TRUE,
5158 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5161 Yeater_s, FALSE, FALSE,
5162 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5165 Yeater_sB, FALSE, TRUE,
5166 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5169 Yeater_w, FALSE, FALSE,
5170 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5173 Yeater_wB, FALSE, TRUE,
5174 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5177 Yeater_stone, FALSE, FALSE,
5178 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5181 Yeater_spring, FALSE, FALSE,
5182 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5185 Xalien, TRUE, FALSE,
5189 Xalien_pause, FALSE, FALSE,
5193 Yalien_n, FALSE, FALSE,
5194 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5197 Yalien_nB, FALSE, TRUE,
5198 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5201 Yalien_e, FALSE, FALSE,
5202 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5205 Yalien_eB, FALSE, TRUE,
5206 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5209 Yalien_s, FALSE, FALSE,
5210 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5213 Yalien_sB, FALSE, TRUE,
5214 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5217 Yalien_w, FALSE, FALSE,
5218 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5221 Yalien_wB, FALSE, TRUE,
5222 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5225 Yalien_stone, FALSE, FALSE,
5226 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5229 Yalien_spring, FALSE, FALSE,
5230 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5233 Xemerald, TRUE, FALSE,
5237 Xemerald_pause, FALSE, FALSE,
5241 Xemerald_fall, FALSE, FALSE,
5245 Xemerald_shine, FALSE, FALSE,
5246 EL_EMERALD, ACTION_TWINKLING, -1
5249 Yemerald_s, FALSE, FALSE,
5250 EL_EMERALD, ACTION_FALLING, -1
5253 Yemerald_sB, FALSE, TRUE,
5254 EL_EMERALD, ACTION_FALLING, -1
5257 Yemerald_e, FALSE, FALSE,
5258 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5261 Yemerald_eB, FALSE, TRUE,
5262 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5265 Yemerald_w, FALSE, FALSE,
5266 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5269 Yemerald_wB, FALSE, TRUE,
5270 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5273 Yemerald_eat, FALSE, FALSE,
5274 EL_EMERALD, ACTION_COLLECTING, -1
5277 Yemerald_stone, FALSE, FALSE,
5278 EL_NUT, ACTION_BREAKING, -1
5281 Xdiamond, TRUE, FALSE,
5285 Xdiamond_pause, FALSE, FALSE,
5289 Xdiamond_fall, FALSE, FALSE,
5293 Xdiamond_shine, FALSE, FALSE,
5294 EL_DIAMOND, ACTION_TWINKLING, -1
5297 Ydiamond_s, FALSE, FALSE,
5298 EL_DIAMOND, ACTION_FALLING, -1
5301 Ydiamond_sB, FALSE, TRUE,
5302 EL_DIAMOND, ACTION_FALLING, -1
5305 Ydiamond_e, FALSE, FALSE,
5306 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5309 Ydiamond_eB, FALSE, TRUE,
5310 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5313 Ydiamond_w, FALSE, FALSE,
5314 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5317 Ydiamond_wB, FALSE, TRUE,
5318 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5321 Ydiamond_eat, FALSE, FALSE,
5322 EL_DIAMOND, ACTION_COLLECTING, -1
5325 Ydiamond_stone, FALSE, FALSE,
5326 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5329 Xdrip_fall, TRUE, FALSE,
5330 EL_AMOEBA_DROP, -1, -1
5333 Xdrip_stretch, FALSE, FALSE,
5334 EL_AMOEBA_DROP, ACTION_FALLING, -1
5337 Xdrip_stretchB, FALSE, TRUE,
5338 EL_AMOEBA_DROP, ACTION_FALLING, -1
5341 Xdrip_eat, FALSE, FALSE,
5342 EL_AMOEBA_DROP, ACTION_GROWING, -1
5345 Ydrip_s1, FALSE, FALSE,
5346 EL_AMOEBA_DROP, ACTION_FALLING, -1
5349 Ydrip_s1B, FALSE, TRUE,
5350 EL_AMOEBA_DROP, ACTION_FALLING, -1
5353 Ydrip_s2, FALSE, FALSE,
5354 EL_AMOEBA_DROP, ACTION_FALLING, -1
5357 Ydrip_s2B, FALSE, TRUE,
5358 EL_AMOEBA_DROP, ACTION_FALLING, -1
5365 Xbomb_pause, FALSE, FALSE,
5369 Xbomb_fall, FALSE, FALSE,
5373 Ybomb_s, FALSE, FALSE,
5374 EL_BOMB, ACTION_FALLING, -1
5377 Ybomb_sB, FALSE, TRUE,
5378 EL_BOMB, ACTION_FALLING, -1
5381 Ybomb_e, FALSE, FALSE,
5382 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5385 Ybomb_eB, FALSE, TRUE,
5386 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5389 Ybomb_w, FALSE, FALSE,
5390 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5393 Ybomb_wB, FALSE, TRUE,
5394 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5397 Ybomb_eat, FALSE, FALSE,
5398 EL_BOMB, ACTION_ACTIVATING, -1
5401 Xballoon, TRUE, FALSE,
5405 Yballoon_n, FALSE, FALSE,
5406 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5409 Yballoon_nB, FALSE, TRUE,
5410 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5413 Yballoon_e, FALSE, FALSE,
5414 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5417 Yballoon_eB, FALSE, TRUE,
5418 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5421 Yballoon_s, FALSE, FALSE,
5422 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5425 Yballoon_sB, FALSE, TRUE,
5426 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5429 Yballoon_w, FALSE, FALSE,
5430 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5433 Yballoon_wB, FALSE, TRUE,
5434 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5437 Xgrass, TRUE, FALSE,
5438 EL_EMC_GRASS, -1, -1
5441 Ygrass_nB, FALSE, FALSE,
5442 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5445 Ygrass_eB, FALSE, FALSE,
5446 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5449 Ygrass_sB, FALSE, FALSE,
5450 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5453 Ygrass_wB, FALSE, FALSE,
5454 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5461 Ydirt_nB, FALSE, FALSE,
5462 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5465 Ydirt_eB, FALSE, FALSE,
5466 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5469 Ydirt_sB, FALSE, FALSE,
5470 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5473 Ydirt_wB, FALSE, FALSE,
5474 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5477 Xacid_ne, TRUE, FALSE,
5478 EL_ACID_POOL_TOPRIGHT, -1, -1
5481 Xacid_se, TRUE, FALSE,
5482 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5485 Xacid_s, TRUE, FALSE,
5486 EL_ACID_POOL_BOTTOM, -1, -1
5489 Xacid_sw, TRUE, FALSE,
5490 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5493 Xacid_nw, TRUE, FALSE,
5494 EL_ACID_POOL_TOPLEFT, -1, -1
5497 Xacid_1, TRUE, FALSE,
5501 Xacid_2, FALSE, FALSE,
5505 Xacid_3, FALSE, FALSE,
5509 Xacid_4, FALSE, FALSE,
5513 Xacid_5, FALSE, FALSE,
5517 Xacid_6, FALSE, FALSE,
5521 Xacid_7, FALSE, FALSE,
5525 Xacid_8, FALSE, FALSE,
5529 Xball_1, TRUE, FALSE,
5530 EL_EMC_MAGIC_BALL, -1, -1
5533 Xball_1B, FALSE, FALSE,
5534 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5537 Xball_2, FALSE, FALSE,
5538 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5541 Xball_2B, FALSE, FALSE,
5542 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5545 Yball_eat, FALSE, FALSE,
5546 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5549 Ykey_1_eat, FALSE, FALSE,
5550 EL_EM_KEY_1, ACTION_COLLECTING, -1
5553 Ykey_2_eat, FALSE, FALSE,
5554 EL_EM_KEY_2, ACTION_COLLECTING, -1
5557 Ykey_3_eat, FALSE, FALSE,
5558 EL_EM_KEY_3, ACTION_COLLECTING, -1
5561 Ykey_4_eat, FALSE, FALSE,
5562 EL_EM_KEY_4, ACTION_COLLECTING, -1
5565 Ykey_5_eat, FALSE, FALSE,
5566 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5569 Ykey_6_eat, FALSE, FALSE,
5570 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5573 Ykey_7_eat, FALSE, FALSE,
5574 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5577 Ykey_8_eat, FALSE, FALSE,
5578 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5581 Ylenses_eat, FALSE, FALSE,
5582 EL_EMC_LENSES, ACTION_COLLECTING, -1
5585 Ymagnify_eat, FALSE, FALSE,
5586 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5589 Ygrass_eat, FALSE, FALSE,
5590 EL_EMC_GRASS, ACTION_SNAPPING, -1
5593 Ydirt_eat, FALSE, FALSE,
5594 EL_SAND, ACTION_SNAPPING, -1
5597 Xgrow_ns, TRUE, FALSE,
5598 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5601 Ygrow_ns_eat, FALSE, FALSE,
5602 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5605 Xgrow_ew, TRUE, FALSE,
5606 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5609 Ygrow_ew_eat, FALSE, FALSE,
5610 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5613 Xwonderwall, TRUE, FALSE,
5614 EL_MAGIC_WALL, -1, -1
5617 XwonderwallB, FALSE, FALSE,
5618 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5621 Xamoeba_1, TRUE, FALSE,
5622 EL_AMOEBA_DRY, ACTION_OTHER, -1
5625 Xamoeba_2, FALSE, FALSE,
5626 EL_AMOEBA_DRY, ACTION_OTHER, -1
5629 Xamoeba_3, FALSE, FALSE,
5630 EL_AMOEBA_DRY, ACTION_OTHER, -1
5633 Xamoeba_4, FALSE, FALSE,
5634 EL_AMOEBA_DRY, ACTION_OTHER, -1
5637 Xamoeba_5, TRUE, FALSE,
5638 EL_AMOEBA_WET, ACTION_OTHER, -1
5641 Xamoeba_6, FALSE, FALSE,
5642 EL_AMOEBA_WET, ACTION_OTHER, -1
5645 Xamoeba_7, FALSE, FALSE,
5646 EL_AMOEBA_WET, ACTION_OTHER, -1
5649 Xamoeba_8, FALSE, FALSE,
5650 EL_AMOEBA_WET, ACTION_OTHER, -1
5653 Xdoor_1, TRUE, FALSE,
5654 EL_EM_GATE_1, -1, -1
5657 Xdoor_2, TRUE, FALSE,
5658 EL_EM_GATE_2, -1, -1
5661 Xdoor_3, TRUE, FALSE,
5662 EL_EM_GATE_3, -1, -1
5665 Xdoor_4, TRUE, FALSE,
5666 EL_EM_GATE_4, -1, -1
5669 Xdoor_5, TRUE, FALSE,
5670 EL_EMC_GATE_5, -1, -1
5673 Xdoor_6, TRUE, FALSE,
5674 EL_EMC_GATE_6, -1, -1
5677 Xdoor_7, TRUE, FALSE,
5678 EL_EMC_GATE_7, -1, -1
5681 Xdoor_8, TRUE, FALSE,
5682 EL_EMC_GATE_8, -1, -1
5685 Xkey_1, TRUE, FALSE,
5689 Xkey_2, TRUE, FALSE,
5693 Xkey_3, TRUE, FALSE,
5697 Xkey_4, TRUE, FALSE,
5701 Xkey_5, TRUE, FALSE,
5702 EL_EMC_KEY_5, -1, -1
5705 Xkey_6, TRUE, FALSE,
5706 EL_EMC_KEY_6, -1, -1
5709 Xkey_7, TRUE, FALSE,
5710 EL_EMC_KEY_7, -1, -1
5713 Xkey_8, TRUE, FALSE,
5714 EL_EMC_KEY_8, -1, -1
5717 Xwind_n, TRUE, FALSE,
5718 EL_BALLOON_SWITCH_UP, -1, -1
5721 Xwind_e, TRUE, FALSE,
5722 EL_BALLOON_SWITCH_RIGHT, -1, -1
5725 Xwind_s, TRUE, FALSE,
5726 EL_BALLOON_SWITCH_DOWN, -1, -1
5729 Xwind_w, TRUE, FALSE,
5730 EL_BALLOON_SWITCH_LEFT, -1, -1
5733 Xwind_nesw, TRUE, FALSE,
5734 EL_BALLOON_SWITCH_ANY, -1, -1
5737 Xwind_stop, TRUE, FALSE,
5738 EL_BALLOON_SWITCH_NONE, -1, -1
5742 EL_EM_EXIT_CLOSED, -1, -1
5745 Xexit_1, TRUE, FALSE,
5746 EL_EM_EXIT_OPEN, -1, -1
5749 Xexit_2, FALSE, FALSE,
5750 EL_EM_EXIT_OPEN, -1, -1
5753 Xexit_3, FALSE, FALSE,
5754 EL_EM_EXIT_OPEN, -1, -1
5757 Xdynamite, TRUE, FALSE,
5758 EL_EM_DYNAMITE, -1, -1
5761 Ydynamite_eat, FALSE, FALSE,
5762 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5765 Xdynamite_1, TRUE, FALSE,
5766 EL_EM_DYNAMITE_ACTIVE, -1, -1
5769 Xdynamite_2, FALSE, FALSE,
5770 EL_EM_DYNAMITE_ACTIVE, -1, -1
5773 Xdynamite_3, FALSE, FALSE,
5774 EL_EM_DYNAMITE_ACTIVE, -1, -1
5777 Xdynamite_4, FALSE, FALSE,
5778 EL_EM_DYNAMITE_ACTIVE, -1, -1
5781 Xbumper, TRUE, FALSE,
5782 EL_EMC_SPRING_BUMPER, -1, -1
5785 XbumperB, FALSE, FALSE,
5786 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5789 Xwheel, TRUE, FALSE,
5790 EL_ROBOT_WHEEL, -1, -1
5793 XwheelB, FALSE, FALSE,
5794 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5797 Xswitch, TRUE, FALSE,
5798 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5801 XswitchB, FALSE, FALSE,
5802 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5806 EL_QUICKSAND_EMPTY, -1, -1
5809 Xsand_stone, TRUE, FALSE,
5810 EL_QUICKSAND_FULL, -1, -1
5813 Xsand_stonein_1, FALSE, TRUE,
5814 EL_ROCK, ACTION_FILLING, -1
5817 Xsand_stonein_2, FALSE, TRUE,
5818 EL_ROCK, ACTION_FILLING, -1
5821 Xsand_stonein_3, FALSE, TRUE,
5822 EL_ROCK, ACTION_FILLING, -1
5825 Xsand_stonein_4, FALSE, TRUE,
5826 EL_ROCK, ACTION_FILLING, -1
5829 Xsand_stonesand_1, FALSE, FALSE,
5830 EL_QUICKSAND_EMPTYING, -1, -1
5833 Xsand_stonesand_2, FALSE, FALSE,
5834 EL_QUICKSAND_EMPTYING, -1, -1
5837 Xsand_stonesand_3, FALSE, FALSE,
5838 EL_QUICKSAND_EMPTYING, -1, -1
5841 Xsand_stonesand_4, FALSE, FALSE,
5842 EL_QUICKSAND_EMPTYING, -1, -1
5845 Xsand_stonesand_quickout_1, FALSE, FALSE,
5846 EL_QUICKSAND_EMPTYING, -1, -1
5849 Xsand_stonesand_quickout_2, FALSE, FALSE,
5850 EL_QUICKSAND_EMPTYING, -1, -1
5853 Xsand_stoneout_1, FALSE, FALSE,
5854 EL_ROCK, ACTION_EMPTYING, -1
5857 Xsand_stoneout_2, FALSE, FALSE,
5858 EL_ROCK, ACTION_EMPTYING, -1
5861 Xsand_sandstone_1, FALSE, FALSE,
5862 EL_QUICKSAND_FILLING, -1, -1
5865 Xsand_sandstone_2, FALSE, FALSE,
5866 EL_QUICKSAND_FILLING, -1, -1
5869 Xsand_sandstone_3, FALSE, FALSE,
5870 EL_QUICKSAND_FILLING, -1, -1
5873 Xsand_sandstone_4, FALSE, FALSE,
5874 EL_QUICKSAND_FILLING, -1, -1
5877 Xplant, TRUE, FALSE,
5878 EL_EMC_PLANT, -1, -1
5881 Yplant, FALSE, FALSE,
5882 EL_EMC_PLANT, -1, -1
5885 Xlenses, TRUE, FALSE,
5886 EL_EMC_LENSES, -1, -1
5889 Xmagnify, TRUE, FALSE,
5890 EL_EMC_MAGNIFIER, -1, -1
5893 Xdripper, TRUE, FALSE,
5894 EL_EMC_DRIPPER, -1, -1
5897 XdripperB, FALSE, FALSE,
5898 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5901 Xfake_blank, TRUE, FALSE,
5902 EL_INVISIBLE_WALL, -1, -1
5905 Xfake_blankB, FALSE, FALSE,
5906 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5909 Xfake_grass, TRUE, FALSE,
5910 EL_EMC_FAKE_GRASS, -1, -1
5913 Xfake_grassB, FALSE, FALSE,
5914 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5917 Xfake_door_1, TRUE, FALSE,
5918 EL_EM_GATE_1_GRAY, -1, -1
5921 Xfake_door_2, TRUE, FALSE,
5922 EL_EM_GATE_2_GRAY, -1, -1
5925 Xfake_door_3, TRUE, FALSE,
5926 EL_EM_GATE_3_GRAY, -1, -1
5929 Xfake_door_4, TRUE, FALSE,
5930 EL_EM_GATE_4_GRAY, -1, -1
5933 Xfake_door_5, TRUE, FALSE,
5934 EL_EMC_GATE_5_GRAY, -1, -1
5937 Xfake_door_6, TRUE, FALSE,
5938 EL_EMC_GATE_6_GRAY, -1, -1
5941 Xfake_door_7, TRUE, FALSE,
5942 EL_EMC_GATE_7_GRAY, -1, -1
5945 Xfake_door_8, TRUE, FALSE,
5946 EL_EMC_GATE_8_GRAY, -1, -1
5949 Xfake_acid_1, TRUE, FALSE,
5950 EL_EMC_FAKE_ACID, -1, -1
5953 Xfake_acid_2, FALSE, FALSE,
5954 EL_EMC_FAKE_ACID, -1, -1
5957 Xfake_acid_3, FALSE, FALSE,
5958 EL_EMC_FAKE_ACID, -1, -1
5961 Xfake_acid_4, FALSE, FALSE,
5962 EL_EMC_FAKE_ACID, -1, -1
5965 Xfake_acid_5, FALSE, FALSE,
5966 EL_EMC_FAKE_ACID, -1, -1
5969 Xfake_acid_6, FALSE, FALSE,
5970 EL_EMC_FAKE_ACID, -1, -1
5973 Xfake_acid_7, FALSE, FALSE,
5974 EL_EMC_FAKE_ACID, -1, -1
5977 Xfake_acid_8, FALSE, FALSE,
5978 EL_EMC_FAKE_ACID, -1, -1
5981 Xsteel_1, TRUE, FALSE,
5982 EL_STEELWALL, -1, -1
5985 Xsteel_2, TRUE, FALSE,
5986 EL_EMC_STEELWALL_2, -1, -1
5989 Xsteel_3, TRUE, FALSE,
5990 EL_EMC_STEELWALL_3, -1, -1
5993 Xsteel_4, TRUE, FALSE,
5994 EL_EMC_STEELWALL_4, -1, -1
5997 Xwall_1, TRUE, FALSE,
6001 Xwall_2, TRUE, FALSE,
6002 EL_EMC_WALL_14, -1, -1
6005 Xwall_3, TRUE, FALSE,
6006 EL_EMC_WALL_15, -1, -1
6009 Xwall_4, TRUE, FALSE,
6010 EL_EMC_WALL_16, -1, -1
6013 Xround_wall_1, TRUE, FALSE,
6014 EL_WALL_SLIPPERY, -1, -1
6017 Xround_wall_2, TRUE, FALSE,
6018 EL_EMC_WALL_SLIPPERY_2, -1, -1
6021 Xround_wall_3, TRUE, FALSE,
6022 EL_EMC_WALL_SLIPPERY_3, -1, -1
6025 Xround_wall_4, TRUE, FALSE,
6026 EL_EMC_WALL_SLIPPERY_4, -1, -1
6029 Xdecor_1, TRUE, FALSE,
6030 EL_EMC_WALL_8, -1, -1
6033 Xdecor_2, TRUE, FALSE,
6034 EL_EMC_WALL_6, -1, -1
6037 Xdecor_3, TRUE, FALSE,
6038 EL_EMC_WALL_4, -1, -1
6041 Xdecor_4, TRUE, FALSE,
6042 EL_EMC_WALL_7, -1, -1
6045 Xdecor_5, TRUE, FALSE,
6046 EL_EMC_WALL_5, -1, -1
6049 Xdecor_6, TRUE, FALSE,
6050 EL_EMC_WALL_9, -1, -1
6053 Xdecor_7, TRUE, FALSE,
6054 EL_EMC_WALL_10, -1, -1
6057 Xdecor_8, TRUE, FALSE,
6058 EL_EMC_WALL_1, -1, -1
6061 Xdecor_9, TRUE, FALSE,
6062 EL_EMC_WALL_2, -1, -1
6065 Xdecor_10, TRUE, FALSE,
6066 EL_EMC_WALL_3, -1, -1
6069 Xdecor_11, TRUE, FALSE,
6070 EL_EMC_WALL_11, -1, -1
6073 Xdecor_12, TRUE, FALSE,
6074 EL_EMC_WALL_12, -1, -1
6077 Xalpha_0, TRUE, FALSE,
6078 EL_CHAR('0'), -1, -1
6081 Xalpha_1, TRUE, FALSE,
6082 EL_CHAR('1'), -1, -1
6085 Xalpha_2, TRUE, FALSE,
6086 EL_CHAR('2'), -1, -1
6089 Xalpha_3, TRUE, FALSE,
6090 EL_CHAR('3'), -1, -1
6093 Xalpha_4, TRUE, FALSE,
6094 EL_CHAR('4'), -1, -1
6097 Xalpha_5, TRUE, FALSE,
6098 EL_CHAR('5'), -1, -1
6101 Xalpha_6, TRUE, FALSE,
6102 EL_CHAR('6'), -1, -1
6105 Xalpha_7, TRUE, FALSE,
6106 EL_CHAR('7'), -1, -1
6109 Xalpha_8, TRUE, FALSE,
6110 EL_CHAR('8'), -1, -1
6113 Xalpha_9, TRUE, FALSE,
6114 EL_CHAR('9'), -1, -1
6117 Xalpha_excla, TRUE, FALSE,
6118 EL_CHAR('!'), -1, -1
6121 Xalpha_quote, TRUE, FALSE,
6122 EL_CHAR('"'), -1, -1
6125 Xalpha_comma, TRUE, FALSE,
6126 EL_CHAR(','), -1, -1
6129 Xalpha_minus, TRUE, FALSE,
6130 EL_CHAR('-'), -1, -1
6133 Xalpha_perio, TRUE, FALSE,
6134 EL_CHAR('.'), -1, -1
6137 Xalpha_colon, TRUE, FALSE,
6138 EL_CHAR(':'), -1, -1
6141 Xalpha_quest, TRUE, FALSE,
6142 EL_CHAR('?'), -1, -1
6145 Xalpha_a, TRUE, FALSE,
6146 EL_CHAR('A'), -1, -1
6149 Xalpha_b, TRUE, FALSE,
6150 EL_CHAR('B'), -1, -1
6153 Xalpha_c, TRUE, FALSE,
6154 EL_CHAR('C'), -1, -1
6157 Xalpha_d, TRUE, FALSE,
6158 EL_CHAR('D'), -1, -1
6161 Xalpha_e, TRUE, FALSE,
6162 EL_CHAR('E'), -1, -1
6165 Xalpha_f, TRUE, FALSE,
6166 EL_CHAR('F'), -1, -1
6169 Xalpha_g, TRUE, FALSE,
6170 EL_CHAR('G'), -1, -1
6173 Xalpha_h, TRUE, FALSE,
6174 EL_CHAR('H'), -1, -1
6177 Xalpha_i, TRUE, FALSE,
6178 EL_CHAR('I'), -1, -1
6181 Xalpha_j, TRUE, FALSE,
6182 EL_CHAR('J'), -1, -1
6185 Xalpha_k, TRUE, FALSE,
6186 EL_CHAR('K'), -1, -1
6189 Xalpha_l, TRUE, FALSE,
6190 EL_CHAR('L'), -1, -1
6193 Xalpha_m, TRUE, FALSE,
6194 EL_CHAR('M'), -1, -1
6197 Xalpha_n, TRUE, FALSE,
6198 EL_CHAR('N'), -1, -1
6201 Xalpha_o, TRUE, FALSE,
6202 EL_CHAR('O'), -1, -1
6205 Xalpha_p, TRUE, FALSE,
6206 EL_CHAR('P'), -1, -1
6209 Xalpha_q, TRUE, FALSE,
6210 EL_CHAR('Q'), -1, -1
6213 Xalpha_r, TRUE, FALSE,
6214 EL_CHAR('R'), -1, -1
6217 Xalpha_s, TRUE, FALSE,
6218 EL_CHAR('S'), -1, -1
6221 Xalpha_t, TRUE, FALSE,
6222 EL_CHAR('T'), -1, -1
6225 Xalpha_u, TRUE, FALSE,
6226 EL_CHAR('U'), -1, -1
6229 Xalpha_v, TRUE, FALSE,
6230 EL_CHAR('V'), -1, -1
6233 Xalpha_w, TRUE, FALSE,
6234 EL_CHAR('W'), -1, -1
6237 Xalpha_x, TRUE, FALSE,
6238 EL_CHAR('X'), -1, -1
6241 Xalpha_y, TRUE, FALSE,
6242 EL_CHAR('Y'), -1, -1
6245 Xalpha_z, TRUE, FALSE,
6246 EL_CHAR('Z'), -1, -1
6249 Xalpha_arrow_e, TRUE, FALSE,
6250 EL_CHAR('>'), -1, -1
6253 Xalpha_arrow_w, TRUE, FALSE,
6254 EL_CHAR('<'), -1, -1
6257 Xalpha_copyr, TRUE, FALSE,
6258 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6262 Xboom_bug, FALSE, FALSE,
6263 EL_BUG, ACTION_EXPLODING, -1
6266 Xboom_bomb, FALSE, FALSE,
6267 EL_BOMB, ACTION_EXPLODING, -1
6270 Xboom_android, FALSE, FALSE,
6271 EL_EMC_ANDROID, ACTION_OTHER, -1
6274 Xboom_1, FALSE, FALSE,
6275 EL_DEFAULT, ACTION_EXPLODING, -1
6278 Xboom_2, FALSE, FALSE,
6279 EL_DEFAULT, ACTION_EXPLODING, -1
6282 Znormal, FALSE, FALSE,
6286 Zdynamite, FALSE, FALSE,
6290 Zplayer, FALSE, FALSE,
6294 ZBORDER, FALSE, FALSE,
6304 static struct Mapping_EM_to_RND_player
6313 em_player_mapping_list[] =
6317 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6321 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6325 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6329 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6333 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6337 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6341 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6345 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6349 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6353 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6357 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6361 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6365 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6369 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6373 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6377 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6381 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6385 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6389 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6393 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6397 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6401 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6405 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6409 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6413 EL_PLAYER_1, ACTION_DEFAULT, -1,
6417 EL_PLAYER_2, ACTION_DEFAULT, -1,
6421 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6425 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6429 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6433 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6437 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6441 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6445 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6449 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6453 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6457 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6461 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6465 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6469 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6473 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6477 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6481 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6485 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6489 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6493 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6497 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6501 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6505 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6509 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6513 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6517 EL_PLAYER_3, ACTION_DEFAULT, -1,
6521 EL_PLAYER_4, ACTION_DEFAULT, -1,
6530 int map_element_RND_to_EM(int element_rnd)
6532 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6533 static boolean mapping_initialized = FALSE;
6535 if (!mapping_initialized)
6539 /* return "Xalpha_quest" for all undefined elements in mapping array */
6540 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6541 mapping_RND_to_EM[i] = Xalpha_quest;
6543 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6544 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6545 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6546 em_object_mapping_list[i].element_em;
6548 mapping_initialized = TRUE;
6551 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6552 return mapping_RND_to_EM[element_rnd];
6554 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6559 int map_element_EM_to_RND(int element_em)
6561 static unsigned short mapping_EM_to_RND[TILE_MAX];
6562 static boolean mapping_initialized = FALSE;
6564 if (!mapping_initialized)
6568 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6569 for (i = 0; i < TILE_MAX; i++)
6570 mapping_EM_to_RND[i] = EL_UNKNOWN;
6572 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6573 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6574 em_object_mapping_list[i].element_rnd;
6576 mapping_initialized = TRUE;
6579 if (element_em >= 0 && element_em < TILE_MAX)
6580 return mapping_EM_to_RND[element_em];
6582 Error(ERR_WARN, "invalid EM level element %d", element_em);
6587 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6589 struct LevelInfo_EM *level_em = level->native_em_level;
6590 struct LEVEL *lev = level_em->lev;
6593 for (i = 0; i < TILE_MAX; i++)
6594 lev->android_array[i] = Xblank;
6596 for (i = 0; i < level->num_android_clone_elements; i++)
6598 int element_rnd = level->android_clone_element[i];
6599 int element_em = map_element_RND_to_EM(element_rnd);
6601 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6602 if (em_object_mapping_list[j].element_rnd == element_rnd)
6603 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6607 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6609 struct LevelInfo_EM *level_em = level->native_em_level;
6610 struct LEVEL *lev = level_em->lev;
6613 level->num_android_clone_elements = 0;
6615 for (i = 0; i < TILE_MAX; i++)
6617 int element_em = lev->android_array[i];
6619 boolean element_found = FALSE;
6621 if (element_em == Xblank)
6624 element_rnd = map_element_EM_to_RND(element_em);
6626 for (j = 0; j < level->num_android_clone_elements; j++)
6627 if (level->android_clone_element[j] == element_rnd)
6628 element_found = TRUE;
6632 level->android_clone_element[level->num_android_clone_elements++] =
6635 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6640 if (level->num_android_clone_elements == 0)
6642 level->num_android_clone_elements = 1;
6643 level->android_clone_element[0] = EL_EMPTY;
6647 int map_direction_RND_to_EM(int direction)
6649 return (direction == MV_UP ? 0 :
6650 direction == MV_RIGHT ? 1 :
6651 direction == MV_DOWN ? 2 :
6652 direction == MV_LEFT ? 3 :
6656 int map_direction_EM_to_RND(int direction)
6658 return (direction == 0 ? MV_UP :
6659 direction == 1 ? MV_RIGHT :
6660 direction == 2 ? MV_DOWN :
6661 direction == 3 ? MV_LEFT :
6665 int map_element_RND_to_SP(int element_rnd)
6667 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6669 if (element_rnd >= EL_SP_START &&
6670 element_rnd <= EL_SP_END)
6671 element_sp = element_rnd - EL_SP_START;
6672 else if (element_rnd == EL_EMPTY_SPACE)
6674 else if (element_rnd == EL_INVISIBLE_WALL)
6680 int map_element_SP_to_RND(int element_sp)
6682 int element_rnd = EL_UNKNOWN;
6684 if (element_sp >= 0x00 &&
6686 element_rnd = EL_SP_START + element_sp;
6687 else if (element_sp == 0x28)
6688 element_rnd = EL_INVISIBLE_WALL;
6693 int map_action_SP_to_RND(int action_sp)
6697 case actActive: return ACTION_ACTIVE;
6698 case actImpact: return ACTION_IMPACT;
6699 case actExploding: return ACTION_EXPLODING;
6700 case actDigging: return ACTION_DIGGING;
6701 case actSnapping: return ACTION_SNAPPING;
6702 case actCollecting: return ACTION_COLLECTING;
6703 case actPassing: return ACTION_PASSING;
6704 case actPushing: return ACTION_PUSHING;
6705 case actDropping: return ACTION_DROPPING;
6707 default: return ACTION_DEFAULT;
6711 int get_next_element(int element)
6715 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6716 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6717 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6718 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6719 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6720 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6721 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6722 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6723 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6724 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6725 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6727 default: return element;
6731 int el_act_dir2img(int element, int action, int direction)
6733 element = GFX_ELEMENT(element);
6734 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6736 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6737 return element_info[element].direction_graphic[action][direction];
6740 static int el_act_dir2crm(int element, int action, int direction)
6742 element = GFX_ELEMENT(element);
6743 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6745 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6746 return element_info[element].direction_crumbled[action][direction];
6749 int el_act2img(int element, int action)
6751 element = GFX_ELEMENT(element);
6753 return element_info[element].graphic[action];
6756 int el_act2crm(int element, int action)
6758 element = GFX_ELEMENT(element);
6760 return element_info[element].crumbled[action];
6763 int el_dir2img(int element, int direction)
6765 element = GFX_ELEMENT(element);
6767 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6770 int el2baseimg(int element)
6772 return element_info[element].graphic[ACTION_DEFAULT];
6775 int el2img(int element)
6777 element = GFX_ELEMENT(element);
6779 return element_info[element].graphic[ACTION_DEFAULT];
6782 int el2edimg(int element)
6784 element = GFX_ELEMENT(element);
6786 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6789 int el2preimg(int element)
6791 element = GFX_ELEMENT(element);
6793 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6796 int el2panelimg(int element)
6798 element = GFX_ELEMENT(element);
6800 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6803 int font2baseimg(int font_nr)
6805 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6808 int getBeltNrFromBeltElement(int element)
6810 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6811 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6812 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6815 int getBeltNrFromBeltActiveElement(int element)
6817 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6818 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6819 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6822 int getBeltNrFromBeltSwitchElement(int element)
6824 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6825 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6826 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6829 int getBeltDirNrFromBeltElement(int element)
6831 static int belt_base_element[4] =
6833 EL_CONVEYOR_BELT_1_LEFT,
6834 EL_CONVEYOR_BELT_2_LEFT,
6835 EL_CONVEYOR_BELT_3_LEFT,
6836 EL_CONVEYOR_BELT_4_LEFT
6839 int belt_nr = getBeltNrFromBeltElement(element);
6840 int belt_dir_nr = element - belt_base_element[belt_nr];
6842 return (belt_dir_nr % 3);
6845 int getBeltDirNrFromBeltSwitchElement(int element)
6847 static int belt_base_element[4] =
6849 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6850 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6851 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6852 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6855 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6856 int belt_dir_nr = element - belt_base_element[belt_nr];
6858 return (belt_dir_nr % 3);
6861 int getBeltDirFromBeltElement(int element)
6863 static int belt_move_dir[3] =
6870 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6872 return belt_move_dir[belt_dir_nr];
6875 int getBeltDirFromBeltSwitchElement(int element)
6877 static int belt_move_dir[3] =
6884 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6886 return belt_move_dir[belt_dir_nr];
6889 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6891 static int belt_base_element[4] =
6893 EL_CONVEYOR_BELT_1_LEFT,
6894 EL_CONVEYOR_BELT_2_LEFT,
6895 EL_CONVEYOR_BELT_3_LEFT,
6896 EL_CONVEYOR_BELT_4_LEFT
6899 return belt_base_element[belt_nr] + belt_dir_nr;
6902 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6904 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6906 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6909 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6911 static int belt_base_element[4] =
6913 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6914 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6915 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6916 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6919 return belt_base_element[belt_nr] + belt_dir_nr;
6922 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6924 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6926 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6929 boolean getTeamMode_EM()
6931 return game.team_mode;
6934 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6936 int game_frame_delay_value;
6938 game_frame_delay_value =
6939 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6940 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6943 if (tape.playing && tape.warp_forward && !tape.pausing)
6944 game_frame_delay_value = 0;
6946 return game_frame_delay_value;
6949 unsigned int InitRND(int seed)
6951 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6952 return InitEngineRandom_EM(seed);
6953 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6954 return InitEngineRandom_SP(seed);
6956 return InitEngineRandom_RND(seed);
6959 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6960 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6962 inline static int get_effective_element_EM(int tile, int frame_em)
6964 int element = object_mapping[tile].element_rnd;
6965 int action = object_mapping[tile].action;
6966 boolean is_backside = object_mapping[tile].is_backside;
6967 boolean action_removing = (action == ACTION_DIGGING ||
6968 action == ACTION_SNAPPING ||
6969 action == ACTION_COLLECTING);
6975 case Yacid_splash_eB:
6976 case Yacid_splash_wB:
6977 return (frame_em > 5 ? EL_EMPTY : element);
6983 else /* frame_em == 7 */
6987 case Yacid_splash_eB:
6988 case Yacid_splash_wB:
6991 case Yemerald_stone:
6994 case Ydiamond_stone:
6998 case Xdrip_stretchB:
7017 case Xsand_stonein_1:
7018 case Xsand_stonein_2:
7019 case Xsand_stonein_3:
7020 case Xsand_stonein_4:
7024 return (is_backside || action_removing ? EL_EMPTY : element);
7029 inline static boolean check_linear_animation_EM(int tile)
7033 case Xsand_stonesand_1:
7034 case Xsand_stonesand_quickout_1:
7035 case Xsand_sandstone_1:
7036 case Xsand_stonein_1:
7037 case Xsand_stoneout_1:
7056 case Yacid_splash_eB:
7057 case Yacid_splash_wB:
7058 case Yemerald_stone:
7065 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7066 boolean has_crumbled_graphics,
7067 int crumbled, int sync_frame)
7069 /* if element can be crumbled, but certain action graphics are just empty
7070 space (like instantly snapping sand to empty space in 1 frame), do not
7071 treat these empty space graphics as crumbled graphics in EMC engine */
7072 if (crumbled == IMG_EMPTY_SPACE)
7073 has_crumbled_graphics = FALSE;
7075 if (has_crumbled_graphics)
7077 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7078 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7079 g_crumbled->anim_delay,
7080 g_crumbled->anim_mode,
7081 g_crumbled->anim_start_frame,
7084 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7085 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7087 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7089 g_em->has_crumbled_graphics = TRUE;
7093 g_em->crumbled_bitmap = NULL;
7094 g_em->crumbled_src_x = 0;
7095 g_em->crumbled_src_y = 0;
7096 g_em->crumbled_border_size = 0;
7098 g_em->has_crumbled_graphics = FALSE;
7102 void ResetGfxAnimation_EM(int x, int y, int tile)
7107 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7108 int tile, int frame_em, int x, int y)
7110 int action = object_mapping[tile].action;
7111 int direction = object_mapping[tile].direction;
7112 int effective_element = get_effective_element_EM(tile, frame_em);
7113 int graphic = (direction == MV_NONE ?
7114 el_act2img(effective_element, action) :
7115 el_act_dir2img(effective_element, action, direction));
7116 struct GraphicInfo *g = &graphic_info[graphic];
7118 boolean action_removing = (action == ACTION_DIGGING ||
7119 action == ACTION_SNAPPING ||
7120 action == ACTION_COLLECTING);
7121 boolean action_moving = (action == ACTION_FALLING ||
7122 action == ACTION_MOVING ||
7123 action == ACTION_PUSHING ||
7124 action == ACTION_EATING ||
7125 action == ACTION_FILLING ||
7126 action == ACTION_EMPTYING);
7127 boolean action_falling = (action == ACTION_FALLING ||
7128 action == ACTION_FILLING ||
7129 action == ACTION_EMPTYING);
7131 /* special case: graphic uses "2nd movement tile" and has defined
7132 7 frames for movement animation (or less) => use default graphic
7133 for last (8th) frame which ends the movement animation */
7134 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7136 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7137 graphic = (direction == MV_NONE ?
7138 el_act2img(effective_element, action) :
7139 el_act_dir2img(effective_element, action, direction));
7141 g = &graphic_info[graphic];
7144 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7148 else if (action_moving)
7150 boolean is_backside = object_mapping[tile].is_backside;
7154 int direction = object_mapping[tile].direction;
7155 int move_dir = (action_falling ? MV_DOWN : direction);
7160 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7161 if (g->double_movement && frame_em == 0)
7165 if (move_dir == MV_LEFT)
7166 GfxFrame[x - 1][y] = GfxFrame[x][y];
7167 else if (move_dir == MV_RIGHT)
7168 GfxFrame[x + 1][y] = GfxFrame[x][y];
7169 else if (move_dir == MV_UP)
7170 GfxFrame[x][y - 1] = GfxFrame[x][y];
7171 else if (move_dir == MV_DOWN)
7172 GfxFrame[x][y + 1] = GfxFrame[x][y];
7179 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7180 if (tile == Xsand_stonesand_quickout_1 ||
7181 tile == Xsand_stonesand_quickout_2)
7185 if (graphic_info[graphic].anim_global_sync)
7186 sync_frame = FrameCounter;
7187 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7188 sync_frame = GfxFrame[x][y];
7190 sync_frame = 0; /* playfield border (pseudo steel) */
7192 SetRandomAnimationValue(x, y);
7194 int frame = getAnimationFrame(g->anim_frames,
7197 g->anim_start_frame,
7200 g_em->unique_identifier =
7201 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7204 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7205 int tile, int frame_em, int x, int y)
7207 int action = object_mapping[tile].action;
7208 int direction = object_mapping[tile].direction;
7209 boolean is_backside = object_mapping[tile].is_backside;
7210 int effective_element = get_effective_element_EM(tile, frame_em);
7211 int effective_action = action;
7212 int graphic = (direction == MV_NONE ?
7213 el_act2img(effective_element, effective_action) :
7214 el_act_dir2img(effective_element, effective_action,
7216 int crumbled = (direction == MV_NONE ?
7217 el_act2crm(effective_element, effective_action) :
7218 el_act_dir2crm(effective_element, effective_action,
7220 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7221 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7222 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7223 struct GraphicInfo *g = &graphic_info[graphic];
7226 /* special case: graphic uses "2nd movement tile" and has defined
7227 7 frames for movement animation (or less) => use default graphic
7228 for last (8th) frame which ends the movement animation */
7229 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7231 effective_action = ACTION_DEFAULT;
7232 graphic = (direction == MV_NONE ?
7233 el_act2img(effective_element, effective_action) :
7234 el_act_dir2img(effective_element, effective_action,
7236 crumbled = (direction == MV_NONE ?
7237 el_act2crm(effective_element, effective_action) :
7238 el_act_dir2crm(effective_element, effective_action,
7241 g = &graphic_info[graphic];
7244 if (graphic_info[graphic].anim_global_sync)
7245 sync_frame = FrameCounter;
7246 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7247 sync_frame = GfxFrame[x][y];
7249 sync_frame = 0; /* playfield border (pseudo steel) */
7251 SetRandomAnimationValue(x, y);
7253 int frame = getAnimationFrame(g->anim_frames,
7256 g->anim_start_frame,
7259 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7260 g->double_movement && is_backside);
7262 /* (updating the "crumbled" graphic definitions is probably not really needed,
7263 as animations for crumbled graphics can't be longer than one EMC cycle) */
7264 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7268 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7269 int player_nr, int anim, int frame_em)
7271 int element = player_mapping[player_nr][anim].element_rnd;
7272 int action = player_mapping[player_nr][anim].action;
7273 int direction = player_mapping[player_nr][anim].direction;
7274 int graphic = (direction == MV_NONE ?
7275 el_act2img(element, action) :
7276 el_act_dir2img(element, action, direction));
7277 struct GraphicInfo *g = &graphic_info[graphic];
7280 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7282 stored_player[player_nr].StepFrame = frame_em;
7284 sync_frame = stored_player[player_nr].Frame;
7286 int frame = getAnimationFrame(g->anim_frames,
7289 g->anim_start_frame,
7292 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7293 &g_em->src_x, &g_em->src_y, FALSE);
7296 void InitGraphicInfo_EM(void)
7301 int num_em_gfx_errors = 0;
7303 if (graphic_info_em_object[0][0].bitmap == NULL)
7305 /* EM graphics not yet initialized in em_open_all() */
7310 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7313 /* always start with reliable default values */
7314 for (i = 0; i < TILE_MAX; i++)
7316 object_mapping[i].element_rnd = EL_UNKNOWN;
7317 object_mapping[i].is_backside = FALSE;
7318 object_mapping[i].action = ACTION_DEFAULT;
7319 object_mapping[i].direction = MV_NONE;
7322 /* always start with reliable default values */
7323 for (p = 0; p < MAX_PLAYERS; p++)
7325 for (i = 0; i < SPR_MAX; i++)
7327 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7328 player_mapping[p][i].action = ACTION_DEFAULT;
7329 player_mapping[p][i].direction = MV_NONE;
7333 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7335 int e = em_object_mapping_list[i].element_em;
7337 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7338 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7340 if (em_object_mapping_list[i].action != -1)
7341 object_mapping[e].action = em_object_mapping_list[i].action;
7343 if (em_object_mapping_list[i].direction != -1)
7344 object_mapping[e].direction =
7345 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7348 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7350 int a = em_player_mapping_list[i].action_em;
7351 int p = em_player_mapping_list[i].player_nr;
7353 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7355 if (em_player_mapping_list[i].action != -1)
7356 player_mapping[p][a].action = em_player_mapping_list[i].action;
7358 if (em_player_mapping_list[i].direction != -1)
7359 player_mapping[p][a].direction =
7360 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7363 for (i = 0; i < TILE_MAX; i++)
7365 int element = object_mapping[i].element_rnd;
7366 int action = object_mapping[i].action;
7367 int direction = object_mapping[i].direction;
7368 boolean is_backside = object_mapping[i].is_backside;
7369 boolean action_exploding = ((action == ACTION_EXPLODING ||
7370 action == ACTION_SMASHED_BY_ROCK ||
7371 action == ACTION_SMASHED_BY_SPRING) &&
7372 element != EL_DIAMOND);
7373 boolean action_active = (action == ACTION_ACTIVE);
7374 boolean action_other = (action == ACTION_OTHER);
7376 for (j = 0; j < 8; j++)
7378 int effective_element = get_effective_element_EM(i, j);
7379 int effective_action = (j < 7 ? action :
7380 i == Xdrip_stretch ? action :
7381 i == Xdrip_stretchB ? action :
7382 i == Ydrip_s1 ? action :
7383 i == Ydrip_s1B ? action :
7384 i == Xball_1B ? action :
7385 i == Xball_2 ? action :
7386 i == Xball_2B ? action :
7387 i == Yball_eat ? action :
7388 i == Ykey_1_eat ? action :
7389 i == Ykey_2_eat ? action :
7390 i == Ykey_3_eat ? action :
7391 i == Ykey_4_eat ? action :
7392 i == Ykey_5_eat ? action :
7393 i == Ykey_6_eat ? action :
7394 i == Ykey_7_eat ? action :
7395 i == Ykey_8_eat ? action :
7396 i == Ylenses_eat ? action :
7397 i == Ymagnify_eat ? action :
7398 i == Ygrass_eat ? action :
7399 i == Ydirt_eat ? action :
7400 i == Xsand_stonein_1 ? action :
7401 i == Xsand_stonein_2 ? action :
7402 i == Xsand_stonein_3 ? action :
7403 i == Xsand_stonein_4 ? action :
7404 i == Xsand_stoneout_1 ? action :
7405 i == Xsand_stoneout_2 ? action :
7406 i == Xboom_android ? ACTION_EXPLODING :
7407 action_exploding ? ACTION_EXPLODING :
7408 action_active ? action :
7409 action_other ? action :
7411 int graphic = (el_act_dir2img(effective_element, effective_action,
7413 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7415 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7416 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7417 boolean has_action_graphics = (graphic != base_graphic);
7418 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7419 struct GraphicInfo *g = &graphic_info[graphic];
7420 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7423 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7424 boolean special_animation = (action != ACTION_DEFAULT &&
7425 g->anim_frames == 3 &&
7426 g->anim_delay == 2 &&
7427 g->anim_mode & ANIM_LINEAR);
7428 int sync_frame = (i == Xdrip_stretch ? 7 :
7429 i == Xdrip_stretchB ? 7 :
7430 i == Ydrip_s2 ? j + 8 :
7431 i == Ydrip_s2B ? j + 8 :
7440 i == Xfake_acid_1 ? 0 :
7441 i == Xfake_acid_2 ? 10 :
7442 i == Xfake_acid_3 ? 20 :
7443 i == Xfake_acid_4 ? 30 :
7444 i == Xfake_acid_5 ? 40 :
7445 i == Xfake_acid_6 ? 50 :
7446 i == Xfake_acid_7 ? 60 :
7447 i == Xfake_acid_8 ? 70 :
7449 i == Xball_2B ? j + 8 :
7450 i == Yball_eat ? j + 1 :
7451 i == Ykey_1_eat ? j + 1 :
7452 i == Ykey_2_eat ? j + 1 :
7453 i == Ykey_3_eat ? j + 1 :
7454 i == Ykey_4_eat ? j + 1 :
7455 i == Ykey_5_eat ? j + 1 :
7456 i == Ykey_6_eat ? j + 1 :
7457 i == Ykey_7_eat ? j + 1 :
7458 i == Ykey_8_eat ? j + 1 :
7459 i == Ylenses_eat ? j + 1 :
7460 i == Ymagnify_eat ? j + 1 :
7461 i == Ygrass_eat ? j + 1 :
7462 i == Ydirt_eat ? j + 1 :
7463 i == Xamoeba_1 ? 0 :
7464 i == Xamoeba_2 ? 1 :
7465 i == Xamoeba_3 ? 2 :
7466 i == Xamoeba_4 ? 3 :
7467 i == Xamoeba_5 ? 0 :
7468 i == Xamoeba_6 ? 1 :
7469 i == Xamoeba_7 ? 2 :
7470 i == Xamoeba_8 ? 3 :
7471 i == Xexit_2 ? j + 8 :
7472 i == Xexit_3 ? j + 16 :
7473 i == Xdynamite_1 ? 0 :
7474 i == Xdynamite_2 ? 8 :
7475 i == Xdynamite_3 ? 16 :
7476 i == Xdynamite_4 ? 24 :
7477 i == Xsand_stonein_1 ? j + 1 :
7478 i == Xsand_stonein_2 ? j + 9 :
7479 i == Xsand_stonein_3 ? j + 17 :
7480 i == Xsand_stonein_4 ? j + 25 :
7481 i == Xsand_stoneout_1 && j == 0 ? 0 :
7482 i == Xsand_stoneout_1 && j == 1 ? 0 :
7483 i == Xsand_stoneout_1 && j == 2 ? 1 :
7484 i == Xsand_stoneout_1 && j == 3 ? 2 :
7485 i == Xsand_stoneout_1 && j == 4 ? 2 :
7486 i == Xsand_stoneout_1 && j == 5 ? 3 :
7487 i == Xsand_stoneout_1 && j == 6 ? 4 :
7488 i == Xsand_stoneout_1 && j == 7 ? 4 :
7489 i == Xsand_stoneout_2 && j == 0 ? 5 :
7490 i == Xsand_stoneout_2 && j == 1 ? 6 :
7491 i == Xsand_stoneout_2 && j == 2 ? 7 :
7492 i == Xsand_stoneout_2 && j == 3 ? 8 :
7493 i == Xsand_stoneout_2 && j == 4 ? 9 :
7494 i == Xsand_stoneout_2 && j == 5 ? 11 :
7495 i == Xsand_stoneout_2 && j == 6 ? 13 :
7496 i == Xsand_stoneout_2 && j == 7 ? 15 :
7497 i == Xboom_bug && j == 1 ? 2 :
7498 i == Xboom_bug && j == 2 ? 2 :
7499 i == Xboom_bug && j == 3 ? 4 :
7500 i == Xboom_bug && j == 4 ? 4 :
7501 i == Xboom_bug && j == 5 ? 2 :
7502 i == Xboom_bug && j == 6 ? 2 :
7503 i == Xboom_bug && j == 7 ? 0 :
7504 i == Xboom_bomb && j == 1 ? 2 :
7505 i == Xboom_bomb && j == 2 ? 2 :
7506 i == Xboom_bomb && j == 3 ? 4 :
7507 i == Xboom_bomb && j == 4 ? 4 :
7508 i == Xboom_bomb && j == 5 ? 2 :
7509 i == Xboom_bomb && j == 6 ? 2 :
7510 i == Xboom_bomb && j == 7 ? 0 :
7511 i == Xboom_android && j == 7 ? 6 :
7512 i == Xboom_1 && j == 1 ? 2 :
7513 i == Xboom_1 && j == 2 ? 2 :
7514 i == Xboom_1 && j == 3 ? 4 :
7515 i == Xboom_1 && j == 4 ? 4 :
7516 i == Xboom_1 && j == 5 ? 6 :
7517 i == Xboom_1 && j == 6 ? 6 :
7518 i == Xboom_1 && j == 7 ? 8 :
7519 i == Xboom_2 && j == 0 ? 8 :
7520 i == Xboom_2 && j == 1 ? 8 :
7521 i == Xboom_2 && j == 2 ? 10 :
7522 i == Xboom_2 && j == 3 ? 10 :
7523 i == Xboom_2 && j == 4 ? 10 :
7524 i == Xboom_2 && j == 5 ? 12 :
7525 i == Xboom_2 && j == 6 ? 12 :
7526 i == Xboom_2 && j == 7 ? 12 :
7527 special_animation && j == 4 ? 3 :
7528 effective_action != action ? 0 :
7532 Bitmap *debug_bitmap = g_em->bitmap;
7533 int debug_src_x = g_em->src_x;
7534 int debug_src_y = g_em->src_y;
7537 int frame = getAnimationFrame(g->anim_frames,
7540 g->anim_start_frame,
7543 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7544 g->double_movement && is_backside);
7546 g_em->bitmap = src_bitmap;
7547 g_em->src_x = src_x;
7548 g_em->src_y = src_y;
7549 g_em->src_offset_x = 0;
7550 g_em->src_offset_y = 0;
7551 g_em->dst_offset_x = 0;
7552 g_em->dst_offset_y = 0;
7553 g_em->width = TILEX;
7554 g_em->height = TILEY;
7556 g_em->preserve_background = FALSE;
7558 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7561 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7562 effective_action == ACTION_MOVING ||
7563 effective_action == ACTION_PUSHING ||
7564 effective_action == ACTION_EATING)) ||
7565 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7566 effective_action == ACTION_EMPTYING)))
7569 (effective_action == ACTION_FALLING ||
7570 effective_action == ACTION_FILLING ||
7571 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7572 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7573 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7574 int num_steps = (i == Ydrip_s1 ? 16 :
7575 i == Ydrip_s1B ? 16 :
7576 i == Ydrip_s2 ? 16 :
7577 i == Ydrip_s2B ? 16 :
7578 i == Xsand_stonein_1 ? 32 :
7579 i == Xsand_stonein_2 ? 32 :
7580 i == Xsand_stonein_3 ? 32 :
7581 i == Xsand_stonein_4 ? 32 :
7582 i == Xsand_stoneout_1 ? 16 :
7583 i == Xsand_stoneout_2 ? 16 : 8);
7584 int cx = ABS(dx) * (TILEX / num_steps);
7585 int cy = ABS(dy) * (TILEY / num_steps);
7586 int step_frame = (i == Ydrip_s2 ? j + 8 :
7587 i == Ydrip_s2B ? j + 8 :
7588 i == Xsand_stonein_2 ? j + 8 :
7589 i == Xsand_stonein_3 ? j + 16 :
7590 i == Xsand_stonein_4 ? j + 24 :
7591 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7592 int step = (is_backside ? step_frame : num_steps - step_frame);
7594 if (is_backside) /* tile where movement starts */
7596 if (dx < 0 || dy < 0)
7598 g_em->src_offset_x = cx * step;
7599 g_em->src_offset_y = cy * step;
7603 g_em->dst_offset_x = cx * step;
7604 g_em->dst_offset_y = cy * step;
7607 else /* tile where movement ends */
7609 if (dx < 0 || dy < 0)
7611 g_em->dst_offset_x = cx * step;
7612 g_em->dst_offset_y = cy * step;
7616 g_em->src_offset_x = cx * step;
7617 g_em->src_offset_y = cy * step;
7621 g_em->width = TILEX - cx * step;
7622 g_em->height = TILEY - cy * step;
7625 /* create unique graphic identifier to decide if tile must be redrawn */
7626 /* bit 31 - 16 (16 bit): EM style graphic
7627 bit 15 - 12 ( 4 bit): EM style frame
7628 bit 11 - 6 ( 6 bit): graphic width
7629 bit 5 - 0 ( 6 bit): graphic height */
7630 g_em->unique_identifier =
7631 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7635 /* skip check for EMC elements not contained in original EMC artwork */
7636 if (element == EL_EMC_FAKE_ACID)
7639 if (g_em->bitmap != debug_bitmap ||
7640 g_em->src_x != debug_src_x ||
7641 g_em->src_y != debug_src_y ||
7642 g_em->src_offset_x != 0 ||
7643 g_em->src_offset_y != 0 ||
7644 g_em->dst_offset_x != 0 ||
7645 g_em->dst_offset_y != 0 ||
7646 g_em->width != TILEX ||
7647 g_em->height != TILEY)
7649 static int last_i = -1;
7657 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7658 i, element, element_info[element].token_name,
7659 element_action_info[effective_action].suffix, direction);
7661 if (element != effective_element)
7662 printf(" [%d ('%s')]",
7664 element_info[effective_element].token_name);
7668 if (g_em->bitmap != debug_bitmap)
7669 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7670 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7672 if (g_em->src_x != debug_src_x ||
7673 g_em->src_y != debug_src_y)
7674 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7675 j, (is_backside ? 'B' : 'F'),
7676 g_em->src_x, g_em->src_y,
7677 g_em->src_x / 32, g_em->src_y / 32,
7678 debug_src_x, debug_src_y,
7679 debug_src_x / 32, debug_src_y / 32);
7681 if (g_em->src_offset_x != 0 ||
7682 g_em->src_offset_y != 0 ||
7683 g_em->dst_offset_x != 0 ||
7684 g_em->dst_offset_y != 0)
7685 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7687 g_em->src_offset_x, g_em->src_offset_y,
7688 g_em->dst_offset_x, g_em->dst_offset_y);
7690 if (g_em->width != TILEX ||
7691 g_em->height != TILEY)
7692 printf(" %d (%d): size %d,%d should be %d,%d\n",
7694 g_em->width, g_em->height, TILEX, TILEY);
7696 num_em_gfx_errors++;
7703 for (i = 0; i < TILE_MAX; i++)
7705 for (j = 0; j < 8; j++)
7707 int element = object_mapping[i].element_rnd;
7708 int action = object_mapping[i].action;
7709 int direction = object_mapping[i].direction;
7710 boolean is_backside = object_mapping[i].is_backside;
7711 int graphic_action = el_act_dir2img(element, action, direction);
7712 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7714 if ((action == ACTION_SMASHED_BY_ROCK ||
7715 action == ACTION_SMASHED_BY_SPRING ||
7716 action == ACTION_EATING) &&
7717 graphic_action == graphic_default)
7719 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7720 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7721 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7722 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7725 /* no separate animation for "smashed by rock" -- use rock instead */
7726 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7727 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7729 g_em->bitmap = g_xx->bitmap;
7730 g_em->src_x = g_xx->src_x;
7731 g_em->src_y = g_xx->src_y;
7732 g_em->src_offset_x = g_xx->src_offset_x;
7733 g_em->src_offset_y = g_xx->src_offset_y;
7734 g_em->dst_offset_x = g_xx->dst_offset_x;
7735 g_em->dst_offset_y = g_xx->dst_offset_y;
7736 g_em->width = g_xx->width;
7737 g_em->height = g_xx->height;
7738 g_em->unique_identifier = g_xx->unique_identifier;
7741 g_em->preserve_background = TRUE;
7746 for (p = 0; p < MAX_PLAYERS; p++)
7748 for (i = 0; i < SPR_MAX; i++)
7750 int element = player_mapping[p][i].element_rnd;
7751 int action = player_mapping[p][i].action;
7752 int direction = player_mapping[p][i].direction;
7754 for (j = 0; j < 8; j++)
7756 int effective_element = element;
7757 int effective_action = action;
7758 int graphic = (direction == MV_NONE ?
7759 el_act2img(effective_element, effective_action) :
7760 el_act_dir2img(effective_element, effective_action,
7762 struct GraphicInfo *g = &graphic_info[graphic];
7763 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7769 Bitmap *debug_bitmap = g_em->bitmap;
7770 int debug_src_x = g_em->src_x;
7771 int debug_src_y = g_em->src_y;
7774 int frame = getAnimationFrame(g->anim_frames,
7777 g->anim_start_frame,
7780 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7782 g_em->bitmap = src_bitmap;
7783 g_em->src_x = src_x;
7784 g_em->src_y = src_y;
7785 g_em->src_offset_x = 0;
7786 g_em->src_offset_y = 0;
7787 g_em->dst_offset_x = 0;
7788 g_em->dst_offset_y = 0;
7789 g_em->width = TILEX;
7790 g_em->height = TILEY;
7794 /* skip check for EMC elements not contained in original EMC artwork */
7795 if (element == EL_PLAYER_3 ||
7796 element == EL_PLAYER_4)
7799 if (g_em->bitmap != debug_bitmap ||
7800 g_em->src_x != debug_src_x ||
7801 g_em->src_y != debug_src_y)
7803 static int last_i = -1;
7811 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7812 p, i, element, element_info[element].token_name,
7813 element_action_info[effective_action].suffix, direction);
7815 if (element != effective_element)
7816 printf(" [%d ('%s')]",
7818 element_info[effective_element].token_name);
7822 if (g_em->bitmap != debug_bitmap)
7823 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7824 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7826 if (g_em->src_x != debug_src_x ||
7827 g_em->src_y != debug_src_y)
7828 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7830 g_em->src_x, g_em->src_y,
7831 g_em->src_x / 32, g_em->src_y / 32,
7832 debug_src_x, debug_src_y,
7833 debug_src_x / 32, debug_src_y / 32);
7835 num_em_gfx_errors++;
7845 printf("::: [%d errors found]\n", num_em_gfx_errors);
7851 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7852 boolean any_player_moving,
7853 boolean any_player_snapping,
7854 boolean any_player_dropping)
7856 static boolean player_was_waiting = TRUE;
7858 if (frame == 0 && !any_player_dropping)
7860 if (!player_was_waiting)
7862 if (!SaveEngineSnapshotToList())
7865 player_was_waiting = TRUE;
7868 else if (any_player_moving || any_player_snapping || any_player_dropping)
7870 player_was_waiting = FALSE;
7874 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7875 boolean murphy_is_dropping)
7877 static boolean player_was_waiting = TRUE;
7879 if (murphy_is_waiting)
7881 if (!player_was_waiting)
7883 if (!SaveEngineSnapshotToList())
7886 player_was_waiting = TRUE;
7891 player_was_waiting = FALSE;
7895 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7896 boolean any_player_moving,
7897 boolean any_player_snapping,
7898 boolean any_player_dropping)
7900 if (tape.single_step && tape.recording && !tape.pausing)
7901 if (frame == 0 && !any_player_dropping)
7902 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7904 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7905 any_player_snapping, any_player_dropping);
7908 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7909 boolean murphy_is_dropping)
7911 if (tape.single_step && tape.recording && !tape.pausing)
7912 if (murphy_is_waiting)
7913 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7915 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7918 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7919 int graphic, int sync_frame, int x, int y)
7921 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7923 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7926 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7928 return (IS_NEXT_FRAME(sync_frame, graphic));
7931 int getGraphicInfo_Delay(int graphic)
7933 return graphic_info[graphic].anim_delay;
7936 void PlayMenuSoundExt(int sound)
7938 if (sound == SND_UNDEFINED)
7941 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7942 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7945 if (IS_LOOP_SOUND(sound))
7946 PlaySoundLoop(sound);
7951 void PlayMenuSound()
7953 PlayMenuSoundExt(menu.sound[game_status]);
7956 void PlayMenuSoundStereo(int sound, int stereo_position)
7958 if (sound == SND_UNDEFINED)
7961 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7962 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7965 if (IS_LOOP_SOUND(sound))
7966 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7968 PlaySoundStereo(sound, stereo_position);
7971 void PlayMenuSoundIfLoopExt(int sound)
7973 if (sound == SND_UNDEFINED)
7976 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7977 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7980 if (IS_LOOP_SOUND(sound))
7981 PlaySoundLoop(sound);
7984 void PlayMenuSoundIfLoop()
7986 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7989 void PlayMenuMusicExt(int music)
7991 if (music == MUS_UNDEFINED)
7994 if (!setup.sound_music)
8000 void PlayMenuMusic()
8002 PlayMenuMusicExt(menu.music[game_status]);
8005 void PlaySoundActivating()
8008 PlaySound(SND_MENU_ITEM_ACTIVATING);
8012 void PlaySoundSelecting()
8015 PlaySound(SND_MENU_ITEM_SELECTING);
8019 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8021 boolean change_fullscreen = (setup.fullscreen !=
8022 video.fullscreen_enabled);
8023 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8024 !strEqual(setup.fullscreen_mode,
8025 video.fullscreen_mode_current));
8026 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8027 setup.window_scaling_percent !=
8028 video.window_scaling_percent);
8030 if (change_window_scaling_percent && video.fullscreen_enabled)
8033 if (!change_window_scaling_percent && !video.fullscreen_available)
8036 #if defined(TARGET_SDL2)
8037 if (change_window_scaling_percent)
8039 SDLSetWindowScaling(setup.window_scaling_percent);
8043 else if (change_fullscreen)
8045 SDLSetWindowFullscreen(setup.fullscreen);
8047 /* set setup value according to successfully changed fullscreen mode */
8048 setup.fullscreen = video.fullscreen_enabled;
8054 if (change_fullscreen ||
8055 change_fullscreen_mode ||
8056 change_window_scaling_percent)
8058 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8060 /* save backbuffer content which gets lost when toggling fullscreen mode */
8061 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8063 if (change_fullscreen_mode)
8065 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8066 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8069 if (change_window_scaling_percent)
8071 /* keep window mode, but change window scaling */
8072 video.fullscreen_enabled = TRUE; /* force new window scaling */
8075 /* toggle fullscreen */
8076 ChangeVideoModeIfNeeded(setup.fullscreen);
8078 /* set setup value according to successfully changed fullscreen mode */
8079 setup.fullscreen = video.fullscreen_enabled;
8081 /* restore backbuffer content from temporary backbuffer backup bitmap */
8082 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8084 FreeBitmap(tmp_backbuffer);
8086 /* update visible window/screen */
8087 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8091 void JoinRectangles(int *x, int *y, int *width, int *height,
8092 int x2, int y2, int width2, int height2)
8094 // do not join with "off-screen" rectangle
8095 if (x2 == -1 || y2 == -1)
8100 *width = MAX(*width, width2);
8101 *height = MAX(*height, height2);
8104 void ChangeViewportPropertiesIfNeeded()
8106 int gfx_game_mode = game_status;
8107 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8109 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8110 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8111 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8112 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8113 int border_size = vp_playfield->border_size;
8114 int new_sx = vp_playfield->x + border_size;
8115 int new_sy = vp_playfield->y + border_size;
8116 int new_sxsize = vp_playfield->width - 2 * border_size;
8117 int new_sysize = vp_playfield->height - 2 * border_size;
8118 int new_real_sx = vp_playfield->x;
8119 int new_real_sy = vp_playfield->y;
8120 int new_full_sxsize = vp_playfield->width;
8121 int new_full_sysize = vp_playfield->height;
8122 int new_dx = vp_door_1->x;
8123 int new_dy = vp_door_1->y;
8124 int new_dxsize = vp_door_1->width;
8125 int new_dysize = vp_door_1->height;
8126 int new_vx = vp_door_2->x;
8127 int new_vy = vp_door_2->y;
8128 int new_vxsize = vp_door_2->width;
8129 int new_vysize = vp_door_2->height;
8130 int new_ex = vp_door_3->x;
8131 int new_ey = vp_door_3->y;
8132 int new_exsize = vp_door_3->width;
8133 int new_eysize = vp_door_3->height;
8134 int new_tilesize_var =
8135 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8137 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8138 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8139 int new_scr_fieldx = new_sxsize / tilesize;
8140 int new_scr_fieldy = new_sysize / tilesize;
8141 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8142 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8143 boolean init_gfx_buffers = FALSE;
8144 boolean init_video_buffer = FALSE;
8145 boolean init_gadgets_and_toons = FALSE;
8146 boolean init_em_graphics = FALSE;
8148 if (viewport.window.width != WIN_XSIZE ||
8149 viewport.window.height != WIN_YSIZE)
8151 WIN_XSIZE = viewport.window.width;
8152 WIN_YSIZE = viewport.window.height;
8154 init_video_buffer = TRUE;
8155 init_gfx_buffers = TRUE;
8157 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8160 if (new_scr_fieldx != SCR_FIELDX ||
8161 new_scr_fieldy != SCR_FIELDY)
8163 /* this always toggles between MAIN and GAME when using small tile size */
8165 SCR_FIELDX = new_scr_fieldx;
8166 SCR_FIELDY = new_scr_fieldy;
8168 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8179 new_sxsize != SXSIZE ||
8180 new_sysize != SYSIZE ||
8181 new_dxsize != DXSIZE ||
8182 new_dysize != DYSIZE ||
8183 new_vxsize != VXSIZE ||
8184 new_vysize != VYSIZE ||
8185 new_exsize != EXSIZE ||
8186 new_eysize != EYSIZE ||
8187 new_real_sx != REAL_SX ||
8188 new_real_sy != REAL_SY ||
8189 new_full_sxsize != FULL_SXSIZE ||
8190 new_full_sysize != FULL_SYSIZE ||
8191 new_tilesize_var != TILESIZE_VAR
8194 // ------------------------------------------------------------------------
8195 // determine next fading area for changed viewport definitions
8196 // ------------------------------------------------------------------------
8198 // start with current playfield area (default fading area)
8201 FADE_SXSIZE = FULL_SXSIZE;
8202 FADE_SYSIZE = FULL_SYSIZE;
8204 // add new playfield area if position or size has changed
8205 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8206 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8208 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8209 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8212 // add current and new door 1 area if position or size has changed
8213 if (new_dx != DX || new_dy != DY ||
8214 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8216 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8217 DX, DY, DXSIZE, DYSIZE);
8218 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8219 new_dx, new_dy, new_dxsize, new_dysize);
8222 // add current and new door 2 area if position or size has changed
8223 if (new_dx != VX || new_dy != VY ||
8224 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8226 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8227 VX, VY, VXSIZE, VYSIZE);
8228 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8229 new_vx, new_vy, new_vxsize, new_vysize);
8232 // ------------------------------------------------------------------------
8233 // handle changed tile size
8234 // ------------------------------------------------------------------------
8236 if (new_tilesize_var != TILESIZE_VAR)
8238 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8240 // changing tile size invalidates scroll values of engine snapshots
8241 FreeEngineSnapshotSingle();
8243 // changing tile size requires update of graphic mapping for EM engine
8244 init_em_graphics = TRUE;
8255 SXSIZE = new_sxsize;
8256 SYSIZE = new_sysize;
8257 DXSIZE = new_dxsize;
8258 DYSIZE = new_dysize;
8259 VXSIZE = new_vxsize;
8260 VYSIZE = new_vysize;
8261 EXSIZE = new_exsize;
8262 EYSIZE = new_eysize;
8263 REAL_SX = new_real_sx;
8264 REAL_SY = new_real_sy;
8265 FULL_SXSIZE = new_full_sxsize;
8266 FULL_SYSIZE = new_full_sysize;
8267 TILESIZE_VAR = new_tilesize_var;
8269 init_gfx_buffers = TRUE;
8270 init_gadgets_and_toons = TRUE;
8272 // printf("::: viewports: init_gfx_buffers\n");
8273 // printf("::: viewports: init_gadgets_and_toons\n");
8276 if (init_gfx_buffers)
8278 // printf("::: init_gfx_buffers\n");
8280 SCR_FIELDX = new_scr_fieldx_buffers;
8281 SCR_FIELDY = new_scr_fieldy_buffers;
8285 SCR_FIELDX = new_scr_fieldx;
8286 SCR_FIELDY = new_scr_fieldy;
8288 SetDrawDeactivationMask(REDRAW_NONE);
8289 SetDrawBackgroundMask(REDRAW_FIELD);
8292 if (init_video_buffer)
8294 // printf("::: init_video_buffer\n");
8296 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8299 if (init_gadgets_and_toons)
8301 // printf("::: init_gadgets_and_toons\n");
8307 if (init_em_graphics)
8309 InitGraphicInfo_EM();