1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static unsigned int sync_frame_delay = 0;
177 static unsigned int sync_frame_delay_value = GAME_FRAME_DELAY;
179 static char *print_if_not_empty(int element)
181 static char *s = NULL;
182 char *token_name = element_info[element].token_name;
187 s = checked_malloc(strlen(token_name) + 10 + 1);
189 if (element != EL_EMPTY)
190 sprintf(s, "%d\t['%s']", element, token_name);
192 sprintf(s, "%d", element);
197 void DumpTile(int x, int y)
202 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
208 printf_line("-", 79);
209 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
210 printf_line("-", 79);
212 if (!IN_LEV_FIELD(x, y))
214 printf("(not in level field)\n");
220 printf(" Feld: %d\t['%s']\n", Feld[x][y],
221 element_info[Feld[x][y]].token_name);
222 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
223 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
224 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
225 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
226 printf(" MovPos: %d\n", MovPos[x][y]);
227 printf(" MovDir: %d\n", MovDir[x][y]);
228 printf(" MovDelay: %d\n", MovDelay[x][y]);
229 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
230 printf(" CustomValue: %d\n", CustomValue[x][y]);
231 printf(" GfxElement: %d\n", GfxElement[x][y]);
232 printf(" GfxAction: %d\n", GfxAction[x][y]);
233 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
237 void SetDrawtoField(int mode)
239 if (mode == DRAW_FIELDBUFFER)
245 BX2 = SCR_FIELDX + 1;
246 BY2 = SCR_FIELDY + 1;
248 drawto_field = fieldbuffer;
250 else /* DRAW_BACKBUFFER */
256 BX2 = SCR_FIELDX - 1;
257 BY2 = SCR_FIELDY - 1;
259 drawto_field = backbuffer;
263 static void RedrawPlayfield_RND()
265 if (game.envelope_active)
268 DrawLevel(REDRAW_ALL);
272 void RedrawPlayfield()
274 if (game_status != GAME_MODE_PLAYING)
277 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
278 RedrawPlayfield_EM(TRUE);
279 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
280 RedrawPlayfield_SP(TRUE);
281 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
282 RedrawPlayfield_RND();
284 BlitScreenToBitmap(backbuffer);
286 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
290 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
292 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
294 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
297 void DrawMaskedBorder_FIELD()
299 if (global.border_status >= GAME_MODE_TITLE &&
300 global.border_status <= GAME_MODE_PLAYING &&
301 border.draw_masked[global.border_status])
302 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
305 void DrawMaskedBorder_DOOR_1()
307 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
308 (global.border_status != GAME_MODE_EDITOR ||
309 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
310 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
313 void DrawMaskedBorder_DOOR_2()
315 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
316 global.border_status != GAME_MODE_EDITOR)
317 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
320 void DrawMaskedBorder_DOOR_3()
322 /* currently not available */
325 void DrawMaskedBorder_ALL()
327 DrawMaskedBorder_FIELD();
328 DrawMaskedBorder_DOOR_1();
329 DrawMaskedBorder_DOOR_2();
330 DrawMaskedBorder_DOOR_3();
333 void DrawMaskedBorder(int redraw_mask)
335 /* never draw masked screen borders on borderless screens */
336 if (game_status == GAME_MODE_LOADING ||
337 game_status == GAME_MODE_TITLE)
340 if (redraw_mask & REDRAW_ALL)
341 DrawMaskedBorder_ALL();
344 if (redraw_mask & REDRAW_FIELD)
345 DrawMaskedBorder_FIELD();
346 if (redraw_mask & REDRAW_DOOR_1)
347 DrawMaskedBorder_DOOR_1();
348 if (redraw_mask & REDRAW_DOOR_2)
349 DrawMaskedBorder_DOOR_2();
350 if (redraw_mask & REDRAW_DOOR_3)
351 DrawMaskedBorder_DOOR_3();
355 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
357 int fx = FX, fy = FY;
358 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
359 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
361 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
362 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
363 int dx_var = dx * TILESIZE_VAR / TILESIZE;
364 int dy_var = dy * TILESIZE_VAR / TILESIZE;
367 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
368 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
370 if (EVEN(SCR_FIELDX))
372 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
373 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
375 fx += (dx_var > 0 ? TILEX_VAR : 0);
382 if (EVEN(SCR_FIELDY))
384 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
385 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
387 fy += (dy_var > 0 ? TILEY_VAR : 0);
394 if (full_lev_fieldx <= SCR_FIELDX)
396 if (EVEN(SCR_FIELDX))
397 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
399 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
402 if (full_lev_fieldy <= SCR_FIELDY)
404 if (EVEN(SCR_FIELDY))
405 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
407 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
410 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
413 void BlitScreenToBitmap(Bitmap *target_bitmap)
415 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
416 BlitScreenToBitmap_EM(target_bitmap);
417 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
418 BlitScreenToBitmap_SP(target_bitmap);
419 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
420 BlitScreenToBitmap_RND(target_bitmap);
422 redraw_mask |= REDRAW_FIELD;
425 void DrawFramesPerSecond()
428 int font_nr = FONT_TEXT_2;
429 int font_width = getFontWidth(font_nr);
431 sprintf(text, "%04.1f fps", global.frames_per_second);
433 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
434 font_nr, BLIT_OPAQUE);
439 if (redraw_mask == REDRAW_NONE)
442 // draw masked border to all viewports, if defined
443 DrawMaskedBorder(redraw_mask);
445 // draw frames per second (only if debug mode is enabled)
446 if (redraw_mask & REDRAW_FPS)
447 DrawFramesPerSecond();
449 // redraw complete window if both playfield and (some) doors need redraw
450 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
451 redraw_mask = REDRAW_ALL;
453 /* although redrawing the whole window would be fine for normal gameplay,
454 being able to only redraw the playfield is required for deactivating
455 certain drawing areas (mainly playfield) to work, which is needed for
456 warp-forward to be fast enough (by skipping redraw of most frames) */
458 if (redraw_mask & REDRAW_ALL)
460 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
462 else if (redraw_mask & REDRAW_FIELD)
464 BlitBitmap(backbuffer, window,
465 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
467 else if (redraw_mask & REDRAW_DOORS)
469 if (redraw_mask & REDRAW_DOOR_1)
470 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
472 if (redraw_mask & REDRAW_DOOR_2)
473 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
475 if (redraw_mask & REDRAW_DOOR_3)
476 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
479 redraw_mask = REDRAW_NONE;
482 static void FadeCrossSaveBackbuffer()
484 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
487 static void FadeCrossRestoreBackbuffer()
489 int redraw_mask_last = redraw_mask;
491 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
493 // do not change redraw mask when restoring backbuffer after cross-fading
494 redraw_mask = redraw_mask_last;
497 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
499 static int fade_type_skip = FADE_TYPE_NONE;
500 void (*draw_border_function)(void) = NULL;
501 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
502 int x, y, width, height;
503 int fade_delay, post_delay;
505 if (fade_type == FADE_TYPE_FADE_OUT)
507 if (fade_type_skip != FADE_TYPE_NONE)
509 /* skip all fade operations until specified fade operation */
510 if (fade_type & fade_type_skip)
511 fade_type_skip = FADE_TYPE_NONE;
517 FadeCrossSaveBackbuffer();
520 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
523 FadeCrossSaveBackbuffer();
530 redraw_mask |= fade_mask;
532 if (fade_type == FADE_TYPE_SKIP)
534 fade_type_skip = fade_mode;
539 fade_delay = fading.fade_delay;
540 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
542 if (fade_type_skip != FADE_TYPE_NONE)
544 /* skip all fade operations until specified fade operation */
545 if (fade_type & fade_type_skip)
546 fade_type_skip = FADE_TYPE_NONE;
551 if (global.autoplay_leveldir)
556 if (fade_mask == REDRAW_FIELD)
561 height = FADE_SYSIZE;
563 if (border.draw_masked_when_fading)
564 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
566 DrawMaskedBorder_FIELD(); /* draw once */
568 else /* REDRAW_ALL */
576 if (!setup.fade_screens ||
578 fading.fade_mode == FADE_MODE_NONE)
580 if (fade_mode == FADE_MODE_FADE_OUT)
583 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
585 redraw_mask &= ~fade_mask;
590 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
591 draw_border_function);
593 if (fade_type == FADE_TYPE_FADE_OUT)
594 FadeCrossRestoreBackbuffer();
596 redraw_mask &= ~fade_mask;
599 void FadeIn(int fade_mask)
601 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
602 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
604 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
608 FADE_SXSIZE = FULL_SXSIZE;
609 FADE_SYSIZE = FULL_SYSIZE;
612 void FadeOut(int fade_mask)
614 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
615 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
617 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
619 global.border_status = game_status;
622 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
624 static struct TitleFadingInfo fading_leave_stored;
627 fading_leave_stored = fading_leave;
629 fading = fading_leave_stored;
632 void FadeSetEnterMenu()
634 fading = menu.enter_menu;
636 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
639 void FadeSetLeaveMenu()
641 fading = menu.leave_menu;
643 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
646 void FadeSetEnterScreen()
648 fading = menu.enter_screen[game_status];
650 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
653 void FadeSetNextScreen()
655 fading = menu.next_screen[game_status];
657 // (do not overwrite fade mode set by FadeSetEnterScreen)
658 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
661 void FadeSetLeaveScreen()
663 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
666 void FadeSetFromType(int type)
668 if (type & TYPE_ENTER_SCREEN)
669 FadeSetEnterScreen();
670 else if (type & TYPE_ENTER)
672 else if (type & TYPE_LEAVE)
676 void FadeSetDisabled()
678 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
680 fading = fading_none;
683 void FadeSkipNextFadeIn()
685 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
688 void FadeSkipNextFadeOut()
690 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
693 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
695 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
697 return (graphic == IMG_UNDEFINED ? NULL :
698 graphic_info[graphic].bitmap != NULL || redefined ?
699 graphic_info[graphic].bitmap :
700 graphic_info[default_graphic].bitmap);
703 Bitmap *getBackgroundBitmap(int graphic)
705 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
708 Bitmap *getGlobalBorderBitmap(int graphic)
710 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
713 Bitmap *getGlobalBorderBitmapFromGameStatus()
716 (game_status == GAME_MODE_MAIN ||
717 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
718 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
719 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
720 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
723 return getGlobalBorderBitmap(graphic);
726 void SetWindowBackgroundImageIfDefined(int graphic)
728 if (graphic_info[graphic].bitmap)
729 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
732 void SetMainBackgroundImageIfDefined(int graphic)
734 if (graphic_info[graphic].bitmap)
735 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
738 void SetDoorBackgroundImageIfDefined(int graphic)
740 if (graphic_info[graphic].bitmap)
741 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
744 void SetWindowBackgroundImage(int graphic)
746 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
749 void SetMainBackgroundImage(int graphic)
751 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
754 void SetDoorBackgroundImage(int graphic)
756 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
759 void SetPanelBackground()
761 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
763 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
764 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
766 SetDoorBackgroundBitmap(bitmap_db_panel);
769 void DrawBackground(int x, int y, int width, int height)
771 /* "drawto" might still point to playfield buffer here (hall of fame) */
772 ClearRectangleOnBackground(backbuffer, x, y, width, height);
774 if (IN_GFX_FIELD_FULL(x, y))
775 redraw_mask |= REDRAW_FIELD;
776 else if (IN_GFX_DOOR_1(x, y))
777 redraw_mask |= REDRAW_DOOR_1;
778 else if (IN_GFX_DOOR_2(x, y))
779 redraw_mask |= REDRAW_DOOR_2;
780 else if (IN_GFX_DOOR_3(x, y))
781 redraw_mask |= REDRAW_DOOR_3;
784 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
786 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
788 if (font->bitmap == NULL)
791 DrawBackground(x, y, width, height);
794 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
796 struct GraphicInfo *g = &graphic_info[graphic];
798 if (g->bitmap == NULL)
801 DrawBackground(x, y, width, height);
804 static int game_status_last = -1;
805 static Bitmap *global_border_bitmap_last = NULL;
806 static Bitmap *global_border_bitmap = NULL;
807 static int real_sx_last = -1, real_sy_last = -1;
808 static int full_sxsize_last = -1, full_sysize_last = -1;
809 static int dx_last = -1, dy_last = -1;
810 static int dxsize_last = -1, dysize_last = -1;
811 static int vx_last = -1, vy_last = -1;
812 static int vxsize_last = -1, vysize_last = -1;
814 boolean CheckIfGlobalBorderHasChanged()
816 // if game status has not changed, global border has not changed either
817 if (game_status == game_status_last)
820 // determine and store new global border bitmap for current game status
821 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
823 return (global_border_bitmap_last != global_border_bitmap);
826 boolean CheckIfGlobalBorderRedrawIsNeeded()
828 // if game status has not changed, nothing has to be redrawn
829 if (game_status == game_status_last)
832 // redraw if last screen was title screen
833 if (game_status_last == GAME_MODE_TITLE)
836 // redraw if global screen border has changed
837 if (CheckIfGlobalBorderHasChanged())
840 // redraw if position or size of playfield area has changed
841 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
842 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
845 // redraw if position or size of door area has changed
846 if (dx_last != DX || dy_last != DY ||
847 dxsize_last != DXSIZE || dysize_last != DYSIZE)
850 // redraw if position or size of tape area has changed
851 if (vx_last != VX || vy_last != VY ||
852 vxsize_last != VXSIZE || vysize_last != VYSIZE)
858 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
861 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
863 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
866 void RedrawGlobalBorder()
868 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
870 RedrawGlobalBorderFromBitmap(bitmap);
872 redraw_mask = REDRAW_ALL;
875 static void RedrawGlobalBorderIfNeeded()
877 if (game_status == game_status_last)
880 // copy current draw buffer to later copy back areas that have not changed
881 if (game_status_last != GAME_MODE_TITLE)
882 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
884 if (CheckIfGlobalBorderRedrawIsNeeded())
886 // redraw global screen border (or clear, if defined to be empty)
887 RedrawGlobalBorderFromBitmap(global_border_bitmap);
889 // copy previous playfield and door areas, if they are defined on both
890 // previous and current screen and if they still have the same size
892 if (real_sx_last != -1 && real_sy_last != -1 &&
893 REAL_SX != -1 && REAL_SY != -1 &&
894 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
895 BlitBitmap(bitmap_db_store, backbuffer,
896 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
899 if (dx_last != -1 && dy_last != -1 &&
900 DX != -1 && DY != -1 &&
901 dxsize_last == DXSIZE && dysize_last == DYSIZE)
902 BlitBitmap(bitmap_db_store, backbuffer,
903 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
905 if (vx_last != -1 && vy_last != -1 &&
906 VX != -1 && VY != -1 &&
907 vxsize_last == VXSIZE && vysize_last == VYSIZE)
908 BlitBitmap(bitmap_db_store, backbuffer,
909 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
911 redraw_mask = REDRAW_ALL;
914 game_status_last = game_status;
916 global_border_bitmap_last = global_border_bitmap;
918 real_sx_last = REAL_SX;
919 real_sy_last = REAL_SY;
920 full_sxsize_last = FULL_SXSIZE;
921 full_sysize_last = FULL_SYSIZE;
924 dxsize_last = DXSIZE;
925 dysize_last = DYSIZE;
928 vxsize_last = VXSIZE;
929 vysize_last = VYSIZE;
934 RedrawGlobalBorderIfNeeded();
936 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
937 /* (when entering hall of fame after playing) */
938 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
940 /* !!! maybe this should be done before clearing the background !!! */
941 if (game_status == GAME_MODE_PLAYING)
943 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
944 SetDrawtoField(DRAW_FIELDBUFFER);
948 SetDrawtoField(DRAW_BACKBUFFER);
952 void MarkTileDirty(int x, int y)
954 redraw_mask |= REDRAW_FIELD;
957 void SetBorderElement()
961 BorderElement = EL_EMPTY;
963 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
965 for (x = 0; x < lev_fieldx; x++)
967 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
968 BorderElement = EL_STEELWALL;
970 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
976 void FloodFillLevel(int from_x, int from_y, int fill_element,
977 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
978 int max_fieldx, int max_fieldy)
982 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
983 static int safety = 0;
985 /* check if starting field still has the desired content */
986 if (field[from_x][from_y] == fill_element)
991 if (safety > max_fieldx * max_fieldy)
992 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
994 old_element = field[from_x][from_y];
995 field[from_x][from_y] = fill_element;
997 for (i = 0; i < 4; i++)
999 x = from_x + check[i][0];
1000 y = from_y + check[i][1];
1002 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1003 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1009 void SetRandomAnimationValue(int x, int y)
1011 gfx.anim_random_frame = GfxRandom[x][y];
1014 int getGraphicAnimationFrame(int graphic, int sync_frame)
1016 /* animation synchronized with global frame counter, not move position */
1017 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1018 sync_frame = FrameCounter;
1020 return getAnimationFrame(graphic_info[graphic].anim_frames,
1021 graphic_info[graphic].anim_delay,
1022 graphic_info[graphic].anim_mode,
1023 graphic_info[graphic].anim_start_frame,
1027 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1028 Bitmap **bitmap, int *x, int *y,
1029 boolean get_backside)
1031 struct GraphicInfo *g = &graphic_info[graphic];
1032 Bitmap *src_bitmap = g->bitmap;
1033 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1034 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1035 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1037 // if no in-game graphics defined, always use standard graphic size
1038 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1039 tilesize = TILESIZE;
1041 if (tilesize == gfx.standard_tile_size)
1042 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1043 else if (tilesize == game.tile_size)
1044 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1046 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1048 if (g->offset_y == 0) /* frames are ordered horizontally */
1050 int max_width = g->anim_frames_per_line * g->width;
1051 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1053 src_x = pos % max_width;
1054 src_y = src_y % g->height + pos / max_width * g->height;
1056 else if (g->offset_x == 0) /* frames are ordered vertically */
1058 int max_height = g->anim_frames_per_line * g->height;
1059 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1061 src_x = src_x % g->width + pos / max_height * g->width;
1062 src_y = pos % max_height;
1064 else /* frames are ordered diagonally */
1066 src_x = src_x + frame * g->offset_x;
1067 src_y = src_y + frame * g->offset_y;
1070 *bitmap = src_bitmap;
1071 *x = src_x * tilesize / g->tile_size;
1072 *y = src_y * tilesize / g->tile_size;
1075 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1076 int *x, int *y, boolean get_backside)
1078 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1082 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1083 Bitmap **bitmap, int *x, int *y)
1085 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1088 void getFixedGraphicSource(int graphic, int frame,
1089 Bitmap **bitmap, int *x, int *y)
1091 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1094 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1096 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1099 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1100 int *x, int *y, boolean get_backside)
1102 struct GraphicInfo *g = &graphic_info[graphic];
1103 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1104 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1106 if (TILESIZE_VAR != TILESIZE)
1107 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1110 *bitmap = g->bitmap;
1112 if (g->offset_y == 0) /* frames are ordered horizontally */
1114 int max_width = g->anim_frames_per_line * g->width;
1115 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1117 *x = pos % max_width;
1118 *y = src_y % g->height + pos / max_width * g->height;
1120 else if (g->offset_x == 0) /* frames are ordered vertically */
1122 int max_height = g->anim_frames_per_line * g->height;
1123 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1125 *x = src_x % g->width + pos / max_height * g->width;
1126 *y = pos % max_height;
1128 else /* frames are ordered diagonally */
1130 *x = src_x + frame * g->offset_x;
1131 *y = src_y + frame * g->offset_y;
1134 *x = *x * TILESIZE_VAR / g->tile_size;
1135 *y = *y * TILESIZE_VAR / g->tile_size;
1138 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1140 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1143 void DrawGraphic(int x, int y, int graphic, int frame)
1146 if (!IN_SCR_FIELD(x, y))
1148 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1149 printf("DrawGraphic(): This should never happen!\n");
1154 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1157 MarkTileDirty(x, y);
1160 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1163 if (!IN_SCR_FIELD(x, y))
1165 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1166 printf("DrawGraphic(): This should never happen!\n");
1171 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1173 MarkTileDirty(x, y);
1176 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1182 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1184 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1187 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1193 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1194 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1197 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1200 if (!IN_SCR_FIELD(x, y))
1202 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1203 printf("DrawGraphicThruMask(): This should never happen!\n");
1208 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1211 MarkTileDirty(x, y);
1214 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1217 if (!IN_SCR_FIELD(x, y))
1219 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1220 printf("DrawGraphicThruMask(): This should never happen!\n");
1225 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1227 MarkTileDirty(x, y);
1230 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1236 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1238 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1242 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1243 int graphic, int frame)
1248 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1250 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1254 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1256 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1258 MarkTileDirty(x / tilesize, y / tilesize);
1261 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1267 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1268 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1271 void DrawMiniGraphic(int x, int y, int graphic)
1273 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1274 MarkTileDirty(x / 2, y / 2);
1277 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1282 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1283 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1286 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1287 int graphic, int frame,
1288 int cut_mode, int mask_mode)
1293 int width = TILEX, height = TILEY;
1296 if (dx || dy) /* shifted graphic */
1298 if (x < BX1) /* object enters playfield from the left */
1305 else if (x > BX2) /* object enters playfield from the right */
1311 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1317 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1319 else if (dx) /* general horizontal movement */
1320 MarkTileDirty(x + SIGN(dx), y);
1322 if (y < BY1) /* object enters playfield from the top */
1324 if (cut_mode == CUT_BELOW) /* object completely above top border */
1332 else if (y > BY2) /* object enters playfield from the bottom */
1338 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1344 else if (dy > 0 && cut_mode == CUT_ABOVE)
1346 if (y == BY2) /* object completely above bottom border */
1352 MarkTileDirty(x, y + 1);
1353 } /* object leaves playfield to the bottom */
1354 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1356 else if (dy) /* general vertical movement */
1357 MarkTileDirty(x, y + SIGN(dy));
1361 if (!IN_SCR_FIELD(x, y))
1363 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1364 printf("DrawGraphicShifted(): This should never happen!\n");
1369 width = width * TILESIZE_VAR / TILESIZE;
1370 height = height * TILESIZE_VAR / TILESIZE;
1371 cx = cx * TILESIZE_VAR / TILESIZE;
1372 cy = cy * TILESIZE_VAR / TILESIZE;
1373 dx = dx * TILESIZE_VAR / TILESIZE;
1374 dy = dy * TILESIZE_VAR / TILESIZE;
1376 if (width > 0 && height > 0)
1378 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1383 dst_x = FX + x * TILEX_VAR + dx;
1384 dst_y = FY + y * TILEY_VAR + dy;
1386 if (mask_mode == USE_MASKING)
1387 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1390 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1393 MarkTileDirty(x, y);
1397 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1398 int graphic, int frame,
1399 int cut_mode, int mask_mode)
1404 int width = TILEX_VAR, height = TILEY_VAR;
1407 int x2 = x + SIGN(dx);
1408 int y2 = y + SIGN(dy);
1410 /* movement with two-tile animations must be sync'ed with movement position,
1411 not with current GfxFrame (which can be higher when using slow movement) */
1412 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1413 int anim_frames = graphic_info[graphic].anim_frames;
1415 /* (we also need anim_delay here for movement animations with less frames) */
1416 int anim_delay = graphic_info[graphic].anim_delay;
1417 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1419 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1420 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1422 /* re-calculate animation frame for two-tile movement animation */
1423 frame = getGraphicAnimationFrame(graphic, sync_frame);
1425 /* check if movement start graphic inside screen area and should be drawn */
1426 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1428 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1430 dst_x = FX + x1 * TILEX_VAR;
1431 dst_y = FY + y1 * TILEY_VAR;
1433 if (mask_mode == USE_MASKING)
1434 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1437 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1440 MarkTileDirty(x1, y1);
1443 /* check if movement end graphic inside screen area and should be drawn */
1444 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1446 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1448 dst_x = FX + x2 * TILEX_VAR;
1449 dst_y = FY + y2 * TILEY_VAR;
1451 if (mask_mode == USE_MASKING)
1452 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1455 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1458 MarkTileDirty(x2, y2);
1462 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1463 int graphic, int frame,
1464 int cut_mode, int mask_mode)
1468 DrawGraphic(x, y, graphic, frame);
1473 if (graphic_info[graphic].double_movement) /* EM style movement images */
1474 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1476 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1479 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1480 int frame, int cut_mode)
1482 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1485 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1486 int cut_mode, int mask_mode)
1488 int lx = LEVELX(x), ly = LEVELY(y);
1492 if (IN_LEV_FIELD(lx, ly))
1494 SetRandomAnimationValue(lx, ly);
1496 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1497 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1499 /* do not use double (EM style) movement graphic when not moving */
1500 if (graphic_info[graphic].double_movement && !dx && !dy)
1502 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1503 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1506 else /* border element */
1508 graphic = el2img(element);
1509 frame = getGraphicAnimationFrame(graphic, -1);
1512 if (element == EL_EXPANDABLE_WALL)
1514 boolean left_stopped = FALSE, right_stopped = FALSE;
1516 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1517 left_stopped = TRUE;
1518 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1519 right_stopped = TRUE;
1521 if (left_stopped && right_stopped)
1523 else if (left_stopped)
1525 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1526 frame = graphic_info[graphic].anim_frames - 1;
1528 else if (right_stopped)
1530 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1531 frame = graphic_info[graphic].anim_frames - 1;
1536 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1537 else if (mask_mode == USE_MASKING)
1538 DrawGraphicThruMask(x, y, graphic, frame);
1540 DrawGraphic(x, y, graphic, frame);
1543 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1544 int cut_mode, int mask_mode)
1546 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1547 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1548 cut_mode, mask_mode);
1551 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1554 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1557 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1560 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1563 void DrawLevelElementThruMask(int x, int y, int element)
1565 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1568 void DrawLevelFieldThruMask(int x, int y)
1570 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1573 /* !!! implementation of quicksand is totally broken !!! */
1574 #define IS_CRUMBLED_TILE(x, y, e) \
1575 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1576 !IS_MOVING(x, y) || \
1577 (e) == EL_QUICKSAND_EMPTYING || \
1578 (e) == EL_QUICKSAND_FAST_EMPTYING))
1580 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1585 int width, height, cx, cy;
1586 int sx = SCREENX(x), sy = SCREENY(y);
1587 int crumbled_border_size = graphic_info[graphic].border_size;
1590 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1592 for (i = 1; i < 4; i++)
1594 int dxx = (i & 1 ? dx : 0);
1595 int dyy = (i & 2 ? dy : 0);
1598 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1601 /* check if neighbour field is of same crumble type */
1602 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1603 graphic_info[graphic].class ==
1604 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1606 /* return if check prevents inner corner */
1607 if (same == (dxx == dx && dyy == dy))
1611 /* if we reach this point, we have an inner corner */
1613 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1615 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1616 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1617 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1618 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1620 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1621 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1624 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1629 int width, height, bx, by, cx, cy;
1630 int sx = SCREENX(x), sy = SCREENY(y);
1631 int crumbled_border_size = graphic_info[graphic].border_size;
1632 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1633 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1636 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1638 /* draw simple, sloppy, non-corner-accurate crumbled border */
1640 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1641 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1642 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1643 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1645 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1646 FX + sx * TILEX_VAR + cx,
1647 FY + sy * TILEY_VAR + cy);
1649 /* (remaining middle border part must be at least as big as corner part) */
1650 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1651 crumbled_border_size >= TILESIZE / 3)
1654 /* correct corners of crumbled border, if needed */
1656 for (i = -1; i <= 1; i += 2)
1658 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1659 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1660 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1663 /* check if neighbour field is of same crumble type */
1664 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1665 graphic_info[graphic].class ==
1666 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1668 /* no crumbled corner, but continued crumbled border */
1670 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1671 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1672 int b1 = (i == 1 ? crumbled_border_size_var :
1673 TILESIZE_VAR - 2 * crumbled_border_size_var);
1675 width = crumbled_border_size_var;
1676 height = crumbled_border_size_var;
1678 if (dir == 1 || dir == 2)
1693 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1695 FX + sx * TILEX_VAR + cx,
1696 FY + sy * TILEY_VAR + cy);
1701 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1703 int sx = SCREENX(x), sy = SCREENY(y);
1706 static int xy[4][2] =
1714 if (!IN_LEV_FIELD(x, y))
1717 element = TILE_GFX_ELEMENT(x, y);
1719 /* crumble field itself */
1720 if (IS_CRUMBLED_TILE(x, y, element))
1722 if (!IN_SCR_FIELD(sx, sy))
1725 for (i = 0; i < 4; i++)
1727 int xx = x + xy[i][0];
1728 int yy = y + xy[i][1];
1730 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1733 /* check if neighbour field is of same crumble type */
1734 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1735 graphic_info[graphic].class ==
1736 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1739 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1742 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1743 graphic_info[graphic].anim_frames == 2)
1745 for (i = 0; i < 4; i++)
1747 int dx = (i & 1 ? +1 : -1);
1748 int dy = (i & 2 ? +1 : -1);
1750 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1754 MarkTileDirty(sx, sy);
1756 else /* center field not crumbled -- crumble neighbour fields */
1758 for (i = 0; i < 4; i++)
1760 int xx = x + xy[i][0];
1761 int yy = y + xy[i][1];
1762 int sxx = sx + xy[i][0];
1763 int syy = sy + xy[i][1];
1765 if (!IN_LEV_FIELD(xx, yy) ||
1766 !IN_SCR_FIELD(sxx, syy))
1769 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1772 element = TILE_GFX_ELEMENT(xx, yy);
1774 if (!IS_CRUMBLED_TILE(xx, yy, element))
1777 graphic = el_act2crm(element, ACTION_DEFAULT);
1779 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1781 MarkTileDirty(sxx, syy);
1786 void DrawLevelFieldCrumbled(int x, int y)
1790 if (!IN_LEV_FIELD(x, y))
1793 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1794 GfxElement[x][y] != EL_UNDEFINED &&
1795 GFX_CRUMBLED(GfxElement[x][y]))
1797 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1802 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1804 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1807 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1810 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1811 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1812 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1813 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1814 int sx = SCREENX(x), sy = SCREENY(y);
1816 DrawGraphic(sx, sy, graphic1, frame1);
1817 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1820 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1822 int sx = SCREENX(x), sy = SCREENY(y);
1823 static int xy[4][2] =
1832 for (i = 0; i < 4; i++)
1834 int xx = x + xy[i][0];
1835 int yy = y + xy[i][1];
1836 int sxx = sx + xy[i][0];
1837 int syy = sy + xy[i][1];
1839 if (!IN_LEV_FIELD(xx, yy) ||
1840 !IN_SCR_FIELD(sxx, syy) ||
1841 !GFX_CRUMBLED(Feld[xx][yy]) ||
1845 DrawLevelField(xx, yy);
1849 static int getBorderElement(int x, int y)
1853 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1854 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1855 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1856 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1857 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1858 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1859 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1861 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1862 int steel_position = (x == -1 && y == -1 ? 0 :
1863 x == lev_fieldx && y == -1 ? 1 :
1864 x == -1 && y == lev_fieldy ? 2 :
1865 x == lev_fieldx && y == lev_fieldy ? 3 :
1866 x == -1 || x == lev_fieldx ? 4 :
1867 y == -1 || y == lev_fieldy ? 5 : 6);
1869 return border[steel_position][steel_type];
1872 void DrawScreenElement(int x, int y, int element)
1874 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1875 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1878 void DrawLevelElement(int x, int y, int element)
1880 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1881 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1884 void DrawScreenField(int x, int y)
1886 int lx = LEVELX(x), ly = LEVELY(y);
1887 int element, content;
1889 if (!IN_LEV_FIELD(lx, ly))
1891 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1894 element = getBorderElement(lx, ly);
1896 DrawScreenElement(x, y, element);
1901 element = Feld[lx][ly];
1902 content = Store[lx][ly];
1904 if (IS_MOVING(lx, ly))
1906 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1907 boolean cut_mode = NO_CUTTING;
1909 if (element == EL_QUICKSAND_EMPTYING ||
1910 element == EL_QUICKSAND_FAST_EMPTYING ||
1911 element == EL_MAGIC_WALL_EMPTYING ||
1912 element == EL_BD_MAGIC_WALL_EMPTYING ||
1913 element == EL_DC_MAGIC_WALL_EMPTYING ||
1914 element == EL_AMOEBA_DROPPING)
1915 cut_mode = CUT_ABOVE;
1916 else if (element == EL_QUICKSAND_FILLING ||
1917 element == EL_QUICKSAND_FAST_FILLING ||
1918 element == EL_MAGIC_WALL_FILLING ||
1919 element == EL_BD_MAGIC_WALL_FILLING ||
1920 element == EL_DC_MAGIC_WALL_FILLING)
1921 cut_mode = CUT_BELOW;
1923 if (cut_mode == CUT_ABOVE)
1924 DrawScreenElement(x, y, element);
1926 DrawScreenElement(x, y, EL_EMPTY);
1929 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1930 else if (cut_mode == NO_CUTTING)
1931 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1934 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1936 if (cut_mode == CUT_BELOW &&
1937 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1938 DrawLevelElement(lx, ly + 1, element);
1941 if (content == EL_ACID)
1943 int dir = MovDir[lx][ly];
1944 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1945 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1947 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1950 else if (IS_BLOCKED(lx, ly))
1955 boolean cut_mode = NO_CUTTING;
1956 int element_old, content_old;
1958 Blocked2Moving(lx, ly, &oldx, &oldy);
1961 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1962 MovDir[oldx][oldy] == MV_RIGHT);
1964 element_old = Feld[oldx][oldy];
1965 content_old = Store[oldx][oldy];
1967 if (element_old == EL_QUICKSAND_EMPTYING ||
1968 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1969 element_old == EL_MAGIC_WALL_EMPTYING ||
1970 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1971 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1972 element_old == EL_AMOEBA_DROPPING)
1973 cut_mode = CUT_ABOVE;
1975 DrawScreenElement(x, y, EL_EMPTY);
1978 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1980 else if (cut_mode == NO_CUTTING)
1981 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1984 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1987 else if (IS_DRAWABLE(element))
1988 DrawScreenElement(x, y, element);
1990 DrawScreenElement(x, y, EL_EMPTY);
1993 void DrawLevelField(int x, int y)
1995 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1996 DrawScreenField(SCREENX(x), SCREENY(y));
1997 else if (IS_MOVING(x, y))
2001 Moving2Blocked(x, y, &newx, &newy);
2002 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2003 DrawScreenField(SCREENX(newx), SCREENY(newy));
2005 else if (IS_BLOCKED(x, y))
2009 Blocked2Moving(x, y, &oldx, &oldy);
2010 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2011 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2015 void DrawSizedElement(int x, int y, int element, int tilesize)
2019 graphic = el2edimg(element);
2020 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2023 void DrawMiniElement(int x, int y, int element)
2027 graphic = el2edimg(element);
2028 DrawMiniGraphic(x, y, graphic);
2031 void DrawSizedElementOrWall(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 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2038 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2039 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2041 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2044 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2046 int x = sx + scroll_x, y = sy + scroll_y;
2048 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2049 DrawMiniElement(sx, sy, EL_EMPTY);
2050 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2051 DrawMiniElement(sx, sy, Feld[x][y]);
2053 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2056 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2057 int x, int y, int xsize, int ysize,
2058 int tile_width, int tile_height)
2062 int dst_x = startx + x * tile_width;
2063 int dst_y = starty + y * tile_height;
2064 int width = graphic_info[graphic].width;
2065 int height = graphic_info[graphic].height;
2066 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2067 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2068 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2069 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2070 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2071 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2072 boolean draw_masked = graphic_info[graphic].draw_masked;
2074 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2076 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2078 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2082 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2083 inner_sx + (x - 1) * tile_width % inner_width);
2084 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2085 inner_sy + (y - 1) * tile_height % inner_height);
2088 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2091 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2095 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2096 int x, int y, int xsize, int ysize, int font_nr)
2098 int font_width = getFontWidth(font_nr);
2099 int font_height = getFontHeight(font_nr);
2101 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2102 font_width, font_height);
2105 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2107 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2108 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2109 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2110 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2111 boolean no_delay = (tape.warp_forward);
2112 unsigned int anim_delay = 0;
2113 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2114 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2115 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2116 int font_width = getFontWidth(font_nr);
2117 int font_height = getFontHeight(font_nr);
2118 int max_xsize = level.envelope[envelope_nr].xsize;
2119 int max_ysize = level.envelope[envelope_nr].ysize;
2120 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2121 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2122 int xend = max_xsize;
2123 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2124 int xstep = (xstart < xend ? 1 : 0);
2125 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2127 int end = MAX(xend - xstart, yend - ystart);
2130 for (i = start; i <= end; i++)
2132 int last_frame = end; // last frame of this "for" loop
2133 int x = xstart + i * xstep;
2134 int y = ystart + i * ystep;
2135 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2136 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2137 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2138 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2141 SetDrawtoField(DRAW_FIELDBUFFER);
2143 BlitScreenToBitmap(backbuffer);
2145 SetDrawtoField(DRAW_BACKBUFFER);
2147 for (yy = 0; yy < ysize; yy++)
2148 for (xx = 0; xx < xsize; xx++)
2149 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2151 DrawTextBuffer(sx + font_width, sy + font_height,
2152 level.envelope[envelope_nr].text, font_nr, max_xsize,
2153 xsize - 2, ysize - 2, 0, mask_mode,
2154 level.envelope[envelope_nr].autowrap,
2155 level.envelope[envelope_nr].centered, FALSE);
2157 redraw_mask |= REDRAW_FIELD;
2160 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2164 void ShowEnvelope(int envelope_nr)
2166 int element = EL_ENVELOPE_1 + envelope_nr;
2167 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2168 int sound_opening = element_info[element].sound[ACTION_OPENING];
2169 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2170 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2171 boolean no_delay = (tape.warp_forward);
2172 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2173 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2174 int anim_mode = graphic_info[graphic].anim_mode;
2175 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2176 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2178 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2180 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2182 if (anim_mode == ANIM_DEFAULT)
2183 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2185 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2188 Delay(wait_delay_value);
2190 WaitForEventToContinue();
2192 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2194 if (anim_mode != ANIM_NONE)
2195 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2197 if (anim_mode == ANIM_DEFAULT)
2198 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2200 game.envelope_active = FALSE;
2202 SetDrawtoField(DRAW_FIELDBUFFER);
2204 redraw_mask |= REDRAW_FIELD;
2208 static void setRequestBasePosition(int *x, int *y)
2210 int sx_base, sy_base;
2212 if (request.x != -1)
2213 sx_base = request.x;
2214 else if (request.align == ALIGN_LEFT)
2216 else if (request.align == ALIGN_RIGHT)
2217 sx_base = SX + SXSIZE;
2219 sx_base = SX + SXSIZE / 2;
2221 if (request.y != -1)
2222 sy_base = request.y;
2223 else if (request.valign == VALIGN_TOP)
2225 else if (request.valign == VALIGN_BOTTOM)
2226 sy_base = SY + SYSIZE;
2228 sy_base = SY + SYSIZE / 2;
2234 static void setRequestPositionExt(int *x, int *y, int width, int height,
2235 boolean add_border_size)
2237 int border_size = request.border_size;
2238 int sx_base, sy_base;
2241 setRequestBasePosition(&sx_base, &sy_base);
2243 if (request.align == ALIGN_LEFT)
2245 else if (request.align == ALIGN_RIGHT)
2246 sx = sx_base - width;
2248 sx = sx_base - width / 2;
2250 if (request.valign == VALIGN_TOP)
2252 else if (request.valign == VALIGN_BOTTOM)
2253 sy = sy_base - height;
2255 sy = sy_base - height / 2;
2257 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2258 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2260 if (add_border_size)
2270 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2272 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2275 void DrawEnvelopeRequest(char *text)
2277 int last_game_status = game_status; /* save current game status */
2278 char *text_final = text;
2279 char *text_door_style = NULL;
2280 int graphic = IMG_BACKGROUND_REQUEST;
2281 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2282 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2283 int font_nr = FONT_REQUEST;
2284 int font_width = getFontWidth(font_nr);
2285 int font_height = getFontHeight(font_nr);
2286 int border_size = request.border_size;
2287 int line_spacing = request.line_spacing;
2288 int line_height = font_height + line_spacing;
2289 int max_text_width = request.width - 2 * border_size;
2290 int max_text_height = request.height - 2 * border_size;
2291 int line_length = max_text_width / font_width;
2292 int max_lines = max_text_height / line_height;
2293 int text_width = line_length * font_width;
2294 int width = request.width;
2295 int height = request.height;
2296 int tile_size = MAX(request.step_offset, 1);
2297 int x_steps = width / tile_size;
2298 int y_steps = height / tile_size;
2299 int sx_offset = border_size;
2300 int sy_offset = border_size;
2304 if (request.centered)
2305 sx_offset = (request.width - text_width) / 2;
2307 if (request.wrap_single_words && !request.autowrap)
2309 char *src_text_ptr, *dst_text_ptr;
2311 text_door_style = checked_malloc(2 * strlen(text) + 1);
2313 src_text_ptr = text;
2314 dst_text_ptr = text_door_style;
2316 while (*src_text_ptr)
2318 if (*src_text_ptr == ' ' ||
2319 *src_text_ptr == '?' ||
2320 *src_text_ptr == '!')
2321 *dst_text_ptr++ = '\n';
2323 if (*src_text_ptr != ' ')
2324 *dst_text_ptr++ = *src_text_ptr;
2329 *dst_text_ptr = '\0';
2331 text_final = text_door_style;
2334 setRequestPosition(&sx, &sy, FALSE);
2336 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2338 for (y = 0; y < y_steps; y++)
2339 for (x = 0; x < x_steps; x++)
2340 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2341 x, y, x_steps, y_steps,
2342 tile_size, tile_size);
2344 /* force DOOR font inside door area */
2345 game_status = GAME_MODE_PSEUDO_DOOR;
2347 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2348 line_length, -1, max_lines, line_spacing, mask_mode,
2349 request.autowrap, request.centered, FALSE);
2351 game_status = last_game_status; /* restore current game status */
2353 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2354 RedrawGadget(tool_gadget[i]);
2356 // store readily prepared envelope request for later use when animating
2357 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2359 if (text_door_style)
2360 free(text_door_style);
2363 void AnimateEnvelopeRequest(int anim_mode, int action)
2365 int graphic = IMG_BACKGROUND_REQUEST;
2366 boolean draw_masked = graphic_info[graphic].draw_masked;
2367 int delay_value_normal = request.step_delay;
2368 int delay_value_fast = delay_value_normal / 2;
2369 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2370 boolean no_delay = (tape.warp_forward);
2371 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2372 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2373 unsigned int anim_delay = 0;
2375 int tile_size = MAX(request.step_offset, 1);
2376 int max_xsize = request.width / tile_size;
2377 int max_ysize = request.height / tile_size;
2378 int max_xsize_inner = max_xsize - 2;
2379 int max_ysize_inner = max_ysize - 2;
2381 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2382 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2383 int xend = max_xsize_inner;
2384 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2385 int xstep = (xstart < xend ? 1 : 0);
2386 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2388 int end = MAX(xend - xstart, yend - ystart);
2391 if (setup.quick_doors)
2398 for (i = start; i <= end; i++)
2400 int last_frame = end; // last frame of this "for" loop
2401 int x = xstart + i * xstep;
2402 int y = ystart + i * ystep;
2403 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2404 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2405 int xsize_size_left = (xsize - 1) * tile_size;
2406 int ysize_size_top = (ysize - 1) * tile_size;
2407 int max_xsize_pos = (max_xsize - 1) * tile_size;
2408 int max_ysize_pos = (max_ysize - 1) * tile_size;
2409 int width = xsize * tile_size;
2410 int height = ysize * tile_size;
2415 setRequestPosition(&src_x, &src_y, FALSE);
2416 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2418 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2420 for (yy = 0; yy < 2; yy++)
2422 for (xx = 0; xx < 2; xx++)
2424 int src_xx = src_x + xx * max_xsize_pos;
2425 int src_yy = src_y + yy * max_ysize_pos;
2426 int dst_xx = dst_x + xx * xsize_size_left;
2427 int dst_yy = dst_y + yy * ysize_size_top;
2428 int xx_size = (xx ? tile_size : xsize_size_left);
2429 int yy_size = (yy ? tile_size : ysize_size_top);
2432 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2433 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2435 BlitBitmap(bitmap_db_cross, backbuffer,
2436 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2440 redraw_mask |= REDRAW_FIELD;
2445 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2449 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2451 int graphic = IMG_BACKGROUND_REQUEST;
2452 int sound_opening = SND_REQUEST_OPENING;
2453 int sound_closing = SND_REQUEST_CLOSING;
2454 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2455 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2456 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2457 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2458 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2460 if (game_status == GAME_MODE_PLAYING)
2461 BlitScreenToBitmap(backbuffer);
2463 SetDrawtoField(DRAW_BACKBUFFER);
2465 // SetDrawBackgroundMask(REDRAW_NONE);
2467 if (action == ACTION_OPENING)
2469 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2471 if (req_state & REQ_ASK)
2473 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2474 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2476 else if (req_state & REQ_CONFIRM)
2478 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2480 else if (req_state & REQ_PLAYER)
2482 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2483 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2484 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2485 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2488 DrawEnvelopeRequest(text);
2490 if (game_status != GAME_MODE_MAIN)
2494 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2496 if (action == ACTION_OPENING)
2498 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2500 if (anim_mode == ANIM_DEFAULT)
2501 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2503 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2507 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2509 if (anim_mode != ANIM_NONE)
2510 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2512 if (anim_mode == ANIM_DEFAULT)
2513 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2516 game.envelope_active = FALSE;
2518 if (action == ACTION_CLOSING)
2520 if (game_status != GAME_MODE_MAIN)
2523 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2526 // SetDrawBackgroundMask(last_draw_background_mask);
2528 redraw_mask |= REDRAW_FIELD;
2530 if (game_status == GAME_MODE_MAIN)
2535 if (action == ACTION_CLOSING &&
2536 game_status == GAME_MODE_PLAYING &&
2537 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2538 SetDrawtoField(DRAW_FIELDBUFFER);
2541 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2545 int graphic = el2preimg(element);
2547 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2548 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2551 void DrawLevel(int draw_background_mask)
2555 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2556 SetDrawBackgroundMask(draw_background_mask);
2560 for (x = BX1; x <= BX2; x++)
2561 for (y = BY1; y <= BY2; y++)
2562 DrawScreenField(x, y);
2564 redraw_mask |= REDRAW_FIELD;
2567 void DrawSizedLevel(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 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2576 redraw_mask |= REDRAW_FIELD;
2579 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2583 for (x = 0; x < size_x; x++)
2584 for (y = 0; y < size_y; y++)
2585 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2587 redraw_mask |= REDRAW_FIELD;
2590 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2592 boolean show_level_border = (BorderElement != EL_EMPTY);
2593 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2594 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2595 int tile_size = preview.tile_size;
2596 int preview_width = preview.xsize * tile_size;
2597 int preview_height = preview.ysize * tile_size;
2598 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2599 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2600 int real_preview_width = real_preview_xsize * tile_size;
2601 int real_preview_height = real_preview_ysize * tile_size;
2602 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2603 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2606 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2609 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2611 dst_x += (preview_width - real_preview_width) / 2;
2612 dst_y += (preview_height - real_preview_height) / 2;
2614 for (x = 0; x < real_preview_xsize; x++)
2616 for (y = 0; y < real_preview_ysize; y++)
2618 int lx = from_x + x + (show_level_border ? -1 : 0);
2619 int ly = from_y + y + (show_level_border ? -1 : 0);
2620 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2621 getBorderElement(lx, ly));
2623 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2624 element, tile_size);
2628 redraw_mask |= REDRAW_FIELD;
2631 #define MICROLABEL_EMPTY 0
2632 #define MICROLABEL_LEVEL_NAME 1
2633 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2634 #define MICROLABEL_LEVEL_AUTHOR 3
2635 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2636 #define MICROLABEL_IMPORTED_FROM 5
2637 #define MICROLABEL_IMPORTED_BY_HEAD 6
2638 #define MICROLABEL_IMPORTED_BY 7
2640 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2642 int max_text_width = SXSIZE;
2643 int font_width = getFontWidth(font_nr);
2645 if (pos->align == ALIGN_CENTER)
2646 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2647 else if (pos->align == ALIGN_RIGHT)
2648 max_text_width = pos->x;
2650 max_text_width = SXSIZE - pos->x;
2652 return max_text_width / font_width;
2655 static void DrawPreviewLevelLabelExt(int mode)
2657 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2658 char label_text[MAX_OUTPUT_LINESIZE + 1];
2659 int max_len_label_text;
2660 int font_nr = pos->font;
2663 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2666 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2667 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2668 mode == MICROLABEL_IMPORTED_BY_HEAD)
2669 font_nr = pos->font_alt;
2671 max_len_label_text = getMaxTextLength(pos, font_nr);
2673 if (pos->size != -1)
2674 max_len_label_text = pos->size;
2676 for (i = 0; i < max_len_label_text; i++)
2677 label_text[i] = ' ';
2678 label_text[max_len_label_text] = '\0';
2680 if (strlen(label_text) > 0)
2681 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2684 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2685 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2686 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2687 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2688 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2689 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2690 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2691 max_len_label_text);
2692 label_text[max_len_label_text] = '\0';
2694 if (strlen(label_text) > 0)
2695 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2697 redraw_mask |= REDRAW_FIELD;
2700 static void DrawPreviewLevelExt(boolean restart)
2702 static unsigned int scroll_delay = 0;
2703 static unsigned int label_delay = 0;
2704 static int from_x, from_y, scroll_direction;
2705 static int label_state, label_counter;
2706 unsigned int scroll_delay_value = preview.step_delay;
2707 boolean show_level_border = (BorderElement != EL_EMPTY);
2708 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2709 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2710 int last_game_status = game_status; /* save current game status */
2717 if (preview.anim_mode == ANIM_CENTERED)
2719 if (level_xsize > preview.xsize)
2720 from_x = (level_xsize - preview.xsize) / 2;
2721 if (level_ysize > preview.ysize)
2722 from_y = (level_ysize - preview.ysize) / 2;
2725 from_x += preview.xoffset;
2726 from_y += preview.yoffset;
2728 scroll_direction = MV_RIGHT;
2732 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2733 DrawPreviewLevelLabelExt(label_state);
2735 /* initialize delay counters */
2736 DelayReached(&scroll_delay, 0);
2737 DelayReached(&label_delay, 0);
2739 if (leveldir_current->name)
2741 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2742 char label_text[MAX_OUTPUT_LINESIZE + 1];
2743 int font_nr = pos->font;
2744 int max_len_label_text = getMaxTextLength(pos, font_nr);
2746 if (pos->size != -1)
2747 max_len_label_text = pos->size;
2749 strncpy(label_text, leveldir_current->name, max_len_label_text);
2750 label_text[max_len_label_text] = '\0';
2752 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2753 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2756 game_status = last_game_status; /* restore current game status */
2761 /* scroll preview level, if needed */
2762 if (preview.anim_mode != ANIM_NONE &&
2763 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2764 DelayReached(&scroll_delay, scroll_delay_value))
2766 switch (scroll_direction)
2771 from_x -= preview.step_offset;
2772 from_x = (from_x < 0 ? 0 : from_x);
2775 scroll_direction = MV_UP;
2779 if (from_x < level_xsize - preview.xsize)
2781 from_x += preview.step_offset;
2782 from_x = (from_x > level_xsize - preview.xsize ?
2783 level_xsize - preview.xsize : from_x);
2786 scroll_direction = MV_DOWN;
2792 from_y -= preview.step_offset;
2793 from_y = (from_y < 0 ? 0 : from_y);
2796 scroll_direction = MV_RIGHT;
2800 if (from_y < level_ysize - preview.ysize)
2802 from_y += preview.step_offset;
2803 from_y = (from_y > level_ysize - preview.ysize ?
2804 level_ysize - preview.ysize : from_y);
2807 scroll_direction = MV_LEFT;
2814 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2817 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2818 /* redraw micro level label, if needed */
2819 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2820 !strEqual(level.author, ANONYMOUS_NAME) &&
2821 !strEqual(level.author, leveldir_current->name) &&
2822 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2824 int max_label_counter = 23;
2826 if (leveldir_current->imported_from != NULL &&
2827 strlen(leveldir_current->imported_from) > 0)
2828 max_label_counter += 14;
2829 if (leveldir_current->imported_by != NULL &&
2830 strlen(leveldir_current->imported_by) > 0)
2831 max_label_counter += 14;
2833 label_counter = (label_counter + 1) % max_label_counter;
2834 label_state = (label_counter >= 0 && label_counter <= 7 ?
2835 MICROLABEL_LEVEL_NAME :
2836 label_counter >= 9 && label_counter <= 12 ?
2837 MICROLABEL_LEVEL_AUTHOR_HEAD :
2838 label_counter >= 14 && label_counter <= 21 ?
2839 MICROLABEL_LEVEL_AUTHOR :
2840 label_counter >= 23 && label_counter <= 26 ?
2841 MICROLABEL_IMPORTED_FROM_HEAD :
2842 label_counter >= 28 && label_counter <= 35 ?
2843 MICROLABEL_IMPORTED_FROM :
2844 label_counter >= 37 && label_counter <= 40 ?
2845 MICROLABEL_IMPORTED_BY_HEAD :
2846 label_counter >= 42 && label_counter <= 49 ?
2847 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2849 if (leveldir_current->imported_from == NULL &&
2850 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2851 label_state == MICROLABEL_IMPORTED_FROM))
2852 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2853 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2855 DrawPreviewLevelLabelExt(label_state);
2858 game_status = last_game_status; /* restore current game status */
2861 void DrawPreviewLevelInitial()
2863 DrawPreviewLevelExt(TRUE);
2866 void DrawPreviewLevelAnimation()
2868 DrawPreviewLevelExt(FALSE);
2871 inline static void DrawGraphicAnimationExt(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 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2880 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2883 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2884 int graphic, int sync_frame, int mask_mode)
2886 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2888 if (mask_mode == USE_MASKING)
2889 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2891 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2894 inline static void DrawGraphicAnimation(int x, int y, int graphic)
2896 int lx = LEVELX(x), ly = LEVELY(y);
2898 if (!IN_SCR_FIELD(x, y))
2901 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2902 graphic, GfxFrame[lx][ly], NO_MASKING);
2904 MarkTileDirty(x, y);
2907 void DrawFixedGraphicAnimation(int x, int y, int graphic)
2909 int lx = LEVELX(x), ly = LEVELY(y);
2911 if (!IN_SCR_FIELD(x, y))
2914 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2915 graphic, GfxFrame[lx][ly], NO_MASKING);
2916 MarkTileDirty(x, y);
2919 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2921 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2924 void DrawLevelElementAnimation(int x, int y, int element)
2926 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2928 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2931 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2933 int sx = SCREENX(x), sy = SCREENY(y);
2935 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2938 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2941 DrawGraphicAnimation(sx, sy, graphic);
2944 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2945 DrawLevelFieldCrumbled(x, y);
2947 if (GFX_CRUMBLED(Feld[x][y]))
2948 DrawLevelFieldCrumbled(x, y);
2952 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2954 int sx = SCREENX(x), sy = SCREENY(y);
2957 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2960 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2962 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2965 DrawGraphicAnimation(sx, sy, graphic);
2967 if (GFX_CRUMBLED(element))
2968 DrawLevelFieldCrumbled(x, y);
2971 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2973 if (player->use_murphy)
2975 /* this works only because currently only one player can be "murphy" ... */
2976 static int last_horizontal_dir = MV_LEFT;
2977 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2979 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2980 last_horizontal_dir = move_dir;
2982 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2984 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2986 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2992 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2995 static boolean equalGraphics(int graphic1, int graphic2)
2997 struct GraphicInfo *g1 = &graphic_info[graphic1];
2998 struct GraphicInfo *g2 = &graphic_info[graphic2];
3000 return (g1->bitmap == g2->bitmap &&
3001 g1->src_x == g2->src_x &&
3002 g1->src_y == g2->src_y &&
3003 g1->anim_frames == g2->anim_frames &&
3004 g1->anim_delay == g2->anim_delay &&
3005 g1->anim_mode == g2->anim_mode);
3008 void DrawAllPlayers()
3012 for (i = 0; i < MAX_PLAYERS; i++)
3013 if (stored_player[i].active)
3014 DrawPlayer(&stored_player[i]);
3017 void DrawPlayerField(int x, int y)
3019 if (!IS_PLAYER(x, y))
3022 DrawPlayer(PLAYERINFO(x, y));
3025 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3027 void DrawPlayer(struct PlayerInfo *player)
3029 int jx = player->jx;
3030 int jy = player->jy;
3031 int move_dir = player->MovDir;
3032 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3033 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3034 int last_jx = (player->is_moving ? jx - dx : jx);
3035 int last_jy = (player->is_moving ? jy - dy : jy);
3036 int next_jx = jx + dx;
3037 int next_jy = jy + dy;
3038 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3039 boolean player_is_opaque = FALSE;
3040 int sx = SCREENX(jx), sy = SCREENY(jy);
3041 int sxx = 0, syy = 0;
3042 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3044 int action = ACTION_DEFAULT;
3045 int last_player_graphic = getPlayerGraphic(player, move_dir);
3046 int last_player_frame = player->Frame;
3049 /* GfxElement[][] is set to the element the player is digging or collecting;
3050 remove also for off-screen player if the player is not moving anymore */
3051 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3052 GfxElement[jx][jy] = EL_UNDEFINED;
3054 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3058 if (!IN_LEV_FIELD(jx, jy))
3060 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3061 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3062 printf("DrawPlayerField(): This should never happen!\n");
3067 if (element == EL_EXPLOSION)
3070 action = (player->is_pushing ? ACTION_PUSHING :
3071 player->is_digging ? ACTION_DIGGING :
3072 player->is_collecting ? ACTION_COLLECTING :
3073 player->is_moving ? ACTION_MOVING :
3074 player->is_snapping ? ACTION_SNAPPING :
3075 player->is_dropping ? ACTION_DROPPING :
3076 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3078 if (player->is_waiting)
3079 move_dir = player->dir_waiting;
3081 InitPlayerGfxAnimation(player, action, move_dir);
3083 /* ----------------------------------------------------------------------- */
3084 /* draw things in the field the player is leaving, if needed */
3085 /* ----------------------------------------------------------------------- */
3087 if (player->is_moving)
3089 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3091 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3093 if (last_element == EL_DYNAMITE_ACTIVE ||
3094 last_element == EL_EM_DYNAMITE_ACTIVE ||
3095 last_element == EL_SP_DISK_RED_ACTIVE)
3096 DrawDynamite(last_jx, last_jy);
3098 DrawLevelFieldThruMask(last_jx, last_jy);
3100 else if (last_element == EL_DYNAMITE_ACTIVE ||
3101 last_element == EL_EM_DYNAMITE_ACTIVE ||
3102 last_element == EL_SP_DISK_RED_ACTIVE)
3103 DrawDynamite(last_jx, last_jy);
3105 /* !!! this is not enough to prevent flickering of players which are
3106 moving next to each others without a free tile between them -- this
3107 can only be solved by drawing all players layer by layer (first the
3108 background, then the foreground etc.) !!! => TODO */
3109 else if (!IS_PLAYER(last_jx, last_jy))
3110 DrawLevelField(last_jx, last_jy);
3113 DrawLevelField(last_jx, last_jy);
3116 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3117 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3120 if (!IN_SCR_FIELD(sx, sy))
3123 /* ----------------------------------------------------------------------- */
3124 /* draw things behind the player, if needed */
3125 /* ----------------------------------------------------------------------- */
3128 DrawLevelElement(jx, jy, Back[jx][jy]);
3129 else if (IS_ACTIVE_BOMB(element))
3130 DrawLevelElement(jx, jy, EL_EMPTY);
3133 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3135 int old_element = GfxElement[jx][jy];
3136 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3137 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3139 if (GFX_CRUMBLED(old_element))
3140 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3142 DrawGraphic(sx, sy, old_graphic, frame);
3144 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3145 player_is_opaque = TRUE;
3149 GfxElement[jx][jy] = EL_UNDEFINED;
3151 /* make sure that pushed elements are drawn with correct frame rate */
3152 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3154 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3155 GfxFrame[jx][jy] = player->StepFrame;
3157 DrawLevelField(jx, jy);
3161 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3162 /* ----------------------------------------------------------------------- */
3163 /* draw player himself */
3164 /* ----------------------------------------------------------------------- */
3166 graphic = getPlayerGraphic(player, move_dir);
3168 /* in the case of changed player action or direction, prevent the current
3169 animation frame from being restarted for identical animations */
3170 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3171 player->Frame = last_player_frame;
3173 frame = getGraphicAnimationFrame(graphic, player->Frame);
3177 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3178 sxx = player->GfxPos;
3180 syy = player->GfxPos;
3183 if (player_is_opaque)
3184 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3186 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3188 if (SHIELD_ON(player))
3190 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3191 IMG_SHIELD_NORMAL_ACTIVE);
3192 int frame = getGraphicAnimationFrame(graphic, -1);
3194 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3198 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3201 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3202 sxx = player->GfxPos;
3204 syy = player->GfxPos;
3208 /* ----------------------------------------------------------------------- */
3209 /* draw things the player is pushing, if needed */
3210 /* ----------------------------------------------------------------------- */
3212 if (player->is_pushing && player->is_moving)
3214 int px = SCREENX(jx), py = SCREENY(jy);
3215 int pxx = (TILEX - ABS(sxx)) * dx;
3216 int pyy = (TILEY - ABS(syy)) * dy;
3217 int gfx_frame = GfxFrame[jx][jy];
3223 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3225 element = Feld[next_jx][next_jy];
3226 gfx_frame = GfxFrame[next_jx][next_jy];
3229 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3231 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3232 frame = getGraphicAnimationFrame(graphic, sync_frame);
3234 /* draw background element under pushed element (like the Sokoban field) */
3235 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3237 /* this allows transparent pushing animation over non-black background */
3240 DrawLevelElement(jx, jy, Back[jx][jy]);
3242 DrawLevelElement(jx, jy, EL_EMPTY);
3244 if (Back[next_jx][next_jy])
3245 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3247 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3249 else if (Back[next_jx][next_jy])
3250 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3253 /* do not draw (EM style) pushing animation when pushing is finished */
3254 /* (two-tile animations usually do not contain start and end frame) */
3255 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3256 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3258 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3260 /* masked drawing is needed for EMC style (double) movement graphics */
3261 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3262 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3266 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3267 /* ----------------------------------------------------------------------- */
3268 /* draw player himself */
3269 /* ----------------------------------------------------------------------- */
3271 graphic = getPlayerGraphic(player, move_dir);
3273 /* in the case of changed player action or direction, prevent the current
3274 animation frame from being restarted for identical animations */
3275 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3276 player->Frame = last_player_frame;
3278 frame = getGraphicAnimationFrame(graphic, player->Frame);
3282 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3283 sxx = player->GfxPos;
3285 syy = player->GfxPos;
3288 if (player_is_opaque)
3289 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3291 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3293 if (SHIELD_ON(player))
3295 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3296 IMG_SHIELD_NORMAL_ACTIVE);
3297 int frame = getGraphicAnimationFrame(graphic, -1);
3299 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3303 /* ----------------------------------------------------------------------- */
3304 /* draw things in front of player (active dynamite or dynabombs) */
3305 /* ----------------------------------------------------------------------- */
3307 if (IS_ACTIVE_BOMB(element))
3309 graphic = el2img(element);
3310 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3312 if (game.emulation == EMU_SUPAPLEX)
3313 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3315 DrawGraphicThruMask(sx, sy, graphic, frame);
3318 if (player_is_moving && last_element == EL_EXPLOSION)
3320 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3321 GfxElement[last_jx][last_jy] : EL_EMPTY);
3322 int graphic = el_act2img(element, ACTION_EXPLODING);
3323 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3324 int phase = ExplodePhase[last_jx][last_jy] - 1;
3325 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3328 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3331 /* ----------------------------------------------------------------------- */
3332 /* draw elements the player is just walking/passing through/under */
3333 /* ----------------------------------------------------------------------- */
3335 if (player_is_moving)
3337 /* handle the field the player is leaving ... */
3338 if (IS_ACCESSIBLE_INSIDE(last_element))
3339 DrawLevelField(last_jx, last_jy);
3340 else if (IS_ACCESSIBLE_UNDER(last_element))
3341 DrawLevelFieldThruMask(last_jx, last_jy);
3344 /* do not redraw accessible elements if the player is just pushing them */
3345 if (!player_is_moving || !player->is_pushing)
3347 /* ... and the field the player is entering */
3348 if (IS_ACCESSIBLE_INSIDE(element))
3349 DrawLevelField(jx, jy);
3350 else if (IS_ACCESSIBLE_UNDER(element))
3351 DrawLevelFieldThruMask(jx, jy);
3354 MarkTileDirty(sx, sy);
3357 /* ------------------------------------------------------------------------- */
3359 void WaitForEventToContinue()
3361 boolean still_wait = TRUE;
3363 /* simulate releasing mouse button over last gadget, if still pressed */
3365 HandleGadgets(-1, -1, 0);
3367 button_status = MB_RELEASED;
3381 case EVENT_BUTTONPRESS:
3382 case EVENT_KEYPRESS:
3386 case EVENT_KEYRELEASE:
3387 ClearPlayerAction();
3391 HandleOtherEvents(&event);
3395 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3402 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3406 #define MAX_REQUEST_LINES 13
3407 #define MAX_REQUEST_LINE_FONT1_LEN 7
3408 #define MAX_REQUEST_LINE_FONT2_LEN 10
3410 static int RequestHandleEvents(unsigned int req_state)
3412 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3413 local_player->LevelSolved_GameEnd);
3414 int width = request.width;
3415 int height = request.height;
3419 setRequestPosition(&sx, &sy, FALSE);
3421 button_status = MB_RELEASED;
3423 request_gadget_id = -1;
3430 SetDrawtoField(DRAW_FIELDBUFFER);
3432 HandleGameActions();
3434 SetDrawtoField(DRAW_BACKBUFFER);
3436 if (global.use_envelope_request)
3438 /* copy current state of request area to middle of playfield area */
3439 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3447 while (NextValidEvent(&event))
3451 case EVENT_BUTTONPRESS:
3452 case EVENT_BUTTONRELEASE:
3453 case EVENT_MOTIONNOTIFY:
3457 if (event.type == EVENT_MOTIONNOTIFY)
3462 motion_status = TRUE;
3463 mx = ((MotionEvent *) &event)->x;
3464 my = ((MotionEvent *) &event)->y;
3468 motion_status = FALSE;
3469 mx = ((ButtonEvent *) &event)->x;
3470 my = ((ButtonEvent *) &event)->y;
3471 if (event.type == EVENT_BUTTONPRESS)
3472 button_status = ((ButtonEvent *) &event)->button;
3474 button_status = MB_RELEASED;
3477 /* this sets 'request_gadget_id' */
3478 HandleGadgets(mx, my, button_status);
3480 switch (request_gadget_id)
3482 case TOOL_CTRL_ID_YES:
3485 case TOOL_CTRL_ID_NO:
3488 case TOOL_CTRL_ID_CONFIRM:
3489 result = TRUE | FALSE;
3492 case TOOL_CTRL_ID_PLAYER_1:
3495 case TOOL_CTRL_ID_PLAYER_2:
3498 case TOOL_CTRL_ID_PLAYER_3:
3501 case TOOL_CTRL_ID_PLAYER_4:
3512 case EVENT_KEYPRESS:
3513 switch (GetEventKey((KeyEvent *)&event, TRUE))
3516 if (req_state & REQ_CONFIRM)
3521 #if defined(TARGET_SDL2)
3528 #if defined(TARGET_SDL2)
3538 if (req_state & REQ_PLAYER)
3542 case EVENT_KEYRELEASE:
3543 ClearPlayerAction();
3547 HandleOtherEvents(&event);
3552 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3554 int joy = AnyJoystick();
3556 if (joy & JOY_BUTTON_1)
3558 else if (joy & JOY_BUTTON_2)
3564 if (global.use_envelope_request)
3566 /* copy back current state of pressed buttons inside request area */
3567 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3577 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3583 static boolean RequestDoor(char *text, unsigned int req_state)
3585 unsigned int old_door_state;
3586 int last_game_status = game_status; /* save current game status */
3587 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3588 int font_nr = FONT_TEXT_2;
3593 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3595 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3596 font_nr = FONT_TEXT_1;
3599 if (game_status == GAME_MODE_PLAYING)
3600 BlitScreenToBitmap(backbuffer);
3602 /* disable deactivated drawing when quick-loading level tape recording */
3603 if (tape.playing && tape.deactivate_display)
3604 TapeDeactivateDisplayOff(TRUE);
3606 SetMouseCursor(CURSOR_DEFAULT);
3608 #if defined(NETWORK_AVALIABLE)
3609 /* pause network game while waiting for request to answer */
3610 if (options.network &&
3611 game_status == GAME_MODE_PLAYING &&
3612 req_state & REQUEST_WAIT_FOR_INPUT)
3613 SendToServer_PausePlaying();
3616 old_door_state = GetDoorState();
3618 /* simulate releasing mouse button over last gadget, if still pressed */
3620 HandleGadgets(-1, -1, 0);
3624 /* draw released gadget before proceeding */
3627 if (old_door_state & DOOR_OPEN_1)
3629 CloseDoor(DOOR_CLOSE_1);
3631 /* save old door content */
3632 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3633 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3636 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3637 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3639 /* clear door drawing field */
3640 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3642 /* force DOOR font inside door area */
3643 game_status = GAME_MODE_PSEUDO_DOOR;
3645 /* write text for request */
3646 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3648 char text_line[max_request_line_len + 1];
3654 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3656 tc = *(text_ptr + tx);
3657 // if (!tc || tc == ' ')
3658 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3662 if ((tc == '?' || tc == '!') && tl == 0)
3672 strncpy(text_line, text_ptr, tl);
3675 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3676 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3677 text_line, font_nr);
3679 text_ptr += tl + (tc == ' ' ? 1 : 0);
3680 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3683 game_status = last_game_status; /* restore current game status */
3685 if (req_state & REQ_ASK)
3687 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3688 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3690 else if (req_state & REQ_CONFIRM)
3692 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3694 else if (req_state & REQ_PLAYER)
3696 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3697 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3698 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3699 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3702 /* copy request gadgets to door backbuffer */
3703 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3705 OpenDoor(DOOR_OPEN_1);
3707 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3709 if (game_status == GAME_MODE_PLAYING)
3711 SetPanelBackground();
3712 SetDrawBackgroundMask(REDRAW_DOOR_1);
3716 SetDrawBackgroundMask(REDRAW_FIELD);
3722 if (game_status != GAME_MODE_MAIN)
3725 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3727 // ---------- handle request buttons ----------
3728 result = RequestHandleEvents(req_state);
3730 if (game_status != GAME_MODE_MAIN)
3735 if (!(req_state & REQ_STAY_OPEN))
3737 CloseDoor(DOOR_CLOSE_1);
3739 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3740 (req_state & REQ_REOPEN))
3741 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3746 if (game_status == GAME_MODE_PLAYING)
3748 SetPanelBackground();
3749 SetDrawBackgroundMask(REDRAW_DOOR_1);
3753 SetDrawBackgroundMask(REDRAW_FIELD);
3756 #if defined(NETWORK_AVALIABLE)
3757 /* continue network game after request */
3758 if (options.network &&
3759 game_status == GAME_MODE_PLAYING &&
3760 req_state & REQUEST_WAIT_FOR_INPUT)
3761 SendToServer_ContinuePlaying();
3764 /* restore deactivated drawing when quick-loading level tape recording */
3765 if (tape.playing && tape.deactivate_display)
3766 TapeDeactivateDisplayOn();
3771 static boolean RequestEnvelope(char *text, unsigned int req_state)
3775 if (game_status == GAME_MODE_PLAYING)
3776 BlitScreenToBitmap(backbuffer);
3778 /* disable deactivated drawing when quick-loading level tape recording */
3779 if (tape.playing && tape.deactivate_display)
3780 TapeDeactivateDisplayOff(TRUE);
3782 SetMouseCursor(CURSOR_DEFAULT);
3784 #if defined(NETWORK_AVALIABLE)
3785 /* pause network game while waiting for request to answer */
3786 if (options.network &&
3787 game_status == GAME_MODE_PLAYING &&
3788 req_state & REQUEST_WAIT_FOR_INPUT)
3789 SendToServer_PausePlaying();
3792 /* simulate releasing mouse button over last gadget, if still pressed */
3794 HandleGadgets(-1, -1, 0);
3798 // (replace with setting corresponding request background)
3799 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3800 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3802 /* clear door drawing field */
3803 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3805 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3807 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3809 if (game_status == GAME_MODE_PLAYING)
3811 SetPanelBackground();
3812 SetDrawBackgroundMask(REDRAW_DOOR_1);
3816 SetDrawBackgroundMask(REDRAW_FIELD);
3822 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3824 // ---------- handle request buttons ----------
3825 result = RequestHandleEvents(req_state);
3827 if (game_status != GAME_MODE_MAIN)
3832 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3836 if (game_status == GAME_MODE_PLAYING)
3838 SetPanelBackground();
3839 SetDrawBackgroundMask(REDRAW_DOOR_1);
3843 SetDrawBackgroundMask(REDRAW_FIELD);
3846 #if defined(NETWORK_AVALIABLE)
3847 /* continue network game after request */
3848 if (options.network &&
3849 game_status == GAME_MODE_PLAYING &&
3850 req_state & REQUEST_WAIT_FOR_INPUT)
3851 SendToServer_ContinuePlaying();
3854 /* restore deactivated drawing when quick-loading level tape recording */
3855 if (tape.playing && tape.deactivate_display)
3856 TapeDeactivateDisplayOn();
3861 boolean Request(char *text, unsigned int req_state)
3863 if (global.use_envelope_request)
3864 return RequestEnvelope(text, req_state);
3866 return RequestDoor(text, req_state);
3869 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3871 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3872 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3875 if (dpo1->sort_priority != dpo2->sort_priority)
3876 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3878 compare_result = dpo1->nr - dpo2->nr;
3880 return compare_result;
3883 void InitGraphicCompatibilityInfo_Doors()
3889 struct DoorInfo *door;
3893 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3894 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3896 { -1, -1, -1, NULL }
3898 struct Rect door_rect_list[] =
3900 { DX, DY, DXSIZE, DYSIZE },
3901 { VX, VY, VXSIZE, VYSIZE }
3905 for (i = 0; doors[i].door_token != -1; i++)
3907 int door_token = doors[i].door_token;
3908 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3909 int part_1 = doors[i].part_1;
3910 int part_8 = doors[i].part_8;
3911 int part_2 = part_1 + 1;
3912 int part_3 = part_1 + 2;
3913 struct DoorInfo *door = doors[i].door;
3914 struct Rect *door_rect = &door_rect_list[door_index];
3915 boolean door_gfx_redefined = FALSE;
3917 /* check if any door part graphic definitions have been redefined */
3919 for (j = 0; door_part_controls[j].door_token != -1; j++)
3921 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3922 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3924 if (dpc->door_token == door_token && fi->redefined)
3925 door_gfx_redefined = TRUE;
3928 /* check for old-style door graphic/animation modifications */
3930 if (!door_gfx_redefined)
3932 if (door->anim_mode & ANIM_STATIC_PANEL)
3934 door->panel.step_xoffset = 0;
3935 door->panel.step_yoffset = 0;
3938 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3940 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3941 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3942 int num_door_steps, num_panel_steps;
3944 /* remove door part graphics other than the two default wings */
3946 for (j = 0; door_part_controls[j].door_token != -1; j++)
3948 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3949 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3951 if (dpc->graphic >= part_3 &&
3952 dpc->graphic <= part_8)
3956 /* set graphics and screen positions of the default wings */
3958 g_part_1->width = door_rect->width;
3959 g_part_1->height = door_rect->height;
3960 g_part_2->width = door_rect->width;
3961 g_part_2->height = door_rect->height;
3962 g_part_2->src_x = door_rect->width;
3963 g_part_2->src_y = g_part_1->src_y;
3965 door->part_2.x = door->part_1.x;
3966 door->part_2.y = door->part_1.y;
3968 if (door->width != -1)
3970 g_part_1->width = door->width;
3971 g_part_2->width = door->width;
3973 // special treatment for graphics and screen position of right wing
3974 g_part_2->src_x += door_rect->width - door->width;
3975 door->part_2.x += door_rect->width - door->width;
3978 if (door->height != -1)
3980 g_part_1->height = door->height;
3981 g_part_2->height = door->height;
3983 // special treatment for graphics and screen position of bottom wing
3984 g_part_2->src_y += door_rect->height - door->height;
3985 door->part_2.y += door_rect->height - door->height;
3988 /* set animation delays for the default wings and panels */
3990 door->part_1.step_delay = door->step_delay;
3991 door->part_2.step_delay = door->step_delay;
3992 door->panel.step_delay = door->step_delay;
3994 /* set animation draw order for the default wings */
3996 door->part_1.sort_priority = 2; /* draw left wing over ... */
3997 door->part_2.sort_priority = 1; /* ... right wing */
3999 /* set animation draw offset for the default wings */
4001 if (door->anim_mode & ANIM_HORIZONTAL)
4003 door->part_1.step_xoffset = door->step_offset;
4004 door->part_1.step_yoffset = 0;
4005 door->part_2.step_xoffset = door->step_offset * -1;
4006 door->part_2.step_yoffset = 0;
4008 num_door_steps = g_part_1->width / door->step_offset;
4010 else // ANIM_VERTICAL
4012 door->part_1.step_xoffset = 0;
4013 door->part_1.step_yoffset = door->step_offset;
4014 door->part_2.step_xoffset = 0;
4015 door->part_2.step_yoffset = door->step_offset * -1;
4017 num_door_steps = g_part_1->height / door->step_offset;
4020 /* set animation draw offset for the default panels */
4022 if (door->step_offset > 1)
4024 num_panel_steps = 2 * door_rect->height / door->step_offset;
4025 door->panel.start_step = num_panel_steps - num_door_steps;
4026 door->panel.start_step_closing = door->panel.start_step;
4030 num_panel_steps = door_rect->height / door->step_offset;
4031 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4032 door->panel.start_step_closing = door->panel.start_step;
4033 door->panel.step_delay *= 2;
4044 for (i = 0; door_part_controls[i].door_token != -1; i++)
4046 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4047 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4049 /* initialize "start_step_opening" and "start_step_closing", if needed */
4050 if (dpc->pos->start_step_opening == 0 &&
4051 dpc->pos->start_step_closing == 0)
4053 // dpc->pos->start_step_opening = dpc->pos->start_step;
4054 dpc->pos->start_step_closing = dpc->pos->start_step;
4057 /* fill structure for door part draw order (sorted below) */
4059 dpo->sort_priority = dpc->pos->sort_priority;
4062 /* sort door part controls according to sort_priority and graphic number */
4063 qsort(door_part_order, MAX_DOOR_PARTS,
4064 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4067 unsigned int OpenDoor(unsigned int door_state)
4069 if (door_state & DOOR_COPY_BACK)
4071 if (door_state & DOOR_OPEN_1)
4072 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4073 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4075 if (door_state & DOOR_OPEN_2)
4076 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4077 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4079 door_state &= ~DOOR_COPY_BACK;
4082 return MoveDoor(door_state);
4085 unsigned int CloseDoor(unsigned int door_state)
4087 unsigned int old_door_state = GetDoorState();
4089 if (!(door_state & DOOR_NO_COPY_BACK))
4091 if (old_door_state & DOOR_OPEN_1)
4092 BlitBitmap(backbuffer, bitmap_db_door_1,
4093 DX, DY, DXSIZE, DYSIZE, 0, 0);
4095 if (old_door_state & DOOR_OPEN_2)
4096 BlitBitmap(backbuffer, bitmap_db_door_2,
4097 VX, VY, VXSIZE, VYSIZE, 0, 0);
4099 door_state &= ~DOOR_NO_COPY_BACK;
4102 return MoveDoor(door_state);
4105 unsigned int GetDoorState()
4107 return MoveDoor(DOOR_GET_STATE);
4110 unsigned int SetDoorState(unsigned int door_state)
4112 return MoveDoor(door_state | DOOR_SET_STATE);
4115 int euclid(int a, int b)
4117 return (b ? euclid(b, a % b) : a);
4120 unsigned int MoveDoor(unsigned int door_state)
4122 struct Rect door_rect_list[] =
4124 { DX, DY, DXSIZE, DYSIZE },
4125 { VX, VY, VXSIZE, VYSIZE }
4127 static int door1 = DOOR_OPEN_1;
4128 static int door2 = DOOR_CLOSE_2;
4129 unsigned int door_delay = 0;
4130 unsigned int door_delay_value;
4133 if (door_state == DOOR_GET_STATE)
4134 return (door1 | door2);
4136 if (door_state & DOOR_SET_STATE)
4138 if (door_state & DOOR_ACTION_1)
4139 door1 = door_state & DOOR_ACTION_1;
4140 if (door_state & DOOR_ACTION_2)
4141 door2 = door_state & DOOR_ACTION_2;
4143 return (door1 | door2);
4146 if (!(door_state & DOOR_FORCE_REDRAW))
4148 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4149 door_state &= ~DOOR_OPEN_1;
4150 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4151 door_state &= ~DOOR_CLOSE_1;
4152 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4153 door_state &= ~DOOR_OPEN_2;
4154 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4155 door_state &= ~DOOR_CLOSE_2;
4158 if (global.autoplay_leveldir)
4160 door_state |= DOOR_NO_DELAY;
4161 door_state &= ~DOOR_CLOSE_ALL;
4164 if (game_status == GAME_MODE_EDITOR)
4165 door_state |= DOOR_NO_DELAY;
4167 if (door_state & DOOR_ACTION)
4169 boolean door_panel_drawn[NUM_DOORS];
4170 boolean panel_has_doors[NUM_DOORS];
4171 boolean door_part_skip[MAX_DOOR_PARTS];
4172 boolean door_part_done[MAX_DOOR_PARTS];
4173 boolean door_part_done_all;
4174 int num_steps[MAX_DOOR_PARTS];
4175 int max_move_delay = 0; // delay for complete animations of all doors
4176 int max_step_delay = 0; // delay (ms) between two animation frames
4177 int num_move_steps = 0; // number of animation steps for all doors
4178 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4179 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4180 int current_move_delay = 0;
4184 for (i = 0; i < NUM_DOORS; i++)
4185 panel_has_doors[i] = FALSE;
4187 for (i = 0; i < MAX_DOOR_PARTS; i++)
4189 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4190 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4191 int door_token = dpc->door_token;
4193 door_part_done[i] = FALSE;
4194 door_part_skip[i] = (!(door_state & door_token) ||
4198 for (i = 0; i < MAX_DOOR_PARTS; i++)
4200 int nr = door_part_order[i].nr;
4201 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4202 struct DoorPartPosInfo *pos = dpc->pos;
4203 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4204 int door_token = dpc->door_token;
4205 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4206 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4207 int step_xoffset = ABS(pos->step_xoffset);
4208 int step_yoffset = ABS(pos->step_yoffset);
4209 int step_delay = pos->step_delay;
4210 int current_door_state = door_state & door_token;
4211 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4212 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4213 boolean part_opening = (is_panel ? door_closing : door_opening);
4214 int start_step = (part_opening ? pos->start_step_opening :
4215 pos->start_step_closing);
4216 float move_xsize = (step_xoffset ? g->width : 0);
4217 float move_ysize = (step_yoffset ? g->height : 0);
4218 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4219 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4220 int move_steps = (move_xsteps && move_ysteps ?
4221 MIN(move_xsteps, move_ysteps) :
4222 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4223 int move_delay = move_steps * step_delay;
4225 if (door_part_skip[nr])
4228 max_move_delay = MAX(max_move_delay, move_delay);
4229 max_step_delay = (max_step_delay == 0 ? step_delay :
4230 euclid(max_step_delay, step_delay));
4231 num_steps[nr] = move_steps;
4235 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4237 panel_has_doors[door_index] = TRUE;
4241 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4243 num_move_steps = max_move_delay / max_step_delay;
4244 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4246 door_delay_value = max_step_delay;
4248 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4250 start = num_move_steps - 1;
4254 /* opening door sound has priority over simultaneously closing door */
4255 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4256 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4257 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4258 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4261 for (k = start; k < num_move_steps; k++)
4263 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4265 door_part_done_all = TRUE;
4267 for (i = 0; i < NUM_DOORS; i++)
4268 door_panel_drawn[i] = FALSE;
4270 for (i = 0; i < MAX_DOOR_PARTS; i++)
4272 int nr = door_part_order[i].nr;
4273 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4274 struct DoorPartPosInfo *pos = dpc->pos;
4275 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4276 int door_token = dpc->door_token;
4277 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4278 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4279 boolean is_panel_and_door_has_closed = FALSE;
4280 struct Rect *door_rect = &door_rect_list[door_index];
4281 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4283 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4284 int current_door_state = door_state & door_token;
4285 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4286 boolean door_closing = !door_opening;
4287 boolean part_opening = (is_panel ? door_closing : door_opening);
4288 boolean part_closing = !part_opening;
4289 int start_step = (part_opening ? pos->start_step_opening :
4290 pos->start_step_closing);
4291 int step_delay = pos->step_delay;
4292 int step_factor = step_delay / max_step_delay;
4293 int k1 = (step_factor ? k / step_factor + 1 : k);
4294 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4295 int kk = MAX(0, k2);
4298 int src_x, src_y, src_xx, src_yy;
4299 int dst_x, dst_y, dst_xx, dst_yy;
4302 if (door_part_skip[nr])
4305 if (!(door_state & door_token))
4313 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4314 int kk_door = MAX(0, k2_door);
4315 int sync_frame = kk_door * door_delay_value;
4316 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4318 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4323 if (!door_panel_drawn[door_index])
4325 ClearRectangle(drawto, door_rect->x, door_rect->y,
4326 door_rect->width, door_rect->height);
4328 door_panel_drawn[door_index] = TRUE;
4331 // draw opening or closing door parts
4333 if (pos->step_xoffset < 0) // door part on right side
4336 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4339 if (dst_xx + width > door_rect->width)
4340 width = door_rect->width - dst_xx;
4342 else // door part on left side
4345 dst_xx = pos->x - kk * pos->step_xoffset;
4349 src_xx = ABS(dst_xx);
4353 width = g->width - src_xx;
4355 if (width > door_rect->width)
4356 width = door_rect->width;
4358 // printf("::: k == %d [%d] \n", k, start_step);
4361 if (pos->step_yoffset < 0) // door part on bottom side
4364 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4367 if (dst_yy + height > door_rect->height)
4368 height = door_rect->height - dst_yy;
4370 else // door part on top side
4373 dst_yy = pos->y - kk * pos->step_yoffset;
4377 src_yy = ABS(dst_yy);
4381 height = g->height - src_yy;
4384 src_x = g_src_x + src_xx;
4385 src_y = g_src_y + src_yy;
4387 dst_x = door_rect->x + dst_xx;
4388 dst_y = door_rect->y + dst_yy;
4390 is_panel_and_door_has_closed =
4393 panel_has_doors[door_index] &&
4394 k >= num_move_steps_doors_only - 1);
4396 if (width >= 0 && width <= g->width &&
4397 height >= 0 && height <= g->height &&
4398 !is_panel_and_door_has_closed)
4400 if (is_panel || !pos->draw_masked)
4401 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4404 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4408 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4410 if ((part_opening && (width < 0 || height < 0)) ||
4411 (part_closing && (width >= g->width && height >= g->height)))
4412 door_part_done[nr] = TRUE;
4414 // continue door part animations, but not panel after door has closed
4415 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4416 door_part_done_all = FALSE;
4419 if (!(door_state & DOOR_NO_DELAY))
4423 if (game_status == GAME_MODE_MAIN)
4426 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4428 current_move_delay += max_step_delay;
4431 if (door_part_done_all)
4436 if (door_state & DOOR_ACTION_1)
4437 door1 = door_state & DOOR_ACTION_1;
4438 if (door_state & DOOR_ACTION_2)
4439 door2 = door_state & DOOR_ACTION_2;
4441 return (door1 | door2);
4444 static boolean useSpecialEditorDoor()
4446 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4447 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4449 // do not draw special editor door if editor border defined or redefined
4450 if (graphic_info[graphic].bitmap != NULL || redefined)
4453 // do not draw special editor door if global border defined to be empty
4454 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4457 // do not draw special editor door if viewport definitions do not match
4461 EY + EYSIZE != VY + VYSIZE)
4467 void DrawSpecialEditorDoor()
4469 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4470 int top_border_width = gfx1->width;
4471 int top_border_height = gfx1->height;
4472 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4473 int ex = EX - outer_border;
4474 int ey = EY - outer_border;
4475 int vy = VY - outer_border;
4476 int exsize = EXSIZE + 2 * outer_border;
4478 if (!useSpecialEditorDoor())
4481 /* draw bigger level editor toolbox window */
4482 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4483 top_border_width, top_border_height, ex, ey - top_border_height);
4484 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4485 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4487 redraw_mask |= REDRAW_ALL;
4490 void UndrawSpecialEditorDoor()
4492 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4493 int top_border_width = gfx1->width;
4494 int top_border_height = gfx1->height;
4495 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4496 int ex = EX - outer_border;
4497 int ey = EY - outer_border;
4498 int ey_top = ey - top_border_height;
4499 int exsize = EXSIZE + 2 * outer_border;
4500 int eysize = EYSIZE + 2 * outer_border;
4502 if (!useSpecialEditorDoor())
4505 /* draw normal tape recorder window */
4506 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4508 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4509 ex, ey_top, top_border_width, top_border_height,
4511 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4512 ex, ey, exsize, eysize, ex, ey);
4516 // if screen background is set to "[NONE]", clear editor toolbox window
4517 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4518 ClearRectangle(drawto, ex, ey, exsize, eysize);
4521 redraw_mask |= REDRAW_ALL;
4525 /* ---------- new tool button stuff ---------------------------------------- */
4530 struct TextPosInfo *pos;
4533 } toolbutton_info[NUM_TOOL_BUTTONS] =
4536 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4537 TOOL_CTRL_ID_YES, "yes"
4540 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4541 TOOL_CTRL_ID_NO, "no"
4544 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4545 TOOL_CTRL_ID_CONFIRM, "confirm"
4548 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4549 TOOL_CTRL_ID_PLAYER_1, "player 1"
4552 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4553 TOOL_CTRL_ID_PLAYER_2, "player 2"
4556 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4557 TOOL_CTRL_ID_PLAYER_3, "player 3"
4560 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4561 TOOL_CTRL_ID_PLAYER_4, "player 4"
4565 void CreateToolButtons()
4569 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4571 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4572 struct TextPosInfo *pos = toolbutton_info[i].pos;
4573 struct GadgetInfo *gi;
4574 Bitmap *deco_bitmap = None;
4575 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4576 unsigned int event_mask = GD_EVENT_RELEASED;
4579 int gd_x = gfx->src_x;
4580 int gd_y = gfx->src_y;
4581 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4582 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4585 if (global.use_envelope_request)
4586 setRequestPosition(&dx, &dy, TRUE);
4588 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4590 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4592 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4593 pos->size, &deco_bitmap, &deco_x, &deco_y);
4594 deco_xpos = (gfx->width - pos->size) / 2;
4595 deco_ypos = (gfx->height - pos->size) / 2;
4598 gi = CreateGadget(GDI_CUSTOM_ID, id,
4599 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4600 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4601 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4602 GDI_WIDTH, gfx->width,
4603 GDI_HEIGHT, gfx->height,
4604 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4605 GDI_STATE, GD_BUTTON_UNPRESSED,
4606 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4607 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4608 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4609 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4610 GDI_DECORATION_SIZE, pos->size, pos->size,
4611 GDI_DECORATION_SHIFTING, 1, 1,
4612 GDI_DIRECT_DRAW, FALSE,
4613 GDI_EVENT_MASK, event_mask,
4614 GDI_CALLBACK_ACTION, HandleToolButtons,
4618 Error(ERR_EXIT, "cannot create gadget");
4620 tool_gadget[id] = gi;
4624 void FreeToolButtons()
4628 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4629 FreeGadget(tool_gadget[i]);
4632 static void UnmapToolButtons()
4636 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4637 UnmapGadget(tool_gadget[i]);
4640 static void HandleToolButtons(struct GadgetInfo *gi)
4642 request_gadget_id = gi->custom_id;
4645 static struct Mapping_EM_to_RND_object
4648 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4649 boolean is_backside; /* backside of moving element */
4655 em_object_mapping_list[] =
4658 Xblank, TRUE, FALSE,
4662 Yacid_splash_eB, FALSE, FALSE,
4663 EL_ACID_SPLASH_RIGHT, -1, -1
4666 Yacid_splash_wB, FALSE, FALSE,
4667 EL_ACID_SPLASH_LEFT, -1, -1
4670 #ifdef EM_ENGINE_BAD_ROLL
4672 Xstone_force_e, FALSE, FALSE,
4673 EL_ROCK, -1, MV_BIT_RIGHT
4676 Xstone_force_w, FALSE, FALSE,
4677 EL_ROCK, -1, MV_BIT_LEFT
4680 Xnut_force_e, FALSE, FALSE,
4681 EL_NUT, -1, MV_BIT_RIGHT
4684 Xnut_force_w, FALSE, FALSE,
4685 EL_NUT, -1, MV_BIT_LEFT
4688 Xspring_force_e, FALSE, FALSE,
4689 EL_SPRING, -1, MV_BIT_RIGHT
4692 Xspring_force_w, FALSE, FALSE,
4693 EL_SPRING, -1, MV_BIT_LEFT
4696 Xemerald_force_e, FALSE, FALSE,
4697 EL_EMERALD, -1, MV_BIT_RIGHT
4700 Xemerald_force_w, FALSE, FALSE,
4701 EL_EMERALD, -1, MV_BIT_LEFT
4704 Xdiamond_force_e, FALSE, FALSE,
4705 EL_DIAMOND, -1, MV_BIT_RIGHT
4708 Xdiamond_force_w, FALSE, FALSE,
4709 EL_DIAMOND, -1, MV_BIT_LEFT
4712 Xbomb_force_e, FALSE, FALSE,
4713 EL_BOMB, -1, MV_BIT_RIGHT
4716 Xbomb_force_w, FALSE, FALSE,
4717 EL_BOMB, -1, MV_BIT_LEFT
4719 #endif /* EM_ENGINE_BAD_ROLL */
4722 Xstone, TRUE, FALSE,
4726 Xstone_pause, FALSE, FALSE,
4730 Xstone_fall, FALSE, FALSE,
4734 Ystone_s, FALSE, FALSE,
4735 EL_ROCK, ACTION_FALLING, -1
4738 Ystone_sB, FALSE, TRUE,
4739 EL_ROCK, ACTION_FALLING, -1
4742 Ystone_e, FALSE, FALSE,
4743 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4746 Ystone_eB, FALSE, TRUE,
4747 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4750 Ystone_w, FALSE, FALSE,
4751 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4754 Ystone_wB, FALSE, TRUE,
4755 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4762 Xnut_pause, FALSE, FALSE,
4766 Xnut_fall, FALSE, FALSE,
4770 Ynut_s, FALSE, FALSE,
4771 EL_NUT, ACTION_FALLING, -1
4774 Ynut_sB, FALSE, TRUE,
4775 EL_NUT, ACTION_FALLING, -1
4778 Ynut_e, FALSE, FALSE,
4779 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4782 Ynut_eB, FALSE, TRUE,
4783 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4786 Ynut_w, FALSE, FALSE,
4787 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4790 Ynut_wB, FALSE, TRUE,
4791 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4794 Xbug_n, TRUE, FALSE,
4798 Xbug_e, TRUE, FALSE,
4799 EL_BUG_RIGHT, -1, -1
4802 Xbug_s, TRUE, FALSE,
4806 Xbug_w, TRUE, FALSE,
4810 Xbug_gon, FALSE, FALSE,
4814 Xbug_goe, FALSE, FALSE,
4815 EL_BUG_RIGHT, -1, -1
4818 Xbug_gos, FALSE, FALSE,
4822 Xbug_gow, FALSE, FALSE,
4826 Ybug_n, FALSE, FALSE,
4827 EL_BUG, ACTION_MOVING, MV_BIT_UP
4830 Ybug_nB, FALSE, TRUE,
4831 EL_BUG, ACTION_MOVING, MV_BIT_UP
4834 Ybug_e, FALSE, FALSE,
4835 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4838 Ybug_eB, FALSE, TRUE,
4839 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4842 Ybug_s, FALSE, FALSE,
4843 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4846 Ybug_sB, FALSE, TRUE,
4847 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4850 Ybug_w, FALSE, FALSE,
4851 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4854 Ybug_wB, FALSE, TRUE,
4855 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4858 Ybug_w_n, FALSE, FALSE,
4859 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4862 Ybug_n_e, FALSE, FALSE,
4863 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4866 Ybug_e_s, FALSE, FALSE,
4867 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4870 Ybug_s_w, FALSE, FALSE,
4871 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4874 Ybug_e_n, FALSE, FALSE,
4875 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4878 Ybug_s_e, FALSE, FALSE,
4879 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4882 Ybug_w_s, FALSE, FALSE,
4883 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4886 Ybug_n_w, FALSE, FALSE,
4887 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4890 Ybug_stone, FALSE, FALSE,
4891 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4894 Ybug_spring, FALSE, FALSE,
4895 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4898 Xtank_n, TRUE, FALSE,
4899 EL_SPACESHIP_UP, -1, -1
4902 Xtank_e, TRUE, FALSE,
4903 EL_SPACESHIP_RIGHT, -1, -1
4906 Xtank_s, TRUE, FALSE,
4907 EL_SPACESHIP_DOWN, -1, -1
4910 Xtank_w, TRUE, FALSE,
4911 EL_SPACESHIP_LEFT, -1, -1
4914 Xtank_gon, FALSE, FALSE,
4915 EL_SPACESHIP_UP, -1, -1
4918 Xtank_goe, FALSE, FALSE,
4919 EL_SPACESHIP_RIGHT, -1, -1
4922 Xtank_gos, FALSE, FALSE,
4923 EL_SPACESHIP_DOWN, -1, -1
4926 Xtank_gow, FALSE, FALSE,
4927 EL_SPACESHIP_LEFT, -1, -1
4930 Ytank_n, FALSE, FALSE,
4931 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4934 Ytank_nB, FALSE, TRUE,
4935 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4938 Ytank_e, FALSE, FALSE,
4939 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4942 Ytank_eB, FALSE, TRUE,
4943 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4946 Ytank_s, FALSE, FALSE,
4947 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4950 Ytank_sB, FALSE, TRUE,
4951 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4954 Ytank_w, FALSE, FALSE,
4955 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4958 Ytank_wB, FALSE, TRUE,
4959 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4962 Ytank_w_n, FALSE, FALSE,
4963 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4966 Ytank_n_e, FALSE, FALSE,
4967 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4970 Ytank_e_s, FALSE, FALSE,
4971 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4974 Ytank_s_w, FALSE, FALSE,
4975 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4978 Ytank_e_n, FALSE, FALSE,
4979 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4982 Ytank_s_e, FALSE, FALSE,
4983 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4986 Ytank_w_s, FALSE, FALSE,
4987 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4990 Ytank_n_w, FALSE, FALSE,
4991 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4994 Ytank_stone, FALSE, FALSE,
4995 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4998 Ytank_spring, FALSE, FALSE,
4999 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5002 Xandroid, TRUE, FALSE,
5003 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5006 Xandroid_1_n, FALSE, FALSE,
5007 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5010 Xandroid_2_n, FALSE, FALSE,
5011 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5014 Xandroid_1_e, FALSE, FALSE,
5015 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5018 Xandroid_2_e, FALSE, FALSE,
5019 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5022 Xandroid_1_w, FALSE, FALSE,
5023 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5026 Xandroid_2_w, FALSE, FALSE,
5027 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5030 Xandroid_1_s, FALSE, FALSE,
5031 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5034 Xandroid_2_s, FALSE, FALSE,
5035 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5038 Yandroid_n, FALSE, FALSE,
5039 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5042 Yandroid_nB, FALSE, TRUE,
5043 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5046 Yandroid_ne, FALSE, FALSE,
5047 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5050 Yandroid_neB, FALSE, TRUE,
5051 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5054 Yandroid_e, FALSE, FALSE,
5055 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5058 Yandroid_eB, FALSE, TRUE,
5059 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5062 Yandroid_se, FALSE, FALSE,
5063 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5066 Yandroid_seB, FALSE, TRUE,
5067 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5070 Yandroid_s, FALSE, FALSE,
5071 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5074 Yandroid_sB, FALSE, TRUE,
5075 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5078 Yandroid_sw, FALSE, FALSE,
5079 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5082 Yandroid_swB, FALSE, TRUE,
5083 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5086 Yandroid_w, FALSE, FALSE,
5087 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5090 Yandroid_wB, FALSE, TRUE,
5091 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5094 Yandroid_nw, FALSE, FALSE,
5095 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5098 Yandroid_nwB, FALSE, TRUE,
5099 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5102 Xspring, TRUE, FALSE,
5106 Xspring_pause, FALSE, FALSE,
5110 Xspring_e, FALSE, FALSE,
5114 Xspring_w, FALSE, FALSE,
5118 Xspring_fall, FALSE, FALSE,
5122 Yspring_s, FALSE, FALSE,
5123 EL_SPRING, ACTION_FALLING, -1
5126 Yspring_sB, FALSE, TRUE,
5127 EL_SPRING, ACTION_FALLING, -1
5130 Yspring_e, FALSE, FALSE,
5131 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5134 Yspring_eB, FALSE, TRUE,
5135 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5138 Yspring_w, FALSE, FALSE,
5139 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5142 Yspring_wB, FALSE, TRUE,
5143 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5146 Yspring_kill_e, FALSE, FALSE,
5147 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5150 Yspring_kill_eB, FALSE, TRUE,
5151 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5154 Yspring_kill_w, FALSE, FALSE,
5155 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5158 Yspring_kill_wB, FALSE, TRUE,
5159 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5162 Xeater_n, TRUE, FALSE,
5163 EL_YAMYAM_UP, -1, -1
5166 Xeater_e, TRUE, FALSE,
5167 EL_YAMYAM_RIGHT, -1, -1
5170 Xeater_w, TRUE, FALSE,
5171 EL_YAMYAM_LEFT, -1, -1
5174 Xeater_s, TRUE, FALSE,
5175 EL_YAMYAM_DOWN, -1, -1
5178 Yeater_n, FALSE, FALSE,
5179 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5182 Yeater_nB, FALSE, TRUE,
5183 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5186 Yeater_e, FALSE, FALSE,
5187 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5190 Yeater_eB, FALSE, TRUE,
5191 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5194 Yeater_s, FALSE, FALSE,
5195 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5198 Yeater_sB, FALSE, TRUE,
5199 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5202 Yeater_w, FALSE, FALSE,
5203 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5206 Yeater_wB, FALSE, TRUE,
5207 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5210 Yeater_stone, FALSE, FALSE,
5211 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5214 Yeater_spring, FALSE, FALSE,
5215 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5218 Xalien, TRUE, FALSE,
5222 Xalien_pause, FALSE, FALSE,
5226 Yalien_n, FALSE, FALSE,
5227 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5230 Yalien_nB, FALSE, TRUE,
5231 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5234 Yalien_e, FALSE, FALSE,
5235 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5238 Yalien_eB, FALSE, TRUE,
5239 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5242 Yalien_s, FALSE, FALSE,
5243 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5246 Yalien_sB, FALSE, TRUE,
5247 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5250 Yalien_w, FALSE, FALSE,
5251 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5254 Yalien_wB, FALSE, TRUE,
5255 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5258 Yalien_stone, FALSE, FALSE,
5259 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5262 Yalien_spring, FALSE, FALSE,
5263 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5266 Xemerald, TRUE, FALSE,
5270 Xemerald_pause, FALSE, FALSE,
5274 Xemerald_fall, FALSE, FALSE,
5278 Xemerald_shine, FALSE, FALSE,
5279 EL_EMERALD, ACTION_TWINKLING, -1
5282 Yemerald_s, FALSE, FALSE,
5283 EL_EMERALD, ACTION_FALLING, -1
5286 Yemerald_sB, FALSE, TRUE,
5287 EL_EMERALD, ACTION_FALLING, -1
5290 Yemerald_e, FALSE, FALSE,
5291 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5294 Yemerald_eB, FALSE, TRUE,
5295 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5298 Yemerald_w, FALSE, FALSE,
5299 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5302 Yemerald_wB, FALSE, TRUE,
5303 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5306 Yemerald_eat, FALSE, FALSE,
5307 EL_EMERALD, ACTION_COLLECTING, -1
5310 Yemerald_stone, FALSE, FALSE,
5311 EL_NUT, ACTION_BREAKING, -1
5314 Xdiamond, TRUE, FALSE,
5318 Xdiamond_pause, FALSE, FALSE,
5322 Xdiamond_fall, FALSE, FALSE,
5326 Xdiamond_shine, FALSE, FALSE,
5327 EL_DIAMOND, ACTION_TWINKLING, -1
5330 Ydiamond_s, FALSE, FALSE,
5331 EL_DIAMOND, ACTION_FALLING, -1
5334 Ydiamond_sB, FALSE, TRUE,
5335 EL_DIAMOND, ACTION_FALLING, -1
5338 Ydiamond_e, FALSE, FALSE,
5339 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5342 Ydiamond_eB, FALSE, TRUE,
5343 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5346 Ydiamond_w, FALSE, FALSE,
5347 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5350 Ydiamond_wB, FALSE, TRUE,
5351 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5354 Ydiamond_eat, FALSE, FALSE,
5355 EL_DIAMOND, ACTION_COLLECTING, -1
5358 Ydiamond_stone, FALSE, FALSE,
5359 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5362 Xdrip_fall, TRUE, FALSE,
5363 EL_AMOEBA_DROP, -1, -1
5366 Xdrip_stretch, FALSE, FALSE,
5367 EL_AMOEBA_DROP, ACTION_FALLING, -1
5370 Xdrip_stretchB, FALSE, TRUE,
5371 EL_AMOEBA_DROP, ACTION_FALLING, -1
5374 Xdrip_eat, FALSE, FALSE,
5375 EL_AMOEBA_DROP, ACTION_GROWING, -1
5378 Ydrip_s1, FALSE, FALSE,
5379 EL_AMOEBA_DROP, ACTION_FALLING, -1
5382 Ydrip_s1B, FALSE, TRUE,
5383 EL_AMOEBA_DROP, ACTION_FALLING, -1
5386 Ydrip_s2, FALSE, FALSE,
5387 EL_AMOEBA_DROP, ACTION_FALLING, -1
5390 Ydrip_s2B, FALSE, TRUE,
5391 EL_AMOEBA_DROP, ACTION_FALLING, -1
5398 Xbomb_pause, FALSE, FALSE,
5402 Xbomb_fall, FALSE, FALSE,
5406 Ybomb_s, FALSE, FALSE,
5407 EL_BOMB, ACTION_FALLING, -1
5410 Ybomb_sB, FALSE, TRUE,
5411 EL_BOMB, ACTION_FALLING, -1
5414 Ybomb_e, FALSE, FALSE,
5415 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5418 Ybomb_eB, FALSE, TRUE,
5419 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5422 Ybomb_w, FALSE, FALSE,
5423 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5426 Ybomb_wB, FALSE, TRUE,
5427 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5430 Ybomb_eat, FALSE, FALSE,
5431 EL_BOMB, ACTION_ACTIVATING, -1
5434 Xballoon, TRUE, FALSE,
5438 Yballoon_n, FALSE, FALSE,
5439 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5442 Yballoon_nB, FALSE, TRUE,
5443 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5446 Yballoon_e, FALSE, FALSE,
5447 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5450 Yballoon_eB, FALSE, TRUE,
5451 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5454 Yballoon_s, FALSE, FALSE,
5455 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5458 Yballoon_sB, FALSE, TRUE,
5459 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5462 Yballoon_w, FALSE, FALSE,
5463 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5466 Yballoon_wB, FALSE, TRUE,
5467 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5470 Xgrass, TRUE, FALSE,
5471 EL_EMC_GRASS, -1, -1
5474 Ygrass_nB, FALSE, FALSE,
5475 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5478 Ygrass_eB, FALSE, FALSE,
5479 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5482 Ygrass_sB, FALSE, FALSE,
5483 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5486 Ygrass_wB, FALSE, FALSE,
5487 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5494 Ydirt_nB, FALSE, FALSE,
5495 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5498 Ydirt_eB, FALSE, FALSE,
5499 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5502 Ydirt_sB, FALSE, FALSE,
5503 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5506 Ydirt_wB, FALSE, FALSE,
5507 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5510 Xacid_ne, TRUE, FALSE,
5511 EL_ACID_POOL_TOPRIGHT, -1, -1
5514 Xacid_se, TRUE, FALSE,
5515 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5518 Xacid_s, TRUE, FALSE,
5519 EL_ACID_POOL_BOTTOM, -1, -1
5522 Xacid_sw, TRUE, FALSE,
5523 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5526 Xacid_nw, TRUE, FALSE,
5527 EL_ACID_POOL_TOPLEFT, -1, -1
5530 Xacid_1, TRUE, FALSE,
5534 Xacid_2, FALSE, FALSE,
5538 Xacid_3, FALSE, FALSE,
5542 Xacid_4, FALSE, FALSE,
5546 Xacid_5, FALSE, FALSE,
5550 Xacid_6, FALSE, FALSE,
5554 Xacid_7, FALSE, FALSE,
5558 Xacid_8, FALSE, FALSE,
5562 Xball_1, TRUE, FALSE,
5563 EL_EMC_MAGIC_BALL, -1, -1
5566 Xball_1B, FALSE, FALSE,
5567 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5570 Xball_2, FALSE, FALSE,
5571 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5574 Xball_2B, FALSE, FALSE,
5575 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5578 Yball_eat, FALSE, FALSE,
5579 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5582 Ykey_1_eat, FALSE, FALSE,
5583 EL_EM_KEY_1, ACTION_COLLECTING, -1
5586 Ykey_2_eat, FALSE, FALSE,
5587 EL_EM_KEY_2, ACTION_COLLECTING, -1
5590 Ykey_3_eat, FALSE, FALSE,
5591 EL_EM_KEY_3, ACTION_COLLECTING, -1
5594 Ykey_4_eat, FALSE, FALSE,
5595 EL_EM_KEY_4, ACTION_COLLECTING, -1
5598 Ykey_5_eat, FALSE, FALSE,
5599 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5602 Ykey_6_eat, FALSE, FALSE,
5603 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5606 Ykey_7_eat, FALSE, FALSE,
5607 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5610 Ykey_8_eat, FALSE, FALSE,
5611 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5614 Ylenses_eat, FALSE, FALSE,
5615 EL_EMC_LENSES, ACTION_COLLECTING, -1
5618 Ymagnify_eat, FALSE, FALSE,
5619 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5622 Ygrass_eat, FALSE, FALSE,
5623 EL_EMC_GRASS, ACTION_SNAPPING, -1
5626 Ydirt_eat, FALSE, FALSE,
5627 EL_SAND, ACTION_SNAPPING, -1
5630 Xgrow_ns, TRUE, FALSE,
5631 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5634 Ygrow_ns_eat, FALSE, FALSE,
5635 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5638 Xgrow_ew, TRUE, FALSE,
5639 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5642 Ygrow_ew_eat, FALSE, FALSE,
5643 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5646 Xwonderwall, TRUE, FALSE,
5647 EL_MAGIC_WALL, -1, -1
5650 XwonderwallB, FALSE, FALSE,
5651 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5654 Xamoeba_1, TRUE, FALSE,
5655 EL_AMOEBA_DRY, ACTION_OTHER, -1
5658 Xamoeba_2, FALSE, FALSE,
5659 EL_AMOEBA_DRY, ACTION_OTHER, -1
5662 Xamoeba_3, FALSE, FALSE,
5663 EL_AMOEBA_DRY, ACTION_OTHER, -1
5666 Xamoeba_4, FALSE, FALSE,
5667 EL_AMOEBA_DRY, ACTION_OTHER, -1
5670 Xamoeba_5, TRUE, FALSE,
5671 EL_AMOEBA_WET, ACTION_OTHER, -1
5674 Xamoeba_6, FALSE, FALSE,
5675 EL_AMOEBA_WET, ACTION_OTHER, -1
5678 Xamoeba_7, FALSE, FALSE,
5679 EL_AMOEBA_WET, ACTION_OTHER, -1
5682 Xamoeba_8, FALSE, FALSE,
5683 EL_AMOEBA_WET, ACTION_OTHER, -1
5686 Xdoor_1, TRUE, FALSE,
5687 EL_EM_GATE_1, -1, -1
5690 Xdoor_2, TRUE, FALSE,
5691 EL_EM_GATE_2, -1, -1
5694 Xdoor_3, TRUE, FALSE,
5695 EL_EM_GATE_3, -1, -1
5698 Xdoor_4, TRUE, FALSE,
5699 EL_EM_GATE_4, -1, -1
5702 Xdoor_5, TRUE, FALSE,
5703 EL_EMC_GATE_5, -1, -1
5706 Xdoor_6, TRUE, FALSE,
5707 EL_EMC_GATE_6, -1, -1
5710 Xdoor_7, TRUE, FALSE,
5711 EL_EMC_GATE_7, -1, -1
5714 Xdoor_8, TRUE, FALSE,
5715 EL_EMC_GATE_8, -1, -1
5718 Xkey_1, TRUE, FALSE,
5722 Xkey_2, TRUE, FALSE,
5726 Xkey_3, TRUE, FALSE,
5730 Xkey_4, TRUE, FALSE,
5734 Xkey_5, TRUE, FALSE,
5735 EL_EMC_KEY_5, -1, -1
5738 Xkey_6, TRUE, FALSE,
5739 EL_EMC_KEY_6, -1, -1
5742 Xkey_7, TRUE, FALSE,
5743 EL_EMC_KEY_7, -1, -1
5746 Xkey_8, TRUE, FALSE,
5747 EL_EMC_KEY_8, -1, -1
5750 Xwind_n, TRUE, FALSE,
5751 EL_BALLOON_SWITCH_UP, -1, -1
5754 Xwind_e, TRUE, FALSE,
5755 EL_BALLOON_SWITCH_RIGHT, -1, -1
5758 Xwind_s, TRUE, FALSE,
5759 EL_BALLOON_SWITCH_DOWN, -1, -1
5762 Xwind_w, TRUE, FALSE,
5763 EL_BALLOON_SWITCH_LEFT, -1, -1
5766 Xwind_nesw, TRUE, FALSE,
5767 EL_BALLOON_SWITCH_ANY, -1, -1
5770 Xwind_stop, TRUE, FALSE,
5771 EL_BALLOON_SWITCH_NONE, -1, -1
5775 EL_EM_EXIT_CLOSED, -1, -1
5778 Xexit_1, TRUE, FALSE,
5779 EL_EM_EXIT_OPEN, -1, -1
5782 Xexit_2, FALSE, FALSE,
5783 EL_EM_EXIT_OPEN, -1, -1
5786 Xexit_3, FALSE, FALSE,
5787 EL_EM_EXIT_OPEN, -1, -1
5790 Xdynamite, TRUE, FALSE,
5791 EL_EM_DYNAMITE, -1, -1
5794 Ydynamite_eat, FALSE, FALSE,
5795 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5798 Xdynamite_1, TRUE, FALSE,
5799 EL_EM_DYNAMITE_ACTIVE, -1, -1
5802 Xdynamite_2, FALSE, FALSE,
5803 EL_EM_DYNAMITE_ACTIVE, -1, -1
5806 Xdynamite_3, FALSE, FALSE,
5807 EL_EM_DYNAMITE_ACTIVE, -1, -1
5810 Xdynamite_4, FALSE, FALSE,
5811 EL_EM_DYNAMITE_ACTIVE, -1, -1
5814 Xbumper, TRUE, FALSE,
5815 EL_EMC_SPRING_BUMPER, -1, -1
5818 XbumperB, FALSE, FALSE,
5819 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5822 Xwheel, TRUE, FALSE,
5823 EL_ROBOT_WHEEL, -1, -1
5826 XwheelB, FALSE, FALSE,
5827 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5830 Xswitch, TRUE, FALSE,
5831 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5834 XswitchB, FALSE, FALSE,
5835 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5839 EL_QUICKSAND_EMPTY, -1, -1
5842 Xsand_stone, TRUE, FALSE,
5843 EL_QUICKSAND_FULL, -1, -1
5846 Xsand_stonein_1, FALSE, TRUE,
5847 EL_ROCK, ACTION_FILLING, -1
5850 Xsand_stonein_2, FALSE, TRUE,
5851 EL_ROCK, ACTION_FILLING, -1
5854 Xsand_stonein_3, FALSE, TRUE,
5855 EL_ROCK, ACTION_FILLING, -1
5858 Xsand_stonein_4, FALSE, TRUE,
5859 EL_ROCK, ACTION_FILLING, -1
5862 Xsand_stonesand_1, FALSE, FALSE,
5863 EL_QUICKSAND_EMPTYING, -1, -1
5866 Xsand_stonesand_2, FALSE, FALSE,
5867 EL_QUICKSAND_EMPTYING, -1, -1
5870 Xsand_stonesand_3, FALSE, FALSE,
5871 EL_QUICKSAND_EMPTYING, -1, -1
5874 Xsand_stonesand_4, FALSE, FALSE,
5875 EL_QUICKSAND_EMPTYING, -1, -1
5878 Xsand_stonesand_quickout_1, FALSE, FALSE,
5879 EL_QUICKSAND_EMPTYING, -1, -1
5882 Xsand_stonesand_quickout_2, FALSE, FALSE,
5883 EL_QUICKSAND_EMPTYING, -1, -1
5886 Xsand_stoneout_1, FALSE, FALSE,
5887 EL_ROCK, ACTION_EMPTYING, -1
5890 Xsand_stoneout_2, FALSE, FALSE,
5891 EL_ROCK, ACTION_EMPTYING, -1
5894 Xsand_sandstone_1, FALSE, FALSE,
5895 EL_QUICKSAND_FILLING, -1, -1
5898 Xsand_sandstone_2, FALSE, FALSE,
5899 EL_QUICKSAND_FILLING, -1, -1
5902 Xsand_sandstone_3, FALSE, FALSE,
5903 EL_QUICKSAND_FILLING, -1, -1
5906 Xsand_sandstone_4, FALSE, FALSE,
5907 EL_QUICKSAND_FILLING, -1, -1
5910 Xplant, TRUE, FALSE,
5911 EL_EMC_PLANT, -1, -1
5914 Yplant, FALSE, FALSE,
5915 EL_EMC_PLANT, -1, -1
5918 Xlenses, TRUE, FALSE,
5919 EL_EMC_LENSES, -1, -1
5922 Xmagnify, TRUE, FALSE,
5923 EL_EMC_MAGNIFIER, -1, -1
5926 Xdripper, TRUE, FALSE,
5927 EL_EMC_DRIPPER, -1, -1
5930 XdripperB, FALSE, FALSE,
5931 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5934 Xfake_blank, TRUE, FALSE,
5935 EL_INVISIBLE_WALL, -1, -1
5938 Xfake_blankB, FALSE, FALSE,
5939 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5942 Xfake_grass, TRUE, FALSE,
5943 EL_EMC_FAKE_GRASS, -1, -1
5946 Xfake_grassB, FALSE, FALSE,
5947 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5950 Xfake_door_1, TRUE, FALSE,
5951 EL_EM_GATE_1_GRAY, -1, -1
5954 Xfake_door_2, TRUE, FALSE,
5955 EL_EM_GATE_2_GRAY, -1, -1
5958 Xfake_door_3, TRUE, FALSE,
5959 EL_EM_GATE_3_GRAY, -1, -1
5962 Xfake_door_4, TRUE, FALSE,
5963 EL_EM_GATE_4_GRAY, -1, -1
5966 Xfake_door_5, TRUE, FALSE,
5967 EL_EMC_GATE_5_GRAY, -1, -1
5970 Xfake_door_6, TRUE, FALSE,
5971 EL_EMC_GATE_6_GRAY, -1, -1
5974 Xfake_door_7, TRUE, FALSE,
5975 EL_EMC_GATE_7_GRAY, -1, -1
5978 Xfake_door_8, TRUE, FALSE,
5979 EL_EMC_GATE_8_GRAY, -1, -1
5982 Xfake_acid_1, TRUE, FALSE,
5983 EL_EMC_FAKE_ACID, -1, -1
5986 Xfake_acid_2, FALSE, FALSE,
5987 EL_EMC_FAKE_ACID, -1, -1
5990 Xfake_acid_3, FALSE, FALSE,
5991 EL_EMC_FAKE_ACID, -1, -1
5994 Xfake_acid_4, FALSE, FALSE,
5995 EL_EMC_FAKE_ACID, -1, -1
5998 Xfake_acid_5, FALSE, FALSE,
5999 EL_EMC_FAKE_ACID, -1, -1
6002 Xfake_acid_6, FALSE, FALSE,
6003 EL_EMC_FAKE_ACID, -1, -1
6006 Xfake_acid_7, FALSE, FALSE,
6007 EL_EMC_FAKE_ACID, -1, -1
6010 Xfake_acid_8, FALSE, FALSE,
6011 EL_EMC_FAKE_ACID, -1, -1
6014 Xsteel_1, TRUE, FALSE,
6015 EL_STEELWALL, -1, -1
6018 Xsteel_2, TRUE, FALSE,
6019 EL_EMC_STEELWALL_2, -1, -1
6022 Xsteel_3, TRUE, FALSE,
6023 EL_EMC_STEELWALL_3, -1, -1
6026 Xsteel_4, TRUE, FALSE,
6027 EL_EMC_STEELWALL_4, -1, -1
6030 Xwall_1, TRUE, FALSE,
6034 Xwall_2, TRUE, FALSE,
6035 EL_EMC_WALL_14, -1, -1
6038 Xwall_3, TRUE, FALSE,
6039 EL_EMC_WALL_15, -1, -1
6042 Xwall_4, TRUE, FALSE,
6043 EL_EMC_WALL_16, -1, -1
6046 Xround_wall_1, TRUE, FALSE,
6047 EL_WALL_SLIPPERY, -1, -1
6050 Xround_wall_2, TRUE, FALSE,
6051 EL_EMC_WALL_SLIPPERY_2, -1, -1
6054 Xround_wall_3, TRUE, FALSE,
6055 EL_EMC_WALL_SLIPPERY_3, -1, -1
6058 Xround_wall_4, TRUE, FALSE,
6059 EL_EMC_WALL_SLIPPERY_4, -1, -1
6062 Xdecor_1, TRUE, FALSE,
6063 EL_EMC_WALL_8, -1, -1
6066 Xdecor_2, TRUE, FALSE,
6067 EL_EMC_WALL_6, -1, -1
6070 Xdecor_3, TRUE, FALSE,
6071 EL_EMC_WALL_4, -1, -1
6074 Xdecor_4, TRUE, FALSE,
6075 EL_EMC_WALL_7, -1, -1
6078 Xdecor_5, TRUE, FALSE,
6079 EL_EMC_WALL_5, -1, -1
6082 Xdecor_6, TRUE, FALSE,
6083 EL_EMC_WALL_9, -1, -1
6086 Xdecor_7, TRUE, FALSE,
6087 EL_EMC_WALL_10, -1, -1
6090 Xdecor_8, TRUE, FALSE,
6091 EL_EMC_WALL_1, -1, -1
6094 Xdecor_9, TRUE, FALSE,
6095 EL_EMC_WALL_2, -1, -1
6098 Xdecor_10, TRUE, FALSE,
6099 EL_EMC_WALL_3, -1, -1
6102 Xdecor_11, TRUE, FALSE,
6103 EL_EMC_WALL_11, -1, -1
6106 Xdecor_12, TRUE, FALSE,
6107 EL_EMC_WALL_12, -1, -1
6110 Xalpha_0, TRUE, FALSE,
6111 EL_CHAR('0'), -1, -1
6114 Xalpha_1, TRUE, FALSE,
6115 EL_CHAR('1'), -1, -1
6118 Xalpha_2, TRUE, FALSE,
6119 EL_CHAR('2'), -1, -1
6122 Xalpha_3, TRUE, FALSE,
6123 EL_CHAR('3'), -1, -1
6126 Xalpha_4, TRUE, FALSE,
6127 EL_CHAR('4'), -1, -1
6130 Xalpha_5, TRUE, FALSE,
6131 EL_CHAR('5'), -1, -1
6134 Xalpha_6, TRUE, FALSE,
6135 EL_CHAR('6'), -1, -1
6138 Xalpha_7, TRUE, FALSE,
6139 EL_CHAR('7'), -1, -1
6142 Xalpha_8, TRUE, FALSE,
6143 EL_CHAR('8'), -1, -1
6146 Xalpha_9, TRUE, FALSE,
6147 EL_CHAR('9'), -1, -1
6150 Xalpha_excla, TRUE, FALSE,
6151 EL_CHAR('!'), -1, -1
6154 Xalpha_quote, TRUE, FALSE,
6155 EL_CHAR('"'), -1, -1
6158 Xalpha_comma, TRUE, FALSE,
6159 EL_CHAR(','), -1, -1
6162 Xalpha_minus, TRUE, FALSE,
6163 EL_CHAR('-'), -1, -1
6166 Xalpha_perio, TRUE, FALSE,
6167 EL_CHAR('.'), -1, -1
6170 Xalpha_colon, TRUE, FALSE,
6171 EL_CHAR(':'), -1, -1
6174 Xalpha_quest, TRUE, FALSE,
6175 EL_CHAR('?'), -1, -1
6178 Xalpha_a, TRUE, FALSE,
6179 EL_CHAR('A'), -1, -1
6182 Xalpha_b, TRUE, FALSE,
6183 EL_CHAR('B'), -1, -1
6186 Xalpha_c, TRUE, FALSE,
6187 EL_CHAR('C'), -1, -1
6190 Xalpha_d, TRUE, FALSE,
6191 EL_CHAR('D'), -1, -1
6194 Xalpha_e, TRUE, FALSE,
6195 EL_CHAR('E'), -1, -1
6198 Xalpha_f, TRUE, FALSE,
6199 EL_CHAR('F'), -1, -1
6202 Xalpha_g, TRUE, FALSE,
6203 EL_CHAR('G'), -1, -1
6206 Xalpha_h, TRUE, FALSE,
6207 EL_CHAR('H'), -1, -1
6210 Xalpha_i, TRUE, FALSE,
6211 EL_CHAR('I'), -1, -1
6214 Xalpha_j, TRUE, FALSE,
6215 EL_CHAR('J'), -1, -1
6218 Xalpha_k, TRUE, FALSE,
6219 EL_CHAR('K'), -1, -1
6222 Xalpha_l, TRUE, FALSE,
6223 EL_CHAR('L'), -1, -1
6226 Xalpha_m, TRUE, FALSE,
6227 EL_CHAR('M'), -1, -1
6230 Xalpha_n, TRUE, FALSE,
6231 EL_CHAR('N'), -1, -1
6234 Xalpha_o, TRUE, FALSE,
6235 EL_CHAR('O'), -1, -1
6238 Xalpha_p, TRUE, FALSE,
6239 EL_CHAR('P'), -1, -1
6242 Xalpha_q, TRUE, FALSE,
6243 EL_CHAR('Q'), -1, -1
6246 Xalpha_r, TRUE, FALSE,
6247 EL_CHAR('R'), -1, -1
6250 Xalpha_s, TRUE, FALSE,
6251 EL_CHAR('S'), -1, -1
6254 Xalpha_t, TRUE, FALSE,
6255 EL_CHAR('T'), -1, -1
6258 Xalpha_u, TRUE, FALSE,
6259 EL_CHAR('U'), -1, -1
6262 Xalpha_v, TRUE, FALSE,
6263 EL_CHAR('V'), -1, -1
6266 Xalpha_w, TRUE, FALSE,
6267 EL_CHAR('W'), -1, -1
6270 Xalpha_x, TRUE, FALSE,
6271 EL_CHAR('X'), -1, -1
6274 Xalpha_y, TRUE, FALSE,
6275 EL_CHAR('Y'), -1, -1
6278 Xalpha_z, TRUE, FALSE,
6279 EL_CHAR('Z'), -1, -1
6282 Xalpha_arrow_e, TRUE, FALSE,
6283 EL_CHAR('>'), -1, -1
6286 Xalpha_arrow_w, TRUE, FALSE,
6287 EL_CHAR('<'), -1, -1
6290 Xalpha_copyr, TRUE, FALSE,
6291 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6295 Xboom_bug, FALSE, FALSE,
6296 EL_BUG, ACTION_EXPLODING, -1
6299 Xboom_bomb, FALSE, FALSE,
6300 EL_BOMB, ACTION_EXPLODING, -1
6303 Xboom_android, FALSE, FALSE,
6304 EL_EMC_ANDROID, ACTION_OTHER, -1
6307 Xboom_1, FALSE, FALSE,
6308 EL_DEFAULT, ACTION_EXPLODING, -1
6311 Xboom_2, FALSE, FALSE,
6312 EL_DEFAULT, ACTION_EXPLODING, -1
6315 Znormal, FALSE, FALSE,
6319 Zdynamite, FALSE, FALSE,
6323 Zplayer, FALSE, FALSE,
6327 ZBORDER, FALSE, FALSE,
6337 static struct Mapping_EM_to_RND_player
6346 em_player_mapping_list[] =
6350 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6354 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6358 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6362 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6366 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6370 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6374 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6378 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6382 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6386 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6390 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6394 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6398 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6402 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6406 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6410 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6414 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6418 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6422 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6426 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6430 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6434 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6438 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6442 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6446 EL_PLAYER_1, ACTION_DEFAULT, -1,
6450 EL_PLAYER_2, ACTION_DEFAULT, -1,
6454 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6458 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6462 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6466 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6470 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6474 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6478 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6482 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6486 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6490 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6494 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6498 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6502 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6506 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6510 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6514 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6518 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6522 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6526 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6530 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6534 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6538 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6542 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6546 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6550 EL_PLAYER_3, ACTION_DEFAULT, -1,
6554 EL_PLAYER_4, ACTION_DEFAULT, -1,
6563 int map_element_RND_to_EM(int element_rnd)
6565 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6566 static boolean mapping_initialized = FALSE;
6568 if (!mapping_initialized)
6572 /* return "Xalpha_quest" for all undefined elements in mapping array */
6573 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6574 mapping_RND_to_EM[i] = Xalpha_quest;
6576 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6577 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6578 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6579 em_object_mapping_list[i].element_em;
6581 mapping_initialized = TRUE;
6584 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6585 return mapping_RND_to_EM[element_rnd];
6587 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6592 int map_element_EM_to_RND(int element_em)
6594 static unsigned short mapping_EM_to_RND[TILE_MAX];
6595 static boolean mapping_initialized = FALSE;
6597 if (!mapping_initialized)
6601 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6602 for (i = 0; i < TILE_MAX; i++)
6603 mapping_EM_to_RND[i] = EL_UNKNOWN;
6605 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6606 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6607 em_object_mapping_list[i].element_rnd;
6609 mapping_initialized = TRUE;
6612 if (element_em >= 0 && element_em < TILE_MAX)
6613 return mapping_EM_to_RND[element_em];
6615 Error(ERR_WARN, "invalid EM level element %d", element_em);
6620 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6622 struct LevelInfo_EM *level_em = level->native_em_level;
6623 struct LEVEL *lev = level_em->lev;
6626 for (i = 0; i < TILE_MAX; i++)
6627 lev->android_array[i] = Xblank;
6629 for (i = 0; i < level->num_android_clone_elements; i++)
6631 int element_rnd = level->android_clone_element[i];
6632 int element_em = map_element_RND_to_EM(element_rnd);
6634 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6635 if (em_object_mapping_list[j].element_rnd == element_rnd)
6636 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6640 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6642 struct LevelInfo_EM *level_em = level->native_em_level;
6643 struct LEVEL *lev = level_em->lev;
6646 level->num_android_clone_elements = 0;
6648 for (i = 0; i < TILE_MAX; i++)
6650 int element_em = lev->android_array[i];
6652 boolean element_found = FALSE;
6654 if (element_em == Xblank)
6657 element_rnd = map_element_EM_to_RND(element_em);
6659 for (j = 0; j < level->num_android_clone_elements; j++)
6660 if (level->android_clone_element[j] == element_rnd)
6661 element_found = TRUE;
6665 level->android_clone_element[level->num_android_clone_elements++] =
6668 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6673 if (level->num_android_clone_elements == 0)
6675 level->num_android_clone_elements = 1;
6676 level->android_clone_element[0] = EL_EMPTY;
6680 int map_direction_RND_to_EM(int direction)
6682 return (direction == MV_UP ? 0 :
6683 direction == MV_RIGHT ? 1 :
6684 direction == MV_DOWN ? 2 :
6685 direction == MV_LEFT ? 3 :
6689 int map_direction_EM_to_RND(int direction)
6691 return (direction == 0 ? MV_UP :
6692 direction == 1 ? MV_RIGHT :
6693 direction == 2 ? MV_DOWN :
6694 direction == 3 ? MV_LEFT :
6698 int map_element_RND_to_SP(int element_rnd)
6700 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6702 if (element_rnd >= EL_SP_START &&
6703 element_rnd <= EL_SP_END)
6704 element_sp = element_rnd - EL_SP_START;
6705 else if (element_rnd == EL_EMPTY_SPACE)
6707 else if (element_rnd == EL_INVISIBLE_WALL)
6713 int map_element_SP_to_RND(int element_sp)
6715 int element_rnd = EL_UNKNOWN;
6717 if (element_sp >= 0x00 &&
6719 element_rnd = EL_SP_START + element_sp;
6720 else if (element_sp == 0x28)
6721 element_rnd = EL_INVISIBLE_WALL;
6726 int map_action_SP_to_RND(int action_sp)
6730 case actActive: return ACTION_ACTIVE;
6731 case actImpact: return ACTION_IMPACT;
6732 case actExploding: return ACTION_EXPLODING;
6733 case actDigging: return ACTION_DIGGING;
6734 case actSnapping: return ACTION_SNAPPING;
6735 case actCollecting: return ACTION_COLLECTING;
6736 case actPassing: return ACTION_PASSING;
6737 case actPushing: return ACTION_PUSHING;
6738 case actDropping: return ACTION_DROPPING;
6740 default: return ACTION_DEFAULT;
6744 int get_next_element(int element)
6748 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6749 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6750 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6751 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6752 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6753 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6754 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6755 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6756 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6757 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6758 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6760 default: return element;
6764 int el_act_dir2img(int element, int action, int direction)
6766 element = GFX_ELEMENT(element);
6767 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6769 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6770 return element_info[element].direction_graphic[action][direction];
6773 static int el_act_dir2crm(int element, int action, int direction)
6775 element = GFX_ELEMENT(element);
6776 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6778 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6779 return element_info[element].direction_crumbled[action][direction];
6782 int el_act2img(int element, int action)
6784 element = GFX_ELEMENT(element);
6786 return element_info[element].graphic[action];
6789 int el_act2crm(int element, int action)
6791 element = GFX_ELEMENT(element);
6793 return element_info[element].crumbled[action];
6796 int el_dir2img(int element, int direction)
6798 element = GFX_ELEMENT(element);
6800 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6803 int el2baseimg(int element)
6805 return element_info[element].graphic[ACTION_DEFAULT];
6808 int el2img(int element)
6810 element = GFX_ELEMENT(element);
6812 return element_info[element].graphic[ACTION_DEFAULT];
6815 int el2edimg(int element)
6817 element = GFX_ELEMENT(element);
6819 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6822 int el2preimg(int element)
6824 element = GFX_ELEMENT(element);
6826 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6829 int el2panelimg(int element)
6831 element = GFX_ELEMENT(element);
6833 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6836 int font2baseimg(int font_nr)
6838 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6841 int getBeltNrFromBeltElement(int element)
6843 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6844 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6845 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6848 int getBeltNrFromBeltActiveElement(int element)
6850 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6851 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6852 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6855 int getBeltNrFromBeltSwitchElement(int element)
6857 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6858 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6859 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6862 int getBeltDirNrFromBeltElement(int element)
6864 static int belt_base_element[4] =
6866 EL_CONVEYOR_BELT_1_LEFT,
6867 EL_CONVEYOR_BELT_2_LEFT,
6868 EL_CONVEYOR_BELT_3_LEFT,
6869 EL_CONVEYOR_BELT_4_LEFT
6872 int belt_nr = getBeltNrFromBeltElement(element);
6873 int belt_dir_nr = element - belt_base_element[belt_nr];
6875 return (belt_dir_nr % 3);
6878 int getBeltDirNrFromBeltSwitchElement(int element)
6880 static int belt_base_element[4] =
6882 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6883 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6884 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6885 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6888 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6889 int belt_dir_nr = element - belt_base_element[belt_nr];
6891 return (belt_dir_nr % 3);
6894 int getBeltDirFromBeltElement(int element)
6896 static int belt_move_dir[3] =
6903 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6905 return belt_move_dir[belt_dir_nr];
6908 int getBeltDirFromBeltSwitchElement(int element)
6910 static int belt_move_dir[3] =
6917 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6919 return belt_move_dir[belt_dir_nr];
6922 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6924 static int belt_base_element[4] =
6926 EL_CONVEYOR_BELT_1_LEFT,
6927 EL_CONVEYOR_BELT_2_LEFT,
6928 EL_CONVEYOR_BELT_3_LEFT,
6929 EL_CONVEYOR_BELT_4_LEFT
6932 return belt_base_element[belt_nr] + belt_dir_nr;
6935 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6937 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6939 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6942 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6944 static int belt_base_element[4] =
6946 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6947 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6948 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6949 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6952 return belt_base_element[belt_nr] + belt_dir_nr;
6955 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6957 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6959 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6962 boolean getTeamMode_EM()
6964 return game.team_mode;
6967 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6969 int game_frame_delay_value;
6971 game_frame_delay_value =
6972 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6973 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6976 if (tape.playing && tape.warp_forward && !tape.pausing)
6977 game_frame_delay_value = 0;
6979 return game_frame_delay_value;
6982 unsigned int InitRND(int seed)
6984 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6985 return InitEngineRandom_EM(seed);
6986 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6987 return InitEngineRandom_SP(seed);
6989 return InitEngineRandom_RND(seed);
6992 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6993 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6995 inline static int get_effective_element_EM(int tile, int frame_em)
6997 int element = object_mapping[tile].element_rnd;
6998 int action = object_mapping[tile].action;
6999 boolean is_backside = object_mapping[tile].is_backside;
7000 boolean action_removing = (action == ACTION_DIGGING ||
7001 action == ACTION_SNAPPING ||
7002 action == ACTION_COLLECTING);
7008 case Yacid_splash_eB:
7009 case Yacid_splash_wB:
7010 return (frame_em > 5 ? EL_EMPTY : element);
7016 else /* frame_em == 7 */
7020 case Yacid_splash_eB:
7021 case Yacid_splash_wB:
7024 case Yemerald_stone:
7027 case Ydiamond_stone:
7031 case Xdrip_stretchB:
7050 case Xsand_stonein_1:
7051 case Xsand_stonein_2:
7052 case Xsand_stonein_3:
7053 case Xsand_stonein_4:
7057 return (is_backside || action_removing ? EL_EMPTY : element);
7062 inline static boolean check_linear_animation_EM(int tile)
7066 case Xsand_stonesand_1:
7067 case Xsand_stonesand_quickout_1:
7068 case Xsand_sandstone_1:
7069 case Xsand_stonein_1:
7070 case Xsand_stoneout_1:
7089 case Yacid_splash_eB:
7090 case Yacid_splash_wB:
7091 case Yemerald_stone:
7098 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7099 boolean has_crumbled_graphics,
7100 int crumbled, int sync_frame)
7102 /* if element can be crumbled, but certain action graphics are just empty
7103 space (like instantly snapping sand to empty space in 1 frame), do not
7104 treat these empty space graphics as crumbled graphics in EMC engine */
7105 if (crumbled == IMG_EMPTY_SPACE)
7106 has_crumbled_graphics = FALSE;
7108 if (has_crumbled_graphics)
7110 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7111 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7112 g_crumbled->anim_delay,
7113 g_crumbled->anim_mode,
7114 g_crumbled->anim_start_frame,
7117 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7118 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7120 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7122 g_em->has_crumbled_graphics = TRUE;
7126 g_em->crumbled_bitmap = NULL;
7127 g_em->crumbled_src_x = 0;
7128 g_em->crumbled_src_y = 0;
7129 g_em->crumbled_border_size = 0;
7131 g_em->has_crumbled_graphics = FALSE;
7135 void ResetGfxAnimation_EM(int x, int y, int tile)
7140 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7141 int tile, int frame_em, int x, int y)
7143 int action = object_mapping[tile].action;
7144 int direction = object_mapping[tile].direction;
7145 int effective_element = get_effective_element_EM(tile, frame_em);
7146 int graphic = (direction == MV_NONE ?
7147 el_act2img(effective_element, action) :
7148 el_act_dir2img(effective_element, action, direction));
7149 struct GraphicInfo *g = &graphic_info[graphic];
7151 boolean action_removing = (action == ACTION_DIGGING ||
7152 action == ACTION_SNAPPING ||
7153 action == ACTION_COLLECTING);
7154 boolean action_moving = (action == ACTION_FALLING ||
7155 action == ACTION_MOVING ||
7156 action == ACTION_PUSHING ||
7157 action == ACTION_EATING ||
7158 action == ACTION_FILLING ||
7159 action == ACTION_EMPTYING);
7160 boolean action_falling = (action == ACTION_FALLING ||
7161 action == ACTION_FILLING ||
7162 action == ACTION_EMPTYING);
7164 /* special case: graphic uses "2nd movement tile" and has defined
7165 7 frames for movement animation (or less) => use default graphic
7166 for last (8th) frame which ends the movement animation */
7167 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7169 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7170 graphic = (direction == MV_NONE ?
7171 el_act2img(effective_element, action) :
7172 el_act_dir2img(effective_element, action, direction));
7174 g = &graphic_info[graphic];
7177 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7181 else if (action_moving)
7183 boolean is_backside = object_mapping[tile].is_backside;
7187 int direction = object_mapping[tile].direction;
7188 int move_dir = (action_falling ? MV_DOWN : direction);
7193 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7194 if (g->double_movement && frame_em == 0)
7198 if (move_dir == MV_LEFT)
7199 GfxFrame[x - 1][y] = GfxFrame[x][y];
7200 else if (move_dir == MV_RIGHT)
7201 GfxFrame[x + 1][y] = GfxFrame[x][y];
7202 else if (move_dir == MV_UP)
7203 GfxFrame[x][y - 1] = GfxFrame[x][y];
7204 else if (move_dir == MV_DOWN)
7205 GfxFrame[x][y + 1] = GfxFrame[x][y];
7212 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7213 if (tile == Xsand_stonesand_quickout_1 ||
7214 tile == Xsand_stonesand_quickout_2)
7218 if (graphic_info[graphic].anim_global_sync)
7219 sync_frame = FrameCounter;
7220 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7221 sync_frame = GfxFrame[x][y];
7223 sync_frame = 0; /* playfield border (pseudo steel) */
7225 SetRandomAnimationValue(x, y);
7227 int frame = getAnimationFrame(g->anim_frames,
7230 g->anim_start_frame,
7233 g_em->unique_identifier =
7234 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7237 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7238 int tile, int frame_em, int x, int y)
7240 int action = object_mapping[tile].action;
7241 int direction = object_mapping[tile].direction;
7242 boolean is_backside = object_mapping[tile].is_backside;
7243 int effective_element = get_effective_element_EM(tile, frame_em);
7244 int effective_action = action;
7245 int graphic = (direction == MV_NONE ?
7246 el_act2img(effective_element, effective_action) :
7247 el_act_dir2img(effective_element, effective_action,
7249 int crumbled = (direction == MV_NONE ?
7250 el_act2crm(effective_element, effective_action) :
7251 el_act_dir2crm(effective_element, effective_action,
7253 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7254 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7255 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7256 struct GraphicInfo *g = &graphic_info[graphic];
7259 /* special case: graphic uses "2nd movement tile" and has defined
7260 7 frames for movement animation (or less) => use default graphic
7261 for last (8th) frame which ends the movement animation */
7262 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7264 effective_action = ACTION_DEFAULT;
7265 graphic = (direction == MV_NONE ?
7266 el_act2img(effective_element, effective_action) :
7267 el_act_dir2img(effective_element, effective_action,
7269 crumbled = (direction == MV_NONE ?
7270 el_act2crm(effective_element, effective_action) :
7271 el_act_dir2crm(effective_element, effective_action,
7274 g = &graphic_info[graphic];
7277 if (graphic_info[graphic].anim_global_sync)
7278 sync_frame = FrameCounter;
7279 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7280 sync_frame = GfxFrame[x][y];
7282 sync_frame = 0; /* playfield border (pseudo steel) */
7284 SetRandomAnimationValue(x, y);
7286 int frame = getAnimationFrame(g->anim_frames,
7289 g->anim_start_frame,
7292 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7293 g->double_movement && is_backside);
7295 /* (updating the "crumbled" graphic definitions is probably not really needed,
7296 as animations for crumbled graphics can't be longer than one EMC cycle) */
7297 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7301 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7302 int player_nr, int anim, int frame_em)
7304 int element = player_mapping[player_nr][anim].element_rnd;
7305 int action = player_mapping[player_nr][anim].action;
7306 int direction = player_mapping[player_nr][anim].direction;
7307 int graphic = (direction == MV_NONE ?
7308 el_act2img(element, action) :
7309 el_act_dir2img(element, action, direction));
7310 struct GraphicInfo *g = &graphic_info[graphic];
7313 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7315 stored_player[player_nr].StepFrame = frame_em;
7317 sync_frame = stored_player[player_nr].Frame;
7319 int frame = getAnimationFrame(g->anim_frames,
7322 g->anim_start_frame,
7325 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7326 &g_em->src_x, &g_em->src_y, FALSE);
7329 void InitGraphicInfo_EM(void)
7334 int num_em_gfx_errors = 0;
7336 if (graphic_info_em_object[0][0].bitmap == NULL)
7338 /* EM graphics not yet initialized in em_open_all() */
7343 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7346 /* always start with reliable default values */
7347 for (i = 0; i < TILE_MAX; i++)
7349 object_mapping[i].element_rnd = EL_UNKNOWN;
7350 object_mapping[i].is_backside = FALSE;
7351 object_mapping[i].action = ACTION_DEFAULT;
7352 object_mapping[i].direction = MV_NONE;
7355 /* always start with reliable default values */
7356 for (p = 0; p < MAX_PLAYERS; p++)
7358 for (i = 0; i < SPR_MAX; i++)
7360 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7361 player_mapping[p][i].action = ACTION_DEFAULT;
7362 player_mapping[p][i].direction = MV_NONE;
7366 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7368 int e = em_object_mapping_list[i].element_em;
7370 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7371 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7373 if (em_object_mapping_list[i].action != -1)
7374 object_mapping[e].action = em_object_mapping_list[i].action;
7376 if (em_object_mapping_list[i].direction != -1)
7377 object_mapping[e].direction =
7378 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7381 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7383 int a = em_player_mapping_list[i].action_em;
7384 int p = em_player_mapping_list[i].player_nr;
7386 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7388 if (em_player_mapping_list[i].action != -1)
7389 player_mapping[p][a].action = em_player_mapping_list[i].action;
7391 if (em_player_mapping_list[i].direction != -1)
7392 player_mapping[p][a].direction =
7393 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7396 for (i = 0; i < TILE_MAX; i++)
7398 int element = object_mapping[i].element_rnd;
7399 int action = object_mapping[i].action;
7400 int direction = object_mapping[i].direction;
7401 boolean is_backside = object_mapping[i].is_backside;
7402 boolean action_exploding = ((action == ACTION_EXPLODING ||
7403 action == ACTION_SMASHED_BY_ROCK ||
7404 action == ACTION_SMASHED_BY_SPRING) &&
7405 element != EL_DIAMOND);
7406 boolean action_active = (action == ACTION_ACTIVE);
7407 boolean action_other = (action == ACTION_OTHER);
7409 for (j = 0; j < 8; j++)
7411 int effective_element = get_effective_element_EM(i, j);
7412 int effective_action = (j < 7 ? action :
7413 i == Xdrip_stretch ? action :
7414 i == Xdrip_stretchB ? action :
7415 i == Ydrip_s1 ? action :
7416 i == Ydrip_s1B ? action :
7417 i == Xball_1B ? action :
7418 i == Xball_2 ? action :
7419 i == Xball_2B ? action :
7420 i == Yball_eat ? action :
7421 i == Ykey_1_eat ? action :
7422 i == Ykey_2_eat ? action :
7423 i == Ykey_3_eat ? action :
7424 i == Ykey_4_eat ? action :
7425 i == Ykey_5_eat ? action :
7426 i == Ykey_6_eat ? action :
7427 i == Ykey_7_eat ? action :
7428 i == Ykey_8_eat ? action :
7429 i == Ylenses_eat ? action :
7430 i == Ymagnify_eat ? action :
7431 i == Ygrass_eat ? action :
7432 i == Ydirt_eat ? action :
7433 i == Xsand_stonein_1 ? action :
7434 i == Xsand_stonein_2 ? action :
7435 i == Xsand_stonein_3 ? action :
7436 i == Xsand_stonein_4 ? action :
7437 i == Xsand_stoneout_1 ? action :
7438 i == Xsand_stoneout_2 ? action :
7439 i == Xboom_android ? ACTION_EXPLODING :
7440 action_exploding ? ACTION_EXPLODING :
7441 action_active ? action :
7442 action_other ? action :
7444 int graphic = (el_act_dir2img(effective_element, effective_action,
7446 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7448 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7449 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7450 boolean has_action_graphics = (graphic != base_graphic);
7451 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7452 struct GraphicInfo *g = &graphic_info[graphic];
7453 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7456 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7457 boolean special_animation = (action != ACTION_DEFAULT &&
7458 g->anim_frames == 3 &&
7459 g->anim_delay == 2 &&
7460 g->anim_mode & ANIM_LINEAR);
7461 int sync_frame = (i == Xdrip_stretch ? 7 :
7462 i == Xdrip_stretchB ? 7 :
7463 i == Ydrip_s2 ? j + 8 :
7464 i == Ydrip_s2B ? j + 8 :
7473 i == Xfake_acid_1 ? 0 :
7474 i == Xfake_acid_2 ? 10 :
7475 i == Xfake_acid_3 ? 20 :
7476 i == Xfake_acid_4 ? 30 :
7477 i == Xfake_acid_5 ? 40 :
7478 i == Xfake_acid_6 ? 50 :
7479 i == Xfake_acid_7 ? 60 :
7480 i == Xfake_acid_8 ? 70 :
7482 i == Xball_2B ? j + 8 :
7483 i == Yball_eat ? j + 1 :
7484 i == Ykey_1_eat ? j + 1 :
7485 i == Ykey_2_eat ? j + 1 :
7486 i == Ykey_3_eat ? j + 1 :
7487 i == Ykey_4_eat ? j + 1 :
7488 i == Ykey_5_eat ? j + 1 :
7489 i == Ykey_6_eat ? j + 1 :
7490 i == Ykey_7_eat ? j + 1 :
7491 i == Ykey_8_eat ? j + 1 :
7492 i == Ylenses_eat ? j + 1 :
7493 i == Ymagnify_eat ? j + 1 :
7494 i == Ygrass_eat ? j + 1 :
7495 i == Ydirt_eat ? j + 1 :
7496 i == Xamoeba_1 ? 0 :
7497 i == Xamoeba_2 ? 1 :
7498 i == Xamoeba_3 ? 2 :
7499 i == Xamoeba_4 ? 3 :
7500 i == Xamoeba_5 ? 0 :
7501 i == Xamoeba_6 ? 1 :
7502 i == Xamoeba_7 ? 2 :
7503 i == Xamoeba_8 ? 3 :
7504 i == Xexit_2 ? j + 8 :
7505 i == Xexit_3 ? j + 16 :
7506 i == Xdynamite_1 ? 0 :
7507 i == Xdynamite_2 ? 8 :
7508 i == Xdynamite_3 ? 16 :
7509 i == Xdynamite_4 ? 24 :
7510 i == Xsand_stonein_1 ? j + 1 :
7511 i == Xsand_stonein_2 ? j + 9 :
7512 i == Xsand_stonein_3 ? j + 17 :
7513 i == Xsand_stonein_4 ? j + 25 :
7514 i == Xsand_stoneout_1 && j == 0 ? 0 :
7515 i == Xsand_stoneout_1 && j == 1 ? 0 :
7516 i == Xsand_stoneout_1 && j == 2 ? 1 :
7517 i == Xsand_stoneout_1 && j == 3 ? 2 :
7518 i == Xsand_stoneout_1 && j == 4 ? 2 :
7519 i == Xsand_stoneout_1 && j == 5 ? 3 :
7520 i == Xsand_stoneout_1 && j == 6 ? 4 :
7521 i == Xsand_stoneout_1 && j == 7 ? 4 :
7522 i == Xsand_stoneout_2 && j == 0 ? 5 :
7523 i == Xsand_stoneout_2 && j == 1 ? 6 :
7524 i == Xsand_stoneout_2 && j == 2 ? 7 :
7525 i == Xsand_stoneout_2 && j == 3 ? 8 :
7526 i == Xsand_stoneout_2 && j == 4 ? 9 :
7527 i == Xsand_stoneout_2 && j == 5 ? 11 :
7528 i == Xsand_stoneout_2 && j == 6 ? 13 :
7529 i == Xsand_stoneout_2 && j == 7 ? 15 :
7530 i == Xboom_bug && j == 1 ? 2 :
7531 i == Xboom_bug && j == 2 ? 2 :
7532 i == Xboom_bug && j == 3 ? 4 :
7533 i == Xboom_bug && j == 4 ? 4 :
7534 i == Xboom_bug && j == 5 ? 2 :
7535 i == Xboom_bug && j == 6 ? 2 :
7536 i == Xboom_bug && j == 7 ? 0 :
7537 i == Xboom_bomb && j == 1 ? 2 :
7538 i == Xboom_bomb && j == 2 ? 2 :
7539 i == Xboom_bomb && j == 3 ? 4 :
7540 i == Xboom_bomb && j == 4 ? 4 :
7541 i == Xboom_bomb && j == 5 ? 2 :
7542 i == Xboom_bomb && j == 6 ? 2 :
7543 i == Xboom_bomb && j == 7 ? 0 :
7544 i == Xboom_android && j == 7 ? 6 :
7545 i == Xboom_1 && j == 1 ? 2 :
7546 i == Xboom_1 && j == 2 ? 2 :
7547 i == Xboom_1 && j == 3 ? 4 :
7548 i == Xboom_1 && j == 4 ? 4 :
7549 i == Xboom_1 && j == 5 ? 6 :
7550 i == Xboom_1 && j == 6 ? 6 :
7551 i == Xboom_1 && j == 7 ? 8 :
7552 i == Xboom_2 && j == 0 ? 8 :
7553 i == Xboom_2 && j == 1 ? 8 :
7554 i == Xboom_2 && j == 2 ? 10 :
7555 i == Xboom_2 && j == 3 ? 10 :
7556 i == Xboom_2 && j == 4 ? 10 :
7557 i == Xboom_2 && j == 5 ? 12 :
7558 i == Xboom_2 && j == 6 ? 12 :
7559 i == Xboom_2 && j == 7 ? 12 :
7560 special_animation && j == 4 ? 3 :
7561 effective_action != action ? 0 :
7565 Bitmap *debug_bitmap = g_em->bitmap;
7566 int debug_src_x = g_em->src_x;
7567 int debug_src_y = g_em->src_y;
7570 int frame = getAnimationFrame(g->anim_frames,
7573 g->anim_start_frame,
7576 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7577 g->double_movement && is_backside);
7579 g_em->bitmap = src_bitmap;
7580 g_em->src_x = src_x;
7581 g_em->src_y = src_y;
7582 g_em->src_offset_x = 0;
7583 g_em->src_offset_y = 0;
7584 g_em->dst_offset_x = 0;
7585 g_em->dst_offset_y = 0;
7586 g_em->width = TILEX;
7587 g_em->height = TILEY;
7589 g_em->preserve_background = FALSE;
7591 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7594 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7595 effective_action == ACTION_MOVING ||
7596 effective_action == ACTION_PUSHING ||
7597 effective_action == ACTION_EATING)) ||
7598 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7599 effective_action == ACTION_EMPTYING)))
7602 (effective_action == ACTION_FALLING ||
7603 effective_action == ACTION_FILLING ||
7604 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7605 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7606 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7607 int num_steps = (i == Ydrip_s1 ? 16 :
7608 i == Ydrip_s1B ? 16 :
7609 i == Ydrip_s2 ? 16 :
7610 i == Ydrip_s2B ? 16 :
7611 i == Xsand_stonein_1 ? 32 :
7612 i == Xsand_stonein_2 ? 32 :
7613 i == Xsand_stonein_3 ? 32 :
7614 i == Xsand_stonein_4 ? 32 :
7615 i == Xsand_stoneout_1 ? 16 :
7616 i == Xsand_stoneout_2 ? 16 : 8);
7617 int cx = ABS(dx) * (TILEX / num_steps);
7618 int cy = ABS(dy) * (TILEY / num_steps);
7619 int step_frame = (i == Ydrip_s2 ? j + 8 :
7620 i == Ydrip_s2B ? j + 8 :
7621 i == Xsand_stonein_2 ? j + 8 :
7622 i == Xsand_stonein_3 ? j + 16 :
7623 i == Xsand_stonein_4 ? j + 24 :
7624 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7625 int step = (is_backside ? step_frame : num_steps - step_frame);
7627 if (is_backside) /* tile where movement starts */
7629 if (dx < 0 || dy < 0)
7631 g_em->src_offset_x = cx * step;
7632 g_em->src_offset_y = cy * step;
7636 g_em->dst_offset_x = cx * step;
7637 g_em->dst_offset_y = cy * step;
7640 else /* tile where movement ends */
7642 if (dx < 0 || dy < 0)
7644 g_em->dst_offset_x = cx * step;
7645 g_em->dst_offset_y = cy * step;
7649 g_em->src_offset_x = cx * step;
7650 g_em->src_offset_y = cy * step;
7654 g_em->width = TILEX - cx * step;
7655 g_em->height = TILEY - cy * step;
7658 /* create unique graphic identifier to decide if tile must be redrawn */
7659 /* bit 31 - 16 (16 bit): EM style graphic
7660 bit 15 - 12 ( 4 bit): EM style frame
7661 bit 11 - 6 ( 6 bit): graphic width
7662 bit 5 - 0 ( 6 bit): graphic height */
7663 g_em->unique_identifier =
7664 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7668 /* skip check for EMC elements not contained in original EMC artwork */
7669 if (element == EL_EMC_FAKE_ACID)
7672 if (g_em->bitmap != debug_bitmap ||
7673 g_em->src_x != debug_src_x ||
7674 g_em->src_y != debug_src_y ||
7675 g_em->src_offset_x != 0 ||
7676 g_em->src_offset_y != 0 ||
7677 g_em->dst_offset_x != 0 ||
7678 g_em->dst_offset_y != 0 ||
7679 g_em->width != TILEX ||
7680 g_em->height != TILEY)
7682 static int last_i = -1;
7690 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7691 i, element, element_info[element].token_name,
7692 element_action_info[effective_action].suffix, direction);
7694 if (element != effective_element)
7695 printf(" [%d ('%s')]",
7697 element_info[effective_element].token_name);
7701 if (g_em->bitmap != debug_bitmap)
7702 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7703 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7705 if (g_em->src_x != debug_src_x ||
7706 g_em->src_y != debug_src_y)
7707 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7708 j, (is_backside ? 'B' : 'F'),
7709 g_em->src_x, g_em->src_y,
7710 g_em->src_x / 32, g_em->src_y / 32,
7711 debug_src_x, debug_src_y,
7712 debug_src_x / 32, debug_src_y / 32);
7714 if (g_em->src_offset_x != 0 ||
7715 g_em->src_offset_y != 0 ||
7716 g_em->dst_offset_x != 0 ||
7717 g_em->dst_offset_y != 0)
7718 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7720 g_em->src_offset_x, g_em->src_offset_y,
7721 g_em->dst_offset_x, g_em->dst_offset_y);
7723 if (g_em->width != TILEX ||
7724 g_em->height != TILEY)
7725 printf(" %d (%d): size %d,%d should be %d,%d\n",
7727 g_em->width, g_em->height, TILEX, TILEY);
7729 num_em_gfx_errors++;
7736 for (i = 0; i < TILE_MAX; i++)
7738 for (j = 0; j < 8; j++)
7740 int element = object_mapping[i].element_rnd;
7741 int action = object_mapping[i].action;
7742 int direction = object_mapping[i].direction;
7743 boolean is_backside = object_mapping[i].is_backside;
7744 int graphic_action = el_act_dir2img(element, action, direction);
7745 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7747 if ((action == ACTION_SMASHED_BY_ROCK ||
7748 action == ACTION_SMASHED_BY_SPRING ||
7749 action == ACTION_EATING) &&
7750 graphic_action == graphic_default)
7752 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7753 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7754 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7755 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7758 /* no separate animation for "smashed by rock" -- use rock instead */
7759 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7760 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7762 g_em->bitmap = g_xx->bitmap;
7763 g_em->src_x = g_xx->src_x;
7764 g_em->src_y = g_xx->src_y;
7765 g_em->src_offset_x = g_xx->src_offset_x;
7766 g_em->src_offset_y = g_xx->src_offset_y;
7767 g_em->dst_offset_x = g_xx->dst_offset_x;
7768 g_em->dst_offset_y = g_xx->dst_offset_y;
7769 g_em->width = g_xx->width;
7770 g_em->height = g_xx->height;
7771 g_em->unique_identifier = g_xx->unique_identifier;
7774 g_em->preserve_background = TRUE;
7779 for (p = 0; p < MAX_PLAYERS; p++)
7781 for (i = 0; i < SPR_MAX; i++)
7783 int element = player_mapping[p][i].element_rnd;
7784 int action = player_mapping[p][i].action;
7785 int direction = player_mapping[p][i].direction;
7787 for (j = 0; j < 8; j++)
7789 int effective_element = element;
7790 int effective_action = action;
7791 int graphic = (direction == MV_NONE ?
7792 el_act2img(effective_element, effective_action) :
7793 el_act_dir2img(effective_element, effective_action,
7795 struct GraphicInfo *g = &graphic_info[graphic];
7796 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7802 Bitmap *debug_bitmap = g_em->bitmap;
7803 int debug_src_x = g_em->src_x;
7804 int debug_src_y = g_em->src_y;
7807 int frame = getAnimationFrame(g->anim_frames,
7810 g->anim_start_frame,
7813 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7815 g_em->bitmap = src_bitmap;
7816 g_em->src_x = src_x;
7817 g_em->src_y = src_y;
7818 g_em->src_offset_x = 0;
7819 g_em->src_offset_y = 0;
7820 g_em->dst_offset_x = 0;
7821 g_em->dst_offset_y = 0;
7822 g_em->width = TILEX;
7823 g_em->height = TILEY;
7827 /* skip check for EMC elements not contained in original EMC artwork */
7828 if (element == EL_PLAYER_3 ||
7829 element == EL_PLAYER_4)
7832 if (g_em->bitmap != debug_bitmap ||
7833 g_em->src_x != debug_src_x ||
7834 g_em->src_y != debug_src_y)
7836 static int last_i = -1;
7844 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7845 p, i, element, element_info[element].token_name,
7846 element_action_info[effective_action].suffix, direction);
7848 if (element != effective_element)
7849 printf(" [%d ('%s')]",
7851 element_info[effective_element].token_name);
7855 if (g_em->bitmap != debug_bitmap)
7856 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7857 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7859 if (g_em->src_x != debug_src_x ||
7860 g_em->src_y != debug_src_y)
7861 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7863 g_em->src_x, g_em->src_y,
7864 g_em->src_x / 32, g_em->src_y / 32,
7865 debug_src_x, debug_src_y,
7866 debug_src_x / 32, debug_src_y / 32);
7868 num_em_gfx_errors++;
7878 printf("::: [%d errors found]\n", num_em_gfx_errors);
7884 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7885 boolean any_player_moving,
7886 boolean any_player_snapping,
7887 boolean any_player_dropping)
7889 static boolean player_was_waiting = TRUE;
7891 if (frame == 0 && !any_player_dropping)
7893 if (!player_was_waiting)
7895 if (!SaveEngineSnapshotToList())
7898 player_was_waiting = TRUE;
7901 else if (any_player_moving || any_player_snapping || any_player_dropping)
7903 player_was_waiting = FALSE;
7907 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7908 boolean murphy_is_dropping)
7910 static boolean player_was_waiting = TRUE;
7912 if (murphy_is_waiting)
7914 if (!player_was_waiting)
7916 if (!SaveEngineSnapshotToList())
7919 player_was_waiting = TRUE;
7924 player_was_waiting = FALSE;
7928 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7929 boolean any_player_moving,
7930 boolean any_player_snapping,
7931 boolean any_player_dropping)
7933 if (tape.single_step && tape.recording && !tape.pausing)
7934 if (frame == 0 && !any_player_dropping)
7935 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7937 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7938 any_player_snapping, any_player_dropping);
7941 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7942 boolean murphy_is_dropping)
7944 if (tape.single_step && tape.recording && !tape.pausing)
7945 if (murphy_is_waiting)
7946 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7948 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7951 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7952 int graphic, int sync_frame, int x, int y)
7954 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7956 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7959 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7961 return (IS_NEXT_FRAME(sync_frame, graphic));
7964 int getGraphicInfo_Delay(int graphic)
7966 return graphic_info[graphic].anim_delay;
7969 void PlayMenuSoundExt(int sound)
7971 if (sound == SND_UNDEFINED)
7974 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7975 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7978 if (IS_LOOP_SOUND(sound))
7979 PlaySoundLoop(sound);
7984 void PlayMenuSound()
7986 PlayMenuSoundExt(menu.sound[game_status]);
7989 void PlayMenuSoundStereo(int sound, int stereo_position)
7991 if (sound == SND_UNDEFINED)
7994 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7995 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7998 if (IS_LOOP_SOUND(sound))
7999 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8001 PlaySoundStereo(sound, stereo_position);
8004 void PlayMenuSoundIfLoopExt(int sound)
8006 if (sound == SND_UNDEFINED)
8009 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8010 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8013 if (IS_LOOP_SOUND(sound))
8014 PlaySoundLoop(sound);
8017 void PlayMenuSoundIfLoop()
8019 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8022 void PlayMenuMusicExt(int music)
8024 if (music == MUS_UNDEFINED)
8027 if (!setup.sound_music)
8033 void PlayMenuMusic()
8035 PlayMenuMusicExt(menu.music[game_status]);
8038 void PlaySoundActivating()
8041 PlaySound(SND_MENU_ITEM_ACTIVATING);
8045 void PlaySoundSelecting()
8048 PlaySound(SND_MENU_ITEM_SELECTING);
8052 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8054 boolean change_fullscreen = (setup.fullscreen !=
8055 video.fullscreen_enabled);
8056 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8057 !strEqual(setup.fullscreen_mode,
8058 video.fullscreen_mode_current));
8059 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8060 setup.window_scaling_percent !=
8061 video.window_scaling_percent);
8063 if (change_window_scaling_percent && video.fullscreen_enabled)
8066 if (!change_window_scaling_percent && !video.fullscreen_available)
8069 #if defined(TARGET_SDL2)
8070 if (change_window_scaling_percent)
8072 SDLSetWindowScaling(setup.window_scaling_percent);
8076 else if (change_fullscreen)
8078 SDLSetWindowFullscreen(setup.fullscreen);
8080 /* set setup value according to successfully changed fullscreen mode */
8081 setup.fullscreen = video.fullscreen_enabled;
8087 if (change_fullscreen ||
8088 change_fullscreen_mode ||
8089 change_window_scaling_percent)
8091 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8093 /* save backbuffer content which gets lost when toggling fullscreen mode */
8094 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8096 if (change_fullscreen_mode)
8098 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8099 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8102 if (change_window_scaling_percent)
8104 /* keep window mode, but change window scaling */
8105 video.fullscreen_enabled = TRUE; /* force new window scaling */
8108 /* toggle fullscreen */
8109 ChangeVideoModeIfNeeded(setup.fullscreen);
8111 /* set setup value according to successfully changed fullscreen mode */
8112 setup.fullscreen = video.fullscreen_enabled;
8114 /* restore backbuffer content from temporary backbuffer backup bitmap */
8115 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8117 FreeBitmap(tmp_backbuffer);
8119 /* update visible window/screen */
8120 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8124 void JoinRectangles(int *x, int *y, int *width, int *height,
8125 int x2, int y2, int width2, int height2)
8127 // do not join with "off-screen" rectangle
8128 if (x2 == -1 || y2 == -1)
8133 *width = MAX(*width, width2);
8134 *height = MAX(*height, height2);
8137 void ChangeViewportPropertiesIfNeeded()
8139 int gfx_game_mode = game_status;
8140 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8142 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8143 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8144 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8145 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8146 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8147 int new_win_xsize = vp_window->width;
8148 int new_win_ysize = vp_window->height;
8149 int border_size = vp_playfield->border_size;
8150 int new_sx = vp_playfield->x + border_size;
8151 int new_sy = vp_playfield->y + border_size;
8152 int new_sxsize = vp_playfield->width - 2 * border_size;
8153 int new_sysize = vp_playfield->height - 2 * border_size;
8154 int new_real_sx = vp_playfield->x;
8155 int new_real_sy = vp_playfield->y;
8156 int new_full_sxsize = vp_playfield->width;
8157 int new_full_sysize = vp_playfield->height;
8158 int new_dx = vp_door_1->x;
8159 int new_dy = vp_door_1->y;
8160 int new_dxsize = vp_door_1->width;
8161 int new_dysize = vp_door_1->height;
8162 int new_vx = vp_door_2->x;
8163 int new_vy = vp_door_2->y;
8164 int new_vxsize = vp_door_2->width;
8165 int new_vysize = vp_door_2->height;
8166 int new_ex = vp_door_3->x;
8167 int new_ey = vp_door_3->y;
8168 int new_exsize = vp_door_3->width;
8169 int new_eysize = vp_door_3->height;
8170 int new_tilesize_var =
8171 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8173 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8174 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8175 int new_scr_fieldx = new_sxsize / tilesize;
8176 int new_scr_fieldy = new_sysize / tilesize;
8177 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8178 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8179 boolean init_gfx_buffers = FALSE;
8180 boolean init_video_buffer = FALSE;
8181 boolean init_gadgets_and_toons = FALSE;
8182 boolean init_em_graphics = FALSE;
8184 if (new_win_xsize != WIN_XSIZE ||
8185 new_win_ysize != WIN_YSIZE)
8187 WIN_XSIZE = new_win_xsize;
8188 WIN_YSIZE = new_win_ysize;
8190 init_video_buffer = TRUE;
8191 init_gfx_buffers = TRUE;
8193 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8196 if (new_scr_fieldx != SCR_FIELDX ||
8197 new_scr_fieldy != SCR_FIELDY)
8199 /* this always toggles between MAIN and GAME when using small tile size */
8201 SCR_FIELDX = new_scr_fieldx;
8202 SCR_FIELDY = new_scr_fieldy;
8204 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8215 new_sxsize != SXSIZE ||
8216 new_sysize != SYSIZE ||
8217 new_dxsize != DXSIZE ||
8218 new_dysize != DYSIZE ||
8219 new_vxsize != VXSIZE ||
8220 new_vysize != VYSIZE ||
8221 new_exsize != EXSIZE ||
8222 new_eysize != EYSIZE ||
8223 new_real_sx != REAL_SX ||
8224 new_real_sy != REAL_SY ||
8225 new_full_sxsize != FULL_SXSIZE ||
8226 new_full_sysize != FULL_SYSIZE ||
8227 new_tilesize_var != TILESIZE_VAR
8230 // ------------------------------------------------------------------------
8231 // determine next fading area for changed viewport definitions
8232 // ------------------------------------------------------------------------
8234 // start with current playfield area (default fading area)
8237 FADE_SXSIZE = FULL_SXSIZE;
8238 FADE_SYSIZE = FULL_SYSIZE;
8240 // add new playfield area if position or size has changed
8241 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8242 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8244 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8245 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8248 // add current and new door 1 area if position or size has changed
8249 if (new_dx != DX || new_dy != DY ||
8250 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8252 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8253 DX, DY, DXSIZE, DYSIZE);
8254 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8255 new_dx, new_dy, new_dxsize, new_dysize);
8258 // add current and new door 2 area if position or size has changed
8259 if (new_dx != VX || new_dy != VY ||
8260 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8262 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8263 VX, VY, VXSIZE, VYSIZE);
8264 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8265 new_vx, new_vy, new_vxsize, new_vysize);
8268 // ------------------------------------------------------------------------
8269 // handle changed tile size
8270 // ------------------------------------------------------------------------
8272 if (new_tilesize_var != TILESIZE_VAR)
8274 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8276 // changing tile size invalidates scroll values of engine snapshots
8277 FreeEngineSnapshotSingle();
8279 // changing tile size requires update of graphic mapping for EM engine
8280 init_em_graphics = TRUE;
8291 SXSIZE = new_sxsize;
8292 SYSIZE = new_sysize;
8293 DXSIZE = new_dxsize;
8294 DYSIZE = new_dysize;
8295 VXSIZE = new_vxsize;
8296 VYSIZE = new_vysize;
8297 EXSIZE = new_exsize;
8298 EYSIZE = new_eysize;
8299 REAL_SX = new_real_sx;
8300 REAL_SY = new_real_sy;
8301 FULL_SXSIZE = new_full_sxsize;
8302 FULL_SYSIZE = new_full_sysize;
8303 TILESIZE_VAR = new_tilesize_var;
8305 init_gfx_buffers = TRUE;
8306 init_gadgets_and_toons = TRUE;
8308 // printf("::: viewports: init_gfx_buffers\n");
8309 // printf("::: viewports: init_gadgets_and_toons\n");
8312 if (init_gfx_buffers)
8314 // printf("::: init_gfx_buffers\n");
8316 SCR_FIELDX = new_scr_fieldx_buffers;
8317 SCR_FIELDY = new_scr_fieldy_buffers;
8321 SCR_FIELDX = new_scr_fieldx;
8322 SCR_FIELDY = new_scr_fieldy;
8324 SetDrawDeactivationMask(REDRAW_NONE);
8325 SetDrawBackgroundMask(REDRAW_FIELD);
8328 if (init_video_buffer)
8330 // printf("::: init_video_buffer\n");
8332 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8333 InitImageTextures();
8336 if (init_gadgets_and_toons)
8338 // printf("::: init_gadgets_and_toons\n");
8344 if (init_em_graphics)
8346 InitGraphicInfo_EM();