1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_DOOR_1_GFX_PART_1,
74 IMG_DOOR_1_GFX_PART_2,
79 IMG_DOOR_1_GFX_PART_3,
84 IMG_DOOR_1_GFX_PART_4,
89 IMG_DOOR_1_GFX_PART_5,
94 IMG_DOOR_1_GFX_PART_6,
99 IMG_DOOR_1_GFX_PART_7,
104 IMG_DOOR_1_GFX_PART_8,
110 IMG_DOOR_2_GFX_PART_1,
115 IMG_DOOR_2_GFX_PART_2,
120 IMG_DOOR_2_GFX_PART_3,
125 IMG_DOOR_2_GFX_PART_4,
130 IMG_DOOR_2_GFX_PART_5,
135 IMG_DOOR_2_GFX_PART_6,
140 IMG_DOOR_2_GFX_PART_7,
145 IMG_DOOR_2_GFX_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static unsigned int sync_frame_delay = 0;
178 static unsigned int sync_frame_delay_value = GAME_FRAME_DELAY;
180 static char *print_if_not_empty(int element)
182 static char *s = NULL;
183 char *token_name = element_info[element].token_name;
188 s = checked_malloc(strlen(token_name) + 10 + 1);
190 if (element != EL_EMPTY)
191 sprintf(s, "%d\t['%s']", element, token_name);
193 sprintf(s, "%d", element);
198 void DumpTile(int x, int y)
203 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
209 printf_line("-", 79);
210 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
211 printf_line("-", 79);
213 if (!IN_LEV_FIELD(x, y))
215 printf("(not in level field)\n");
221 printf(" Feld: %d\t['%s']\n", Feld[x][y],
222 element_info[Feld[x][y]].token_name);
223 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
224 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
225 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
226 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
227 printf(" MovPos: %d\n", MovPos[x][y]);
228 printf(" MovDir: %d\n", MovDir[x][y]);
229 printf(" MovDelay: %d\n", MovDelay[x][y]);
230 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
231 printf(" CustomValue: %d\n", CustomValue[x][y]);
232 printf(" GfxElement: %d\n", GfxElement[x][y]);
233 printf(" GfxAction: %d\n", GfxAction[x][y]);
234 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
238 void SetDrawtoField(int mode)
240 if (mode == DRAW_FIELDBUFFER)
246 BX2 = SCR_FIELDX + 1;
247 BY2 = SCR_FIELDY + 1;
249 drawto_field = fieldbuffer;
251 else /* DRAW_BACKBUFFER */
257 BX2 = SCR_FIELDX - 1;
258 BY2 = SCR_FIELDY - 1;
260 drawto_field = backbuffer;
264 static void RedrawPlayfield_RND()
266 if (game.envelope_active)
269 DrawLevel(REDRAW_ALL);
273 void RedrawPlayfield()
275 if (game_status != GAME_MODE_PLAYING)
278 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
279 RedrawPlayfield_EM(TRUE);
280 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
281 RedrawPlayfield_SP(TRUE);
282 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
283 RedrawPlayfield_RND();
285 BlitScreenToBitmap(backbuffer);
287 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
291 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
292 boolean blit_to_screen)
294 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
297 BlitToScreenMasked(bitmap, x, y, width, height, x, y);
299 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
302 static void DrawMaskedBorderExt_FIELD(boolean blit_to_screen)
304 if (global.border_status >= GAME_MODE_TITLE &&
305 global.border_status <= GAME_MODE_PLAYING &&
306 border.draw_masked[global.border_status])
307 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
311 static void DrawMaskedBorderExt_DOOR_1(boolean blit_to_screen)
313 // only draw border over closed doors when drawing to backbuffer
314 if (!blit_to_screen && (GetDoorState() & DOOR_OPEN_1))
317 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
318 (global.border_status != GAME_MODE_EDITOR ||
319 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
320 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, blit_to_screen);
323 static void DrawMaskedBorderExt_DOOR_2(boolean blit_to_screen)
325 // only draw border over closed doors when drawing to backbuffer
326 if (!blit_to_screen && (GetDoorState() & DOOR_OPEN_2))
329 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
330 global.border_status != GAME_MODE_EDITOR)
331 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, blit_to_screen);
334 static void DrawMaskedBorderExt_DOOR_3(boolean blit_to_screen)
336 /* currently not available */
339 static void DrawMaskedBorderExt_ALL(boolean blit_to_screen)
341 DrawMaskedBorderExt_FIELD(blit_to_screen);
342 DrawMaskedBorderExt_DOOR_1(blit_to_screen);
343 DrawMaskedBorderExt_DOOR_2(blit_to_screen);
344 DrawMaskedBorderExt_DOOR_3(blit_to_screen);
347 static void DrawMaskedBorderExt(int redraw_mask, boolean blit_to_screen)
349 /* never draw masked screen borders on borderless screens */
350 if (game_status == GAME_MODE_LOADING ||
351 game_status == GAME_MODE_TITLE)
354 if (redraw_mask & REDRAW_ALL)
355 DrawMaskedBorderExt_ALL(blit_to_screen);
358 if (redraw_mask & REDRAW_FIELD)
359 DrawMaskedBorderExt_FIELD(blit_to_screen);
360 if (redraw_mask & REDRAW_DOOR_1)
361 DrawMaskedBorderExt_DOOR_1(blit_to_screen);
362 if (redraw_mask & REDRAW_DOOR_2)
363 DrawMaskedBorderExt_DOOR_2(blit_to_screen);
364 if (redraw_mask & REDRAW_DOOR_3)
365 DrawMaskedBorderExt_DOOR_3(blit_to_screen);
369 void DrawMaskedBorder_FIELD()
371 DrawMaskedBorderExt_FIELD(FALSE);
374 void DrawMaskedBorder(int redraw_mask)
376 DrawMaskedBorderExt(redraw_mask, FALSE);
379 void DrawMaskedBorderToScreen(int redraw_mask)
381 DrawMaskedBorderExt(redraw_mask, TRUE);
384 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
386 int fx = FX, fy = FY;
387 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
388 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
390 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
391 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
392 int dx_var = dx * TILESIZE_VAR / TILESIZE;
393 int dy_var = dy * TILESIZE_VAR / TILESIZE;
396 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
397 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
399 if (EVEN(SCR_FIELDX))
401 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
402 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
404 fx += (dx_var > 0 ? TILEX_VAR : 0);
411 if (EVEN(SCR_FIELDY))
413 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
414 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
416 fy += (dy_var > 0 ? TILEY_VAR : 0);
423 if (full_lev_fieldx <= SCR_FIELDX)
425 if (EVEN(SCR_FIELDX))
426 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
428 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
431 if (full_lev_fieldy <= SCR_FIELDY)
433 if (EVEN(SCR_FIELDY))
434 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
436 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
439 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
442 void BlitScreenToBitmap(Bitmap *target_bitmap)
444 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
445 BlitScreenToBitmap_EM(target_bitmap);
446 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
447 BlitScreenToBitmap_SP(target_bitmap);
448 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
449 BlitScreenToBitmap_RND(target_bitmap);
451 redraw_mask |= REDRAW_FIELD;
454 void DrawFramesPerSecond()
457 int font_nr = FONT_TEXT_2;
458 int font_width = getFontWidth(font_nr);
460 sprintf(text, "%04.1f fps", global.frames_per_second);
462 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
463 font_nr, BLIT_OPAQUE);
467 static void PrintFrameTimeDebugging()
469 static unsigned int last_counter = 0;
470 unsigned int counter = Counter();
471 int diff_1 = counter - last_counter;
472 int diff_2 = diff_1 - GAME_FRAME_DELAY;
474 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
475 char diff_bar[2 * diff_2_max + 5];
479 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
481 for (i = 0; i < diff_2_max; i++)
482 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
483 i >= diff_2_max - diff_2_cut ? '-' : ' ');
485 diff_bar[pos++] = '|';
487 for (i = 0; i < diff_2_max; i++)
488 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
490 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
492 diff_bar[pos++] = '\0';
494 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
497 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
500 last_counter = counter;
506 if (redraw_mask == REDRAW_NONE)
510 // masked border now drawn immediately when blitting backbuffer to window
512 // draw masked border to all viewports, if defined
513 DrawMaskedBorder(redraw_mask);
516 // draw frames per second (only if debug mode is enabled)
517 if (redraw_mask & REDRAW_FPS)
518 DrawFramesPerSecond();
520 // redraw complete window if both playfield and (some) doors need redraw
521 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
522 redraw_mask = REDRAW_ALL;
524 /* although redrawing the whole window would be fine for normal gameplay,
525 being able to only redraw the playfield is required for deactivating
526 certain drawing areas (mainly playfield) to work, which is needed for
527 warp-forward to be fast enough (by skipping redraw of most frames) */
529 if (redraw_mask & REDRAW_ALL)
531 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
533 else if (redraw_mask & REDRAW_FIELD)
535 BlitBitmap(backbuffer, window,
536 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
538 else if (redraw_mask & REDRAW_DOORS)
540 if (redraw_mask & REDRAW_DOOR_1)
541 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
543 if (redraw_mask & REDRAW_DOOR_2)
544 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
546 if (redraw_mask & REDRAW_DOOR_3)
547 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
550 redraw_mask = REDRAW_NONE;
552 // force screen redraw in every frame to continue drawing global animations
553 redraw_mask = REDRAW_FIELD;
556 PrintFrameTimeDebugging();
560 static void FadeCrossSaveBackbuffer()
562 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
565 static void FadeCrossRestoreBackbuffer()
567 int redraw_mask_last = redraw_mask;
569 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
571 // do not change redraw mask when restoring backbuffer after cross-fading
572 redraw_mask = redraw_mask_last;
575 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
577 static int fade_type_skip = FADE_TYPE_NONE;
578 void (*draw_border_function)(void) = NULL;
579 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
580 int x, y, width, height;
581 int fade_delay, post_delay;
583 if (fade_type == FADE_TYPE_FADE_OUT)
585 if (fade_type_skip != FADE_TYPE_NONE)
587 /* skip all fade operations until specified fade operation */
588 if (fade_type & fade_type_skip)
589 fade_type_skip = FADE_TYPE_NONE;
595 FadeCrossSaveBackbuffer();
598 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
601 FadeCrossSaveBackbuffer();
608 redraw_mask |= fade_mask;
610 if (fade_type == FADE_TYPE_SKIP)
612 fade_type_skip = fade_mode;
617 fade_delay = fading.fade_delay;
618 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
620 if (fade_type_skip != FADE_TYPE_NONE)
622 /* skip all fade operations until specified fade operation */
623 if (fade_type & fade_type_skip)
624 fade_type_skip = FADE_TYPE_NONE;
629 if (global.autoplay_leveldir)
634 if (fade_mask == REDRAW_FIELD)
639 height = FADE_SYSIZE;
641 if (border.draw_masked_when_fading)
642 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
644 DrawMaskedBorder_FIELD(); /* draw once */
646 else /* REDRAW_ALL */
654 if (!setup.fade_screens ||
656 fading.fade_mode == FADE_MODE_NONE)
658 if (fade_mode == FADE_MODE_FADE_OUT)
661 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
663 redraw_mask &= ~fade_mask;
668 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
669 draw_border_function);
671 if (fade_type == FADE_TYPE_FADE_OUT)
672 FadeCrossRestoreBackbuffer();
674 redraw_mask &= ~fade_mask;
677 static void SetAnimStatus_BeforeFadingOut()
679 global.anim_status = GAME_MODE_PSEUDO_FADING;
682 static void SetAnimStatus_AfterFadingIn()
684 global.anim_status = global.anim_status_next;
686 // force update of global animation status in case of rapid screen changes
687 redraw_mask = REDRAW_ALL;
691 void FadeIn(int fade_mask)
694 DrawMaskedBorder(REDRAW_ALL);
697 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
698 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
700 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
704 FADE_SXSIZE = FULL_SXSIZE;
705 FADE_SYSIZE = FULL_SYSIZE;
707 SetAnimStatus_AfterFadingIn();
710 void FadeOut(int fade_mask)
712 SetAnimStatus_BeforeFadingOut();
715 DrawMaskedBorder(REDRAW_ALL);
718 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
719 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
721 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
723 global.border_status = game_status;
726 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
728 static struct TitleFadingInfo fading_leave_stored;
731 fading_leave_stored = fading_leave;
733 fading = fading_leave_stored;
736 void FadeSetEnterMenu()
738 fading = menu.enter_menu;
740 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
743 void FadeSetLeaveMenu()
745 fading = menu.leave_menu;
747 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
750 void FadeSetEnterScreen()
752 fading = menu.enter_screen[game_status];
754 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
757 void FadeSetNextScreen()
759 fading = menu.next_screen[game_status];
761 // (do not overwrite fade mode set by FadeSetEnterScreen)
762 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
765 void FadeSetLeaveScreen()
767 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
770 void FadeSetFromType(int type)
772 if (type & TYPE_ENTER_SCREEN)
773 FadeSetEnterScreen();
774 else if (type & TYPE_ENTER)
776 else if (type & TYPE_LEAVE)
780 void FadeSetDisabled()
782 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
784 fading = fading_none;
787 void FadeSkipNextFadeIn()
789 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
792 void FadeSkipNextFadeOut()
794 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
797 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
799 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
801 return (graphic == IMG_UNDEFINED ? NULL :
802 graphic_info[graphic].bitmap != NULL || redefined ?
803 graphic_info[graphic].bitmap :
804 graphic_info[default_graphic].bitmap);
807 Bitmap *getBackgroundBitmap(int graphic)
809 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
812 Bitmap *getGlobalBorderBitmap(int graphic)
814 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
817 Bitmap *getGlobalBorderBitmapFromGameStatus()
820 (game_status == GAME_MODE_MAIN ||
821 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
822 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
823 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
824 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
827 return getGlobalBorderBitmap(graphic);
830 void SetWindowBackgroundImageIfDefined(int graphic)
832 if (graphic_info[graphic].bitmap)
833 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
836 void SetMainBackgroundImageIfDefined(int graphic)
838 if (graphic_info[graphic].bitmap)
839 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
842 void SetDoorBackgroundImageIfDefined(int graphic)
844 if (graphic_info[graphic].bitmap)
845 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
848 void SetWindowBackgroundImage(int graphic)
850 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
853 void SetMainBackgroundImage(int graphic)
855 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
858 void SetDoorBackgroundImage(int graphic)
860 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
863 void SetPanelBackground()
865 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
867 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
868 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
870 SetDoorBackgroundBitmap(bitmap_db_panel);
873 void DrawBackground(int x, int y, int width, int height)
875 /* "drawto" might still point to playfield buffer here (hall of fame) */
876 ClearRectangleOnBackground(backbuffer, x, y, width, height);
878 if (IN_GFX_FIELD_FULL(x, y))
879 redraw_mask |= REDRAW_FIELD;
880 else if (IN_GFX_DOOR_1(x, y))
881 redraw_mask |= REDRAW_DOOR_1;
882 else if (IN_GFX_DOOR_2(x, y))
883 redraw_mask |= REDRAW_DOOR_2;
884 else if (IN_GFX_DOOR_3(x, y))
885 redraw_mask |= REDRAW_DOOR_3;
888 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
890 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
892 if (font->bitmap == NULL)
895 DrawBackground(x, y, width, height);
898 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
900 struct GraphicInfo *g = &graphic_info[graphic];
902 if (g->bitmap == NULL)
905 DrawBackground(x, y, width, height);
908 static int game_status_last = -1;
909 static Bitmap *global_border_bitmap_last = NULL;
910 static Bitmap *global_border_bitmap = NULL;
911 static int real_sx_last = -1, real_sy_last = -1;
912 static int full_sxsize_last = -1, full_sysize_last = -1;
913 static int dx_last = -1, dy_last = -1;
914 static int dxsize_last = -1, dysize_last = -1;
915 static int vx_last = -1, vy_last = -1;
916 static int vxsize_last = -1, vysize_last = -1;
918 boolean CheckIfGlobalBorderHasChanged()
920 // if game status has not changed, global border has not changed either
921 if (game_status == game_status_last)
924 // determine and store new global border bitmap for current game status
925 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
927 return (global_border_bitmap_last != global_border_bitmap);
930 boolean CheckIfGlobalBorderRedrawIsNeeded()
932 // if game status has not changed, nothing has to be redrawn
933 if (game_status == game_status_last)
936 // redraw if last screen was title screen
937 if (game_status_last == GAME_MODE_TITLE)
940 // redraw if global screen border has changed
941 if (CheckIfGlobalBorderHasChanged())
944 // redraw if position or size of playfield area has changed
945 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
946 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
949 // redraw if position or size of door area has changed
950 if (dx_last != DX || dy_last != DY ||
951 dxsize_last != DXSIZE || dysize_last != DYSIZE)
954 // redraw if position or size of tape area has changed
955 if (vx_last != VX || vy_last != VY ||
956 vxsize_last != VXSIZE || vysize_last != VYSIZE)
962 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
965 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
967 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
970 void RedrawGlobalBorder()
972 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
974 RedrawGlobalBorderFromBitmap(bitmap);
976 redraw_mask = REDRAW_ALL;
979 static void RedrawGlobalBorderIfNeeded()
981 if (game_status == game_status_last)
984 // copy current draw buffer to later copy back areas that have not changed
985 if (game_status_last != GAME_MODE_TITLE)
986 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
988 if (CheckIfGlobalBorderRedrawIsNeeded())
990 // redraw global screen border (or clear, if defined to be empty)
991 RedrawGlobalBorderFromBitmap(global_border_bitmap);
993 // copy previous playfield and door areas, if they are defined on both
994 // previous and current screen and if they still have the same size
996 if (real_sx_last != -1 && real_sy_last != -1 &&
997 REAL_SX != -1 && REAL_SY != -1 &&
998 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
999 BlitBitmap(bitmap_db_store, backbuffer,
1000 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1003 if (dx_last != -1 && dy_last != -1 &&
1004 DX != -1 && DY != -1 &&
1005 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1006 BlitBitmap(bitmap_db_store, backbuffer,
1007 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1009 if (vx_last != -1 && vy_last != -1 &&
1010 VX != -1 && VY != -1 &&
1011 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1012 BlitBitmap(bitmap_db_store, backbuffer,
1013 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1015 redraw_mask = REDRAW_ALL;
1018 game_status_last = game_status;
1020 global_border_bitmap_last = global_border_bitmap;
1022 real_sx_last = REAL_SX;
1023 real_sy_last = REAL_SY;
1024 full_sxsize_last = FULL_SXSIZE;
1025 full_sysize_last = FULL_SYSIZE;
1028 dxsize_last = DXSIZE;
1029 dysize_last = DYSIZE;
1032 vxsize_last = VXSIZE;
1033 vysize_last = VYSIZE;
1038 RedrawGlobalBorderIfNeeded();
1040 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1041 /* (when entering hall of fame after playing) */
1042 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1044 /* !!! maybe this should be done before clearing the background !!! */
1045 if (game_status == GAME_MODE_PLAYING)
1047 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1048 SetDrawtoField(DRAW_FIELDBUFFER);
1052 SetDrawtoField(DRAW_BACKBUFFER);
1056 void MarkTileDirty(int x, int y)
1058 redraw_mask |= REDRAW_FIELD;
1061 void SetBorderElement()
1065 BorderElement = EL_EMPTY;
1067 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1069 for (x = 0; x < lev_fieldx; x++)
1071 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1072 BorderElement = EL_STEELWALL;
1074 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1080 void FloodFillLevel(int from_x, int from_y, int fill_element,
1081 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1082 int max_fieldx, int max_fieldy)
1086 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1087 static int safety = 0;
1089 /* check if starting field still has the desired content */
1090 if (field[from_x][from_y] == fill_element)
1095 if (safety > max_fieldx * max_fieldy)
1096 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1098 old_element = field[from_x][from_y];
1099 field[from_x][from_y] = fill_element;
1101 for (i = 0; i < 4; i++)
1103 x = from_x + check[i][0];
1104 y = from_y + check[i][1];
1106 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1107 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1113 void SetRandomAnimationValue(int x, int y)
1115 gfx.anim_random_frame = GfxRandom[x][y];
1118 int getGraphicAnimationFrame(int graphic, int sync_frame)
1120 /* animation synchronized with global frame counter, not move position */
1121 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1122 sync_frame = FrameCounter;
1124 return getAnimationFrame(graphic_info[graphic].anim_frames,
1125 graphic_info[graphic].anim_delay,
1126 graphic_info[graphic].anim_mode,
1127 graphic_info[graphic].anim_start_frame,
1131 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1132 Bitmap **bitmap, int *x, int *y,
1133 boolean get_backside)
1135 struct GraphicInfo *g = &graphic_info[graphic];
1136 Bitmap *src_bitmap = g->bitmap;
1137 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1138 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1139 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1141 // if no in-game graphics defined, always use standard graphic size
1142 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1143 tilesize = TILESIZE;
1145 if (tilesize == gfx.standard_tile_size)
1146 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1147 else if (tilesize == game.tile_size)
1148 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1150 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1152 if (g->offset_y == 0) /* frames are ordered horizontally */
1154 int max_width = g->anim_frames_per_line * g->width;
1155 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1157 src_x = pos % max_width;
1158 src_y = src_y % g->height + pos / max_width * g->height;
1160 else if (g->offset_x == 0) /* frames are ordered vertically */
1162 int max_height = g->anim_frames_per_line * g->height;
1163 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1165 src_x = src_x % g->width + pos / max_height * g->width;
1166 src_y = pos % max_height;
1168 else /* frames are ordered diagonally */
1170 src_x = src_x + frame * g->offset_x;
1171 src_y = src_y + frame * g->offset_y;
1174 *bitmap = src_bitmap;
1175 *x = src_x * tilesize / g->tile_size;
1176 *y = src_y * tilesize / g->tile_size;
1179 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1180 int *x, int *y, boolean get_backside)
1182 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1186 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1187 Bitmap **bitmap, int *x, int *y)
1189 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1192 void getFixedGraphicSource(int graphic, int frame,
1193 Bitmap **bitmap, int *x, int *y)
1195 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1198 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1200 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1203 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1204 int *x, int *y, boolean get_backside)
1206 struct GraphicInfo *g = &graphic_info[graphic];
1207 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1208 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1210 if (TILESIZE_VAR != TILESIZE)
1211 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1214 *bitmap = g->bitmap;
1216 if (g->offset_y == 0) /* frames are ordered horizontally */
1218 int max_width = g->anim_frames_per_line * g->width;
1219 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1221 *x = pos % max_width;
1222 *y = src_y % g->height + pos / max_width * g->height;
1224 else if (g->offset_x == 0) /* frames are ordered vertically */
1226 int max_height = g->anim_frames_per_line * g->height;
1227 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1229 *x = src_x % g->width + pos / max_height * g->width;
1230 *y = pos % max_height;
1232 else /* frames are ordered diagonally */
1234 *x = src_x + frame * g->offset_x;
1235 *y = src_y + frame * g->offset_y;
1238 *x = *x * TILESIZE_VAR / g->tile_size;
1239 *y = *y * TILESIZE_VAR / g->tile_size;
1242 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1244 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1247 void DrawGraphic(int x, int y, int graphic, int frame)
1250 if (!IN_SCR_FIELD(x, y))
1252 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1253 printf("DrawGraphic(): This should never happen!\n");
1258 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1261 MarkTileDirty(x, y);
1264 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1267 if (!IN_SCR_FIELD(x, y))
1269 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1270 printf("DrawGraphic(): This should never happen!\n");
1275 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1277 MarkTileDirty(x, y);
1280 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1286 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1288 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1291 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1297 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1298 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1301 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1304 if (!IN_SCR_FIELD(x, y))
1306 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1307 printf("DrawGraphicThruMask(): This should never happen!\n");
1312 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1315 MarkTileDirty(x, y);
1318 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1321 if (!IN_SCR_FIELD(x, y))
1323 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1324 printf("DrawGraphicThruMask(): This should never happen!\n");
1329 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1331 MarkTileDirty(x, y);
1334 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1340 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1342 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1346 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1347 int graphic, int frame)
1352 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1354 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1358 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1360 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1362 MarkTileDirty(x / tilesize, y / tilesize);
1365 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1371 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1372 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1375 void DrawMiniGraphic(int x, int y, int graphic)
1377 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1378 MarkTileDirty(x / 2, y / 2);
1381 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1386 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1387 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1390 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1391 int graphic, int frame,
1392 int cut_mode, int mask_mode)
1397 int width = TILEX, height = TILEY;
1400 if (dx || dy) /* shifted graphic */
1402 if (x < BX1) /* object enters playfield from the left */
1409 else if (x > BX2) /* object enters playfield from the right */
1415 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1421 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1423 else if (dx) /* general horizontal movement */
1424 MarkTileDirty(x + SIGN(dx), y);
1426 if (y < BY1) /* object enters playfield from the top */
1428 if (cut_mode == CUT_BELOW) /* object completely above top border */
1436 else if (y > BY2) /* object enters playfield from the bottom */
1442 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1448 else if (dy > 0 && cut_mode == CUT_ABOVE)
1450 if (y == BY2) /* object completely above bottom border */
1456 MarkTileDirty(x, y + 1);
1457 } /* object leaves playfield to the bottom */
1458 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1460 else if (dy) /* general vertical movement */
1461 MarkTileDirty(x, y + SIGN(dy));
1465 if (!IN_SCR_FIELD(x, y))
1467 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1468 printf("DrawGraphicShifted(): This should never happen!\n");
1473 width = width * TILESIZE_VAR / TILESIZE;
1474 height = height * TILESIZE_VAR / TILESIZE;
1475 cx = cx * TILESIZE_VAR / TILESIZE;
1476 cy = cy * TILESIZE_VAR / TILESIZE;
1477 dx = dx * TILESIZE_VAR / TILESIZE;
1478 dy = dy * TILESIZE_VAR / TILESIZE;
1480 if (width > 0 && height > 0)
1482 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1487 dst_x = FX + x * TILEX_VAR + dx;
1488 dst_y = FY + y * TILEY_VAR + dy;
1490 if (mask_mode == USE_MASKING)
1491 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1494 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1497 MarkTileDirty(x, y);
1501 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1502 int graphic, int frame,
1503 int cut_mode, int mask_mode)
1508 int width = TILEX_VAR, height = TILEY_VAR;
1511 int x2 = x + SIGN(dx);
1512 int y2 = y + SIGN(dy);
1514 /* movement with two-tile animations must be sync'ed with movement position,
1515 not with current GfxFrame (which can be higher when using slow movement) */
1516 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1517 int anim_frames = graphic_info[graphic].anim_frames;
1519 /* (we also need anim_delay here for movement animations with less frames) */
1520 int anim_delay = graphic_info[graphic].anim_delay;
1521 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1523 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1524 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1526 /* re-calculate animation frame for two-tile movement animation */
1527 frame = getGraphicAnimationFrame(graphic, sync_frame);
1529 /* check if movement start graphic inside screen area and should be drawn */
1530 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1532 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1534 dst_x = FX + x1 * TILEX_VAR;
1535 dst_y = FY + y1 * TILEY_VAR;
1537 if (mask_mode == USE_MASKING)
1538 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1541 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1544 MarkTileDirty(x1, y1);
1547 /* check if movement end graphic inside screen area and should be drawn */
1548 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1550 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1552 dst_x = FX + x2 * TILEX_VAR;
1553 dst_y = FY + y2 * TILEY_VAR;
1555 if (mask_mode == USE_MASKING)
1556 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1559 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1562 MarkTileDirty(x2, y2);
1566 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1567 int graphic, int frame,
1568 int cut_mode, int mask_mode)
1572 DrawGraphic(x, y, graphic, frame);
1577 if (graphic_info[graphic].double_movement) /* EM style movement images */
1578 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1580 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1583 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1584 int frame, int cut_mode)
1586 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1589 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1590 int cut_mode, int mask_mode)
1592 int lx = LEVELX(x), ly = LEVELY(y);
1596 if (IN_LEV_FIELD(lx, ly))
1598 SetRandomAnimationValue(lx, ly);
1600 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1601 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1603 /* do not use double (EM style) movement graphic when not moving */
1604 if (graphic_info[graphic].double_movement && !dx && !dy)
1606 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1607 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1610 else /* border element */
1612 graphic = el2img(element);
1613 frame = getGraphicAnimationFrame(graphic, -1);
1616 if (element == EL_EXPANDABLE_WALL)
1618 boolean left_stopped = FALSE, right_stopped = FALSE;
1620 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1621 left_stopped = TRUE;
1622 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1623 right_stopped = TRUE;
1625 if (left_stopped && right_stopped)
1627 else if (left_stopped)
1629 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1630 frame = graphic_info[graphic].anim_frames - 1;
1632 else if (right_stopped)
1634 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1635 frame = graphic_info[graphic].anim_frames - 1;
1640 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1641 else if (mask_mode == USE_MASKING)
1642 DrawGraphicThruMask(x, y, graphic, frame);
1644 DrawGraphic(x, y, graphic, frame);
1647 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1648 int cut_mode, int mask_mode)
1650 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1651 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1652 cut_mode, mask_mode);
1655 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1658 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1661 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1664 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1667 void DrawLevelElementThruMask(int x, int y, int element)
1669 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1672 void DrawLevelFieldThruMask(int x, int y)
1674 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1677 /* !!! implementation of quicksand is totally broken !!! */
1678 #define IS_CRUMBLED_TILE(x, y, e) \
1679 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1680 !IS_MOVING(x, y) || \
1681 (e) == EL_QUICKSAND_EMPTYING || \
1682 (e) == EL_QUICKSAND_FAST_EMPTYING))
1684 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1689 int width, height, cx, cy;
1690 int sx = SCREENX(x), sy = SCREENY(y);
1691 int crumbled_border_size = graphic_info[graphic].border_size;
1694 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1696 for (i = 1; i < 4; i++)
1698 int dxx = (i & 1 ? dx : 0);
1699 int dyy = (i & 2 ? dy : 0);
1702 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1705 /* check if neighbour field is of same crumble type */
1706 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1707 graphic_info[graphic].class ==
1708 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1710 /* return if check prevents inner corner */
1711 if (same == (dxx == dx && dyy == dy))
1715 /* if we reach this point, we have an inner corner */
1717 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1719 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1720 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1721 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1722 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1724 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1725 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1728 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1733 int width, height, bx, by, cx, cy;
1734 int sx = SCREENX(x), sy = SCREENY(y);
1735 int crumbled_border_size = graphic_info[graphic].border_size;
1736 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1737 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1740 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1742 /* draw simple, sloppy, non-corner-accurate crumbled border */
1744 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1745 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1746 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1747 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1749 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1750 FX + sx * TILEX_VAR + cx,
1751 FY + sy * TILEY_VAR + cy);
1753 /* (remaining middle border part must be at least as big as corner part) */
1754 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1755 crumbled_border_size >= TILESIZE / 3)
1758 /* correct corners of crumbled border, if needed */
1760 for (i = -1; i <= 1; i += 2)
1762 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1763 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1764 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1767 /* check if neighbour field is of same crumble type */
1768 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1769 graphic_info[graphic].class ==
1770 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1772 /* no crumbled corner, but continued crumbled border */
1774 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1775 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1776 int b1 = (i == 1 ? crumbled_border_size_var :
1777 TILESIZE_VAR - 2 * crumbled_border_size_var);
1779 width = crumbled_border_size_var;
1780 height = crumbled_border_size_var;
1782 if (dir == 1 || dir == 2)
1797 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1799 FX + sx * TILEX_VAR + cx,
1800 FY + sy * TILEY_VAR + cy);
1805 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1807 int sx = SCREENX(x), sy = SCREENY(y);
1810 static int xy[4][2] =
1818 if (!IN_LEV_FIELD(x, y))
1821 element = TILE_GFX_ELEMENT(x, y);
1823 /* crumble field itself */
1824 if (IS_CRUMBLED_TILE(x, y, element))
1826 if (!IN_SCR_FIELD(sx, sy))
1829 for (i = 0; i < 4; i++)
1831 int xx = x + xy[i][0];
1832 int yy = y + xy[i][1];
1834 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1837 /* check if neighbour field is of same crumble type */
1838 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1839 graphic_info[graphic].class ==
1840 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1843 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1846 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1847 graphic_info[graphic].anim_frames == 2)
1849 for (i = 0; i < 4; i++)
1851 int dx = (i & 1 ? +1 : -1);
1852 int dy = (i & 2 ? +1 : -1);
1854 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1858 MarkTileDirty(sx, sy);
1860 else /* center field not crumbled -- crumble neighbour fields */
1862 for (i = 0; i < 4; i++)
1864 int xx = x + xy[i][0];
1865 int yy = y + xy[i][1];
1866 int sxx = sx + xy[i][0];
1867 int syy = sy + xy[i][1];
1869 if (!IN_LEV_FIELD(xx, yy) ||
1870 !IN_SCR_FIELD(sxx, syy))
1873 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1876 element = TILE_GFX_ELEMENT(xx, yy);
1878 if (!IS_CRUMBLED_TILE(xx, yy, element))
1881 graphic = el_act2crm(element, ACTION_DEFAULT);
1883 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1885 MarkTileDirty(sxx, syy);
1890 void DrawLevelFieldCrumbled(int x, int y)
1894 if (!IN_LEV_FIELD(x, y))
1897 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1898 GfxElement[x][y] != EL_UNDEFINED &&
1899 GFX_CRUMBLED(GfxElement[x][y]))
1901 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1906 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1908 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1911 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1914 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1915 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1916 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1917 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1918 int sx = SCREENX(x), sy = SCREENY(y);
1920 DrawGraphic(sx, sy, graphic1, frame1);
1921 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1924 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1926 int sx = SCREENX(x), sy = SCREENY(y);
1927 static int xy[4][2] =
1936 for (i = 0; i < 4; i++)
1938 int xx = x + xy[i][0];
1939 int yy = y + xy[i][1];
1940 int sxx = sx + xy[i][0];
1941 int syy = sy + xy[i][1];
1943 if (!IN_LEV_FIELD(xx, yy) ||
1944 !IN_SCR_FIELD(sxx, syy) ||
1945 !GFX_CRUMBLED(Feld[xx][yy]) ||
1949 DrawLevelField(xx, yy);
1953 static int getBorderElement(int x, int y)
1957 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1958 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1959 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1960 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1961 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1962 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1963 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1965 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1966 int steel_position = (x == -1 && y == -1 ? 0 :
1967 x == lev_fieldx && y == -1 ? 1 :
1968 x == -1 && y == lev_fieldy ? 2 :
1969 x == lev_fieldx && y == lev_fieldy ? 3 :
1970 x == -1 || x == lev_fieldx ? 4 :
1971 y == -1 || y == lev_fieldy ? 5 : 6);
1973 return border[steel_position][steel_type];
1976 void DrawScreenElement(int x, int y, int element)
1978 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1979 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1982 void DrawLevelElement(int x, int y, int element)
1984 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1985 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1988 void DrawScreenField(int x, int y)
1990 int lx = LEVELX(x), ly = LEVELY(y);
1991 int element, content;
1993 if (!IN_LEV_FIELD(lx, ly))
1995 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1998 element = getBorderElement(lx, ly);
2000 DrawScreenElement(x, y, element);
2005 element = Feld[lx][ly];
2006 content = Store[lx][ly];
2008 if (IS_MOVING(lx, ly))
2010 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2011 boolean cut_mode = NO_CUTTING;
2013 if (element == EL_QUICKSAND_EMPTYING ||
2014 element == EL_QUICKSAND_FAST_EMPTYING ||
2015 element == EL_MAGIC_WALL_EMPTYING ||
2016 element == EL_BD_MAGIC_WALL_EMPTYING ||
2017 element == EL_DC_MAGIC_WALL_EMPTYING ||
2018 element == EL_AMOEBA_DROPPING)
2019 cut_mode = CUT_ABOVE;
2020 else if (element == EL_QUICKSAND_FILLING ||
2021 element == EL_QUICKSAND_FAST_FILLING ||
2022 element == EL_MAGIC_WALL_FILLING ||
2023 element == EL_BD_MAGIC_WALL_FILLING ||
2024 element == EL_DC_MAGIC_WALL_FILLING)
2025 cut_mode = CUT_BELOW;
2027 if (cut_mode == CUT_ABOVE)
2028 DrawScreenElement(x, y, element);
2030 DrawScreenElement(x, y, EL_EMPTY);
2033 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2034 else if (cut_mode == NO_CUTTING)
2035 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2038 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2040 if (cut_mode == CUT_BELOW &&
2041 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2042 DrawLevelElement(lx, ly + 1, element);
2045 if (content == EL_ACID)
2047 int dir = MovDir[lx][ly];
2048 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2049 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2051 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2054 else if (IS_BLOCKED(lx, ly))
2059 boolean cut_mode = NO_CUTTING;
2060 int element_old, content_old;
2062 Blocked2Moving(lx, ly, &oldx, &oldy);
2065 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2066 MovDir[oldx][oldy] == MV_RIGHT);
2068 element_old = Feld[oldx][oldy];
2069 content_old = Store[oldx][oldy];
2071 if (element_old == EL_QUICKSAND_EMPTYING ||
2072 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2073 element_old == EL_MAGIC_WALL_EMPTYING ||
2074 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2075 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2076 element_old == EL_AMOEBA_DROPPING)
2077 cut_mode = CUT_ABOVE;
2079 DrawScreenElement(x, y, EL_EMPTY);
2082 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2084 else if (cut_mode == NO_CUTTING)
2085 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2088 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2091 else if (IS_DRAWABLE(element))
2092 DrawScreenElement(x, y, element);
2094 DrawScreenElement(x, y, EL_EMPTY);
2097 void DrawLevelField(int x, int y)
2099 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2100 DrawScreenField(SCREENX(x), SCREENY(y));
2101 else if (IS_MOVING(x, y))
2105 Moving2Blocked(x, y, &newx, &newy);
2106 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2107 DrawScreenField(SCREENX(newx), SCREENY(newy));
2109 else if (IS_BLOCKED(x, y))
2113 Blocked2Moving(x, y, &oldx, &oldy);
2114 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2115 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2119 void DrawSizedElement(int x, int y, int element, int tilesize)
2123 graphic = el2edimg(element);
2124 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2127 void DrawMiniElement(int x, int y, int element)
2131 graphic = el2edimg(element);
2132 DrawMiniGraphic(x, y, graphic);
2135 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2138 int x = sx + scroll_x, y = sy + scroll_y;
2140 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2141 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2142 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2143 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2145 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2148 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2150 int x = sx + scroll_x, y = sy + scroll_y;
2152 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2153 DrawMiniElement(sx, sy, EL_EMPTY);
2154 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2155 DrawMiniElement(sx, sy, Feld[x][y]);
2157 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2160 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2161 int x, int y, int xsize, int ysize,
2162 int tile_width, int tile_height)
2166 int dst_x = startx + x * tile_width;
2167 int dst_y = starty + y * tile_height;
2168 int width = graphic_info[graphic].width;
2169 int height = graphic_info[graphic].height;
2170 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2171 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2172 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2173 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2174 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2175 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2176 boolean draw_masked = graphic_info[graphic].draw_masked;
2178 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2180 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2182 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2186 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2187 inner_sx + (x - 1) * tile_width % inner_width);
2188 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2189 inner_sy + (y - 1) * tile_height % inner_height);
2192 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2195 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2199 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2200 int x, int y, int xsize, int ysize, int font_nr)
2202 int font_width = getFontWidth(font_nr);
2203 int font_height = getFontHeight(font_nr);
2205 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2206 font_width, font_height);
2209 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2211 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2212 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2213 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2214 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2215 boolean no_delay = (tape.warp_forward);
2216 unsigned int anim_delay = 0;
2217 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2218 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2219 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2220 int font_width = getFontWidth(font_nr);
2221 int font_height = getFontHeight(font_nr);
2222 int max_xsize = level.envelope[envelope_nr].xsize;
2223 int max_ysize = level.envelope[envelope_nr].ysize;
2224 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2225 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2226 int xend = max_xsize;
2227 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2228 int xstep = (xstart < xend ? 1 : 0);
2229 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2231 int end = MAX(xend - xstart, yend - ystart);
2234 for (i = start; i <= end; i++)
2236 int last_frame = end; // last frame of this "for" loop
2237 int x = xstart + i * xstep;
2238 int y = ystart + i * ystep;
2239 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2240 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2241 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2242 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2245 SetDrawtoField(DRAW_FIELDBUFFER);
2247 BlitScreenToBitmap(backbuffer);
2249 SetDrawtoField(DRAW_BACKBUFFER);
2251 for (yy = 0; yy < ysize; yy++)
2252 for (xx = 0; xx < xsize; xx++)
2253 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2255 DrawTextBuffer(sx + font_width, sy + font_height,
2256 level.envelope[envelope_nr].text, font_nr, max_xsize,
2257 xsize - 2, ysize - 2, 0, mask_mode,
2258 level.envelope[envelope_nr].autowrap,
2259 level.envelope[envelope_nr].centered, FALSE);
2261 redraw_mask |= REDRAW_FIELD;
2264 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2268 void ShowEnvelope(int envelope_nr)
2270 int element = EL_ENVELOPE_1 + envelope_nr;
2271 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2272 int sound_opening = element_info[element].sound[ACTION_OPENING];
2273 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2274 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2275 boolean no_delay = (tape.warp_forward);
2276 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2277 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2278 int anim_mode = graphic_info[graphic].anim_mode;
2279 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2280 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2282 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2284 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2286 if (anim_mode == ANIM_DEFAULT)
2287 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2289 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2292 Delay(wait_delay_value);
2294 WaitForEventToContinue();
2296 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2298 if (anim_mode != ANIM_NONE)
2299 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2301 if (anim_mode == ANIM_DEFAULT)
2302 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2304 game.envelope_active = FALSE;
2306 SetDrawtoField(DRAW_FIELDBUFFER);
2308 redraw_mask |= REDRAW_FIELD;
2312 static void setRequestBasePosition(int *x, int *y)
2314 int sx_base, sy_base;
2316 if (request.x != -1)
2317 sx_base = request.x;
2318 else if (request.align == ALIGN_LEFT)
2320 else if (request.align == ALIGN_RIGHT)
2321 sx_base = SX + SXSIZE;
2323 sx_base = SX + SXSIZE / 2;
2325 if (request.y != -1)
2326 sy_base = request.y;
2327 else if (request.valign == VALIGN_TOP)
2329 else if (request.valign == VALIGN_BOTTOM)
2330 sy_base = SY + SYSIZE;
2332 sy_base = SY + SYSIZE / 2;
2338 static void setRequestPositionExt(int *x, int *y, int width, int height,
2339 boolean add_border_size)
2341 int border_size = request.border_size;
2342 int sx_base, sy_base;
2345 setRequestBasePosition(&sx_base, &sy_base);
2347 if (request.align == ALIGN_LEFT)
2349 else if (request.align == ALIGN_RIGHT)
2350 sx = sx_base - width;
2352 sx = sx_base - width / 2;
2354 if (request.valign == VALIGN_TOP)
2356 else if (request.valign == VALIGN_BOTTOM)
2357 sy = sy_base - height;
2359 sy = sy_base - height / 2;
2361 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2362 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2364 if (add_border_size)
2374 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2376 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2379 void DrawEnvelopeRequest(char *text)
2381 int last_game_status = game_status; /* save current game status */
2382 char *text_final = text;
2383 char *text_door_style = NULL;
2384 int graphic = IMG_BACKGROUND_REQUEST;
2385 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2386 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2387 int font_nr = FONT_REQUEST;
2388 int font_width = getFontWidth(font_nr);
2389 int font_height = getFontHeight(font_nr);
2390 int border_size = request.border_size;
2391 int line_spacing = request.line_spacing;
2392 int line_height = font_height + line_spacing;
2393 int max_text_width = request.width - 2 * border_size;
2394 int max_text_height = request.height - 2 * border_size;
2395 int line_length = max_text_width / font_width;
2396 int max_lines = max_text_height / line_height;
2397 int text_width = line_length * font_width;
2398 int width = request.width;
2399 int height = request.height;
2400 int tile_size = MAX(request.step_offset, 1);
2401 int x_steps = width / tile_size;
2402 int y_steps = height / tile_size;
2403 int sx_offset = border_size;
2404 int sy_offset = border_size;
2408 if (request.centered)
2409 sx_offset = (request.width - text_width) / 2;
2411 if (request.wrap_single_words && !request.autowrap)
2413 char *src_text_ptr, *dst_text_ptr;
2415 text_door_style = checked_malloc(2 * strlen(text) + 1);
2417 src_text_ptr = text;
2418 dst_text_ptr = text_door_style;
2420 while (*src_text_ptr)
2422 if (*src_text_ptr == ' ' ||
2423 *src_text_ptr == '?' ||
2424 *src_text_ptr == '!')
2425 *dst_text_ptr++ = '\n';
2427 if (*src_text_ptr != ' ')
2428 *dst_text_ptr++ = *src_text_ptr;
2433 *dst_text_ptr = '\0';
2435 text_final = text_door_style;
2438 setRequestPosition(&sx, &sy, FALSE);
2440 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2442 for (y = 0; y < y_steps; y++)
2443 for (x = 0; x < x_steps; x++)
2444 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2445 x, y, x_steps, y_steps,
2446 tile_size, tile_size);
2448 /* force DOOR font inside door area */
2449 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
2451 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2452 line_length, -1, max_lines, line_spacing, mask_mode,
2453 request.autowrap, request.centered, FALSE);
2455 SetGameStatus(last_game_status); /* restore current game status */
2457 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2458 RedrawGadget(tool_gadget[i]);
2460 // store readily prepared envelope request for later use when animating
2461 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2463 if (text_door_style)
2464 free(text_door_style);
2467 void AnimateEnvelopeRequest(int anim_mode, int action)
2469 int graphic = IMG_BACKGROUND_REQUEST;
2470 boolean draw_masked = graphic_info[graphic].draw_masked;
2471 int delay_value_normal = request.step_delay;
2472 int delay_value_fast = delay_value_normal / 2;
2473 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2474 boolean no_delay = (tape.warp_forward);
2475 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2476 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2477 unsigned int anim_delay = 0;
2479 int tile_size = MAX(request.step_offset, 1);
2480 int max_xsize = request.width / tile_size;
2481 int max_ysize = request.height / tile_size;
2482 int max_xsize_inner = max_xsize - 2;
2483 int max_ysize_inner = max_ysize - 2;
2485 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2486 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2487 int xend = max_xsize_inner;
2488 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2489 int xstep = (xstart < xend ? 1 : 0);
2490 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2492 int end = MAX(xend - xstart, yend - ystart);
2495 if (setup.quick_doors)
2502 for (i = start; i <= end; i++)
2504 int last_frame = end; // last frame of this "for" loop
2505 int x = xstart + i * xstep;
2506 int y = ystart + i * ystep;
2507 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2508 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2509 int xsize_size_left = (xsize - 1) * tile_size;
2510 int ysize_size_top = (ysize - 1) * tile_size;
2511 int max_xsize_pos = (max_xsize - 1) * tile_size;
2512 int max_ysize_pos = (max_ysize - 1) * tile_size;
2513 int width = xsize * tile_size;
2514 int height = ysize * tile_size;
2519 setRequestPosition(&src_x, &src_y, FALSE);
2520 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2522 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2524 for (yy = 0; yy < 2; yy++)
2526 for (xx = 0; xx < 2; xx++)
2528 int src_xx = src_x + xx * max_xsize_pos;
2529 int src_yy = src_y + yy * max_ysize_pos;
2530 int dst_xx = dst_x + xx * xsize_size_left;
2531 int dst_yy = dst_y + yy * ysize_size_top;
2532 int xx_size = (xx ? tile_size : xsize_size_left);
2533 int yy_size = (yy ? tile_size : ysize_size_top);
2536 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2537 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2539 BlitBitmap(bitmap_db_cross, backbuffer,
2540 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2544 redraw_mask |= REDRAW_FIELD;
2549 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2553 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2555 int graphic = IMG_BACKGROUND_REQUEST;
2556 int sound_opening = SND_REQUEST_OPENING;
2557 int sound_closing = SND_REQUEST_CLOSING;
2558 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2559 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2560 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2561 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2562 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2564 if (game_status == GAME_MODE_PLAYING)
2565 BlitScreenToBitmap(backbuffer);
2567 SetDrawtoField(DRAW_BACKBUFFER);
2569 // SetDrawBackgroundMask(REDRAW_NONE);
2571 if (action == ACTION_OPENING)
2573 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2575 if (req_state & REQ_ASK)
2577 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2578 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2580 else if (req_state & REQ_CONFIRM)
2582 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2584 else if (req_state & REQ_PLAYER)
2586 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2587 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2588 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2589 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2592 DrawEnvelopeRequest(text);
2594 if (game_status != GAME_MODE_MAIN)
2598 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2600 if (action == ACTION_OPENING)
2602 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2604 if (anim_mode == ANIM_DEFAULT)
2605 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2607 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2611 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2613 if (anim_mode != ANIM_NONE)
2614 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2616 if (anim_mode == ANIM_DEFAULT)
2617 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2620 game.envelope_active = FALSE;
2622 if (action == ACTION_CLOSING)
2624 if (game_status != GAME_MODE_MAIN)
2627 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2630 // SetDrawBackgroundMask(last_draw_background_mask);
2632 redraw_mask |= REDRAW_FIELD;
2634 if (game_status == GAME_MODE_MAIN)
2639 if (action == ACTION_CLOSING &&
2640 game_status == GAME_MODE_PLAYING &&
2641 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2642 SetDrawtoField(DRAW_FIELDBUFFER);
2645 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2649 int graphic = el2preimg(element);
2651 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2652 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2655 void DrawLevel(int draw_background_mask)
2659 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2660 SetDrawBackgroundMask(draw_background_mask);
2664 for (x = BX1; x <= BX2; x++)
2665 for (y = BY1; y <= BY2; y++)
2666 DrawScreenField(x, y);
2668 redraw_mask |= REDRAW_FIELD;
2671 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2676 for (x = 0; x < size_x; x++)
2677 for (y = 0; y < size_y; y++)
2678 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2680 redraw_mask |= REDRAW_FIELD;
2683 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2687 for (x = 0; x < size_x; x++)
2688 for (y = 0; y < size_y; y++)
2689 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2691 redraw_mask |= REDRAW_FIELD;
2694 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2696 boolean show_level_border = (BorderElement != EL_EMPTY);
2697 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2698 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2699 int tile_size = preview.tile_size;
2700 int preview_width = preview.xsize * tile_size;
2701 int preview_height = preview.ysize * tile_size;
2702 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2703 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2704 int real_preview_width = real_preview_xsize * tile_size;
2705 int real_preview_height = real_preview_ysize * tile_size;
2706 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2707 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2710 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2713 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2715 dst_x += (preview_width - real_preview_width) / 2;
2716 dst_y += (preview_height - real_preview_height) / 2;
2718 for (x = 0; x < real_preview_xsize; x++)
2720 for (y = 0; y < real_preview_ysize; y++)
2722 int lx = from_x + x + (show_level_border ? -1 : 0);
2723 int ly = from_y + y + (show_level_border ? -1 : 0);
2724 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2725 getBorderElement(lx, ly));
2727 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2728 element, tile_size);
2732 redraw_mask |= REDRAW_FIELD;
2735 #define MICROLABEL_EMPTY 0
2736 #define MICROLABEL_LEVEL_NAME 1
2737 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2738 #define MICROLABEL_LEVEL_AUTHOR 3
2739 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2740 #define MICROLABEL_IMPORTED_FROM 5
2741 #define MICROLABEL_IMPORTED_BY_HEAD 6
2742 #define MICROLABEL_IMPORTED_BY 7
2744 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2746 int max_text_width = SXSIZE;
2747 int font_width = getFontWidth(font_nr);
2749 if (pos->align == ALIGN_CENTER)
2750 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2751 else if (pos->align == ALIGN_RIGHT)
2752 max_text_width = pos->x;
2754 max_text_width = SXSIZE - pos->x;
2756 return max_text_width / font_width;
2759 static void DrawPreviewLevelLabelExt(int mode)
2761 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2762 char label_text[MAX_OUTPUT_LINESIZE + 1];
2763 int max_len_label_text;
2764 int font_nr = pos->font;
2767 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2770 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2771 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2772 mode == MICROLABEL_IMPORTED_BY_HEAD)
2773 font_nr = pos->font_alt;
2775 max_len_label_text = getMaxTextLength(pos, font_nr);
2777 if (pos->size != -1)
2778 max_len_label_text = pos->size;
2780 for (i = 0; i < max_len_label_text; i++)
2781 label_text[i] = ' ';
2782 label_text[max_len_label_text] = '\0';
2784 if (strlen(label_text) > 0)
2785 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2788 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2789 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2790 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2791 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2792 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2793 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2794 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2795 max_len_label_text);
2796 label_text[max_len_label_text] = '\0';
2798 if (strlen(label_text) > 0)
2799 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2801 redraw_mask |= REDRAW_FIELD;
2804 static void DrawPreviewLevelExt(boolean restart)
2806 static unsigned int scroll_delay = 0;
2807 static unsigned int label_delay = 0;
2808 static int from_x, from_y, scroll_direction;
2809 static int label_state, label_counter;
2810 unsigned int scroll_delay_value = preview.step_delay;
2811 boolean show_level_border = (BorderElement != EL_EMPTY);
2812 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2813 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2814 int last_game_status = game_status; /* save current game status */
2821 if (preview.anim_mode == ANIM_CENTERED)
2823 if (level_xsize > preview.xsize)
2824 from_x = (level_xsize - preview.xsize) / 2;
2825 if (level_ysize > preview.ysize)
2826 from_y = (level_ysize - preview.ysize) / 2;
2829 from_x += preview.xoffset;
2830 from_y += preview.yoffset;
2832 scroll_direction = MV_RIGHT;
2836 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2837 DrawPreviewLevelLabelExt(label_state);
2839 /* initialize delay counters */
2840 DelayReached(&scroll_delay, 0);
2841 DelayReached(&label_delay, 0);
2843 if (leveldir_current->name)
2845 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2846 char label_text[MAX_OUTPUT_LINESIZE + 1];
2847 int font_nr = pos->font;
2848 int max_len_label_text = getMaxTextLength(pos, font_nr);
2850 if (pos->size != -1)
2851 max_len_label_text = pos->size;
2853 strncpy(label_text, leveldir_current->name, max_len_label_text);
2854 label_text[max_len_label_text] = '\0';
2856 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2857 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2860 SetGameStatus(last_game_status); /* restore current game status */
2865 /* scroll preview level, if needed */
2866 if (preview.anim_mode != ANIM_NONE &&
2867 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2868 DelayReached(&scroll_delay, scroll_delay_value))
2870 switch (scroll_direction)
2875 from_x -= preview.step_offset;
2876 from_x = (from_x < 0 ? 0 : from_x);
2879 scroll_direction = MV_UP;
2883 if (from_x < level_xsize - preview.xsize)
2885 from_x += preview.step_offset;
2886 from_x = (from_x > level_xsize - preview.xsize ?
2887 level_xsize - preview.xsize : from_x);
2890 scroll_direction = MV_DOWN;
2896 from_y -= preview.step_offset;
2897 from_y = (from_y < 0 ? 0 : from_y);
2900 scroll_direction = MV_RIGHT;
2904 if (from_y < level_ysize - preview.ysize)
2906 from_y += preview.step_offset;
2907 from_y = (from_y > level_ysize - preview.ysize ?
2908 level_ysize - preview.ysize : from_y);
2911 scroll_direction = MV_LEFT;
2918 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2921 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2922 /* redraw micro level label, if needed */
2923 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2924 !strEqual(level.author, ANONYMOUS_NAME) &&
2925 !strEqual(level.author, leveldir_current->name) &&
2926 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2928 int max_label_counter = 23;
2930 if (leveldir_current->imported_from != NULL &&
2931 strlen(leveldir_current->imported_from) > 0)
2932 max_label_counter += 14;
2933 if (leveldir_current->imported_by != NULL &&
2934 strlen(leveldir_current->imported_by) > 0)
2935 max_label_counter += 14;
2937 label_counter = (label_counter + 1) % max_label_counter;
2938 label_state = (label_counter >= 0 && label_counter <= 7 ?
2939 MICROLABEL_LEVEL_NAME :
2940 label_counter >= 9 && label_counter <= 12 ?
2941 MICROLABEL_LEVEL_AUTHOR_HEAD :
2942 label_counter >= 14 && label_counter <= 21 ?
2943 MICROLABEL_LEVEL_AUTHOR :
2944 label_counter >= 23 && label_counter <= 26 ?
2945 MICROLABEL_IMPORTED_FROM_HEAD :
2946 label_counter >= 28 && label_counter <= 35 ?
2947 MICROLABEL_IMPORTED_FROM :
2948 label_counter >= 37 && label_counter <= 40 ?
2949 MICROLABEL_IMPORTED_BY_HEAD :
2950 label_counter >= 42 && label_counter <= 49 ?
2951 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2953 if (leveldir_current->imported_from == NULL &&
2954 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2955 label_state == MICROLABEL_IMPORTED_FROM))
2956 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2957 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2959 DrawPreviewLevelLabelExt(label_state);
2962 SetGameStatus(last_game_status); /* restore current game status */
2965 void DrawPreviewLevelInitial()
2967 DrawPreviewLevelExt(TRUE);
2970 void DrawPreviewLevelAnimation()
2972 DrawPreviewLevelExt(FALSE);
2975 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2976 int graphic, int sync_frame,
2979 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2981 if (mask_mode == USE_MASKING)
2982 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2984 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2987 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2988 int graphic, int sync_frame, int mask_mode)
2990 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2992 if (mask_mode == USE_MASKING)
2993 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2995 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2998 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3000 int lx = LEVELX(x), ly = LEVELY(y);
3002 if (!IN_SCR_FIELD(x, y))
3005 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3006 graphic, GfxFrame[lx][ly], NO_MASKING);
3008 MarkTileDirty(x, y);
3011 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3013 int lx = LEVELX(x), ly = LEVELY(y);
3015 if (!IN_SCR_FIELD(x, y))
3018 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3019 graphic, GfxFrame[lx][ly], NO_MASKING);
3020 MarkTileDirty(x, y);
3023 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3025 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3028 void DrawLevelElementAnimation(int x, int y, int element)
3030 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3032 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3035 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3037 int sx = SCREENX(x), sy = SCREENY(y);
3039 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3042 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3045 DrawGraphicAnimation(sx, sy, graphic);
3048 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3049 DrawLevelFieldCrumbled(x, y);
3051 if (GFX_CRUMBLED(Feld[x][y]))
3052 DrawLevelFieldCrumbled(x, y);
3056 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3058 int sx = SCREENX(x), sy = SCREENY(y);
3061 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3064 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3066 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3069 DrawGraphicAnimation(sx, sy, graphic);
3071 if (GFX_CRUMBLED(element))
3072 DrawLevelFieldCrumbled(x, y);
3075 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3077 if (player->use_murphy)
3079 /* this works only because currently only one player can be "murphy" ... */
3080 static int last_horizontal_dir = MV_LEFT;
3081 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3083 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3084 last_horizontal_dir = move_dir;
3086 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3088 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3090 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3096 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3099 static boolean equalGraphics(int graphic1, int graphic2)
3101 struct GraphicInfo *g1 = &graphic_info[graphic1];
3102 struct GraphicInfo *g2 = &graphic_info[graphic2];
3104 return (g1->bitmap == g2->bitmap &&
3105 g1->src_x == g2->src_x &&
3106 g1->src_y == g2->src_y &&
3107 g1->anim_frames == g2->anim_frames &&
3108 g1->anim_delay == g2->anim_delay &&
3109 g1->anim_mode == g2->anim_mode);
3112 void DrawAllPlayers()
3116 for (i = 0; i < MAX_PLAYERS; i++)
3117 if (stored_player[i].active)
3118 DrawPlayer(&stored_player[i]);
3121 void DrawPlayerField(int x, int y)
3123 if (!IS_PLAYER(x, y))
3126 DrawPlayer(PLAYERINFO(x, y));
3129 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3131 void DrawPlayer(struct PlayerInfo *player)
3133 int jx = player->jx;
3134 int jy = player->jy;
3135 int move_dir = player->MovDir;
3136 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3137 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3138 int last_jx = (player->is_moving ? jx - dx : jx);
3139 int last_jy = (player->is_moving ? jy - dy : jy);
3140 int next_jx = jx + dx;
3141 int next_jy = jy + dy;
3142 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3143 boolean player_is_opaque = FALSE;
3144 int sx = SCREENX(jx), sy = SCREENY(jy);
3145 int sxx = 0, syy = 0;
3146 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3148 int action = ACTION_DEFAULT;
3149 int last_player_graphic = getPlayerGraphic(player, move_dir);
3150 int last_player_frame = player->Frame;
3153 /* GfxElement[][] is set to the element the player is digging or collecting;
3154 remove also for off-screen player if the player is not moving anymore */
3155 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3156 GfxElement[jx][jy] = EL_UNDEFINED;
3158 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3162 if (!IN_LEV_FIELD(jx, jy))
3164 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3165 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3166 printf("DrawPlayerField(): This should never happen!\n");
3171 if (element == EL_EXPLOSION)
3174 action = (player->is_pushing ? ACTION_PUSHING :
3175 player->is_digging ? ACTION_DIGGING :
3176 player->is_collecting ? ACTION_COLLECTING :
3177 player->is_moving ? ACTION_MOVING :
3178 player->is_snapping ? ACTION_SNAPPING :
3179 player->is_dropping ? ACTION_DROPPING :
3180 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3182 if (player->is_waiting)
3183 move_dir = player->dir_waiting;
3185 InitPlayerGfxAnimation(player, action, move_dir);
3187 /* ----------------------------------------------------------------------- */
3188 /* draw things in the field the player is leaving, if needed */
3189 /* ----------------------------------------------------------------------- */
3191 if (player->is_moving)
3193 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3195 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3197 if (last_element == EL_DYNAMITE_ACTIVE ||
3198 last_element == EL_EM_DYNAMITE_ACTIVE ||
3199 last_element == EL_SP_DISK_RED_ACTIVE)
3200 DrawDynamite(last_jx, last_jy);
3202 DrawLevelFieldThruMask(last_jx, last_jy);
3204 else if (last_element == EL_DYNAMITE_ACTIVE ||
3205 last_element == EL_EM_DYNAMITE_ACTIVE ||
3206 last_element == EL_SP_DISK_RED_ACTIVE)
3207 DrawDynamite(last_jx, last_jy);
3209 /* !!! this is not enough to prevent flickering of players which are
3210 moving next to each others without a free tile between them -- this
3211 can only be solved by drawing all players layer by layer (first the
3212 background, then the foreground etc.) !!! => TODO */
3213 else if (!IS_PLAYER(last_jx, last_jy))
3214 DrawLevelField(last_jx, last_jy);
3217 DrawLevelField(last_jx, last_jy);
3220 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3221 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3224 if (!IN_SCR_FIELD(sx, sy))
3227 /* ----------------------------------------------------------------------- */
3228 /* draw things behind the player, if needed */
3229 /* ----------------------------------------------------------------------- */
3232 DrawLevelElement(jx, jy, Back[jx][jy]);
3233 else if (IS_ACTIVE_BOMB(element))
3234 DrawLevelElement(jx, jy, EL_EMPTY);
3237 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3239 int old_element = GfxElement[jx][jy];
3240 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3241 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3243 if (GFX_CRUMBLED(old_element))
3244 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3246 DrawGraphic(sx, sy, old_graphic, frame);
3248 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3249 player_is_opaque = TRUE;
3253 GfxElement[jx][jy] = EL_UNDEFINED;
3255 /* make sure that pushed elements are drawn with correct frame rate */
3256 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3258 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3259 GfxFrame[jx][jy] = player->StepFrame;
3261 DrawLevelField(jx, jy);
3265 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3266 /* ----------------------------------------------------------------------- */
3267 /* draw player himself */
3268 /* ----------------------------------------------------------------------- */
3270 graphic = getPlayerGraphic(player, move_dir);
3272 /* in the case of changed player action or direction, prevent the current
3273 animation frame from being restarted for identical animations */
3274 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3275 player->Frame = last_player_frame;
3277 frame = getGraphicAnimationFrame(graphic, player->Frame);
3281 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3282 sxx = player->GfxPos;
3284 syy = player->GfxPos;
3287 if (player_is_opaque)
3288 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3290 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3292 if (SHIELD_ON(player))
3294 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3295 IMG_SHIELD_NORMAL_ACTIVE);
3296 int frame = getGraphicAnimationFrame(graphic, -1);
3298 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3302 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3305 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3306 sxx = player->GfxPos;
3308 syy = player->GfxPos;
3312 /* ----------------------------------------------------------------------- */
3313 /* draw things the player is pushing, if needed */
3314 /* ----------------------------------------------------------------------- */
3316 if (player->is_pushing && player->is_moving)
3318 int px = SCREENX(jx), py = SCREENY(jy);
3319 int pxx = (TILEX - ABS(sxx)) * dx;
3320 int pyy = (TILEY - ABS(syy)) * dy;
3321 int gfx_frame = GfxFrame[jx][jy];
3327 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3329 element = Feld[next_jx][next_jy];
3330 gfx_frame = GfxFrame[next_jx][next_jy];
3333 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3335 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3336 frame = getGraphicAnimationFrame(graphic, sync_frame);
3338 /* draw background element under pushed element (like the Sokoban field) */
3339 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3341 /* this allows transparent pushing animation over non-black background */
3344 DrawLevelElement(jx, jy, Back[jx][jy]);
3346 DrawLevelElement(jx, jy, EL_EMPTY);
3348 if (Back[next_jx][next_jy])
3349 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3351 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3353 else if (Back[next_jx][next_jy])
3354 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3357 /* do not draw (EM style) pushing animation when pushing is finished */
3358 /* (two-tile animations usually do not contain start and end frame) */
3359 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3360 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3362 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3364 /* masked drawing is needed for EMC style (double) movement graphics */
3365 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3366 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3370 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3371 /* ----------------------------------------------------------------------- */
3372 /* draw player himself */
3373 /* ----------------------------------------------------------------------- */
3375 graphic = getPlayerGraphic(player, move_dir);
3377 /* in the case of changed player action or direction, prevent the current
3378 animation frame from being restarted for identical animations */
3379 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3380 player->Frame = last_player_frame;
3382 frame = getGraphicAnimationFrame(graphic, player->Frame);
3386 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3387 sxx = player->GfxPos;
3389 syy = player->GfxPos;
3392 if (player_is_opaque)
3393 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3395 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3397 if (SHIELD_ON(player))
3399 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3400 IMG_SHIELD_NORMAL_ACTIVE);
3401 int frame = getGraphicAnimationFrame(graphic, -1);
3403 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3407 /* ----------------------------------------------------------------------- */
3408 /* draw things in front of player (active dynamite or dynabombs) */
3409 /* ----------------------------------------------------------------------- */
3411 if (IS_ACTIVE_BOMB(element))
3413 graphic = el2img(element);
3414 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3416 if (game.emulation == EMU_SUPAPLEX)
3417 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3419 DrawGraphicThruMask(sx, sy, graphic, frame);
3422 if (player_is_moving && last_element == EL_EXPLOSION)
3424 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3425 GfxElement[last_jx][last_jy] : EL_EMPTY);
3426 int graphic = el_act2img(element, ACTION_EXPLODING);
3427 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3428 int phase = ExplodePhase[last_jx][last_jy] - 1;
3429 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3432 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3435 /* ----------------------------------------------------------------------- */
3436 /* draw elements the player is just walking/passing through/under */
3437 /* ----------------------------------------------------------------------- */
3439 if (player_is_moving)
3441 /* handle the field the player is leaving ... */
3442 if (IS_ACCESSIBLE_INSIDE(last_element))
3443 DrawLevelField(last_jx, last_jy);
3444 else if (IS_ACCESSIBLE_UNDER(last_element))
3445 DrawLevelFieldThruMask(last_jx, last_jy);
3448 /* do not redraw accessible elements if the player is just pushing them */
3449 if (!player_is_moving || !player->is_pushing)
3451 /* ... and the field the player is entering */
3452 if (IS_ACCESSIBLE_INSIDE(element))
3453 DrawLevelField(jx, jy);
3454 else if (IS_ACCESSIBLE_UNDER(element))
3455 DrawLevelFieldThruMask(jx, jy);
3458 MarkTileDirty(sx, sy);
3461 /* ------------------------------------------------------------------------- */
3463 void WaitForEventToContinue()
3465 boolean still_wait = TRUE;
3467 /* simulate releasing mouse button over last gadget, if still pressed */
3469 HandleGadgets(-1, -1, 0);
3471 button_status = MB_RELEASED;
3485 case EVENT_BUTTONPRESS:
3486 case EVENT_KEYPRESS:
3490 case EVENT_KEYRELEASE:
3491 ClearPlayerAction();
3495 HandleOtherEvents(&event);
3499 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3506 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3510 #define MAX_REQUEST_LINES 13
3511 #define MAX_REQUEST_LINE_FONT1_LEN 7
3512 #define MAX_REQUEST_LINE_FONT2_LEN 10
3514 static int RequestHandleEvents(unsigned int req_state)
3516 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3517 local_player->LevelSolved_GameEnd);
3518 int width = request.width;
3519 int height = request.height;
3523 setRequestPosition(&sx, &sy, FALSE);
3525 button_status = MB_RELEASED;
3527 request_gadget_id = -1;
3534 SetDrawtoField(DRAW_FIELDBUFFER);
3536 HandleGameActions();
3538 SetDrawtoField(DRAW_BACKBUFFER);
3540 if (global.use_envelope_request)
3542 /* copy current state of request area to middle of playfield area */
3543 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3551 while (NextValidEvent(&event))
3555 case EVENT_BUTTONPRESS:
3556 case EVENT_BUTTONRELEASE:
3557 case EVENT_MOTIONNOTIFY:
3561 if (event.type == EVENT_MOTIONNOTIFY)
3566 motion_status = TRUE;
3567 mx = ((MotionEvent *) &event)->x;
3568 my = ((MotionEvent *) &event)->y;
3572 motion_status = FALSE;
3573 mx = ((ButtonEvent *) &event)->x;
3574 my = ((ButtonEvent *) &event)->y;
3575 if (event.type == EVENT_BUTTONPRESS)
3576 button_status = ((ButtonEvent *) &event)->button;
3578 button_status = MB_RELEASED;
3581 /* this sets 'request_gadget_id' */
3582 HandleGadgets(mx, my, button_status);
3584 switch (request_gadget_id)
3586 case TOOL_CTRL_ID_YES:
3589 case TOOL_CTRL_ID_NO:
3592 case TOOL_CTRL_ID_CONFIRM:
3593 result = TRUE | FALSE;
3596 case TOOL_CTRL_ID_PLAYER_1:
3599 case TOOL_CTRL_ID_PLAYER_2:
3602 case TOOL_CTRL_ID_PLAYER_3:
3605 case TOOL_CTRL_ID_PLAYER_4:
3616 case EVENT_KEYPRESS:
3617 switch (GetEventKey((KeyEvent *)&event, TRUE))
3620 if (req_state & REQ_CONFIRM)
3625 #if defined(TARGET_SDL2)
3632 #if defined(TARGET_SDL2)
3642 if (req_state & REQ_PLAYER)
3646 case EVENT_KEYRELEASE:
3647 ClearPlayerAction();
3651 HandleOtherEvents(&event);
3656 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3658 int joy = AnyJoystick();
3660 if (joy & JOY_BUTTON_1)
3662 else if (joy & JOY_BUTTON_2)
3668 if (global.use_envelope_request)
3670 /* copy back current state of pressed buttons inside request area */
3671 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3681 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3687 static boolean RequestDoor(char *text, unsigned int req_state)
3689 unsigned int old_door_state;
3690 int last_game_status = game_status; /* save current game status */
3691 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3692 int font_nr = FONT_TEXT_2;
3697 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3699 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3700 font_nr = FONT_TEXT_1;
3703 if (game_status == GAME_MODE_PLAYING)
3704 BlitScreenToBitmap(backbuffer);
3706 /* disable deactivated drawing when quick-loading level tape recording */
3707 if (tape.playing && tape.deactivate_display)
3708 TapeDeactivateDisplayOff(TRUE);
3710 SetMouseCursor(CURSOR_DEFAULT);
3712 #if defined(NETWORK_AVALIABLE)
3713 /* pause network game while waiting for request to answer */
3714 if (options.network &&
3715 game_status == GAME_MODE_PLAYING &&
3716 req_state & REQUEST_WAIT_FOR_INPUT)
3717 SendToServer_PausePlaying();
3720 old_door_state = GetDoorState();
3722 /* simulate releasing mouse button over last gadget, if still pressed */
3724 HandleGadgets(-1, -1, 0);
3728 /* draw released gadget before proceeding */
3731 if (old_door_state & DOOR_OPEN_1)
3733 CloseDoor(DOOR_CLOSE_1);
3735 /* save old door content */
3736 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3737 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3740 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3741 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3743 /* clear door drawing field */
3744 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3746 /* force DOOR font inside door area */
3747 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
3749 /* write text for request */
3750 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3752 char text_line[max_request_line_len + 1];
3758 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3760 tc = *(text_ptr + tx);
3761 // if (!tc || tc == ' ')
3762 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3766 if ((tc == '?' || tc == '!') && tl == 0)
3776 strncpy(text_line, text_ptr, tl);
3779 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3780 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3781 text_line, font_nr);
3783 text_ptr += tl + (tc == ' ' ? 1 : 0);
3784 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3787 SetGameStatus(last_game_status); /* restore current game status */
3789 if (req_state & REQ_ASK)
3791 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3792 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3794 else if (req_state & REQ_CONFIRM)
3796 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3798 else if (req_state & REQ_PLAYER)
3800 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3801 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3802 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3803 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3806 /* copy request gadgets to door backbuffer */
3807 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3809 OpenDoor(DOOR_OPEN_1);
3811 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3813 if (game_status == GAME_MODE_PLAYING)
3815 SetPanelBackground();
3816 SetDrawBackgroundMask(REDRAW_DOOR_1);
3820 SetDrawBackgroundMask(REDRAW_FIELD);
3826 if (game_status != GAME_MODE_MAIN)
3829 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3831 // ---------- handle request buttons ----------
3832 result = RequestHandleEvents(req_state);
3834 if (game_status != GAME_MODE_MAIN)
3839 if (!(req_state & REQ_STAY_OPEN))
3841 CloseDoor(DOOR_CLOSE_1);
3843 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3844 (req_state & REQ_REOPEN))
3845 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3850 if (game_status == GAME_MODE_PLAYING)
3852 SetPanelBackground();
3853 SetDrawBackgroundMask(REDRAW_DOOR_1);
3857 SetDrawBackgroundMask(REDRAW_FIELD);
3860 #if defined(NETWORK_AVALIABLE)
3861 /* continue network game after request */
3862 if (options.network &&
3863 game_status == GAME_MODE_PLAYING &&
3864 req_state & REQUEST_WAIT_FOR_INPUT)
3865 SendToServer_ContinuePlaying();
3868 /* restore deactivated drawing when quick-loading level tape recording */
3869 if (tape.playing && tape.deactivate_display)
3870 TapeDeactivateDisplayOn();
3875 static boolean RequestEnvelope(char *text, unsigned int req_state)
3879 if (game_status == GAME_MODE_PLAYING)
3880 BlitScreenToBitmap(backbuffer);
3882 /* disable deactivated drawing when quick-loading level tape recording */
3883 if (tape.playing && tape.deactivate_display)
3884 TapeDeactivateDisplayOff(TRUE);
3886 SetMouseCursor(CURSOR_DEFAULT);
3888 #if defined(NETWORK_AVALIABLE)
3889 /* pause network game while waiting for request to answer */
3890 if (options.network &&
3891 game_status == GAME_MODE_PLAYING &&
3892 req_state & REQUEST_WAIT_FOR_INPUT)
3893 SendToServer_PausePlaying();
3896 /* simulate releasing mouse button over last gadget, if still pressed */
3898 HandleGadgets(-1, -1, 0);
3902 // (replace with setting corresponding request background)
3903 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3904 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3906 /* clear door drawing field */
3907 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3909 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3911 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3913 if (game_status == GAME_MODE_PLAYING)
3915 SetPanelBackground();
3916 SetDrawBackgroundMask(REDRAW_DOOR_1);
3920 SetDrawBackgroundMask(REDRAW_FIELD);
3926 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3928 // ---------- handle request buttons ----------
3929 result = RequestHandleEvents(req_state);
3931 if (game_status != GAME_MODE_MAIN)
3936 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3940 if (game_status == GAME_MODE_PLAYING)
3942 SetPanelBackground();
3943 SetDrawBackgroundMask(REDRAW_DOOR_1);
3947 SetDrawBackgroundMask(REDRAW_FIELD);
3950 #if defined(NETWORK_AVALIABLE)
3951 /* continue network game after request */
3952 if (options.network &&
3953 game_status == GAME_MODE_PLAYING &&
3954 req_state & REQUEST_WAIT_FOR_INPUT)
3955 SendToServer_ContinuePlaying();
3958 /* restore deactivated drawing when quick-loading level tape recording */
3959 if (tape.playing && tape.deactivate_display)
3960 TapeDeactivateDisplayOn();
3965 boolean Request(char *text, unsigned int req_state)
3967 if (global.use_envelope_request)
3968 return RequestEnvelope(text, req_state);
3970 return RequestDoor(text, req_state);
3973 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3975 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3976 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3979 if (dpo1->sort_priority != dpo2->sort_priority)
3980 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3982 compare_result = dpo1->nr - dpo2->nr;
3984 return compare_result;
3987 void InitGraphicCompatibilityInfo_Doors()
3993 struct DoorInfo *door;
3997 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3998 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
4000 { -1, -1, -1, NULL }
4002 struct Rect door_rect_list[] =
4004 { DX, DY, DXSIZE, DYSIZE },
4005 { VX, VY, VXSIZE, VYSIZE }
4009 for (i = 0; doors[i].door_token != -1; i++)
4011 int door_token = doors[i].door_token;
4012 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4013 int part_1 = doors[i].part_1;
4014 int part_8 = doors[i].part_8;
4015 int part_2 = part_1 + 1;
4016 int part_3 = part_1 + 2;
4017 struct DoorInfo *door = doors[i].door;
4018 struct Rect *door_rect = &door_rect_list[door_index];
4019 boolean door_gfx_redefined = FALSE;
4021 /* check if any door part graphic definitions have been redefined */
4023 for (j = 0; door_part_controls[j].door_token != -1; j++)
4025 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4026 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4028 if (dpc->door_token == door_token && fi->redefined)
4029 door_gfx_redefined = TRUE;
4032 /* check for old-style door graphic/animation modifications */
4034 if (!door_gfx_redefined)
4036 if (door->anim_mode & ANIM_STATIC_PANEL)
4038 door->panel.step_xoffset = 0;
4039 door->panel.step_yoffset = 0;
4042 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4044 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4045 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4046 int num_door_steps, num_panel_steps;
4048 /* remove door part graphics other than the two default wings */
4050 for (j = 0; door_part_controls[j].door_token != -1; j++)
4052 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4053 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4055 if (dpc->graphic >= part_3 &&
4056 dpc->graphic <= part_8)
4060 /* set graphics and screen positions of the default wings */
4062 g_part_1->width = door_rect->width;
4063 g_part_1->height = door_rect->height;
4064 g_part_2->width = door_rect->width;
4065 g_part_2->height = door_rect->height;
4066 g_part_2->src_x = door_rect->width;
4067 g_part_2->src_y = g_part_1->src_y;
4069 door->part_2.x = door->part_1.x;
4070 door->part_2.y = door->part_1.y;
4072 if (door->width != -1)
4074 g_part_1->width = door->width;
4075 g_part_2->width = door->width;
4077 // special treatment for graphics and screen position of right wing
4078 g_part_2->src_x += door_rect->width - door->width;
4079 door->part_2.x += door_rect->width - door->width;
4082 if (door->height != -1)
4084 g_part_1->height = door->height;
4085 g_part_2->height = door->height;
4087 // special treatment for graphics and screen position of bottom wing
4088 g_part_2->src_y += door_rect->height - door->height;
4089 door->part_2.y += door_rect->height - door->height;
4092 /* set animation delays for the default wings and panels */
4094 door->part_1.step_delay = door->step_delay;
4095 door->part_2.step_delay = door->step_delay;
4096 door->panel.step_delay = door->step_delay;
4098 /* set animation draw order for the default wings */
4100 door->part_1.sort_priority = 2; /* draw left wing over ... */
4101 door->part_2.sort_priority = 1; /* ... right wing */
4103 /* set animation draw offset for the default wings */
4105 if (door->anim_mode & ANIM_HORIZONTAL)
4107 door->part_1.step_xoffset = door->step_offset;
4108 door->part_1.step_yoffset = 0;
4109 door->part_2.step_xoffset = door->step_offset * -1;
4110 door->part_2.step_yoffset = 0;
4112 num_door_steps = g_part_1->width / door->step_offset;
4114 else // ANIM_VERTICAL
4116 door->part_1.step_xoffset = 0;
4117 door->part_1.step_yoffset = door->step_offset;
4118 door->part_2.step_xoffset = 0;
4119 door->part_2.step_yoffset = door->step_offset * -1;
4121 num_door_steps = g_part_1->height / door->step_offset;
4124 /* set animation draw offset for the default panels */
4126 if (door->step_offset > 1)
4128 num_panel_steps = 2 * door_rect->height / door->step_offset;
4129 door->panel.start_step = num_panel_steps - num_door_steps;
4130 door->panel.start_step_closing = door->panel.start_step;
4134 num_panel_steps = door_rect->height / door->step_offset;
4135 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4136 door->panel.start_step_closing = door->panel.start_step;
4137 door->panel.step_delay *= 2;
4148 for (i = 0; door_part_controls[i].door_token != -1; i++)
4150 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4151 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4153 /* initialize "start_step_opening" and "start_step_closing", if needed */
4154 if (dpc->pos->start_step_opening == 0 &&
4155 dpc->pos->start_step_closing == 0)
4157 // dpc->pos->start_step_opening = dpc->pos->start_step;
4158 dpc->pos->start_step_closing = dpc->pos->start_step;
4161 /* fill structure for door part draw order (sorted below) */
4163 dpo->sort_priority = dpc->pos->sort_priority;
4166 /* sort door part controls according to sort_priority and graphic number */
4167 qsort(door_part_order, MAX_DOOR_PARTS,
4168 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4171 unsigned int OpenDoor(unsigned int door_state)
4173 if (door_state & DOOR_COPY_BACK)
4175 if (door_state & DOOR_OPEN_1)
4176 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4177 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4179 if (door_state & DOOR_OPEN_2)
4180 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4181 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4183 door_state &= ~DOOR_COPY_BACK;
4186 return MoveDoor(door_state);
4189 unsigned int CloseDoor(unsigned int door_state)
4191 unsigned int old_door_state = GetDoorState();
4193 if (!(door_state & DOOR_NO_COPY_BACK))
4195 if (old_door_state & DOOR_OPEN_1)
4196 BlitBitmap(backbuffer, bitmap_db_door_1,
4197 DX, DY, DXSIZE, DYSIZE, 0, 0);
4199 if (old_door_state & DOOR_OPEN_2)
4200 BlitBitmap(backbuffer, bitmap_db_door_2,
4201 VX, VY, VXSIZE, VYSIZE, 0, 0);
4203 door_state &= ~DOOR_NO_COPY_BACK;
4206 return MoveDoor(door_state);
4209 unsigned int GetDoorState()
4211 return MoveDoor(DOOR_GET_STATE);
4214 unsigned int SetDoorState(unsigned int door_state)
4216 return MoveDoor(door_state | DOOR_SET_STATE);
4219 int euclid(int a, int b)
4221 return (b ? euclid(b, a % b) : a);
4224 unsigned int MoveDoor(unsigned int door_state)
4226 struct Rect door_rect_list[] =
4228 { DX, DY, DXSIZE, DYSIZE },
4229 { VX, VY, VXSIZE, VYSIZE }
4231 static int door1 = DOOR_OPEN_1;
4232 static int door2 = DOOR_CLOSE_2;
4233 unsigned int door_delay = 0;
4234 unsigned int door_delay_value;
4237 if (door_state == DOOR_GET_STATE)
4238 return (door1 | door2);
4240 if (door_state & DOOR_SET_STATE)
4242 if (door_state & DOOR_ACTION_1)
4243 door1 = door_state & DOOR_ACTION_1;
4244 if (door_state & DOOR_ACTION_2)
4245 door2 = door_state & DOOR_ACTION_2;
4247 return (door1 | door2);
4250 if (!(door_state & DOOR_FORCE_REDRAW))
4252 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4253 door_state &= ~DOOR_OPEN_1;
4254 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4255 door_state &= ~DOOR_CLOSE_1;
4256 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4257 door_state &= ~DOOR_OPEN_2;
4258 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4259 door_state &= ~DOOR_CLOSE_2;
4262 if (global.autoplay_leveldir)
4264 door_state |= DOOR_NO_DELAY;
4265 door_state &= ~DOOR_CLOSE_ALL;
4268 if (game_status == GAME_MODE_EDITOR)
4269 door_state |= DOOR_NO_DELAY;
4271 if (door_state & DOOR_ACTION)
4273 boolean door_panel_drawn[NUM_DOORS];
4274 boolean panel_has_doors[NUM_DOORS];
4275 boolean door_part_skip[MAX_DOOR_PARTS];
4276 boolean door_part_done[MAX_DOOR_PARTS];
4277 boolean door_part_done_all;
4278 int num_steps[MAX_DOOR_PARTS];
4279 int max_move_delay = 0; // delay for complete animations of all doors
4280 int max_step_delay = 0; // delay (ms) between two animation frames
4281 int num_move_steps = 0; // number of animation steps for all doors
4282 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4283 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4284 int current_move_delay = 0;
4288 for (i = 0; i < NUM_DOORS; i++)
4289 panel_has_doors[i] = FALSE;
4291 for (i = 0; i < MAX_DOOR_PARTS; i++)
4293 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4294 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4295 int door_token = dpc->door_token;
4297 door_part_done[i] = FALSE;
4298 door_part_skip[i] = (!(door_state & door_token) ||
4302 for (i = 0; i < MAX_DOOR_PARTS; i++)
4304 int nr = door_part_order[i].nr;
4305 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4306 struct DoorPartPosInfo *pos = dpc->pos;
4307 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4308 int door_token = dpc->door_token;
4309 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4310 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4311 int step_xoffset = ABS(pos->step_xoffset);
4312 int step_yoffset = ABS(pos->step_yoffset);
4313 int step_delay = pos->step_delay;
4314 int current_door_state = door_state & door_token;
4315 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4316 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4317 boolean part_opening = (is_panel ? door_closing : door_opening);
4318 int start_step = (part_opening ? pos->start_step_opening :
4319 pos->start_step_closing);
4320 float move_xsize = (step_xoffset ? g->width : 0);
4321 float move_ysize = (step_yoffset ? g->height : 0);
4322 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4323 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4324 int move_steps = (move_xsteps && move_ysteps ?
4325 MIN(move_xsteps, move_ysteps) :
4326 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4327 int move_delay = move_steps * step_delay;
4329 if (door_part_skip[nr])
4332 max_move_delay = MAX(max_move_delay, move_delay);
4333 max_step_delay = (max_step_delay == 0 ? step_delay :
4334 euclid(max_step_delay, step_delay));
4335 num_steps[nr] = move_steps;
4339 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4341 panel_has_doors[door_index] = TRUE;
4345 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4347 num_move_steps = max_move_delay / max_step_delay;
4348 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4350 door_delay_value = max_step_delay;
4352 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4354 start = num_move_steps - 1;
4358 /* opening door sound has priority over simultaneously closing door */
4359 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4360 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4361 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4362 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4365 for (k = start; k < num_move_steps; k++)
4367 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4369 door_part_done_all = TRUE;
4371 for (i = 0; i < NUM_DOORS; i++)
4372 door_panel_drawn[i] = FALSE;
4374 for (i = 0; i < MAX_DOOR_PARTS; i++)
4376 int nr = door_part_order[i].nr;
4377 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4378 struct DoorPartPosInfo *pos = dpc->pos;
4379 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4380 int door_token = dpc->door_token;
4381 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4382 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4383 boolean is_panel_and_door_has_closed = FALSE;
4384 struct Rect *door_rect = &door_rect_list[door_index];
4385 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4387 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4388 int current_door_state = door_state & door_token;
4389 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4390 boolean door_closing = !door_opening;
4391 boolean part_opening = (is_panel ? door_closing : door_opening);
4392 boolean part_closing = !part_opening;
4393 int start_step = (part_opening ? pos->start_step_opening :
4394 pos->start_step_closing);
4395 int step_delay = pos->step_delay;
4396 int step_factor = step_delay / max_step_delay;
4397 int k1 = (step_factor ? k / step_factor + 1 : k);
4398 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4399 int kk = MAX(0, k2);
4402 int src_x, src_y, src_xx, src_yy;
4403 int dst_x, dst_y, dst_xx, dst_yy;
4406 if (door_part_skip[nr])
4409 if (!(door_state & door_token))
4417 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4418 int kk_door = MAX(0, k2_door);
4419 int sync_frame = kk_door * door_delay_value;
4420 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4422 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4427 if (!door_panel_drawn[door_index])
4429 ClearRectangle(drawto, door_rect->x, door_rect->y,
4430 door_rect->width, door_rect->height);
4432 door_panel_drawn[door_index] = TRUE;
4435 // draw opening or closing door parts
4437 if (pos->step_xoffset < 0) // door part on right side
4440 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4443 if (dst_xx + width > door_rect->width)
4444 width = door_rect->width - dst_xx;
4446 else // door part on left side
4449 dst_xx = pos->x - kk * pos->step_xoffset;
4453 src_xx = ABS(dst_xx);
4457 width = g->width - src_xx;
4459 if (width > door_rect->width)
4460 width = door_rect->width;
4462 // printf("::: k == %d [%d] \n", k, start_step);
4465 if (pos->step_yoffset < 0) // door part on bottom side
4468 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4471 if (dst_yy + height > door_rect->height)
4472 height = door_rect->height - dst_yy;
4474 else // door part on top side
4477 dst_yy = pos->y - kk * pos->step_yoffset;
4481 src_yy = ABS(dst_yy);
4485 height = g->height - src_yy;
4488 src_x = g_src_x + src_xx;
4489 src_y = g_src_y + src_yy;
4491 dst_x = door_rect->x + dst_xx;
4492 dst_y = door_rect->y + dst_yy;
4494 is_panel_and_door_has_closed =
4497 panel_has_doors[door_index] &&
4498 k >= num_move_steps_doors_only - 1);
4500 if (width >= 0 && width <= g->width &&
4501 height >= 0 && height <= g->height &&
4502 !is_panel_and_door_has_closed)
4504 if (is_panel || !pos->draw_masked)
4505 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4508 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4512 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4514 if ((part_opening && (width < 0 || height < 0)) ||
4515 (part_closing && (width >= g->width && height >= g->height)))
4516 door_part_done[nr] = TRUE;
4518 // continue door part animations, but not panel after door has closed
4519 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4520 door_part_done_all = FALSE;
4523 if (!(door_state & DOOR_NO_DELAY))
4527 if (game_status == GAME_MODE_MAIN)
4530 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4532 current_move_delay += max_step_delay;
4535 if (door_part_done_all)
4540 if (door_state & DOOR_ACTION_1)
4541 door1 = door_state & DOOR_ACTION_1;
4542 if (door_state & DOOR_ACTION_2)
4543 door2 = door_state & DOOR_ACTION_2;
4545 // draw masked border over door area
4546 DrawMaskedBorder(REDRAW_DOOR_1);
4547 DrawMaskedBorder(REDRAW_DOOR_2);
4549 return (door1 | door2);
4552 static boolean useSpecialEditorDoor()
4554 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4555 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4557 // do not draw special editor door if editor border defined or redefined
4558 if (graphic_info[graphic].bitmap != NULL || redefined)
4561 // do not draw special editor door if global border defined to be empty
4562 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4565 // do not draw special editor door if viewport definitions do not match
4569 EY + EYSIZE != VY + VYSIZE)
4575 void DrawSpecialEditorDoor()
4577 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4578 int top_border_width = gfx1->width;
4579 int top_border_height = gfx1->height;
4580 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4581 int ex = EX - outer_border;
4582 int ey = EY - outer_border;
4583 int vy = VY - outer_border;
4584 int exsize = EXSIZE + 2 * outer_border;
4586 if (!useSpecialEditorDoor())
4589 /* draw bigger level editor toolbox window */
4590 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4591 top_border_width, top_border_height, ex, ey - top_border_height);
4592 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4593 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4595 redraw_mask |= REDRAW_ALL;
4598 void UndrawSpecialEditorDoor()
4600 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4601 int top_border_width = gfx1->width;
4602 int top_border_height = gfx1->height;
4603 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4604 int ex = EX - outer_border;
4605 int ey = EY - outer_border;
4606 int ey_top = ey - top_border_height;
4607 int exsize = EXSIZE + 2 * outer_border;
4608 int eysize = EYSIZE + 2 * outer_border;
4610 if (!useSpecialEditorDoor())
4613 /* draw normal tape recorder window */
4614 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4616 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4617 ex, ey_top, top_border_width, top_border_height,
4619 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4620 ex, ey, exsize, eysize, ex, ey);
4624 // if screen background is set to "[NONE]", clear editor toolbox window
4625 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4626 ClearRectangle(drawto, ex, ey, exsize, eysize);
4629 redraw_mask |= REDRAW_ALL;
4633 /* ---------- new tool button stuff ---------------------------------------- */
4638 struct TextPosInfo *pos;
4641 } toolbutton_info[NUM_TOOL_BUTTONS] =
4644 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4645 TOOL_CTRL_ID_YES, "yes"
4648 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4649 TOOL_CTRL_ID_NO, "no"
4652 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4653 TOOL_CTRL_ID_CONFIRM, "confirm"
4656 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4657 TOOL_CTRL_ID_PLAYER_1, "player 1"
4660 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4661 TOOL_CTRL_ID_PLAYER_2, "player 2"
4664 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4665 TOOL_CTRL_ID_PLAYER_3, "player 3"
4668 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4669 TOOL_CTRL_ID_PLAYER_4, "player 4"
4673 void CreateToolButtons()
4677 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4679 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4680 struct TextPosInfo *pos = toolbutton_info[i].pos;
4681 struct GadgetInfo *gi;
4682 Bitmap *deco_bitmap = None;
4683 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4684 unsigned int event_mask = GD_EVENT_RELEASED;
4687 int gd_x = gfx->src_x;
4688 int gd_y = gfx->src_y;
4689 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4690 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4693 if (global.use_envelope_request)
4694 setRequestPosition(&dx, &dy, TRUE);
4696 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4698 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4700 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4701 pos->size, &deco_bitmap, &deco_x, &deco_y);
4702 deco_xpos = (gfx->width - pos->size) / 2;
4703 deco_ypos = (gfx->height - pos->size) / 2;
4706 gi = CreateGadget(GDI_CUSTOM_ID, id,
4707 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4708 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4709 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4710 GDI_WIDTH, gfx->width,
4711 GDI_HEIGHT, gfx->height,
4712 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4713 GDI_STATE, GD_BUTTON_UNPRESSED,
4714 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4715 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4716 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4717 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4718 GDI_DECORATION_SIZE, pos->size, pos->size,
4719 GDI_DECORATION_SHIFTING, 1, 1,
4720 GDI_DIRECT_DRAW, FALSE,
4721 GDI_EVENT_MASK, event_mask,
4722 GDI_CALLBACK_ACTION, HandleToolButtons,
4726 Error(ERR_EXIT, "cannot create gadget");
4728 tool_gadget[id] = gi;
4732 void FreeToolButtons()
4736 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4737 FreeGadget(tool_gadget[i]);
4740 static void UnmapToolButtons()
4744 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4745 UnmapGadget(tool_gadget[i]);
4748 static void HandleToolButtons(struct GadgetInfo *gi)
4750 request_gadget_id = gi->custom_id;
4753 static struct Mapping_EM_to_RND_object
4756 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4757 boolean is_backside; /* backside of moving element */
4763 em_object_mapping_list[] =
4766 Xblank, TRUE, FALSE,
4770 Yacid_splash_eB, FALSE, FALSE,
4771 EL_ACID_SPLASH_RIGHT, -1, -1
4774 Yacid_splash_wB, FALSE, FALSE,
4775 EL_ACID_SPLASH_LEFT, -1, -1
4778 #ifdef EM_ENGINE_BAD_ROLL
4780 Xstone_force_e, FALSE, FALSE,
4781 EL_ROCK, -1, MV_BIT_RIGHT
4784 Xstone_force_w, FALSE, FALSE,
4785 EL_ROCK, -1, MV_BIT_LEFT
4788 Xnut_force_e, FALSE, FALSE,
4789 EL_NUT, -1, MV_BIT_RIGHT
4792 Xnut_force_w, FALSE, FALSE,
4793 EL_NUT, -1, MV_BIT_LEFT
4796 Xspring_force_e, FALSE, FALSE,
4797 EL_SPRING, -1, MV_BIT_RIGHT
4800 Xspring_force_w, FALSE, FALSE,
4801 EL_SPRING, -1, MV_BIT_LEFT
4804 Xemerald_force_e, FALSE, FALSE,
4805 EL_EMERALD, -1, MV_BIT_RIGHT
4808 Xemerald_force_w, FALSE, FALSE,
4809 EL_EMERALD, -1, MV_BIT_LEFT
4812 Xdiamond_force_e, FALSE, FALSE,
4813 EL_DIAMOND, -1, MV_BIT_RIGHT
4816 Xdiamond_force_w, FALSE, FALSE,
4817 EL_DIAMOND, -1, MV_BIT_LEFT
4820 Xbomb_force_e, FALSE, FALSE,
4821 EL_BOMB, -1, MV_BIT_RIGHT
4824 Xbomb_force_w, FALSE, FALSE,
4825 EL_BOMB, -1, MV_BIT_LEFT
4827 #endif /* EM_ENGINE_BAD_ROLL */
4830 Xstone, TRUE, FALSE,
4834 Xstone_pause, FALSE, FALSE,
4838 Xstone_fall, FALSE, FALSE,
4842 Ystone_s, FALSE, FALSE,
4843 EL_ROCK, ACTION_FALLING, -1
4846 Ystone_sB, FALSE, TRUE,
4847 EL_ROCK, ACTION_FALLING, -1
4850 Ystone_e, FALSE, FALSE,
4851 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4854 Ystone_eB, FALSE, TRUE,
4855 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4858 Ystone_w, FALSE, FALSE,
4859 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4862 Ystone_wB, FALSE, TRUE,
4863 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4870 Xnut_pause, FALSE, FALSE,
4874 Xnut_fall, FALSE, FALSE,
4878 Ynut_s, FALSE, FALSE,
4879 EL_NUT, ACTION_FALLING, -1
4882 Ynut_sB, FALSE, TRUE,
4883 EL_NUT, ACTION_FALLING, -1
4886 Ynut_e, FALSE, FALSE,
4887 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4890 Ynut_eB, FALSE, TRUE,
4891 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4894 Ynut_w, FALSE, FALSE,
4895 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4898 Ynut_wB, FALSE, TRUE,
4899 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4902 Xbug_n, TRUE, FALSE,
4906 Xbug_e, TRUE, FALSE,
4907 EL_BUG_RIGHT, -1, -1
4910 Xbug_s, TRUE, FALSE,
4914 Xbug_w, TRUE, FALSE,
4918 Xbug_gon, FALSE, FALSE,
4922 Xbug_goe, FALSE, FALSE,
4923 EL_BUG_RIGHT, -1, -1
4926 Xbug_gos, FALSE, FALSE,
4930 Xbug_gow, FALSE, FALSE,
4934 Ybug_n, FALSE, FALSE,
4935 EL_BUG, ACTION_MOVING, MV_BIT_UP
4938 Ybug_nB, FALSE, TRUE,
4939 EL_BUG, ACTION_MOVING, MV_BIT_UP
4942 Ybug_e, FALSE, FALSE,
4943 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4946 Ybug_eB, FALSE, TRUE,
4947 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4950 Ybug_s, FALSE, FALSE,
4951 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4954 Ybug_sB, FALSE, TRUE,
4955 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4958 Ybug_w, FALSE, FALSE,
4959 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4962 Ybug_wB, FALSE, TRUE,
4963 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4966 Ybug_w_n, FALSE, FALSE,
4967 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4970 Ybug_n_e, FALSE, FALSE,
4971 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4974 Ybug_e_s, FALSE, FALSE,
4975 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4978 Ybug_s_w, FALSE, FALSE,
4979 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4982 Ybug_e_n, FALSE, FALSE,
4983 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4986 Ybug_s_e, FALSE, FALSE,
4987 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4990 Ybug_w_s, FALSE, FALSE,
4991 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4994 Ybug_n_w, FALSE, FALSE,
4995 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4998 Ybug_stone, FALSE, FALSE,
4999 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5002 Ybug_spring, FALSE, FALSE,
5003 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5006 Xtank_n, TRUE, FALSE,
5007 EL_SPACESHIP_UP, -1, -1
5010 Xtank_e, TRUE, FALSE,
5011 EL_SPACESHIP_RIGHT, -1, -1
5014 Xtank_s, TRUE, FALSE,
5015 EL_SPACESHIP_DOWN, -1, -1
5018 Xtank_w, TRUE, FALSE,
5019 EL_SPACESHIP_LEFT, -1, -1
5022 Xtank_gon, FALSE, FALSE,
5023 EL_SPACESHIP_UP, -1, -1
5026 Xtank_goe, FALSE, FALSE,
5027 EL_SPACESHIP_RIGHT, -1, -1
5030 Xtank_gos, FALSE, FALSE,
5031 EL_SPACESHIP_DOWN, -1, -1
5034 Xtank_gow, FALSE, FALSE,
5035 EL_SPACESHIP_LEFT, -1, -1
5038 Ytank_n, FALSE, FALSE,
5039 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5042 Ytank_nB, FALSE, TRUE,
5043 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5046 Ytank_e, FALSE, FALSE,
5047 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5050 Ytank_eB, FALSE, TRUE,
5051 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5054 Ytank_s, FALSE, FALSE,
5055 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5058 Ytank_sB, FALSE, TRUE,
5059 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5062 Ytank_w, FALSE, FALSE,
5063 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5066 Ytank_wB, FALSE, TRUE,
5067 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5070 Ytank_w_n, FALSE, FALSE,
5071 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5074 Ytank_n_e, FALSE, FALSE,
5075 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5078 Ytank_e_s, FALSE, FALSE,
5079 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5082 Ytank_s_w, FALSE, FALSE,
5083 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5086 Ytank_e_n, FALSE, FALSE,
5087 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5090 Ytank_s_e, FALSE, FALSE,
5091 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5094 Ytank_w_s, FALSE, FALSE,
5095 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5098 Ytank_n_w, FALSE, FALSE,
5099 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5102 Ytank_stone, FALSE, FALSE,
5103 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5106 Ytank_spring, FALSE, FALSE,
5107 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5110 Xandroid, TRUE, FALSE,
5111 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5114 Xandroid_1_n, FALSE, FALSE,
5115 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5118 Xandroid_2_n, FALSE, FALSE,
5119 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5122 Xandroid_1_e, FALSE, FALSE,
5123 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5126 Xandroid_2_e, FALSE, FALSE,
5127 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5130 Xandroid_1_w, FALSE, FALSE,
5131 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5134 Xandroid_2_w, FALSE, FALSE,
5135 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5138 Xandroid_1_s, FALSE, FALSE,
5139 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5142 Xandroid_2_s, FALSE, FALSE,
5143 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5146 Yandroid_n, FALSE, FALSE,
5147 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5150 Yandroid_nB, FALSE, TRUE,
5151 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5154 Yandroid_ne, FALSE, FALSE,
5155 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5158 Yandroid_neB, FALSE, TRUE,
5159 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5162 Yandroid_e, FALSE, FALSE,
5163 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5166 Yandroid_eB, FALSE, TRUE,
5167 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5170 Yandroid_se, FALSE, FALSE,
5171 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5174 Yandroid_seB, FALSE, TRUE,
5175 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5178 Yandroid_s, FALSE, FALSE,
5179 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5182 Yandroid_sB, FALSE, TRUE,
5183 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5186 Yandroid_sw, FALSE, FALSE,
5187 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5190 Yandroid_swB, FALSE, TRUE,
5191 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5194 Yandroid_w, FALSE, FALSE,
5195 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5198 Yandroid_wB, FALSE, TRUE,
5199 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5202 Yandroid_nw, FALSE, FALSE,
5203 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5206 Yandroid_nwB, FALSE, TRUE,
5207 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5210 Xspring, TRUE, FALSE,
5214 Xspring_pause, FALSE, FALSE,
5218 Xspring_e, FALSE, FALSE,
5222 Xspring_w, FALSE, FALSE,
5226 Xspring_fall, FALSE, FALSE,
5230 Yspring_s, FALSE, FALSE,
5231 EL_SPRING, ACTION_FALLING, -1
5234 Yspring_sB, FALSE, TRUE,
5235 EL_SPRING, ACTION_FALLING, -1
5238 Yspring_e, FALSE, FALSE,
5239 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5242 Yspring_eB, FALSE, TRUE,
5243 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5246 Yspring_w, FALSE, FALSE,
5247 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5250 Yspring_wB, FALSE, TRUE,
5251 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5254 Yspring_kill_e, FALSE, FALSE,
5255 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5258 Yspring_kill_eB, FALSE, TRUE,
5259 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5262 Yspring_kill_w, FALSE, FALSE,
5263 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5266 Yspring_kill_wB, FALSE, TRUE,
5267 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5270 Xeater_n, TRUE, FALSE,
5271 EL_YAMYAM_UP, -1, -1
5274 Xeater_e, TRUE, FALSE,
5275 EL_YAMYAM_RIGHT, -1, -1
5278 Xeater_w, TRUE, FALSE,
5279 EL_YAMYAM_LEFT, -1, -1
5282 Xeater_s, TRUE, FALSE,
5283 EL_YAMYAM_DOWN, -1, -1
5286 Yeater_n, FALSE, FALSE,
5287 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5290 Yeater_nB, FALSE, TRUE,
5291 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5294 Yeater_e, FALSE, FALSE,
5295 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5298 Yeater_eB, FALSE, TRUE,
5299 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5302 Yeater_s, FALSE, FALSE,
5303 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5306 Yeater_sB, FALSE, TRUE,
5307 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5310 Yeater_w, FALSE, FALSE,
5311 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5314 Yeater_wB, FALSE, TRUE,
5315 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5318 Yeater_stone, FALSE, FALSE,
5319 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5322 Yeater_spring, FALSE, FALSE,
5323 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5326 Xalien, TRUE, FALSE,
5330 Xalien_pause, FALSE, FALSE,
5334 Yalien_n, FALSE, FALSE,
5335 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5338 Yalien_nB, FALSE, TRUE,
5339 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5342 Yalien_e, FALSE, FALSE,
5343 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5346 Yalien_eB, FALSE, TRUE,
5347 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5350 Yalien_s, FALSE, FALSE,
5351 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5354 Yalien_sB, FALSE, TRUE,
5355 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5358 Yalien_w, FALSE, FALSE,
5359 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5362 Yalien_wB, FALSE, TRUE,
5363 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5366 Yalien_stone, FALSE, FALSE,
5367 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5370 Yalien_spring, FALSE, FALSE,
5371 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5374 Xemerald, TRUE, FALSE,
5378 Xemerald_pause, FALSE, FALSE,
5382 Xemerald_fall, FALSE, FALSE,
5386 Xemerald_shine, FALSE, FALSE,
5387 EL_EMERALD, ACTION_TWINKLING, -1
5390 Yemerald_s, FALSE, FALSE,
5391 EL_EMERALD, ACTION_FALLING, -1
5394 Yemerald_sB, FALSE, TRUE,
5395 EL_EMERALD, ACTION_FALLING, -1
5398 Yemerald_e, FALSE, FALSE,
5399 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5402 Yemerald_eB, FALSE, TRUE,
5403 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5406 Yemerald_w, FALSE, FALSE,
5407 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5410 Yemerald_wB, FALSE, TRUE,
5411 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5414 Yemerald_eat, FALSE, FALSE,
5415 EL_EMERALD, ACTION_COLLECTING, -1
5418 Yemerald_stone, FALSE, FALSE,
5419 EL_NUT, ACTION_BREAKING, -1
5422 Xdiamond, TRUE, FALSE,
5426 Xdiamond_pause, FALSE, FALSE,
5430 Xdiamond_fall, FALSE, FALSE,
5434 Xdiamond_shine, FALSE, FALSE,
5435 EL_DIAMOND, ACTION_TWINKLING, -1
5438 Ydiamond_s, FALSE, FALSE,
5439 EL_DIAMOND, ACTION_FALLING, -1
5442 Ydiamond_sB, FALSE, TRUE,
5443 EL_DIAMOND, ACTION_FALLING, -1
5446 Ydiamond_e, FALSE, FALSE,
5447 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5450 Ydiamond_eB, FALSE, TRUE,
5451 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5454 Ydiamond_w, FALSE, FALSE,
5455 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5458 Ydiamond_wB, FALSE, TRUE,
5459 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5462 Ydiamond_eat, FALSE, FALSE,
5463 EL_DIAMOND, ACTION_COLLECTING, -1
5466 Ydiamond_stone, FALSE, FALSE,
5467 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5470 Xdrip_fall, TRUE, FALSE,
5471 EL_AMOEBA_DROP, -1, -1
5474 Xdrip_stretch, FALSE, FALSE,
5475 EL_AMOEBA_DROP, ACTION_FALLING, -1
5478 Xdrip_stretchB, FALSE, TRUE,
5479 EL_AMOEBA_DROP, ACTION_FALLING, -1
5482 Xdrip_eat, FALSE, FALSE,
5483 EL_AMOEBA_DROP, ACTION_GROWING, -1
5486 Ydrip_s1, FALSE, FALSE,
5487 EL_AMOEBA_DROP, ACTION_FALLING, -1
5490 Ydrip_s1B, FALSE, TRUE,
5491 EL_AMOEBA_DROP, ACTION_FALLING, -1
5494 Ydrip_s2, FALSE, FALSE,
5495 EL_AMOEBA_DROP, ACTION_FALLING, -1
5498 Ydrip_s2B, FALSE, TRUE,
5499 EL_AMOEBA_DROP, ACTION_FALLING, -1
5506 Xbomb_pause, FALSE, FALSE,
5510 Xbomb_fall, FALSE, FALSE,
5514 Ybomb_s, FALSE, FALSE,
5515 EL_BOMB, ACTION_FALLING, -1
5518 Ybomb_sB, FALSE, TRUE,
5519 EL_BOMB, ACTION_FALLING, -1
5522 Ybomb_e, FALSE, FALSE,
5523 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5526 Ybomb_eB, FALSE, TRUE,
5527 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5530 Ybomb_w, FALSE, FALSE,
5531 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5534 Ybomb_wB, FALSE, TRUE,
5535 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5538 Ybomb_eat, FALSE, FALSE,
5539 EL_BOMB, ACTION_ACTIVATING, -1
5542 Xballoon, TRUE, FALSE,
5546 Yballoon_n, FALSE, FALSE,
5547 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5550 Yballoon_nB, FALSE, TRUE,
5551 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5554 Yballoon_e, FALSE, FALSE,
5555 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5558 Yballoon_eB, FALSE, TRUE,
5559 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5562 Yballoon_s, FALSE, FALSE,
5563 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5566 Yballoon_sB, FALSE, TRUE,
5567 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5570 Yballoon_w, FALSE, FALSE,
5571 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5574 Yballoon_wB, FALSE, TRUE,
5575 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5578 Xgrass, TRUE, FALSE,
5579 EL_EMC_GRASS, -1, -1
5582 Ygrass_nB, FALSE, FALSE,
5583 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5586 Ygrass_eB, FALSE, FALSE,
5587 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5590 Ygrass_sB, FALSE, FALSE,
5591 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5594 Ygrass_wB, FALSE, FALSE,
5595 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5602 Ydirt_nB, FALSE, FALSE,
5603 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5606 Ydirt_eB, FALSE, FALSE,
5607 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5610 Ydirt_sB, FALSE, FALSE,
5611 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5614 Ydirt_wB, FALSE, FALSE,
5615 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5618 Xacid_ne, TRUE, FALSE,
5619 EL_ACID_POOL_TOPRIGHT, -1, -1
5622 Xacid_se, TRUE, FALSE,
5623 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5626 Xacid_s, TRUE, FALSE,
5627 EL_ACID_POOL_BOTTOM, -1, -1
5630 Xacid_sw, TRUE, FALSE,
5631 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5634 Xacid_nw, TRUE, FALSE,
5635 EL_ACID_POOL_TOPLEFT, -1, -1
5638 Xacid_1, TRUE, FALSE,
5642 Xacid_2, FALSE, FALSE,
5646 Xacid_3, FALSE, FALSE,
5650 Xacid_4, FALSE, FALSE,
5654 Xacid_5, FALSE, FALSE,
5658 Xacid_6, FALSE, FALSE,
5662 Xacid_7, FALSE, FALSE,
5666 Xacid_8, FALSE, FALSE,
5670 Xball_1, TRUE, FALSE,
5671 EL_EMC_MAGIC_BALL, -1, -1
5674 Xball_1B, FALSE, FALSE,
5675 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5678 Xball_2, FALSE, FALSE,
5679 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5682 Xball_2B, FALSE, FALSE,
5683 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5686 Yball_eat, FALSE, FALSE,
5687 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5690 Ykey_1_eat, FALSE, FALSE,
5691 EL_EM_KEY_1, ACTION_COLLECTING, -1
5694 Ykey_2_eat, FALSE, FALSE,
5695 EL_EM_KEY_2, ACTION_COLLECTING, -1
5698 Ykey_3_eat, FALSE, FALSE,
5699 EL_EM_KEY_3, ACTION_COLLECTING, -1
5702 Ykey_4_eat, FALSE, FALSE,
5703 EL_EM_KEY_4, ACTION_COLLECTING, -1
5706 Ykey_5_eat, FALSE, FALSE,
5707 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5710 Ykey_6_eat, FALSE, FALSE,
5711 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5714 Ykey_7_eat, FALSE, FALSE,
5715 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5718 Ykey_8_eat, FALSE, FALSE,
5719 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5722 Ylenses_eat, FALSE, FALSE,
5723 EL_EMC_LENSES, ACTION_COLLECTING, -1
5726 Ymagnify_eat, FALSE, FALSE,
5727 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5730 Ygrass_eat, FALSE, FALSE,
5731 EL_EMC_GRASS, ACTION_SNAPPING, -1
5734 Ydirt_eat, FALSE, FALSE,
5735 EL_SAND, ACTION_SNAPPING, -1
5738 Xgrow_ns, TRUE, FALSE,
5739 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5742 Ygrow_ns_eat, FALSE, FALSE,
5743 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5746 Xgrow_ew, TRUE, FALSE,
5747 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5750 Ygrow_ew_eat, FALSE, FALSE,
5751 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5754 Xwonderwall, TRUE, FALSE,
5755 EL_MAGIC_WALL, -1, -1
5758 XwonderwallB, FALSE, FALSE,
5759 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5762 Xamoeba_1, TRUE, FALSE,
5763 EL_AMOEBA_DRY, ACTION_OTHER, -1
5766 Xamoeba_2, FALSE, FALSE,
5767 EL_AMOEBA_DRY, ACTION_OTHER, -1
5770 Xamoeba_3, FALSE, FALSE,
5771 EL_AMOEBA_DRY, ACTION_OTHER, -1
5774 Xamoeba_4, FALSE, FALSE,
5775 EL_AMOEBA_DRY, ACTION_OTHER, -1
5778 Xamoeba_5, TRUE, FALSE,
5779 EL_AMOEBA_WET, ACTION_OTHER, -1
5782 Xamoeba_6, FALSE, FALSE,
5783 EL_AMOEBA_WET, ACTION_OTHER, -1
5786 Xamoeba_7, FALSE, FALSE,
5787 EL_AMOEBA_WET, ACTION_OTHER, -1
5790 Xamoeba_8, FALSE, FALSE,
5791 EL_AMOEBA_WET, ACTION_OTHER, -1
5794 Xdoor_1, TRUE, FALSE,
5795 EL_EM_GATE_1, -1, -1
5798 Xdoor_2, TRUE, FALSE,
5799 EL_EM_GATE_2, -1, -1
5802 Xdoor_3, TRUE, FALSE,
5803 EL_EM_GATE_3, -1, -1
5806 Xdoor_4, TRUE, FALSE,
5807 EL_EM_GATE_4, -1, -1
5810 Xdoor_5, TRUE, FALSE,
5811 EL_EMC_GATE_5, -1, -1
5814 Xdoor_6, TRUE, FALSE,
5815 EL_EMC_GATE_6, -1, -1
5818 Xdoor_7, TRUE, FALSE,
5819 EL_EMC_GATE_7, -1, -1
5822 Xdoor_8, TRUE, FALSE,
5823 EL_EMC_GATE_8, -1, -1
5826 Xkey_1, TRUE, FALSE,
5830 Xkey_2, TRUE, FALSE,
5834 Xkey_3, TRUE, FALSE,
5838 Xkey_4, TRUE, FALSE,
5842 Xkey_5, TRUE, FALSE,
5843 EL_EMC_KEY_5, -1, -1
5846 Xkey_6, TRUE, FALSE,
5847 EL_EMC_KEY_6, -1, -1
5850 Xkey_7, TRUE, FALSE,
5851 EL_EMC_KEY_7, -1, -1
5854 Xkey_8, TRUE, FALSE,
5855 EL_EMC_KEY_8, -1, -1
5858 Xwind_n, TRUE, FALSE,
5859 EL_BALLOON_SWITCH_UP, -1, -1
5862 Xwind_e, TRUE, FALSE,
5863 EL_BALLOON_SWITCH_RIGHT, -1, -1
5866 Xwind_s, TRUE, FALSE,
5867 EL_BALLOON_SWITCH_DOWN, -1, -1
5870 Xwind_w, TRUE, FALSE,
5871 EL_BALLOON_SWITCH_LEFT, -1, -1
5874 Xwind_nesw, TRUE, FALSE,
5875 EL_BALLOON_SWITCH_ANY, -1, -1
5878 Xwind_stop, TRUE, FALSE,
5879 EL_BALLOON_SWITCH_NONE, -1, -1
5883 EL_EM_EXIT_CLOSED, -1, -1
5886 Xexit_1, TRUE, FALSE,
5887 EL_EM_EXIT_OPEN, -1, -1
5890 Xexit_2, FALSE, FALSE,
5891 EL_EM_EXIT_OPEN, -1, -1
5894 Xexit_3, FALSE, FALSE,
5895 EL_EM_EXIT_OPEN, -1, -1
5898 Xdynamite, TRUE, FALSE,
5899 EL_EM_DYNAMITE, -1, -1
5902 Ydynamite_eat, FALSE, FALSE,
5903 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5906 Xdynamite_1, TRUE, FALSE,
5907 EL_EM_DYNAMITE_ACTIVE, -1, -1
5910 Xdynamite_2, FALSE, FALSE,
5911 EL_EM_DYNAMITE_ACTIVE, -1, -1
5914 Xdynamite_3, FALSE, FALSE,
5915 EL_EM_DYNAMITE_ACTIVE, -1, -1
5918 Xdynamite_4, FALSE, FALSE,
5919 EL_EM_DYNAMITE_ACTIVE, -1, -1
5922 Xbumper, TRUE, FALSE,
5923 EL_EMC_SPRING_BUMPER, -1, -1
5926 XbumperB, FALSE, FALSE,
5927 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5930 Xwheel, TRUE, FALSE,
5931 EL_ROBOT_WHEEL, -1, -1
5934 XwheelB, FALSE, FALSE,
5935 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5938 Xswitch, TRUE, FALSE,
5939 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5942 XswitchB, FALSE, FALSE,
5943 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5947 EL_QUICKSAND_EMPTY, -1, -1
5950 Xsand_stone, TRUE, FALSE,
5951 EL_QUICKSAND_FULL, -1, -1
5954 Xsand_stonein_1, FALSE, TRUE,
5955 EL_ROCK, ACTION_FILLING, -1
5958 Xsand_stonein_2, FALSE, TRUE,
5959 EL_ROCK, ACTION_FILLING, -1
5962 Xsand_stonein_3, FALSE, TRUE,
5963 EL_ROCK, ACTION_FILLING, -1
5966 Xsand_stonein_4, FALSE, TRUE,
5967 EL_ROCK, ACTION_FILLING, -1
5970 Xsand_stonesand_1, FALSE, FALSE,
5971 EL_QUICKSAND_EMPTYING, -1, -1
5974 Xsand_stonesand_2, FALSE, FALSE,
5975 EL_QUICKSAND_EMPTYING, -1, -1
5978 Xsand_stonesand_3, FALSE, FALSE,
5979 EL_QUICKSAND_EMPTYING, -1, -1
5982 Xsand_stonesand_4, FALSE, FALSE,
5983 EL_QUICKSAND_EMPTYING, -1, -1
5986 Xsand_stonesand_quickout_1, FALSE, FALSE,
5987 EL_QUICKSAND_EMPTYING, -1, -1
5990 Xsand_stonesand_quickout_2, FALSE, FALSE,
5991 EL_QUICKSAND_EMPTYING, -1, -1
5994 Xsand_stoneout_1, FALSE, FALSE,
5995 EL_ROCK, ACTION_EMPTYING, -1
5998 Xsand_stoneout_2, FALSE, FALSE,
5999 EL_ROCK, ACTION_EMPTYING, -1
6002 Xsand_sandstone_1, FALSE, FALSE,
6003 EL_QUICKSAND_FILLING, -1, -1
6006 Xsand_sandstone_2, FALSE, FALSE,
6007 EL_QUICKSAND_FILLING, -1, -1
6010 Xsand_sandstone_3, FALSE, FALSE,
6011 EL_QUICKSAND_FILLING, -1, -1
6014 Xsand_sandstone_4, FALSE, FALSE,
6015 EL_QUICKSAND_FILLING, -1, -1
6018 Xplant, TRUE, FALSE,
6019 EL_EMC_PLANT, -1, -1
6022 Yplant, FALSE, FALSE,
6023 EL_EMC_PLANT, -1, -1
6026 Xlenses, TRUE, FALSE,
6027 EL_EMC_LENSES, -1, -1
6030 Xmagnify, TRUE, FALSE,
6031 EL_EMC_MAGNIFIER, -1, -1
6034 Xdripper, TRUE, FALSE,
6035 EL_EMC_DRIPPER, -1, -1
6038 XdripperB, FALSE, FALSE,
6039 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6042 Xfake_blank, TRUE, FALSE,
6043 EL_INVISIBLE_WALL, -1, -1
6046 Xfake_blankB, FALSE, FALSE,
6047 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6050 Xfake_grass, TRUE, FALSE,
6051 EL_EMC_FAKE_GRASS, -1, -1
6054 Xfake_grassB, FALSE, FALSE,
6055 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6058 Xfake_door_1, TRUE, FALSE,
6059 EL_EM_GATE_1_GRAY, -1, -1
6062 Xfake_door_2, TRUE, FALSE,
6063 EL_EM_GATE_2_GRAY, -1, -1
6066 Xfake_door_3, TRUE, FALSE,
6067 EL_EM_GATE_3_GRAY, -1, -1
6070 Xfake_door_4, TRUE, FALSE,
6071 EL_EM_GATE_4_GRAY, -1, -1
6074 Xfake_door_5, TRUE, FALSE,
6075 EL_EMC_GATE_5_GRAY, -1, -1
6078 Xfake_door_6, TRUE, FALSE,
6079 EL_EMC_GATE_6_GRAY, -1, -1
6082 Xfake_door_7, TRUE, FALSE,
6083 EL_EMC_GATE_7_GRAY, -1, -1
6086 Xfake_door_8, TRUE, FALSE,
6087 EL_EMC_GATE_8_GRAY, -1, -1
6090 Xfake_acid_1, TRUE, FALSE,
6091 EL_EMC_FAKE_ACID, -1, -1
6094 Xfake_acid_2, FALSE, FALSE,
6095 EL_EMC_FAKE_ACID, -1, -1
6098 Xfake_acid_3, FALSE, FALSE,
6099 EL_EMC_FAKE_ACID, -1, -1
6102 Xfake_acid_4, FALSE, FALSE,
6103 EL_EMC_FAKE_ACID, -1, -1
6106 Xfake_acid_5, FALSE, FALSE,
6107 EL_EMC_FAKE_ACID, -1, -1
6110 Xfake_acid_6, FALSE, FALSE,
6111 EL_EMC_FAKE_ACID, -1, -1
6114 Xfake_acid_7, FALSE, FALSE,
6115 EL_EMC_FAKE_ACID, -1, -1
6118 Xfake_acid_8, FALSE, FALSE,
6119 EL_EMC_FAKE_ACID, -1, -1
6122 Xsteel_1, TRUE, FALSE,
6123 EL_STEELWALL, -1, -1
6126 Xsteel_2, TRUE, FALSE,
6127 EL_EMC_STEELWALL_2, -1, -1
6130 Xsteel_3, TRUE, FALSE,
6131 EL_EMC_STEELWALL_3, -1, -1
6134 Xsteel_4, TRUE, FALSE,
6135 EL_EMC_STEELWALL_4, -1, -1
6138 Xwall_1, TRUE, FALSE,
6142 Xwall_2, TRUE, FALSE,
6143 EL_EMC_WALL_14, -1, -1
6146 Xwall_3, TRUE, FALSE,
6147 EL_EMC_WALL_15, -1, -1
6150 Xwall_4, TRUE, FALSE,
6151 EL_EMC_WALL_16, -1, -1
6154 Xround_wall_1, TRUE, FALSE,
6155 EL_WALL_SLIPPERY, -1, -1
6158 Xround_wall_2, TRUE, FALSE,
6159 EL_EMC_WALL_SLIPPERY_2, -1, -1
6162 Xround_wall_3, TRUE, FALSE,
6163 EL_EMC_WALL_SLIPPERY_3, -1, -1
6166 Xround_wall_4, TRUE, FALSE,
6167 EL_EMC_WALL_SLIPPERY_4, -1, -1
6170 Xdecor_1, TRUE, FALSE,
6171 EL_EMC_WALL_8, -1, -1
6174 Xdecor_2, TRUE, FALSE,
6175 EL_EMC_WALL_6, -1, -1
6178 Xdecor_3, TRUE, FALSE,
6179 EL_EMC_WALL_4, -1, -1
6182 Xdecor_4, TRUE, FALSE,
6183 EL_EMC_WALL_7, -1, -1
6186 Xdecor_5, TRUE, FALSE,
6187 EL_EMC_WALL_5, -1, -1
6190 Xdecor_6, TRUE, FALSE,
6191 EL_EMC_WALL_9, -1, -1
6194 Xdecor_7, TRUE, FALSE,
6195 EL_EMC_WALL_10, -1, -1
6198 Xdecor_8, TRUE, FALSE,
6199 EL_EMC_WALL_1, -1, -1
6202 Xdecor_9, TRUE, FALSE,
6203 EL_EMC_WALL_2, -1, -1
6206 Xdecor_10, TRUE, FALSE,
6207 EL_EMC_WALL_3, -1, -1
6210 Xdecor_11, TRUE, FALSE,
6211 EL_EMC_WALL_11, -1, -1
6214 Xdecor_12, TRUE, FALSE,
6215 EL_EMC_WALL_12, -1, -1
6218 Xalpha_0, TRUE, FALSE,
6219 EL_CHAR('0'), -1, -1
6222 Xalpha_1, TRUE, FALSE,
6223 EL_CHAR('1'), -1, -1
6226 Xalpha_2, TRUE, FALSE,
6227 EL_CHAR('2'), -1, -1
6230 Xalpha_3, TRUE, FALSE,
6231 EL_CHAR('3'), -1, -1
6234 Xalpha_4, TRUE, FALSE,
6235 EL_CHAR('4'), -1, -1
6238 Xalpha_5, TRUE, FALSE,
6239 EL_CHAR('5'), -1, -1
6242 Xalpha_6, TRUE, FALSE,
6243 EL_CHAR('6'), -1, -1
6246 Xalpha_7, TRUE, FALSE,
6247 EL_CHAR('7'), -1, -1
6250 Xalpha_8, TRUE, FALSE,
6251 EL_CHAR('8'), -1, -1
6254 Xalpha_9, TRUE, FALSE,
6255 EL_CHAR('9'), -1, -1
6258 Xalpha_excla, TRUE, FALSE,
6259 EL_CHAR('!'), -1, -1
6262 Xalpha_quote, TRUE, FALSE,
6263 EL_CHAR('"'), -1, -1
6266 Xalpha_comma, TRUE, FALSE,
6267 EL_CHAR(','), -1, -1
6270 Xalpha_minus, TRUE, FALSE,
6271 EL_CHAR('-'), -1, -1
6274 Xalpha_perio, TRUE, FALSE,
6275 EL_CHAR('.'), -1, -1
6278 Xalpha_colon, TRUE, FALSE,
6279 EL_CHAR(':'), -1, -1
6282 Xalpha_quest, TRUE, FALSE,
6283 EL_CHAR('?'), -1, -1
6286 Xalpha_a, TRUE, FALSE,
6287 EL_CHAR('A'), -1, -1
6290 Xalpha_b, TRUE, FALSE,
6291 EL_CHAR('B'), -1, -1
6294 Xalpha_c, TRUE, FALSE,
6295 EL_CHAR('C'), -1, -1
6298 Xalpha_d, TRUE, FALSE,
6299 EL_CHAR('D'), -1, -1
6302 Xalpha_e, TRUE, FALSE,
6303 EL_CHAR('E'), -1, -1
6306 Xalpha_f, TRUE, FALSE,
6307 EL_CHAR('F'), -1, -1
6310 Xalpha_g, TRUE, FALSE,
6311 EL_CHAR('G'), -1, -1
6314 Xalpha_h, TRUE, FALSE,
6315 EL_CHAR('H'), -1, -1
6318 Xalpha_i, TRUE, FALSE,
6319 EL_CHAR('I'), -1, -1
6322 Xalpha_j, TRUE, FALSE,
6323 EL_CHAR('J'), -1, -1
6326 Xalpha_k, TRUE, FALSE,
6327 EL_CHAR('K'), -1, -1
6330 Xalpha_l, TRUE, FALSE,
6331 EL_CHAR('L'), -1, -1
6334 Xalpha_m, TRUE, FALSE,
6335 EL_CHAR('M'), -1, -1
6338 Xalpha_n, TRUE, FALSE,
6339 EL_CHAR('N'), -1, -1
6342 Xalpha_o, TRUE, FALSE,
6343 EL_CHAR('O'), -1, -1
6346 Xalpha_p, TRUE, FALSE,
6347 EL_CHAR('P'), -1, -1
6350 Xalpha_q, TRUE, FALSE,
6351 EL_CHAR('Q'), -1, -1
6354 Xalpha_r, TRUE, FALSE,
6355 EL_CHAR('R'), -1, -1
6358 Xalpha_s, TRUE, FALSE,
6359 EL_CHAR('S'), -1, -1
6362 Xalpha_t, TRUE, FALSE,
6363 EL_CHAR('T'), -1, -1
6366 Xalpha_u, TRUE, FALSE,
6367 EL_CHAR('U'), -1, -1
6370 Xalpha_v, TRUE, FALSE,
6371 EL_CHAR('V'), -1, -1
6374 Xalpha_w, TRUE, FALSE,
6375 EL_CHAR('W'), -1, -1
6378 Xalpha_x, TRUE, FALSE,
6379 EL_CHAR('X'), -1, -1
6382 Xalpha_y, TRUE, FALSE,
6383 EL_CHAR('Y'), -1, -1
6386 Xalpha_z, TRUE, FALSE,
6387 EL_CHAR('Z'), -1, -1
6390 Xalpha_arrow_e, TRUE, FALSE,
6391 EL_CHAR('>'), -1, -1
6394 Xalpha_arrow_w, TRUE, FALSE,
6395 EL_CHAR('<'), -1, -1
6398 Xalpha_copyr, TRUE, FALSE,
6399 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6403 Xboom_bug, FALSE, FALSE,
6404 EL_BUG, ACTION_EXPLODING, -1
6407 Xboom_bomb, FALSE, FALSE,
6408 EL_BOMB, ACTION_EXPLODING, -1
6411 Xboom_android, FALSE, FALSE,
6412 EL_EMC_ANDROID, ACTION_OTHER, -1
6415 Xboom_1, FALSE, FALSE,
6416 EL_DEFAULT, ACTION_EXPLODING, -1
6419 Xboom_2, FALSE, FALSE,
6420 EL_DEFAULT, ACTION_EXPLODING, -1
6423 Znormal, FALSE, FALSE,
6427 Zdynamite, FALSE, FALSE,
6431 Zplayer, FALSE, FALSE,
6435 ZBORDER, FALSE, FALSE,
6445 static struct Mapping_EM_to_RND_player
6454 em_player_mapping_list[] =
6458 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6462 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6466 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6470 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6474 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6478 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6482 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6486 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6490 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6494 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6498 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6502 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6506 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6510 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6514 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6518 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6522 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6526 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6530 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6534 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6538 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6542 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6546 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6550 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6554 EL_PLAYER_1, ACTION_DEFAULT, -1,
6558 EL_PLAYER_2, ACTION_DEFAULT, -1,
6562 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6566 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6570 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6574 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6578 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6582 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6586 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6590 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6594 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6598 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6602 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6606 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6610 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6614 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6618 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6622 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6626 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6630 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6634 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6638 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6642 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6646 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6650 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6654 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6658 EL_PLAYER_3, ACTION_DEFAULT, -1,
6662 EL_PLAYER_4, ACTION_DEFAULT, -1,
6671 int map_element_RND_to_EM(int element_rnd)
6673 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6674 static boolean mapping_initialized = FALSE;
6676 if (!mapping_initialized)
6680 /* return "Xalpha_quest" for all undefined elements in mapping array */
6681 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6682 mapping_RND_to_EM[i] = Xalpha_quest;
6684 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6685 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6686 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6687 em_object_mapping_list[i].element_em;
6689 mapping_initialized = TRUE;
6692 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6693 return mapping_RND_to_EM[element_rnd];
6695 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6700 int map_element_EM_to_RND(int element_em)
6702 static unsigned short mapping_EM_to_RND[TILE_MAX];
6703 static boolean mapping_initialized = FALSE;
6705 if (!mapping_initialized)
6709 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6710 for (i = 0; i < TILE_MAX; i++)
6711 mapping_EM_to_RND[i] = EL_UNKNOWN;
6713 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6714 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6715 em_object_mapping_list[i].element_rnd;
6717 mapping_initialized = TRUE;
6720 if (element_em >= 0 && element_em < TILE_MAX)
6721 return mapping_EM_to_RND[element_em];
6723 Error(ERR_WARN, "invalid EM level element %d", element_em);
6728 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6730 struct LevelInfo_EM *level_em = level->native_em_level;
6731 struct LEVEL *lev = level_em->lev;
6734 for (i = 0; i < TILE_MAX; i++)
6735 lev->android_array[i] = Xblank;
6737 for (i = 0; i < level->num_android_clone_elements; i++)
6739 int element_rnd = level->android_clone_element[i];
6740 int element_em = map_element_RND_to_EM(element_rnd);
6742 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6743 if (em_object_mapping_list[j].element_rnd == element_rnd)
6744 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6748 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6750 struct LevelInfo_EM *level_em = level->native_em_level;
6751 struct LEVEL *lev = level_em->lev;
6754 level->num_android_clone_elements = 0;
6756 for (i = 0; i < TILE_MAX; i++)
6758 int element_em = lev->android_array[i];
6760 boolean element_found = FALSE;
6762 if (element_em == Xblank)
6765 element_rnd = map_element_EM_to_RND(element_em);
6767 for (j = 0; j < level->num_android_clone_elements; j++)
6768 if (level->android_clone_element[j] == element_rnd)
6769 element_found = TRUE;
6773 level->android_clone_element[level->num_android_clone_elements++] =
6776 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6781 if (level->num_android_clone_elements == 0)
6783 level->num_android_clone_elements = 1;
6784 level->android_clone_element[0] = EL_EMPTY;
6788 int map_direction_RND_to_EM(int direction)
6790 return (direction == MV_UP ? 0 :
6791 direction == MV_RIGHT ? 1 :
6792 direction == MV_DOWN ? 2 :
6793 direction == MV_LEFT ? 3 :
6797 int map_direction_EM_to_RND(int direction)
6799 return (direction == 0 ? MV_UP :
6800 direction == 1 ? MV_RIGHT :
6801 direction == 2 ? MV_DOWN :
6802 direction == 3 ? MV_LEFT :
6806 int map_element_RND_to_SP(int element_rnd)
6808 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6810 if (element_rnd >= EL_SP_START &&
6811 element_rnd <= EL_SP_END)
6812 element_sp = element_rnd - EL_SP_START;
6813 else if (element_rnd == EL_EMPTY_SPACE)
6815 else if (element_rnd == EL_INVISIBLE_WALL)
6821 int map_element_SP_to_RND(int element_sp)
6823 int element_rnd = EL_UNKNOWN;
6825 if (element_sp >= 0x00 &&
6827 element_rnd = EL_SP_START + element_sp;
6828 else if (element_sp == 0x28)
6829 element_rnd = EL_INVISIBLE_WALL;
6834 int map_action_SP_to_RND(int action_sp)
6838 case actActive: return ACTION_ACTIVE;
6839 case actImpact: return ACTION_IMPACT;
6840 case actExploding: return ACTION_EXPLODING;
6841 case actDigging: return ACTION_DIGGING;
6842 case actSnapping: return ACTION_SNAPPING;
6843 case actCollecting: return ACTION_COLLECTING;
6844 case actPassing: return ACTION_PASSING;
6845 case actPushing: return ACTION_PUSHING;
6846 case actDropping: return ACTION_DROPPING;
6848 default: return ACTION_DEFAULT;
6852 int get_next_element(int element)
6856 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6857 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6858 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6859 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6860 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6861 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6862 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6863 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6864 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6865 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6866 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6868 default: return element;
6872 int el_act_dir2img(int element, int action, int direction)
6874 element = GFX_ELEMENT(element);
6875 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6877 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6878 return element_info[element].direction_graphic[action][direction];
6881 static int el_act_dir2crm(int element, int action, int direction)
6883 element = GFX_ELEMENT(element);
6884 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6886 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6887 return element_info[element].direction_crumbled[action][direction];
6890 int el_act2img(int element, int action)
6892 element = GFX_ELEMENT(element);
6894 return element_info[element].graphic[action];
6897 int el_act2crm(int element, int action)
6899 element = GFX_ELEMENT(element);
6901 return element_info[element].crumbled[action];
6904 int el_dir2img(int element, int direction)
6906 element = GFX_ELEMENT(element);
6908 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6911 int el2baseimg(int element)
6913 return element_info[element].graphic[ACTION_DEFAULT];
6916 int el2img(int element)
6918 element = GFX_ELEMENT(element);
6920 return element_info[element].graphic[ACTION_DEFAULT];
6923 int el2edimg(int element)
6925 element = GFX_ELEMENT(element);
6927 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6930 int el2preimg(int element)
6932 element = GFX_ELEMENT(element);
6934 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6937 int el2panelimg(int element)
6939 element = GFX_ELEMENT(element);
6941 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6944 int font2baseimg(int font_nr)
6946 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6949 int getBeltNrFromBeltElement(int element)
6951 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6952 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6953 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6956 int getBeltNrFromBeltActiveElement(int element)
6958 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6959 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6960 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6963 int getBeltNrFromBeltSwitchElement(int element)
6965 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6966 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6967 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6970 int getBeltDirNrFromBeltElement(int element)
6972 static int belt_base_element[4] =
6974 EL_CONVEYOR_BELT_1_LEFT,
6975 EL_CONVEYOR_BELT_2_LEFT,
6976 EL_CONVEYOR_BELT_3_LEFT,
6977 EL_CONVEYOR_BELT_4_LEFT
6980 int belt_nr = getBeltNrFromBeltElement(element);
6981 int belt_dir_nr = element - belt_base_element[belt_nr];
6983 return (belt_dir_nr % 3);
6986 int getBeltDirNrFromBeltSwitchElement(int element)
6988 static int belt_base_element[4] =
6990 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6991 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6992 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6993 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6996 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6997 int belt_dir_nr = element - belt_base_element[belt_nr];
6999 return (belt_dir_nr % 3);
7002 int getBeltDirFromBeltElement(int element)
7004 static int belt_move_dir[3] =
7011 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7013 return belt_move_dir[belt_dir_nr];
7016 int getBeltDirFromBeltSwitchElement(int element)
7018 static int belt_move_dir[3] =
7025 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7027 return belt_move_dir[belt_dir_nr];
7030 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7032 static int belt_base_element[4] =
7034 EL_CONVEYOR_BELT_1_LEFT,
7035 EL_CONVEYOR_BELT_2_LEFT,
7036 EL_CONVEYOR_BELT_3_LEFT,
7037 EL_CONVEYOR_BELT_4_LEFT
7040 return belt_base_element[belt_nr] + belt_dir_nr;
7043 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7045 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7047 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7050 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7052 static int belt_base_element[4] =
7054 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7055 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7056 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7057 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7060 return belt_base_element[belt_nr] + belt_dir_nr;
7063 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7065 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7067 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7070 boolean getTeamMode_EM()
7072 return game.team_mode;
7075 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7077 int game_frame_delay_value;
7079 game_frame_delay_value =
7080 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7081 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7084 if (tape.playing && tape.warp_forward && !tape.pausing)
7085 game_frame_delay_value = 0;
7087 return game_frame_delay_value;
7090 unsigned int InitRND(int seed)
7092 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7093 return InitEngineRandom_EM(seed);
7094 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7095 return InitEngineRandom_SP(seed);
7097 return InitEngineRandom_RND(seed);
7100 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7101 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7103 inline static int get_effective_element_EM(int tile, int frame_em)
7105 int element = object_mapping[tile].element_rnd;
7106 int action = object_mapping[tile].action;
7107 boolean is_backside = object_mapping[tile].is_backside;
7108 boolean action_removing = (action == ACTION_DIGGING ||
7109 action == ACTION_SNAPPING ||
7110 action == ACTION_COLLECTING);
7116 case Yacid_splash_eB:
7117 case Yacid_splash_wB:
7118 return (frame_em > 5 ? EL_EMPTY : element);
7124 else /* frame_em == 7 */
7128 case Yacid_splash_eB:
7129 case Yacid_splash_wB:
7132 case Yemerald_stone:
7135 case Ydiamond_stone:
7139 case Xdrip_stretchB:
7158 case Xsand_stonein_1:
7159 case Xsand_stonein_2:
7160 case Xsand_stonein_3:
7161 case Xsand_stonein_4:
7165 return (is_backside || action_removing ? EL_EMPTY : element);
7170 inline static boolean check_linear_animation_EM(int tile)
7174 case Xsand_stonesand_1:
7175 case Xsand_stonesand_quickout_1:
7176 case Xsand_sandstone_1:
7177 case Xsand_stonein_1:
7178 case Xsand_stoneout_1:
7197 case Yacid_splash_eB:
7198 case Yacid_splash_wB:
7199 case Yemerald_stone:
7206 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7207 boolean has_crumbled_graphics,
7208 int crumbled, int sync_frame)
7210 /* if element can be crumbled, but certain action graphics are just empty
7211 space (like instantly snapping sand to empty space in 1 frame), do not
7212 treat these empty space graphics as crumbled graphics in EMC engine */
7213 if (crumbled == IMG_EMPTY_SPACE)
7214 has_crumbled_graphics = FALSE;
7216 if (has_crumbled_graphics)
7218 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7219 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7220 g_crumbled->anim_delay,
7221 g_crumbled->anim_mode,
7222 g_crumbled->anim_start_frame,
7225 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7226 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7228 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7230 g_em->has_crumbled_graphics = TRUE;
7234 g_em->crumbled_bitmap = NULL;
7235 g_em->crumbled_src_x = 0;
7236 g_em->crumbled_src_y = 0;
7237 g_em->crumbled_border_size = 0;
7239 g_em->has_crumbled_graphics = FALSE;
7243 void ResetGfxAnimation_EM(int x, int y, int tile)
7248 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7249 int tile, int frame_em, int x, int y)
7251 int action = object_mapping[tile].action;
7252 int direction = object_mapping[tile].direction;
7253 int effective_element = get_effective_element_EM(tile, frame_em);
7254 int graphic = (direction == MV_NONE ?
7255 el_act2img(effective_element, action) :
7256 el_act_dir2img(effective_element, action, direction));
7257 struct GraphicInfo *g = &graphic_info[graphic];
7259 boolean action_removing = (action == ACTION_DIGGING ||
7260 action == ACTION_SNAPPING ||
7261 action == ACTION_COLLECTING);
7262 boolean action_moving = (action == ACTION_FALLING ||
7263 action == ACTION_MOVING ||
7264 action == ACTION_PUSHING ||
7265 action == ACTION_EATING ||
7266 action == ACTION_FILLING ||
7267 action == ACTION_EMPTYING);
7268 boolean action_falling = (action == ACTION_FALLING ||
7269 action == ACTION_FILLING ||
7270 action == ACTION_EMPTYING);
7272 /* special case: graphic uses "2nd movement tile" and has defined
7273 7 frames for movement animation (or less) => use default graphic
7274 for last (8th) frame which ends the movement animation */
7275 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7277 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7278 graphic = (direction == MV_NONE ?
7279 el_act2img(effective_element, action) :
7280 el_act_dir2img(effective_element, action, direction));
7282 g = &graphic_info[graphic];
7285 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7289 else if (action_moving)
7291 boolean is_backside = object_mapping[tile].is_backside;
7295 int direction = object_mapping[tile].direction;
7296 int move_dir = (action_falling ? MV_DOWN : direction);
7301 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7302 if (g->double_movement && frame_em == 0)
7306 if (move_dir == MV_LEFT)
7307 GfxFrame[x - 1][y] = GfxFrame[x][y];
7308 else if (move_dir == MV_RIGHT)
7309 GfxFrame[x + 1][y] = GfxFrame[x][y];
7310 else if (move_dir == MV_UP)
7311 GfxFrame[x][y - 1] = GfxFrame[x][y];
7312 else if (move_dir == MV_DOWN)
7313 GfxFrame[x][y + 1] = GfxFrame[x][y];
7320 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7321 if (tile == Xsand_stonesand_quickout_1 ||
7322 tile == Xsand_stonesand_quickout_2)
7326 if (graphic_info[graphic].anim_global_sync)
7327 sync_frame = FrameCounter;
7328 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7329 sync_frame = GfxFrame[x][y];
7331 sync_frame = 0; /* playfield border (pseudo steel) */
7333 SetRandomAnimationValue(x, y);
7335 int frame = getAnimationFrame(g->anim_frames,
7338 g->anim_start_frame,
7341 g_em->unique_identifier =
7342 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7345 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7346 int tile, int frame_em, int x, int y)
7348 int action = object_mapping[tile].action;
7349 int direction = object_mapping[tile].direction;
7350 boolean is_backside = object_mapping[tile].is_backside;
7351 int effective_element = get_effective_element_EM(tile, frame_em);
7352 int effective_action = action;
7353 int graphic = (direction == MV_NONE ?
7354 el_act2img(effective_element, effective_action) :
7355 el_act_dir2img(effective_element, effective_action,
7357 int crumbled = (direction == MV_NONE ?
7358 el_act2crm(effective_element, effective_action) :
7359 el_act_dir2crm(effective_element, effective_action,
7361 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7362 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7363 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7364 struct GraphicInfo *g = &graphic_info[graphic];
7367 /* special case: graphic uses "2nd movement tile" and has defined
7368 7 frames for movement animation (or less) => use default graphic
7369 for last (8th) frame which ends the movement animation */
7370 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7372 effective_action = ACTION_DEFAULT;
7373 graphic = (direction == MV_NONE ?
7374 el_act2img(effective_element, effective_action) :
7375 el_act_dir2img(effective_element, effective_action,
7377 crumbled = (direction == MV_NONE ?
7378 el_act2crm(effective_element, effective_action) :
7379 el_act_dir2crm(effective_element, effective_action,
7382 g = &graphic_info[graphic];
7385 if (graphic_info[graphic].anim_global_sync)
7386 sync_frame = FrameCounter;
7387 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7388 sync_frame = GfxFrame[x][y];
7390 sync_frame = 0; /* playfield border (pseudo steel) */
7392 SetRandomAnimationValue(x, y);
7394 int frame = getAnimationFrame(g->anim_frames,
7397 g->anim_start_frame,
7400 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7401 g->double_movement && is_backside);
7403 /* (updating the "crumbled" graphic definitions is probably not really needed,
7404 as animations for crumbled graphics can't be longer than one EMC cycle) */
7405 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7409 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7410 int player_nr, int anim, int frame_em)
7412 int element = player_mapping[player_nr][anim].element_rnd;
7413 int action = player_mapping[player_nr][anim].action;
7414 int direction = player_mapping[player_nr][anim].direction;
7415 int graphic = (direction == MV_NONE ?
7416 el_act2img(element, action) :
7417 el_act_dir2img(element, action, direction));
7418 struct GraphicInfo *g = &graphic_info[graphic];
7421 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7423 stored_player[player_nr].StepFrame = frame_em;
7425 sync_frame = stored_player[player_nr].Frame;
7427 int frame = getAnimationFrame(g->anim_frames,
7430 g->anim_start_frame,
7433 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7434 &g_em->src_x, &g_em->src_y, FALSE);
7437 void InitGraphicInfo_EM(void)
7442 int num_em_gfx_errors = 0;
7444 if (graphic_info_em_object[0][0].bitmap == NULL)
7446 /* EM graphics not yet initialized in em_open_all() */
7451 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7454 /* always start with reliable default values */
7455 for (i = 0; i < TILE_MAX; i++)
7457 object_mapping[i].element_rnd = EL_UNKNOWN;
7458 object_mapping[i].is_backside = FALSE;
7459 object_mapping[i].action = ACTION_DEFAULT;
7460 object_mapping[i].direction = MV_NONE;
7463 /* always start with reliable default values */
7464 for (p = 0; p < MAX_PLAYERS; p++)
7466 for (i = 0; i < SPR_MAX; i++)
7468 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7469 player_mapping[p][i].action = ACTION_DEFAULT;
7470 player_mapping[p][i].direction = MV_NONE;
7474 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7476 int e = em_object_mapping_list[i].element_em;
7478 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7479 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7481 if (em_object_mapping_list[i].action != -1)
7482 object_mapping[e].action = em_object_mapping_list[i].action;
7484 if (em_object_mapping_list[i].direction != -1)
7485 object_mapping[e].direction =
7486 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7489 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7491 int a = em_player_mapping_list[i].action_em;
7492 int p = em_player_mapping_list[i].player_nr;
7494 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7496 if (em_player_mapping_list[i].action != -1)
7497 player_mapping[p][a].action = em_player_mapping_list[i].action;
7499 if (em_player_mapping_list[i].direction != -1)
7500 player_mapping[p][a].direction =
7501 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7504 for (i = 0; i < TILE_MAX; i++)
7506 int element = object_mapping[i].element_rnd;
7507 int action = object_mapping[i].action;
7508 int direction = object_mapping[i].direction;
7509 boolean is_backside = object_mapping[i].is_backside;
7510 boolean action_exploding = ((action == ACTION_EXPLODING ||
7511 action == ACTION_SMASHED_BY_ROCK ||
7512 action == ACTION_SMASHED_BY_SPRING) &&
7513 element != EL_DIAMOND);
7514 boolean action_active = (action == ACTION_ACTIVE);
7515 boolean action_other = (action == ACTION_OTHER);
7517 for (j = 0; j < 8; j++)
7519 int effective_element = get_effective_element_EM(i, j);
7520 int effective_action = (j < 7 ? action :
7521 i == Xdrip_stretch ? action :
7522 i == Xdrip_stretchB ? action :
7523 i == Ydrip_s1 ? action :
7524 i == Ydrip_s1B ? action :
7525 i == Xball_1B ? action :
7526 i == Xball_2 ? action :
7527 i == Xball_2B ? action :
7528 i == Yball_eat ? action :
7529 i == Ykey_1_eat ? action :
7530 i == Ykey_2_eat ? action :
7531 i == Ykey_3_eat ? action :
7532 i == Ykey_4_eat ? action :
7533 i == Ykey_5_eat ? action :
7534 i == Ykey_6_eat ? action :
7535 i == Ykey_7_eat ? action :
7536 i == Ykey_8_eat ? action :
7537 i == Ylenses_eat ? action :
7538 i == Ymagnify_eat ? action :
7539 i == Ygrass_eat ? action :
7540 i == Ydirt_eat ? action :
7541 i == Xsand_stonein_1 ? action :
7542 i == Xsand_stonein_2 ? action :
7543 i == Xsand_stonein_3 ? action :
7544 i == Xsand_stonein_4 ? action :
7545 i == Xsand_stoneout_1 ? action :
7546 i == Xsand_stoneout_2 ? action :
7547 i == Xboom_android ? ACTION_EXPLODING :
7548 action_exploding ? ACTION_EXPLODING :
7549 action_active ? action :
7550 action_other ? action :
7552 int graphic = (el_act_dir2img(effective_element, effective_action,
7554 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7556 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7557 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7558 boolean has_action_graphics = (graphic != base_graphic);
7559 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7560 struct GraphicInfo *g = &graphic_info[graphic];
7561 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7564 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7565 boolean special_animation = (action != ACTION_DEFAULT &&
7566 g->anim_frames == 3 &&
7567 g->anim_delay == 2 &&
7568 g->anim_mode & ANIM_LINEAR);
7569 int sync_frame = (i == Xdrip_stretch ? 7 :
7570 i == Xdrip_stretchB ? 7 :
7571 i == Ydrip_s2 ? j + 8 :
7572 i == Ydrip_s2B ? j + 8 :
7581 i == Xfake_acid_1 ? 0 :
7582 i == Xfake_acid_2 ? 10 :
7583 i == Xfake_acid_3 ? 20 :
7584 i == Xfake_acid_4 ? 30 :
7585 i == Xfake_acid_5 ? 40 :
7586 i == Xfake_acid_6 ? 50 :
7587 i == Xfake_acid_7 ? 60 :
7588 i == Xfake_acid_8 ? 70 :
7590 i == Xball_2B ? j + 8 :
7591 i == Yball_eat ? j + 1 :
7592 i == Ykey_1_eat ? j + 1 :
7593 i == Ykey_2_eat ? j + 1 :
7594 i == Ykey_3_eat ? j + 1 :
7595 i == Ykey_4_eat ? j + 1 :
7596 i == Ykey_5_eat ? j + 1 :
7597 i == Ykey_6_eat ? j + 1 :
7598 i == Ykey_7_eat ? j + 1 :
7599 i == Ykey_8_eat ? j + 1 :
7600 i == Ylenses_eat ? j + 1 :
7601 i == Ymagnify_eat ? j + 1 :
7602 i == Ygrass_eat ? j + 1 :
7603 i == Ydirt_eat ? j + 1 :
7604 i == Xamoeba_1 ? 0 :
7605 i == Xamoeba_2 ? 1 :
7606 i == Xamoeba_3 ? 2 :
7607 i == Xamoeba_4 ? 3 :
7608 i == Xamoeba_5 ? 0 :
7609 i == Xamoeba_6 ? 1 :
7610 i == Xamoeba_7 ? 2 :
7611 i == Xamoeba_8 ? 3 :
7612 i == Xexit_2 ? j + 8 :
7613 i == Xexit_3 ? j + 16 :
7614 i == Xdynamite_1 ? 0 :
7615 i == Xdynamite_2 ? 8 :
7616 i == Xdynamite_3 ? 16 :
7617 i == Xdynamite_4 ? 24 :
7618 i == Xsand_stonein_1 ? j + 1 :
7619 i == Xsand_stonein_2 ? j + 9 :
7620 i == Xsand_stonein_3 ? j + 17 :
7621 i == Xsand_stonein_4 ? j + 25 :
7622 i == Xsand_stoneout_1 && j == 0 ? 0 :
7623 i == Xsand_stoneout_1 && j == 1 ? 0 :
7624 i == Xsand_stoneout_1 && j == 2 ? 1 :
7625 i == Xsand_stoneout_1 && j == 3 ? 2 :
7626 i == Xsand_stoneout_1 && j == 4 ? 2 :
7627 i == Xsand_stoneout_1 && j == 5 ? 3 :
7628 i == Xsand_stoneout_1 && j == 6 ? 4 :
7629 i == Xsand_stoneout_1 && j == 7 ? 4 :
7630 i == Xsand_stoneout_2 && j == 0 ? 5 :
7631 i == Xsand_stoneout_2 && j == 1 ? 6 :
7632 i == Xsand_stoneout_2 && j == 2 ? 7 :
7633 i == Xsand_stoneout_2 && j == 3 ? 8 :
7634 i == Xsand_stoneout_2 && j == 4 ? 9 :
7635 i == Xsand_stoneout_2 && j == 5 ? 11 :
7636 i == Xsand_stoneout_2 && j == 6 ? 13 :
7637 i == Xsand_stoneout_2 && j == 7 ? 15 :
7638 i == Xboom_bug && j == 1 ? 2 :
7639 i == Xboom_bug && j == 2 ? 2 :
7640 i == Xboom_bug && j == 3 ? 4 :
7641 i == Xboom_bug && j == 4 ? 4 :
7642 i == Xboom_bug && j == 5 ? 2 :
7643 i == Xboom_bug && j == 6 ? 2 :
7644 i == Xboom_bug && j == 7 ? 0 :
7645 i == Xboom_bomb && j == 1 ? 2 :
7646 i == Xboom_bomb && j == 2 ? 2 :
7647 i == Xboom_bomb && j == 3 ? 4 :
7648 i == Xboom_bomb && j == 4 ? 4 :
7649 i == Xboom_bomb && j == 5 ? 2 :
7650 i == Xboom_bomb && j == 6 ? 2 :
7651 i == Xboom_bomb && j == 7 ? 0 :
7652 i == Xboom_android && j == 7 ? 6 :
7653 i == Xboom_1 && j == 1 ? 2 :
7654 i == Xboom_1 && j == 2 ? 2 :
7655 i == Xboom_1 && j == 3 ? 4 :
7656 i == Xboom_1 && j == 4 ? 4 :
7657 i == Xboom_1 && j == 5 ? 6 :
7658 i == Xboom_1 && j == 6 ? 6 :
7659 i == Xboom_1 && j == 7 ? 8 :
7660 i == Xboom_2 && j == 0 ? 8 :
7661 i == Xboom_2 && j == 1 ? 8 :
7662 i == Xboom_2 && j == 2 ? 10 :
7663 i == Xboom_2 && j == 3 ? 10 :
7664 i == Xboom_2 && j == 4 ? 10 :
7665 i == Xboom_2 && j == 5 ? 12 :
7666 i == Xboom_2 && j == 6 ? 12 :
7667 i == Xboom_2 && j == 7 ? 12 :
7668 special_animation && j == 4 ? 3 :
7669 effective_action != action ? 0 :
7673 Bitmap *debug_bitmap = g_em->bitmap;
7674 int debug_src_x = g_em->src_x;
7675 int debug_src_y = g_em->src_y;
7678 int frame = getAnimationFrame(g->anim_frames,
7681 g->anim_start_frame,
7684 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7685 g->double_movement && is_backside);
7687 g_em->bitmap = src_bitmap;
7688 g_em->src_x = src_x;
7689 g_em->src_y = src_y;
7690 g_em->src_offset_x = 0;
7691 g_em->src_offset_y = 0;
7692 g_em->dst_offset_x = 0;
7693 g_em->dst_offset_y = 0;
7694 g_em->width = TILEX;
7695 g_em->height = TILEY;
7697 g_em->preserve_background = FALSE;
7699 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7702 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7703 effective_action == ACTION_MOVING ||
7704 effective_action == ACTION_PUSHING ||
7705 effective_action == ACTION_EATING)) ||
7706 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7707 effective_action == ACTION_EMPTYING)))
7710 (effective_action == ACTION_FALLING ||
7711 effective_action == ACTION_FILLING ||
7712 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7713 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7714 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7715 int num_steps = (i == Ydrip_s1 ? 16 :
7716 i == Ydrip_s1B ? 16 :
7717 i == Ydrip_s2 ? 16 :
7718 i == Ydrip_s2B ? 16 :
7719 i == Xsand_stonein_1 ? 32 :
7720 i == Xsand_stonein_2 ? 32 :
7721 i == Xsand_stonein_3 ? 32 :
7722 i == Xsand_stonein_4 ? 32 :
7723 i == Xsand_stoneout_1 ? 16 :
7724 i == Xsand_stoneout_2 ? 16 : 8);
7725 int cx = ABS(dx) * (TILEX / num_steps);
7726 int cy = ABS(dy) * (TILEY / num_steps);
7727 int step_frame = (i == Ydrip_s2 ? j + 8 :
7728 i == Ydrip_s2B ? j + 8 :
7729 i == Xsand_stonein_2 ? j + 8 :
7730 i == Xsand_stonein_3 ? j + 16 :
7731 i == Xsand_stonein_4 ? j + 24 :
7732 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7733 int step = (is_backside ? step_frame : num_steps - step_frame);
7735 if (is_backside) /* tile where movement starts */
7737 if (dx < 0 || dy < 0)
7739 g_em->src_offset_x = cx * step;
7740 g_em->src_offset_y = cy * step;
7744 g_em->dst_offset_x = cx * step;
7745 g_em->dst_offset_y = cy * step;
7748 else /* tile where movement ends */
7750 if (dx < 0 || dy < 0)
7752 g_em->dst_offset_x = cx * step;
7753 g_em->dst_offset_y = cy * step;
7757 g_em->src_offset_x = cx * step;
7758 g_em->src_offset_y = cy * step;
7762 g_em->width = TILEX - cx * step;
7763 g_em->height = TILEY - cy * step;
7766 /* create unique graphic identifier to decide if tile must be redrawn */
7767 /* bit 31 - 16 (16 bit): EM style graphic
7768 bit 15 - 12 ( 4 bit): EM style frame
7769 bit 11 - 6 ( 6 bit): graphic width
7770 bit 5 - 0 ( 6 bit): graphic height */
7771 g_em->unique_identifier =
7772 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7776 /* skip check for EMC elements not contained in original EMC artwork */
7777 if (element == EL_EMC_FAKE_ACID)
7780 if (g_em->bitmap != debug_bitmap ||
7781 g_em->src_x != debug_src_x ||
7782 g_em->src_y != debug_src_y ||
7783 g_em->src_offset_x != 0 ||
7784 g_em->src_offset_y != 0 ||
7785 g_em->dst_offset_x != 0 ||
7786 g_em->dst_offset_y != 0 ||
7787 g_em->width != TILEX ||
7788 g_em->height != TILEY)
7790 static int last_i = -1;
7798 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7799 i, element, element_info[element].token_name,
7800 element_action_info[effective_action].suffix, direction);
7802 if (element != effective_element)
7803 printf(" [%d ('%s')]",
7805 element_info[effective_element].token_name);
7809 if (g_em->bitmap != debug_bitmap)
7810 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7811 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7813 if (g_em->src_x != debug_src_x ||
7814 g_em->src_y != debug_src_y)
7815 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7816 j, (is_backside ? 'B' : 'F'),
7817 g_em->src_x, g_em->src_y,
7818 g_em->src_x / 32, g_em->src_y / 32,
7819 debug_src_x, debug_src_y,
7820 debug_src_x / 32, debug_src_y / 32);
7822 if (g_em->src_offset_x != 0 ||
7823 g_em->src_offset_y != 0 ||
7824 g_em->dst_offset_x != 0 ||
7825 g_em->dst_offset_y != 0)
7826 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7828 g_em->src_offset_x, g_em->src_offset_y,
7829 g_em->dst_offset_x, g_em->dst_offset_y);
7831 if (g_em->width != TILEX ||
7832 g_em->height != TILEY)
7833 printf(" %d (%d): size %d,%d should be %d,%d\n",
7835 g_em->width, g_em->height, TILEX, TILEY);
7837 num_em_gfx_errors++;
7844 for (i = 0; i < TILE_MAX; i++)
7846 for (j = 0; j < 8; j++)
7848 int element = object_mapping[i].element_rnd;
7849 int action = object_mapping[i].action;
7850 int direction = object_mapping[i].direction;
7851 boolean is_backside = object_mapping[i].is_backside;
7852 int graphic_action = el_act_dir2img(element, action, direction);
7853 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7855 if ((action == ACTION_SMASHED_BY_ROCK ||
7856 action == ACTION_SMASHED_BY_SPRING ||
7857 action == ACTION_EATING) &&
7858 graphic_action == graphic_default)
7860 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7861 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7862 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7863 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7866 /* no separate animation for "smashed by rock" -- use rock instead */
7867 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7868 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7870 g_em->bitmap = g_xx->bitmap;
7871 g_em->src_x = g_xx->src_x;
7872 g_em->src_y = g_xx->src_y;
7873 g_em->src_offset_x = g_xx->src_offset_x;
7874 g_em->src_offset_y = g_xx->src_offset_y;
7875 g_em->dst_offset_x = g_xx->dst_offset_x;
7876 g_em->dst_offset_y = g_xx->dst_offset_y;
7877 g_em->width = g_xx->width;
7878 g_em->height = g_xx->height;
7879 g_em->unique_identifier = g_xx->unique_identifier;
7882 g_em->preserve_background = TRUE;
7887 for (p = 0; p < MAX_PLAYERS; p++)
7889 for (i = 0; i < SPR_MAX; i++)
7891 int element = player_mapping[p][i].element_rnd;
7892 int action = player_mapping[p][i].action;
7893 int direction = player_mapping[p][i].direction;
7895 for (j = 0; j < 8; j++)
7897 int effective_element = element;
7898 int effective_action = action;
7899 int graphic = (direction == MV_NONE ?
7900 el_act2img(effective_element, effective_action) :
7901 el_act_dir2img(effective_element, effective_action,
7903 struct GraphicInfo *g = &graphic_info[graphic];
7904 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7910 Bitmap *debug_bitmap = g_em->bitmap;
7911 int debug_src_x = g_em->src_x;
7912 int debug_src_y = g_em->src_y;
7915 int frame = getAnimationFrame(g->anim_frames,
7918 g->anim_start_frame,
7921 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7923 g_em->bitmap = src_bitmap;
7924 g_em->src_x = src_x;
7925 g_em->src_y = src_y;
7926 g_em->src_offset_x = 0;
7927 g_em->src_offset_y = 0;
7928 g_em->dst_offset_x = 0;
7929 g_em->dst_offset_y = 0;
7930 g_em->width = TILEX;
7931 g_em->height = TILEY;
7935 /* skip check for EMC elements not contained in original EMC artwork */
7936 if (element == EL_PLAYER_3 ||
7937 element == EL_PLAYER_4)
7940 if (g_em->bitmap != debug_bitmap ||
7941 g_em->src_x != debug_src_x ||
7942 g_em->src_y != debug_src_y)
7944 static int last_i = -1;
7952 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7953 p, i, element, element_info[element].token_name,
7954 element_action_info[effective_action].suffix, direction);
7956 if (element != effective_element)
7957 printf(" [%d ('%s')]",
7959 element_info[effective_element].token_name);
7963 if (g_em->bitmap != debug_bitmap)
7964 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7965 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7967 if (g_em->src_x != debug_src_x ||
7968 g_em->src_y != debug_src_y)
7969 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7971 g_em->src_x, g_em->src_y,
7972 g_em->src_x / 32, g_em->src_y / 32,
7973 debug_src_x, debug_src_y,
7974 debug_src_x / 32, debug_src_y / 32);
7976 num_em_gfx_errors++;
7986 printf("::: [%d errors found]\n", num_em_gfx_errors);
7992 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7993 boolean any_player_moving,
7994 boolean any_player_snapping,
7995 boolean any_player_dropping)
7997 static boolean player_was_waiting = TRUE;
7999 if (frame == 0 && !any_player_dropping)
8001 if (!player_was_waiting)
8003 if (!SaveEngineSnapshotToList())
8006 player_was_waiting = TRUE;
8009 else if (any_player_moving || any_player_snapping || any_player_dropping)
8011 player_was_waiting = FALSE;
8015 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8016 boolean murphy_is_dropping)
8018 static boolean player_was_waiting = TRUE;
8020 if (murphy_is_waiting)
8022 if (!player_was_waiting)
8024 if (!SaveEngineSnapshotToList())
8027 player_was_waiting = TRUE;
8032 player_was_waiting = FALSE;
8036 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8037 boolean any_player_moving,
8038 boolean any_player_snapping,
8039 boolean any_player_dropping)
8041 if (tape.single_step && tape.recording && !tape.pausing)
8042 if (frame == 0 && !any_player_dropping)
8043 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8045 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8046 any_player_snapping, any_player_dropping);
8049 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8050 boolean murphy_is_dropping)
8052 if (tape.single_step && tape.recording && !tape.pausing)
8053 if (murphy_is_waiting)
8054 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8056 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8059 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8060 int graphic, int sync_frame, int x, int y)
8062 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8064 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8067 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8069 return (IS_NEXT_FRAME(sync_frame, graphic));
8072 int getGraphicInfo_Delay(int graphic)
8074 return graphic_info[graphic].anim_delay;
8077 void PlayMenuSoundExt(int sound)
8079 if (sound == SND_UNDEFINED)
8082 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8083 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8086 if (IS_LOOP_SOUND(sound))
8087 PlaySoundLoop(sound);
8092 void PlayMenuSound()
8094 PlayMenuSoundExt(menu.sound[game_status]);
8097 void PlayMenuSoundStereo(int sound, int stereo_position)
8099 if (sound == SND_UNDEFINED)
8102 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8103 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8106 if (IS_LOOP_SOUND(sound))
8107 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8109 PlaySoundStereo(sound, stereo_position);
8112 void PlayMenuSoundIfLoopExt(int sound)
8114 if (sound == SND_UNDEFINED)
8117 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8118 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8121 if (IS_LOOP_SOUND(sound))
8122 PlaySoundLoop(sound);
8125 void PlayMenuSoundIfLoop()
8127 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8130 void PlayMenuMusicExt(int music)
8132 if (music == MUS_UNDEFINED)
8135 if (!setup.sound_music)
8141 void PlayMenuMusic()
8143 PlayMenuMusicExt(menu.music[game_status]);
8146 void PlaySoundActivating()
8149 PlaySound(SND_MENU_ITEM_ACTIVATING);
8153 void PlaySoundSelecting()
8156 PlaySound(SND_MENU_ITEM_SELECTING);
8160 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8162 boolean change_fullscreen = (setup.fullscreen !=
8163 video.fullscreen_enabled);
8164 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8165 !strEqual(setup.fullscreen_mode,
8166 video.fullscreen_mode_current));
8167 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8168 setup.window_scaling_percent !=
8169 video.window_scaling_percent);
8171 if (change_window_scaling_percent && video.fullscreen_enabled)
8174 if (!change_window_scaling_percent && !video.fullscreen_available)
8177 #if defined(TARGET_SDL2)
8178 if (change_window_scaling_percent)
8180 SDLSetWindowScaling(setup.window_scaling_percent);
8184 else if (change_fullscreen)
8186 SDLSetWindowFullscreen(setup.fullscreen);
8188 /* set setup value according to successfully changed fullscreen mode */
8189 setup.fullscreen = video.fullscreen_enabled;
8195 if (change_fullscreen ||
8196 change_fullscreen_mode ||
8197 change_window_scaling_percent)
8199 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8201 /* save backbuffer content which gets lost when toggling fullscreen mode */
8202 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8204 if (change_fullscreen_mode)
8206 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8207 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8210 if (change_window_scaling_percent)
8212 /* keep window mode, but change window scaling */
8213 video.fullscreen_enabled = TRUE; /* force new window scaling */
8216 /* toggle fullscreen */
8217 ChangeVideoModeIfNeeded(setup.fullscreen);
8219 /* set setup value according to successfully changed fullscreen mode */
8220 setup.fullscreen = video.fullscreen_enabled;
8222 /* restore backbuffer content from temporary backbuffer backup bitmap */
8223 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8225 FreeBitmap(tmp_backbuffer);
8227 /* update visible window/screen */
8228 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8232 void JoinRectangles(int *x, int *y, int *width, int *height,
8233 int x2, int y2, int width2, int height2)
8235 // do not join with "off-screen" rectangle
8236 if (x2 == -1 || y2 == -1)
8241 *width = MAX(*width, width2);
8242 *height = MAX(*height, height2);
8245 void SetGameStatus(int game_status_new)
8247 game_status = game_status_new;
8249 global.anim_status_next = game_status;
8252 void ChangeViewportPropertiesIfNeeded()
8254 int gfx_game_mode = game_status;
8255 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8257 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8258 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8259 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8260 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8261 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8262 int new_win_xsize = vp_window->width;
8263 int new_win_ysize = vp_window->height;
8264 int border_size = vp_playfield->border_size;
8265 int new_sx = vp_playfield->x + border_size;
8266 int new_sy = vp_playfield->y + border_size;
8267 int new_sxsize = vp_playfield->width - 2 * border_size;
8268 int new_sysize = vp_playfield->height - 2 * border_size;
8269 int new_real_sx = vp_playfield->x;
8270 int new_real_sy = vp_playfield->y;
8271 int new_full_sxsize = vp_playfield->width;
8272 int new_full_sysize = vp_playfield->height;
8273 int new_dx = vp_door_1->x;
8274 int new_dy = vp_door_1->y;
8275 int new_dxsize = vp_door_1->width;
8276 int new_dysize = vp_door_1->height;
8277 int new_vx = vp_door_2->x;
8278 int new_vy = vp_door_2->y;
8279 int new_vxsize = vp_door_2->width;
8280 int new_vysize = vp_door_2->height;
8281 int new_ex = vp_door_3->x;
8282 int new_ey = vp_door_3->y;
8283 int new_exsize = vp_door_3->width;
8284 int new_eysize = vp_door_3->height;
8285 int new_tilesize_var =
8286 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8288 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8289 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8290 int new_scr_fieldx = new_sxsize / tilesize;
8291 int new_scr_fieldy = new_sysize / tilesize;
8292 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8293 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8294 boolean init_gfx_buffers = FALSE;
8295 boolean init_video_buffer = FALSE;
8296 boolean init_gadgets_and_toons = FALSE;
8297 boolean init_em_graphics = FALSE;
8299 if (new_win_xsize != WIN_XSIZE ||
8300 new_win_ysize != WIN_YSIZE)
8302 WIN_XSIZE = new_win_xsize;
8303 WIN_YSIZE = new_win_ysize;
8305 init_video_buffer = TRUE;
8306 init_gfx_buffers = TRUE;
8307 init_gadgets_and_toons = TRUE;
8309 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8312 if (new_scr_fieldx != SCR_FIELDX ||
8313 new_scr_fieldy != SCR_FIELDY)
8315 /* this always toggles between MAIN and GAME when using small tile size */
8317 SCR_FIELDX = new_scr_fieldx;
8318 SCR_FIELDY = new_scr_fieldy;
8320 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8331 new_sxsize != SXSIZE ||
8332 new_sysize != SYSIZE ||
8333 new_dxsize != DXSIZE ||
8334 new_dysize != DYSIZE ||
8335 new_vxsize != VXSIZE ||
8336 new_vysize != VYSIZE ||
8337 new_exsize != EXSIZE ||
8338 new_eysize != EYSIZE ||
8339 new_real_sx != REAL_SX ||
8340 new_real_sy != REAL_SY ||
8341 new_full_sxsize != FULL_SXSIZE ||
8342 new_full_sysize != FULL_SYSIZE ||
8343 new_tilesize_var != TILESIZE_VAR
8346 // ------------------------------------------------------------------------
8347 // determine next fading area for changed viewport definitions
8348 // ------------------------------------------------------------------------
8350 // start with current playfield area (default fading area)
8353 FADE_SXSIZE = FULL_SXSIZE;
8354 FADE_SYSIZE = FULL_SYSIZE;
8356 // add new playfield area if position or size has changed
8357 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8358 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8360 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8361 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8364 // add current and new door 1 area if position or size has changed
8365 if (new_dx != DX || new_dy != DY ||
8366 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8368 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8369 DX, DY, DXSIZE, DYSIZE);
8370 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8371 new_dx, new_dy, new_dxsize, new_dysize);
8374 // add current and new door 2 area if position or size has changed
8375 if (new_dx != VX || new_dy != VY ||
8376 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8378 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8379 VX, VY, VXSIZE, VYSIZE);
8380 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8381 new_vx, new_vy, new_vxsize, new_vysize);
8384 // ------------------------------------------------------------------------
8385 // handle changed tile size
8386 // ------------------------------------------------------------------------
8388 if (new_tilesize_var != TILESIZE_VAR)
8390 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8392 // changing tile size invalidates scroll values of engine snapshots
8393 FreeEngineSnapshotSingle();
8395 // changing tile size requires update of graphic mapping for EM engine
8396 init_em_graphics = TRUE;
8407 SXSIZE = new_sxsize;
8408 SYSIZE = new_sysize;
8409 DXSIZE = new_dxsize;
8410 DYSIZE = new_dysize;
8411 VXSIZE = new_vxsize;
8412 VYSIZE = new_vysize;
8413 EXSIZE = new_exsize;
8414 EYSIZE = new_eysize;
8415 REAL_SX = new_real_sx;
8416 REAL_SY = new_real_sy;
8417 FULL_SXSIZE = new_full_sxsize;
8418 FULL_SYSIZE = new_full_sysize;
8419 TILESIZE_VAR = new_tilesize_var;
8421 init_gfx_buffers = TRUE;
8422 init_gadgets_and_toons = TRUE;
8424 // printf("::: viewports: init_gfx_buffers\n");
8425 // printf("::: viewports: init_gadgets_and_toons\n");
8428 if (init_gfx_buffers)
8430 // printf("::: init_gfx_buffers\n");
8432 SCR_FIELDX = new_scr_fieldx_buffers;
8433 SCR_FIELDY = new_scr_fieldy_buffers;
8437 SCR_FIELDX = new_scr_fieldx;
8438 SCR_FIELDY = new_scr_fieldy;
8440 SetDrawDeactivationMask(REDRAW_NONE);
8441 SetDrawBackgroundMask(REDRAW_FIELD);
8444 if (init_video_buffer)
8446 // printf("::: init_video_buffer\n");
8448 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8449 InitImageTextures();
8452 if (init_gadgets_and_toons)
8454 // printf("::: init_gadgets_and_toons\n");
8458 InitGlobalAnimations();
8461 if (init_em_graphics)
8463 InitGraphicInfo_EM();