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,
294 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
296 if (x == -1 && y == -1)
299 if (draw_target == DRAW_BORDER_TO_SCREEN)
300 BlitToScreenMasked(bitmap, x, y, width, height, x, y);
302 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
305 static void DrawMaskedBorderExt_FIELD(int draw_target)
307 if (global.border_status >= GAME_MODE_TITLE &&
308 global.border_status <= GAME_MODE_PLAYING &&
309 border.draw_masked[global.border_status])
310 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
314 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
316 // when drawing to backbuffer, never draw border over open doors
317 if (draw_target == DRAW_BORDER_TO_BACKBUFFER &&
318 (GetDoorState() & DOOR_OPEN_1))
321 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
322 (global.border_status != GAME_MODE_EDITOR ||
323 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
324 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
327 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
329 // when drawing to backbuffer, never draw border over open doors
330 if (draw_target == DRAW_BORDER_TO_BACKBUFFER &&
331 (GetDoorState() & DOOR_OPEN_2))
334 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
335 global.border_status != GAME_MODE_EDITOR)
336 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
339 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
341 /* currently not available */
344 static void DrawMaskedBorderExt_ALL(int draw_target)
346 DrawMaskedBorderExt_FIELD(draw_target);
347 DrawMaskedBorderExt_DOOR_1(draw_target);
348 DrawMaskedBorderExt_DOOR_2(draw_target);
349 DrawMaskedBorderExt_DOOR_3(draw_target);
352 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
354 /* never draw masked screen borders on borderless screens */
355 if (game_status == GAME_MODE_LOADING ||
356 game_status == GAME_MODE_TITLE)
359 if (redraw_mask & REDRAW_ALL)
360 DrawMaskedBorderExt_ALL(draw_target);
363 if (redraw_mask & REDRAW_FIELD)
364 DrawMaskedBorderExt_FIELD(draw_target);
365 if (redraw_mask & REDRAW_DOOR_1)
366 DrawMaskedBorderExt_DOOR_1(draw_target);
367 if (redraw_mask & REDRAW_DOOR_2)
368 DrawMaskedBorderExt_DOOR_2(draw_target);
369 if (redraw_mask & REDRAW_DOOR_3)
370 DrawMaskedBorderExt_DOOR_3(draw_target);
374 void DrawMaskedBorder_FIELD()
376 DrawMaskedBorderExt_FIELD(DRAW_BORDER_TO_BACKBUFFER);
379 void DrawMaskedBorder(int redraw_mask)
381 DrawMaskedBorderExt(redraw_mask, DRAW_BORDER_TO_BACKBUFFER);
384 void DrawMaskedBorderToScreen(int draw_target)
386 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
389 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
391 int fx = FX, fy = FY;
392 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
393 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
395 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
396 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
397 int dx_var = dx * TILESIZE_VAR / TILESIZE;
398 int dy_var = dy * TILESIZE_VAR / TILESIZE;
401 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
402 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
404 if (EVEN(SCR_FIELDX))
406 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
407 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
409 fx += (dx_var > 0 ? TILEX_VAR : 0);
416 if (EVEN(SCR_FIELDY))
418 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
419 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
421 fy += (dy_var > 0 ? TILEY_VAR : 0);
428 if (full_lev_fieldx <= SCR_FIELDX)
430 if (EVEN(SCR_FIELDX))
431 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
433 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
436 if (full_lev_fieldy <= SCR_FIELDY)
438 if (EVEN(SCR_FIELDY))
439 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
441 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
444 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
447 void BlitScreenToBitmap(Bitmap *target_bitmap)
449 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
450 BlitScreenToBitmap_EM(target_bitmap);
451 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
452 BlitScreenToBitmap_SP(target_bitmap);
453 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
454 BlitScreenToBitmap_RND(target_bitmap);
456 redraw_mask |= REDRAW_FIELD;
459 void DrawFramesPerSecond()
462 int font_nr = FONT_TEXT_2;
463 int font_width = getFontWidth(font_nr);
465 sprintf(text, "%04.1f fps", global.frames_per_second);
467 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
468 font_nr, BLIT_OPAQUE);
472 static void PrintFrameTimeDebugging()
474 static unsigned int last_counter = 0;
475 unsigned int counter = Counter();
476 int diff_1 = counter - last_counter;
477 int diff_2 = diff_1 - GAME_FRAME_DELAY;
479 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
480 char diff_bar[2 * diff_2_max + 5];
484 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
486 for (i = 0; i < diff_2_max; i++)
487 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
488 i >= diff_2_max - diff_2_cut ? '-' : ' ');
490 diff_bar[pos++] = '|';
492 for (i = 0; i < diff_2_max; i++)
493 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
495 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
497 diff_bar[pos++] = '\0';
499 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
502 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
505 last_counter = counter;
511 static int last_redraw_mask = REDRAW_NONE;
513 // force screen redraw in every frame to continue drawing global animations
514 // (but always use the last redraw mask to prevent unwanted side effects)
515 if (redraw_mask == REDRAW_NONE)
516 redraw_mask = last_redraw_mask;
518 last_redraw_mask = redraw_mask;
521 // masked border now drawn immediately when blitting backbuffer to window
523 // draw masked border to all viewports, if defined
524 DrawMaskedBorder(redraw_mask);
527 // draw frames per second (only if debug mode is enabled)
528 if (redraw_mask & REDRAW_FPS)
529 DrawFramesPerSecond();
531 // redraw complete window if both playfield and (some) doors need redraw
532 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
533 redraw_mask = REDRAW_ALL;
535 /* although redrawing the whole window would be fine for normal gameplay,
536 being able to only redraw the playfield is required for deactivating
537 certain drawing areas (mainly playfield) to work, which is needed for
538 warp-forward to be fast enough (by skipping redraw of most frames) */
540 if (redraw_mask & REDRAW_ALL)
542 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
544 else if (redraw_mask & REDRAW_FIELD)
546 BlitBitmap(backbuffer, window,
547 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
549 else if (redraw_mask & REDRAW_DOORS)
551 if (redraw_mask & REDRAW_DOOR_1)
552 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
554 if (redraw_mask & REDRAW_DOOR_2)
555 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
557 if (redraw_mask & REDRAW_DOOR_3)
558 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
561 redraw_mask = REDRAW_NONE;
564 PrintFrameTimeDebugging();
568 static void FadeCrossSaveBackbuffer()
570 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
573 static void FadeCrossRestoreBackbuffer()
575 int redraw_mask_last = redraw_mask;
577 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
579 // do not change redraw mask when restoring backbuffer after cross-fading
580 redraw_mask = redraw_mask_last;
583 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
585 static int fade_type_skip = FADE_TYPE_NONE;
586 void (*draw_border_function)(void) = NULL;
587 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
588 int x, y, width, height;
589 int fade_delay, post_delay;
591 if (fade_type == FADE_TYPE_FADE_OUT)
593 if (fade_type_skip != FADE_TYPE_NONE)
595 /* skip all fade operations until specified fade operation */
596 if (fade_type & fade_type_skip)
597 fade_type_skip = FADE_TYPE_NONE;
603 FadeCrossSaveBackbuffer();
606 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
609 FadeCrossSaveBackbuffer();
616 redraw_mask |= fade_mask;
618 if (fade_type == FADE_TYPE_SKIP)
620 fade_type_skip = fade_mode;
625 fade_delay = fading.fade_delay;
626 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
628 if (fade_type_skip != FADE_TYPE_NONE)
630 /* skip all fade operations until specified fade operation */
631 if (fade_type & fade_type_skip)
632 fade_type_skip = FADE_TYPE_NONE;
637 if (global.autoplay_leveldir)
642 if (fade_mask == REDRAW_FIELD)
647 height = FADE_SYSIZE;
649 if (border.draw_masked_when_fading)
650 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
652 DrawMaskedBorder_FIELD(); /* draw once */
654 else /* REDRAW_ALL */
662 if (!setup.fade_screens ||
664 fading.fade_mode == FADE_MODE_NONE)
666 if (fade_mode == FADE_MODE_FADE_OUT)
669 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
671 redraw_mask &= ~fade_mask;
676 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
677 draw_border_function);
679 if (fade_type == FADE_TYPE_FADE_OUT)
680 FadeCrossRestoreBackbuffer();
682 redraw_mask &= ~fade_mask;
685 static void SetAnimStatus_BeforeFadingOut()
687 global.anim_status = GAME_MODE_PSEUDO_FADING;
690 static void SetAnimStatus_AfterFadingIn()
692 global.anim_status = global.anim_status_next;
694 // force update of global animation status in case of rapid screen changes
695 redraw_mask = REDRAW_ALL;
699 void FadeIn(int fade_mask)
702 DrawMaskedBorder(REDRAW_ALL);
705 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
706 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
708 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
712 FADE_SXSIZE = FULL_SXSIZE;
713 FADE_SYSIZE = FULL_SYSIZE;
715 SetAnimStatus_AfterFadingIn();
718 void FadeOut(int fade_mask)
720 SetAnimStatus_BeforeFadingOut();
723 DrawMaskedBorder(REDRAW_ALL);
726 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
727 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
729 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
731 global.border_status = game_status;
734 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
736 static struct TitleFadingInfo fading_leave_stored;
739 fading_leave_stored = fading_leave;
741 fading = fading_leave_stored;
744 void FadeSetEnterMenu()
746 fading = menu.enter_menu;
748 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
751 void FadeSetLeaveMenu()
753 fading = menu.leave_menu;
755 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
758 void FadeSetEnterScreen()
760 fading = menu.enter_screen[game_status];
762 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
765 void FadeSetNextScreen()
767 fading = menu.next_screen[game_status];
769 // (do not overwrite fade mode set by FadeSetEnterScreen)
770 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
773 void FadeSetLeaveScreen()
775 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
778 void FadeSetFromType(int type)
780 if (type & TYPE_ENTER_SCREEN)
781 FadeSetEnterScreen();
782 else if (type & TYPE_ENTER)
784 else if (type & TYPE_LEAVE)
788 void FadeSetDisabled()
790 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
792 fading = fading_none;
795 void FadeSkipNextFadeIn()
797 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
800 void FadeSkipNextFadeOut()
802 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
805 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
807 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
809 return (graphic == IMG_UNDEFINED ? NULL :
810 graphic_info[graphic].bitmap != NULL || redefined ?
811 graphic_info[graphic].bitmap :
812 graphic_info[default_graphic].bitmap);
815 Bitmap *getBackgroundBitmap(int graphic)
817 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
820 Bitmap *getGlobalBorderBitmap(int graphic)
822 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
825 Bitmap *getGlobalBorderBitmapFromGameStatus()
828 (game_status == GAME_MODE_MAIN ||
829 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
830 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
831 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
832 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
835 return getGlobalBorderBitmap(graphic);
838 void SetWindowBackgroundImageIfDefined(int graphic)
840 if (graphic_info[graphic].bitmap)
841 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
844 void SetMainBackgroundImageIfDefined(int graphic)
846 if (graphic_info[graphic].bitmap)
847 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
850 void SetDoorBackgroundImageIfDefined(int graphic)
852 if (graphic_info[graphic].bitmap)
853 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
856 void SetWindowBackgroundImage(int graphic)
858 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
861 void SetMainBackgroundImage(int graphic)
863 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
866 void SetDoorBackgroundImage(int graphic)
868 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
871 void SetPanelBackground()
873 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
875 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
876 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
878 SetDoorBackgroundBitmap(bitmap_db_panel);
881 void DrawBackground(int x, int y, int width, int height)
883 /* "drawto" might still point to playfield buffer here (hall of fame) */
884 ClearRectangleOnBackground(backbuffer, x, y, width, height);
886 if (IN_GFX_FIELD_FULL(x, y))
887 redraw_mask |= REDRAW_FIELD;
888 else if (IN_GFX_DOOR_1(x, y))
889 redraw_mask |= REDRAW_DOOR_1;
890 else if (IN_GFX_DOOR_2(x, y))
891 redraw_mask |= REDRAW_DOOR_2;
892 else if (IN_GFX_DOOR_3(x, y))
893 redraw_mask |= REDRAW_DOOR_3;
896 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
898 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
900 if (font->bitmap == NULL)
903 DrawBackground(x, y, width, height);
906 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
908 struct GraphicInfo *g = &graphic_info[graphic];
910 if (g->bitmap == NULL)
913 DrawBackground(x, y, width, height);
916 static int game_status_last = -1;
917 static Bitmap *global_border_bitmap_last = NULL;
918 static Bitmap *global_border_bitmap = NULL;
919 static int real_sx_last = -1, real_sy_last = -1;
920 static int full_sxsize_last = -1, full_sysize_last = -1;
921 static int dx_last = -1, dy_last = -1;
922 static int dxsize_last = -1, dysize_last = -1;
923 static int vx_last = -1, vy_last = -1;
924 static int vxsize_last = -1, vysize_last = -1;
926 boolean CheckIfGlobalBorderHasChanged()
928 // if game status has not changed, global border has not changed either
929 if (game_status == game_status_last)
932 // determine and store new global border bitmap for current game status
933 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
935 return (global_border_bitmap_last != global_border_bitmap);
938 boolean CheckIfGlobalBorderRedrawIsNeeded()
940 // if game status has not changed, nothing has to be redrawn
941 if (game_status == game_status_last)
944 // redraw if last screen was title screen
945 if (game_status_last == GAME_MODE_TITLE)
948 // redraw if global screen border has changed
949 if (CheckIfGlobalBorderHasChanged())
952 // redraw if position or size of playfield area has changed
953 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
954 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
957 // redraw if position or size of door area has changed
958 if (dx_last != DX || dy_last != DY ||
959 dxsize_last != DXSIZE || dysize_last != DYSIZE)
962 // redraw if position or size of tape area has changed
963 if (vx_last != VX || vy_last != VY ||
964 vxsize_last != VXSIZE || vysize_last != VYSIZE)
970 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
973 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
975 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
978 void RedrawGlobalBorder()
980 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
982 RedrawGlobalBorderFromBitmap(bitmap);
984 redraw_mask = REDRAW_ALL;
987 static void RedrawGlobalBorderIfNeeded()
989 if (game_status == game_status_last)
992 // copy current draw buffer to later copy back areas that have not changed
993 if (game_status_last != GAME_MODE_TITLE)
994 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
996 if (CheckIfGlobalBorderRedrawIsNeeded())
998 // redraw global screen border (or clear, if defined to be empty)
999 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1001 // copy previous playfield and door areas, if they are defined on both
1002 // previous and current screen and if they still have the same size
1004 if (real_sx_last != -1 && real_sy_last != -1 &&
1005 REAL_SX != -1 && REAL_SY != -1 &&
1006 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1007 BlitBitmap(bitmap_db_store, backbuffer,
1008 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1011 if (dx_last != -1 && dy_last != -1 &&
1012 DX != -1 && DY != -1 &&
1013 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1014 BlitBitmap(bitmap_db_store, backbuffer,
1015 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1017 if (vx_last != -1 && vy_last != -1 &&
1018 VX != -1 && VY != -1 &&
1019 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1020 BlitBitmap(bitmap_db_store, backbuffer,
1021 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1023 redraw_mask = REDRAW_ALL;
1026 game_status_last = game_status;
1028 global_border_bitmap_last = global_border_bitmap;
1030 real_sx_last = REAL_SX;
1031 real_sy_last = REAL_SY;
1032 full_sxsize_last = FULL_SXSIZE;
1033 full_sysize_last = FULL_SYSIZE;
1036 dxsize_last = DXSIZE;
1037 dysize_last = DYSIZE;
1040 vxsize_last = VXSIZE;
1041 vysize_last = VYSIZE;
1046 RedrawGlobalBorderIfNeeded();
1048 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1049 /* (when entering hall of fame after playing) */
1050 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1052 /* !!! maybe this should be done before clearing the background !!! */
1053 if (game_status == GAME_MODE_PLAYING)
1055 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1056 SetDrawtoField(DRAW_FIELDBUFFER);
1060 SetDrawtoField(DRAW_BACKBUFFER);
1064 void MarkTileDirty(int x, int y)
1066 redraw_mask |= REDRAW_FIELD;
1069 void SetBorderElement()
1073 BorderElement = EL_EMPTY;
1075 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1077 for (x = 0; x < lev_fieldx; x++)
1079 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1080 BorderElement = EL_STEELWALL;
1082 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1088 void FloodFillLevel(int from_x, int from_y, int fill_element,
1089 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1090 int max_fieldx, int max_fieldy)
1094 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1095 static int safety = 0;
1097 /* check if starting field still has the desired content */
1098 if (field[from_x][from_y] == fill_element)
1103 if (safety > max_fieldx * max_fieldy)
1104 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1106 old_element = field[from_x][from_y];
1107 field[from_x][from_y] = fill_element;
1109 for (i = 0; i < 4; i++)
1111 x = from_x + check[i][0];
1112 y = from_y + check[i][1];
1114 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1115 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1121 void SetRandomAnimationValue(int x, int y)
1123 gfx.anim_random_frame = GfxRandom[x][y];
1126 int getGraphicAnimationFrame(int graphic, int sync_frame)
1128 /* animation synchronized with global frame counter, not move position */
1129 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1130 sync_frame = FrameCounter;
1132 return getAnimationFrame(graphic_info[graphic].anim_frames,
1133 graphic_info[graphic].anim_delay,
1134 graphic_info[graphic].anim_mode,
1135 graphic_info[graphic].anim_start_frame,
1139 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1140 Bitmap **bitmap, int *x, int *y,
1141 boolean get_backside)
1143 struct GraphicInfo *g = &graphic_info[graphic];
1144 Bitmap *src_bitmap = g->bitmap;
1145 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1146 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1147 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1149 // if no in-game graphics defined, always use standard graphic size
1150 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1151 tilesize = TILESIZE;
1153 if (tilesize == gfx.standard_tile_size)
1154 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1155 else if (tilesize == game.tile_size)
1156 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1158 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1160 if (g->offset_y == 0) /* frames are ordered horizontally */
1162 int max_width = g->anim_frames_per_line * g->width;
1163 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1165 src_x = pos % max_width;
1166 src_y = src_y % g->height + pos / max_width * g->height;
1168 else if (g->offset_x == 0) /* frames are ordered vertically */
1170 int max_height = g->anim_frames_per_line * g->height;
1171 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1173 src_x = src_x % g->width + pos / max_height * g->width;
1174 src_y = pos % max_height;
1176 else /* frames are ordered diagonally */
1178 src_x = src_x + frame * g->offset_x;
1179 src_y = src_y + frame * g->offset_y;
1182 *bitmap = src_bitmap;
1183 *x = src_x * tilesize / g->tile_size;
1184 *y = src_y * tilesize / g->tile_size;
1187 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1188 int *x, int *y, boolean get_backside)
1190 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1194 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1195 Bitmap **bitmap, int *x, int *y)
1197 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1200 void getFixedGraphicSource(int graphic, int frame,
1201 Bitmap **bitmap, int *x, int *y)
1203 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1206 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1208 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1211 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1212 int *x, int *y, boolean get_backside)
1214 struct GraphicInfo *g = &graphic_info[graphic];
1215 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1216 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1218 if (TILESIZE_VAR != TILESIZE)
1219 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1222 *bitmap = g->bitmap;
1224 if (g->offset_y == 0) /* frames are ordered horizontally */
1226 int max_width = g->anim_frames_per_line * g->width;
1227 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1229 *x = pos % max_width;
1230 *y = src_y % g->height + pos / max_width * g->height;
1232 else if (g->offset_x == 0) /* frames are ordered vertically */
1234 int max_height = g->anim_frames_per_line * g->height;
1235 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1237 *x = src_x % g->width + pos / max_height * g->width;
1238 *y = pos % max_height;
1240 else /* frames are ordered diagonally */
1242 *x = src_x + frame * g->offset_x;
1243 *y = src_y + frame * g->offset_y;
1246 *x = *x * TILESIZE_VAR / g->tile_size;
1247 *y = *y * TILESIZE_VAR / g->tile_size;
1250 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1252 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1255 void DrawGraphic(int x, int y, int graphic, int frame)
1258 if (!IN_SCR_FIELD(x, y))
1260 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1261 printf("DrawGraphic(): This should never happen!\n");
1266 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1269 MarkTileDirty(x, y);
1272 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1275 if (!IN_SCR_FIELD(x, y))
1277 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1278 printf("DrawGraphic(): This should never happen!\n");
1283 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1285 MarkTileDirty(x, y);
1288 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1294 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1296 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1299 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1305 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1306 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1309 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1312 if (!IN_SCR_FIELD(x, y))
1314 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1315 printf("DrawGraphicThruMask(): This should never happen!\n");
1320 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1323 MarkTileDirty(x, y);
1326 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1329 if (!IN_SCR_FIELD(x, y))
1331 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1332 printf("DrawGraphicThruMask(): This should never happen!\n");
1337 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1339 MarkTileDirty(x, y);
1342 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1348 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1350 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1354 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1355 int graphic, int frame)
1360 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1362 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1366 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1368 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1370 MarkTileDirty(x / tilesize, y / tilesize);
1373 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1379 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1380 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1383 void DrawMiniGraphic(int x, int y, int graphic)
1385 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1386 MarkTileDirty(x / 2, y / 2);
1389 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1394 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1395 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1398 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1399 int graphic, int frame,
1400 int cut_mode, int mask_mode)
1405 int width = TILEX, height = TILEY;
1408 if (dx || dy) /* shifted graphic */
1410 if (x < BX1) /* object enters playfield from the left */
1417 else if (x > BX2) /* object enters playfield from the right */
1423 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1429 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1431 else if (dx) /* general horizontal movement */
1432 MarkTileDirty(x + SIGN(dx), y);
1434 if (y < BY1) /* object enters playfield from the top */
1436 if (cut_mode == CUT_BELOW) /* object completely above top border */
1444 else if (y > BY2) /* object enters playfield from the bottom */
1450 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1456 else if (dy > 0 && cut_mode == CUT_ABOVE)
1458 if (y == BY2) /* object completely above bottom border */
1464 MarkTileDirty(x, y + 1);
1465 } /* object leaves playfield to the bottom */
1466 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1468 else if (dy) /* general vertical movement */
1469 MarkTileDirty(x, y + SIGN(dy));
1473 if (!IN_SCR_FIELD(x, y))
1475 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1476 printf("DrawGraphicShifted(): This should never happen!\n");
1481 width = width * TILESIZE_VAR / TILESIZE;
1482 height = height * TILESIZE_VAR / TILESIZE;
1483 cx = cx * TILESIZE_VAR / TILESIZE;
1484 cy = cy * TILESIZE_VAR / TILESIZE;
1485 dx = dx * TILESIZE_VAR / TILESIZE;
1486 dy = dy * TILESIZE_VAR / TILESIZE;
1488 if (width > 0 && height > 0)
1490 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1495 dst_x = FX + x * TILEX_VAR + dx;
1496 dst_y = FY + y * TILEY_VAR + dy;
1498 if (mask_mode == USE_MASKING)
1499 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1502 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1505 MarkTileDirty(x, y);
1509 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1510 int graphic, int frame,
1511 int cut_mode, int mask_mode)
1516 int width = TILEX_VAR, height = TILEY_VAR;
1519 int x2 = x + SIGN(dx);
1520 int y2 = y + SIGN(dy);
1522 /* movement with two-tile animations must be sync'ed with movement position,
1523 not with current GfxFrame (which can be higher when using slow movement) */
1524 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1525 int anim_frames = graphic_info[graphic].anim_frames;
1527 /* (we also need anim_delay here for movement animations with less frames) */
1528 int anim_delay = graphic_info[graphic].anim_delay;
1529 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1531 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1532 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1534 /* re-calculate animation frame for two-tile movement animation */
1535 frame = getGraphicAnimationFrame(graphic, sync_frame);
1537 /* check if movement start graphic inside screen area and should be drawn */
1538 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1540 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1542 dst_x = FX + x1 * TILEX_VAR;
1543 dst_y = FY + y1 * TILEY_VAR;
1545 if (mask_mode == USE_MASKING)
1546 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1549 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1552 MarkTileDirty(x1, y1);
1555 /* check if movement end graphic inside screen area and should be drawn */
1556 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1558 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1560 dst_x = FX + x2 * TILEX_VAR;
1561 dst_y = FY + y2 * TILEY_VAR;
1563 if (mask_mode == USE_MASKING)
1564 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1567 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1570 MarkTileDirty(x2, y2);
1574 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1575 int graphic, int frame,
1576 int cut_mode, int mask_mode)
1580 DrawGraphic(x, y, graphic, frame);
1585 if (graphic_info[graphic].double_movement) /* EM style movement images */
1586 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1588 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1591 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1592 int frame, int cut_mode)
1594 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1597 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1598 int cut_mode, int mask_mode)
1600 int lx = LEVELX(x), ly = LEVELY(y);
1604 if (IN_LEV_FIELD(lx, ly))
1606 SetRandomAnimationValue(lx, ly);
1608 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1609 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1611 /* do not use double (EM style) movement graphic when not moving */
1612 if (graphic_info[graphic].double_movement && !dx && !dy)
1614 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1615 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1618 else /* border element */
1620 graphic = el2img(element);
1621 frame = getGraphicAnimationFrame(graphic, -1);
1624 if (element == EL_EXPANDABLE_WALL)
1626 boolean left_stopped = FALSE, right_stopped = FALSE;
1628 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1629 left_stopped = TRUE;
1630 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1631 right_stopped = TRUE;
1633 if (left_stopped && right_stopped)
1635 else if (left_stopped)
1637 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1638 frame = graphic_info[graphic].anim_frames - 1;
1640 else if (right_stopped)
1642 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1643 frame = graphic_info[graphic].anim_frames - 1;
1648 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1649 else if (mask_mode == USE_MASKING)
1650 DrawGraphicThruMask(x, y, graphic, frame);
1652 DrawGraphic(x, y, graphic, frame);
1655 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1656 int cut_mode, int mask_mode)
1658 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1659 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1660 cut_mode, mask_mode);
1663 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1666 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1669 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1672 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1675 void DrawLevelElementThruMask(int x, int y, int element)
1677 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1680 void DrawLevelFieldThruMask(int x, int y)
1682 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1685 /* !!! implementation of quicksand is totally broken !!! */
1686 #define IS_CRUMBLED_TILE(x, y, e) \
1687 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1688 !IS_MOVING(x, y) || \
1689 (e) == EL_QUICKSAND_EMPTYING || \
1690 (e) == EL_QUICKSAND_FAST_EMPTYING))
1692 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1697 int width, height, cx, cy;
1698 int sx = SCREENX(x), sy = SCREENY(y);
1699 int crumbled_border_size = graphic_info[graphic].border_size;
1702 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1704 for (i = 1; i < 4; i++)
1706 int dxx = (i & 1 ? dx : 0);
1707 int dyy = (i & 2 ? dy : 0);
1710 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1713 /* check if neighbour field is of same crumble type */
1714 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1715 graphic_info[graphic].class ==
1716 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1718 /* return if check prevents inner corner */
1719 if (same == (dxx == dx && dyy == dy))
1723 /* if we reach this point, we have an inner corner */
1725 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1727 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1728 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1729 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1730 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1732 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1733 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1736 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1741 int width, height, bx, by, cx, cy;
1742 int sx = SCREENX(x), sy = SCREENY(y);
1743 int crumbled_border_size = graphic_info[graphic].border_size;
1744 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1745 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1748 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1750 /* draw simple, sloppy, non-corner-accurate crumbled border */
1752 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1753 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1754 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1755 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1757 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1758 FX + sx * TILEX_VAR + cx,
1759 FY + sy * TILEY_VAR + cy);
1761 /* (remaining middle border part must be at least as big as corner part) */
1762 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1763 crumbled_border_size >= TILESIZE / 3)
1766 /* correct corners of crumbled border, if needed */
1768 for (i = -1; i <= 1; i += 2)
1770 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1771 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1772 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1775 /* check if neighbour field is of same crumble type */
1776 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1777 graphic_info[graphic].class ==
1778 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1780 /* no crumbled corner, but continued crumbled border */
1782 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1783 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1784 int b1 = (i == 1 ? crumbled_border_size_var :
1785 TILESIZE_VAR - 2 * crumbled_border_size_var);
1787 width = crumbled_border_size_var;
1788 height = crumbled_border_size_var;
1790 if (dir == 1 || dir == 2)
1805 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1807 FX + sx * TILEX_VAR + cx,
1808 FY + sy * TILEY_VAR + cy);
1813 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1815 int sx = SCREENX(x), sy = SCREENY(y);
1818 static int xy[4][2] =
1826 if (!IN_LEV_FIELD(x, y))
1829 element = TILE_GFX_ELEMENT(x, y);
1831 /* crumble field itself */
1832 if (IS_CRUMBLED_TILE(x, y, element))
1834 if (!IN_SCR_FIELD(sx, sy))
1837 for (i = 0; i < 4; i++)
1839 int xx = x + xy[i][0];
1840 int yy = y + xy[i][1];
1842 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1845 /* check if neighbour field is of same crumble type */
1846 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1847 graphic_info[graphic].class ==
1848 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1851 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1854 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1855 graphic_info[graphic].anim_frames == 2)
1857 for (i = 0; i < 4; i++)
1859 int dx = (i & 1 ? +1 : -1);
1860 int dy = (i & 2 ? +1 : -1);
1862 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1866 MarkTileDirty(sx, sy);
1868 else /* center field not crumbled -- crumble neighbour fields */
1870 for (i = 0; i < 4; i++)
1872 int xx = x + xy[i][0];
1873 int yy = y + xy[i][1];
1874 int sxx = sx + xy[i][0];
1875 int syy = sy + xy[i][1];
1877 if (!IN_LEV_FIELD(xx, yy) ||
1878 !IN_SCR_FIELD(sxx, syy))
1881 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1884 element = TILE_GFX_ELEMENT(xx, yy);
1886 if (!IS_CRUMBLED_TILE(xx, yy, element))
1889 graphic = el_act2crm(element, ACTION_DEFAULT);
1891 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1893 MarkTileDirty(sxx, syy);
1898 void DrawLevelFieldCrumbled(int x, int y)
1902 if (!IN_LEV_FIELD(x, y))
1905 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1906 GfxElement[x][y] != EL_UNDEFINED &&
1907 GFX_CRUMBLED(GfxElement[x][y]))
1909 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1914 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1916 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1919 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1922 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1923 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1924 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1925 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1926 int sx = SCREENX(x), sy = SCREENY(y);
1928 DrawGraphic(sx, sy, graphic1, frame1);
1929 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1932 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1934 int sx = SCREENX(x), sy = SCREENY(y);
1935 static int xy[4][2] =
1944 for (i = 0; i < 4; i++)
1946 int xx = x + xy[i][0];
1947 int yy = y + xy[i][1];
1948 int sxx = sx + xy[i][0];
1949 int syy = sy + xy[i][1];
1951 if (!IN_LEV_FIELD(xx, yy) ||
1952 !IN_SCR_FIELD(sxx, syy) ||
1953 !GFX_CRUMBLED(Feld[xx][yy]) ||
1957 DrawLevelField(xx, yy);
1961 static int getBorderElement(int x, int y)
1965 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1966 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1967 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1968 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1969 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1970 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1971 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1973 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1974 int steel_position = (x == -1 && y == -1 ? 0 :
1975 x == lev_fieldx && y == -1 ? 1 :
1976 x == -1 && y == lev_fieldy ? 2 :
1977 x == lev_fieldx && y == lev_fieldy ? 3 :
1978 x == -1 || x == lev_fieldx ? 4 :
1979 y == -1 || y == lev_fieldy ? 5 : 6);
1981 return border[steel_position][steel_type];
1984 void DrawScreenElement(int x, int y, int element)
1986 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1987 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1990 void DrawLevelElement(int x, int y, int element)
1992 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1993 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1996 void DrawScreenField(int x, int y)
1998 int lx = LEVELX(x), ly = LEVELY(y);
1999 int element, content;
2001 if (!IN_LEV_FIELD(lx, ly))
2003 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2006 element = getBorderElement(lx, ly);
2008 DrawScreenElement(x, y, element);
2013 element = Feld[lx][ly];
2014 content = Store[lx][ly];
2016 if (IS_MOVING(lx, ly))
2018 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2019 boolean cut_mode = NO_CUTTING;
2021 if (element == EL_QUICKSAND_EMPTYING ||
2022 element == EL_QUICKSAND_FAST_EMPTYING ||
2023 element == EL_MAGIC_WALL_EMPTYING ||
2024 element == EL_BD_MAGIC_WALL_EMPTYING ||
2025 element == EL_DC_MAGIC_WALL_EMPTYING ||
2026 element == EL_AMOEBA_DROPPING)
2027 cut_mode = CUT_ABOVE;
2028 else if (element == EL_QUICKSAND_FILLING ||
2029 element == EL_QUICKSAND_FAST_FILLING ||
2030 element == EL_MAGIC_WALL_FILLING ||
2031 element == EL_BD_MAGIC_WALL_FILLING ||
2032 element == EL_DC_MAGIC_WALL_FILLING)
2033 cut_mode = CUT_BELOW;
2035 if (cut_mode == CUT_ABOVE)
2036 DrawScreenElement(x, y, element);
2038 DrawScreenElement(x, y, EL_EMPTY);
2041 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2042 else if (cut_mode == NO_CUTTING)
2043 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2046 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2048 if (cut_mode == CUT_BELOW &&
2049 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2050 DrawLevelElement(lx, ly + 1, element);
2053 if (content == EL_ACID)
2055 int dir = MovDir[lx][ly];
2056 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2057 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2059 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2062 else if (IS_BLOCKED(lx, ly))
2067 boolean cut_mode = NO_CUTTING;
2068 int element_old, content_old;
2070 Blocked2Moving(lx, ly, &oldx, &oldy);
2073 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2074 MovDir[oldx][oldy] == MV_RIGHT);
2076 element_old = Feld[oldx][oldy];
2077 content_old = Store[oldx][oldy];
2079 if (element_old == EL_QUICKSAND_EMPTYING ||
2080 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2081 element_old == EL_MAGIC_WALL_EMPTYING ||
2082 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2083 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2084 element_old == EL_AMOEBA_DROPPING)
2085 cut_mode = CUT_ABOVE;
2087 DrawScreenElement(x, y, EL_EMPTY);
2090 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2092 else if (cut_mode == NO_CUTTING)
2093 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2096 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2099 else if (IS_DRAWABLE(element))
2100 DrawScreenElement(x, y, element);
2102 DrawScreenElement(x, y, EL_EMPTY);
2105 void DrawLevelField(int x, int y)
2107 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2108 DrawScreenField(SCREENX(x), SCREENY(y));
2109 else if (IS_MOVING(x, y))
2113 Moving2Blocked(x, y, &newx, &newy);
2114 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2115 DrawScreenField(SCREENX(newx), SCREENY(newy));
2117 else if (IS_BLOCKED(x, y))
2121 Blocked2Moving(x, y, &oldx, &oldy);
2122 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2123 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2127 void DrawSizedElement(int x, int y, int element, int tilesize)
2131 graphic = el2edimg(element);
2132 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2135 void DrawMiniElement(int x, int y, int element)
2139 graphic = el2edimg(element);
2140 DrawMiniGraphic(x, y, graphic);
2143 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2146 int x = sx + scroll_x, y = sy + scroll_y;
2148 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2149 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2150 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2151 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2153 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2156 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2158 int x = sx + scroll_x, y = sy + scroll_y;
2160 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2161 DrawMiniElement(sx, sy, EL_EMPTY);
2162 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2163 DrawMiniElement(sx, sy, Feld[x][y]);
2165 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2168 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2169 int x, int y, int xsize, int ysize,
2170 int tile_width, int tile_height)
2174 int dst_x = startx + x * tile_width;
2175 int dst_y = starty + y * tile_height;
2176 int width = graphic_info[graphic].width;
2177 int height = graphic_info[graphic].height;
2178 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2179 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2180 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2181 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2182 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2183 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2184 boolean draw_masked = graphic_info[graphic].draw_masked;
2186 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2188 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2190 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2194 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2195 inner_sx + (x - 1) * tile_width % inner_width);
2196 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2197 inner_sy + (y - 1) * tile_height % inner_height);
2200 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2203 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2207 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2208 int x, int y, int xsize, int ysize, int font_nr)
2210 int font_width = getFontWidth(font_nr);
2211 int font_height = getFontHeight(font_nr);
2213 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2214 font_width, font_height);
2217 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2219 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2220 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2221 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2222 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2223 boolean no_delay = (tape.warp_forward);
2224 unsigned int anim_delay = 0;
2225 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2226 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2227 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2228 int font_width = getFontWidth(font_nr);
2229 int font_height = getFontHeight(font_nr);
2230 int max_xsize = level.envelope[envelope_nr].xsize;
2231 int max_ysize = level.envelope[envelope_nr].ysize;
2232 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2233 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2234 int xend = max_xsize;
2235 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2236 int xstep = (xstart < xend ? 1 : 0);
2237 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2239 int end = MAX(xend - xstart, yend - ystart);
2242 for (i = start; i <= end; i++)
2244 int last_frame = end; // last frame of this "for" loop
2245 int x = xstart + i * xstep;
2246 int y = ystart + i * ystep;
2247 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2248 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2249 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2250 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2253 SetDrawtoField(DRAW_FIELDBUFFER);
2255 BlitScreenToBitmap(backbuffer);
2257 SetDrawtoField(DRAW_BACKBUFFER);
2259 for (yy = 0; yy < ysize; yy++)
2260 for (xx = 0; xx < xsize; xx++)
2261 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2263 DrawTextBuffer(sx + font_width, sy + font_height,
2264 level.envelope[envelope_nr].text, font_nr, max_xsize,
2265 xsize - 2, ysize - 2, 0, mask_mode,
2266 level.envelope[envelope_nr].autowrap,
2267 level.envelope[envelope_nr].centered, FALSE);
2269 redraw_mask |= REDRAW_FIELD;
2272 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2276 void ShowEnvelope(int envelope_nr)
2278 int element = EL_ENVELOPE_1 + envelope_nr;
2279 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2280 int sound_opening = element_info[element].sound[ACTION_OPENING];
2281 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2282 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2283 boolean no_delay = (tape.warp_forward);
2284 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2285 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2286 int anim_mode = graphic_info[graphic].anim_mode;
2287 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2288 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2290 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2292 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2294 if (anim_mode == ANIM_DEFAULT)
2295 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2297 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2300 Delay(wait_delay_value);
2302 WaitForEventToContinue();
2304 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2306 if (anim_mode != ANIM_NONE)
2307 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2309 if (anim_mode == ANIM_DEFAULT)
2310 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2312 game.envelope_active = FALSE;
2314 SetDrawtoField(DRAW_FIELDBUFFER);
2316 redraw_mask |= REDRAW_FIELD;
2320 static void setRequestBasePosition(int *x, int *y)
2322 int sx_base, sy_base;
2324 if (request.x != -1)
2325 sx_base = request.x;
2326 else if (request.align == ALIGN_LEFT)
2328 else if (request.align == ALIGN_RIGHT)
2329 sx_base = SX + SXSIZE;
2331 sx_base = SX + SXSIZE / 2;
2333 if (request.y != -1)
2334 sy_base = request.y;
2335 else if (request.valign == VALIGN_TOP)
2337 else if (request.valign == VALIGN_BOTTOM)
2338 sy_base = SY + SYSIZE;
2340 sy_base = SY + SYSIZE / 2;
2346 static void setRequestPositionExt(int *x, int *y, int width, int height,
2347 boolean add_border_size)
2349 int border_size = request.border_size;
2350 int sx_base, sy_base;
2353 setRequestBasePosition(&sx_base, &sy_base);
2355 if (request.align == ALIGN_LEFT)
2357 else if (request.align == ALIGN_RIGHT)
2358 sx = sx_base - width;
2360 sx = sx_base - width / 2;
2362 if (request.valign == VALIGN_TOP)
2364 else if (request.valign == VALIGN_BOTTOM)
2365 sy = sy_base - height;
2367 sy = sy_base - height / 2;
2369 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2370 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2372 if (add_border_size)
2382 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2384 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2387 void DrawEnvelopeRequest(char *text)
2389 int last_game_status = game_status; /* save current game status */
2390 char *text_final = text;
2391 char *text_door_style = NULL;
2392 int graphic = IMG_BACKGROUND_REQUEST;
2393 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2394 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2395 int font_nr = FONT_REQUEST;
2396 int font_width = getFontWidth(font_nr);
2397 int font_height = getFontHeight(font_nr);
2398 int border_size = request.border_size;
2399 int line_spacing = request.line_spacing;
2400 int line_height = font_height + line_spacing;
2401 int max_text_width = request.width - 2 * border_size;
2402 int max_text_height = request.height - 2 * border_size;
2403 int line_length = max_text_width / font_width;
2404 int max_lines = max_text_height / line_height;
2405 int text_width = line_length * font_width;
2406 int width = request.width;
2407 int height = request.height;
2408 int tile_size = MAX(request.step_offset, 1);
2409 int x_steps = width / tile_size;
2410 int y_steps = height / tile_size;
2411 int sx_offset = border_size;
2412 int sy_offset = border_size;
2416 if (request.centered)
2417 sx_offset = (request.width - text_width) / 2;
2419 if (request.wrap_single_words && !request.autowrap)
2421 char *src_text_ptr, *dst_text_ptr;
2423 text_door_style = checked_malloc(2 * strlen(text) + 1);
2425 src_text_ptr = text;
2426 dst_text_ptr = text_door_style;
2428 while (*src_text_ptr)
2430 if (*src_text_ptr == ' ' ||
2431 *src_text_ptr == '?' ||
2432 *src_text_ptr == '!')
2433 *dst_text_ptr++ = '\n';
2435 if (*src_text_ptr != ' ')
2436 *dst_text_ptr++ = *src_text_ptr;
2441 *dst_text_ptr = '\0';
2443 text_final = text_door_style;
2446 setRequestPosition(&sx, &sy, FALSE);
2448 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2450 for (y = 0; y < y_steps; y++)
2451 for (x = 0; x < x_steps; x++)
2452 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2453 x, y, x_steps, y_steps,
2454 tile_size, tile_size);
2456 /* force DOOR font inside door area */
2457 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
2459 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2460 line_length, -1, max_lines, line_spacing, mask_mode,
2461 request.autowrap, request.centered, FALSE);
2463 SetGameStatus(last_game_status); /* restore current game status */
2465 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2466 RedrawGadget(tool_gadget[i]);
2468 // store readily prepared envelope request for later use when animating
2469 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2471 if (text_door_style)
2472 free(text_door_style);
2475 void AnimateEnvelopeRequest(int anim_mode, int action)
2477 int graphic = IMG_BACKGROUND_REQUEST;
2478 boolean draw_masked = graphic_info[graphic].draw_masked;
2479 int delay_value_normal = request.step_delay;
2480 int delay_value_fast = delay_value_normal / 2;
2481 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2482 boolean no_delay = (tape.warp_forward);
2483 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2484 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2485 unsigned int anim_delay = 0;
2487 int tile_size = MAX(request.step_offset, 1);
2488 int max_xsize = request.width / tile_size;
2489 int max_ysize = request.height / tile_size;
2490 int max_xsize_inner = max_xsize - 2;
2491 int max_ysize_inner = max_ysize - 2;
2493 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2494 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2495 int xend = max_xsize_inner;
2496 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2497 int xstep = (xstart < xend ? 1 : 0);
2498 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2500 int end = MAX(xend - xstart, yend - ystart);
2503 if (setup.quick_doors)
2510 for (i = start; i <= end; i++)
2512 int last_frame = end; // last frame of this "for" loop
2513 int x = xstart + i * xstep;
2514 int y = ystart + i * ystep;
2515 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2516 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2517 int xsize_size_left = (xsize - 1) * tile_size;
2518 int ysize_size_top = (ysize - 1) * tile_size;
2519 int max_xsize_pos = (max_xsize - 1) * tile_size;
2520 int max_ysize_pos = (max_ysize - 1) * tile_size;
2521 int width = xsize * tile_size;
2522 int height = ysize * tile_size;
2527 setRequestPosition(&src_x, &src_y, FALSE);
2528 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2530 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2532 for (yy = 0; yy < 2; yy++)
2534 for (xx = 0; xx < 2; xx++)
2536 int src_xx = src_x + xx * max_xsize_pos;
2537 int src_yy = src_y + yy * max_ysize_pos;
2538 int dst_xx = dst_x + xx * xsize_size_left;
2539 int dst_yy = dst_y + yy * ysize_size_top;
2540 int xx_size = (xx ? tile_size : xsize_size_left);
2541 int yy_size = (yy ? tile_size : ysize_size_top);
2544 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2545 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2547 BlitBitmap(bitmap_db_cross, backbuffer,
2548 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2552 redraw_mask |= REDRAW_FIELD;
2557 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2561 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2563 int graphic = IMG_BACKGROUND_REQUEST;
2564 int sound_opening = SND_REQUEST_OPENING;
2565 int sound_closing = SND_REQUEST_CLOSING;
2566 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2567 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2568 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2569 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2570 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2572 if (game_status == GAME_MODE_PLAYING)
2573 BlitScreenToBitmap(backbuffer);
2575 SetDrawtoField(DRAW_BACKBUFFER);
2577 // SetDrawBackgroundMask(REDRAW_NONE);
2579 if (action == ACTION_OPENING)
2581 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2583 if (req_state & REQ_ASK)
2585 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2586 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2588 else if (req_state & REQ_CONFIRM)
2590 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2592 else if (req_state & REQ_PLAYER)
2594 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2595 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2596 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2597 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2600 DrawEnvelopeRequest(text);
2602 if (game_status != GAME_MODE_MAIN)
2606 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2608 if (action == ACTION_OPENING)
2610 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2612 if (anim_mode == ANIM_DEFAULT)
2613 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2615 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2619 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2621 if (anim_mode != ANIM_NONE)
2622 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2624 if (anim_mode == ANIM_DEFAULT)
2625 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2628 game.envelope_active = FALSE;
2630 if (action == ACTION_CLOSING)
2632 if (game_status != GAME_MODE_MAIN)
2635 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2638 // SetDrawBackgroundMask(last_draw_background_mask);
2640 redraw_mask |= REDRAW_FIELD;
2642 if (game_status == GAME_MODE_MAIN)
2647 if (action == ACTION_CLOSING &&
2648 game_status == GAME_MODE_PLAYING &&
2649 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2650 SetDrawtoField(DRAW_FIELDBUFFER);
2653 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2657 int graphic = el2preimg(element);
2659 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2660 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2663 void DrawLevel(int draw_background_mask)
2667 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2668 SetDrawBackgroundMask(draw_background_mask);
2672 for (x = BX1; x <= BX2; x++)
2673 for (y = BY1; y <= BY2; y++)
2674 DrawScreenField(x, y);
2676 redraw_mask |= REDRAW_FIELD;
2679 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2684 for (x = 0; x < size_x; x++)
2685 for (y = 0; y < size_y; y++)
2686 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2688 redraw_mask |= REDRAW_FIELD;
2691 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2695 for (x = 0; x < size_x; x++)
2696 for (y = 0; y < size_y; y++)
2697 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2699 redraw_mask |= REDRAW_FIELD;
2702 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2704 boolean show_level_border = (BorderElement != EL_EMPTY);
2705 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2706 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2707 int tile_size = preview.tile_size;
2708 int preview_width = preview.xsize * tile_size;
2709 int preview_height = preview.ysize * tile_size;
2710 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2711 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2712 int real_preview_width = real_preview_xsize * tile_size;
2713 int real_preview_height = real_preview_ysize * tile_size;
2714 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2715 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2718 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2721 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2723 dst_x += (preview_width - real_preview_width) / 2;
2724 dst_y += (preview_height - real_preview_height) / 2;
2726 for (x = 0; x < real_preview_xsize; x++)
2728 for (y = 0; y < real_preview_ysize; y++)
2730 int lx = from_x + x + (show_level_border ? -1 : 0);
2731 int ly = from_y + y + (show_level_border ? -1 : 0);
2732 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2733 getBorderElement(lx, ly));
2735 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2736 element, tile_size);
2740 redraw_mask |= REDRAW_FIELD;
2743 #define MICROLABEL_EMPTY 0
2744 #define MICROLABEL_LEVEL_NAME 1
2745 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2746 #define MICROLABEL_LEVEL_AUTHOR 3
2747 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2748 #define MICROLABEL_IMPORTED_FROM 5
2749 #define MICROLABEL_IMPORTED_BY_HEAD 6
2750 #define MICROLABEL_IMPORTED_BY 7
2752 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2754 int max_text_width = SXSIZE;
2755 int font_width = getFontWidth(font_nr);
2757 if (pos->align == ALIGN_CENTER)
2758 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2759 else if (pos->align == ALIGN_RIGHT)
2760 max_text_width = pos->x;
2762 max_text_width = SXSIZE - pos->x;
2764 return max_text_width / font_width;
2767 static void DrawPreviewLevelLabelExt(int mode)
2769 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2770 char label_text[MAX_OUTPUT_LINESIZE + 1];
2771 int max_len_label_text;
2772 int font_nr = pos->font;
2775 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2778 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2779 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2780 mode == MICROLABEL_IMPORTED_BY_HEAD)
2781 font_nr = pos->font_alt;
2783 max_len_label_text = getMaxTextLength(pos, font_nr);
2785 if (pos->size != -1)
2786 max_len_label_text = pos->size;
2788 for (i = 0; i < max_len_label_text; i++)
2789 label_text[i] = ' ';
2790 label_text[max_len_label_text] = '\0';
2792 if (strlen(label_text) > 0)
2793 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2796 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2797 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2798 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2799 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2800 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2801 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2802 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2803 max_len_label_text);
2804 label_text[max_len_label_text] = '\0';
2806 if (strlen(label_text) > 0)
2807 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2809 redraw_mask |= REDRAW_FIELD;
2812 static void DrawPreviewLevelExt(boolean restart)
2814 static unsigned int scroll_delay = 0;
2815 static unsigned int label_delay = 0;
2816 static int from_x, from_y, scroll_direction;
2817 static int label_state, label_counter;
2818 unsigned int scroll_delay_value = preview.step_delay;
2819 boolean show_level_border = (BorderElement != EL_EMPTY);
2820 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2821 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2822 int last_game_status = game_status; /* save current game status */
2829 if (preview.anim_mode == ANIM_CENTERED)
2831 if (level_xsize > preview.xsize)
2832 from_x = (level_xsize - preview.xsize) / 2;
2833 if (level_ysize > preview.ysize)
2834 from_y = (level_ysize - preview.ysize) / 2;
2837 from_x += preview.xoffset;
2838 from_y += preview.yoffset;
2840 scroll_direction = MV_RIGHT;
2844 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2845 DrawPreviewLevelLabelExt(label_state);
2847 /* initialize delay counters */
2848 DelayReached(&scroll_delay, 0);
2849 DelayReached(&label_delay, 0);
2851 if (leveldir_current->name)
2853 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2854 char label_text[MAX_OUTPUT_LINESIZE + 1];
2855 int font_nr = pos->font;
2856 int max_len_label_text = getMaxTextLength(pos, font_nr);
2858 if (pos->size != -1)
2859 max_len_label_text = pos->size;
2861 strncpy(label_text, leveldir_current->name, max_len_label_text);
2862 label_text[max_len_label_text] = '\0';
2864 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2865 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2868 SetGameStatus(last_game_status); /* restore current game status */
2873 /* scroll preview level, if needed */
2874 if (preview.anim_mode != ANIM_NONE &&
2875 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2876 DelayReached(&scroll_delay, scroll_delay_value))
2878 switch (scroll_direction)
2883 from_x -= preview.step_offset;
2884 from_x = (from_x < 0 ? 0 : from_x);
2887 scroll_direction = MV_UP;
2891 if (from_x < level_xsize - preview.xsize)
2893 from_x += preview.step_offset;
2894 from_x = (from_x > level_xsize - preview.xsize ?
2895 level_xsize - preview.xsize : from_x);
2898 scroll_direction = MV_DOWN;
2904 from_y -= preview.step_offset;
2905 from_y = (from_y < 0 ? 0 : from_y);
2908 scroll_direction = MV_RIGHT;
2912 if (from_y < level_ysize - preview.ysize)
2914 from_y += preview.step_offset;
2915 from_y = (from_y > level_ysize - preview.ysize ?
2916 level_ysize - preview.ysize : from_y);
2919 scroll_direction = MV_LEFT;
2926 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2929 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2930 /* redraw micro level label, if needed */
2931 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2932 !strEqual(level.author, ANONYMOUS_NAME) &&
2933 !strEqual(level.author, leveldir_current->name) &&
2934 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2936 int max_label_counter = 23;
2938 if (leveldir_current->imported_from != NULL &&
2939 strlen(leveldir_current->imported_from) > 0)
2940 max_label_counter += 14;
2941 if (leveldir_current->imported_by != NULL &&
2942 strlen(leveldir_current->imported_by) > 0)
2943 max_label_counter += 14;
2945 label_counter = (label_counter + 1) % max_label_counter;
2946 label_state = (label_counter >= 0 && label_counter <= 7 ?
2947 MICROLABEL_LEVEL_NAME :
2948 label_counter >= 9 && label_counter <= 12 ?
2949 MICROLABEL_LEVEL_AUTHOR_HEAD :
2950 label_counter >= 14 && label_counter <= 21 ?
2951 MICROLABEL_LEVEL_AUTHOR :
2952 label_counter >= 23 && label_counter <= 26 ?
2953 MICROLABEL_IMPORTED_FROM_HEAD :
2954 label_counter >= 28 && label_counter <= 35 ?
2955 MICROLABEL_IMPORTED_FROM :
2956 label_counter >= 37 && label_counter <= 40 ?
2957 MICROLABEL_IMPORTED_BY_HEAD :
2958 label_counter >= 42 && label_counter <= 49 ?
2959 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2961 if (leveldir_current->imported_from == NULL &&
2962 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2963 label_state == MICROLABEL_IMPORTED_FROM))
2964 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2965 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2967 DrawPreviewLevelLabelExt(label_state);
2970 SetGameStatus(last_game_status); /* restore current game status */
2973 void DrawPreviewLevelInitial()
2975 DrawPreviewLevelExt(TRUE);
2978 void DrawPreviewLevelAnimation()
2980 DrawPreviewLevelExt(FALSE);
2983 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2984 int graphic, int sync_frame,
2987 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2989 if (mask_mode == USE_MASKING)
2990 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2992 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2995 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2996 int graphic, int sync_frame, int mask_mode)
2998 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3000 if (mask_mode == USE_MASKING)
3001 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3003 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3006 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3008 int lx = LEVELX(x), ly = LEVELY(y);
3010 if (!IN_SCR_FIELD(x, y))
3013 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3014 graphic, GfxFrame[lx][ly], NO_MASKING);
3016 MarkTileDirty(x, y);
3019 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3021 int lx = LEVELX(x), ly = LEVELY(y);
3023 if (!IN_SCR_FIELD(x, y))
3026 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3027 graphic, GfxFrame[lx][ly], NO_MASKING);
3028 MarkTileDirty(x, y);
3031 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3033 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3036 void DrawLevelElementAnimation(int x, int y, int element)
3038 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3040 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3043 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3045 int sx = SCREENX(x), sy = SCREENY(y);
3047 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3050 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3053 DrawGraphicAnimation(sx, sy, graphic);
3056 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3057 DrawLevelFieldCrumbled(x, y);
3059 if (GFX_CRUMBLED(Feld[x][y]))
3060 DrawLevelFieldCrumbled(x, y);
3064 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3066 int sx = SCREENX(x), sy = SCREENY(y);
3069 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3072 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3074 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3077 DrawGraphicAnimation(sx, sy, graphic);
3079 if (GFX_CRUMBLED(element))
3080 DrawLevelFieldCrumbled(x, y);
3083 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3085 if (player->use_murphy)
3087 /* this works only because currently only one player can be "murphy" ... */
3088 static int last_horizontal_dir = MV_LEFT;
3089 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3091 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3092 last_horizontal_dir = move_dir;
3094 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3096 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3098 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3104 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3107 static boolean equalGraphics(int graphic1, int graphic2)
3109 struct GraphicInfo *g1 = &graphic_info[graphic1];
3110 struct GraphicInfo *g2 = &graphic_info[graphic2];
3112 return (g1->bitmap == g2->bitmap &&
3113 g1->src_x == g2->src_x &&
3114 g1->src_y == g2->src_y &&
3115 g1->anim_frames == g2->anim_frames &&
3116 g1->anim_delay == g2->anim_delay &&
3117 g1->anim_mode == g2->anim_mode);
3120 void DrawAllPlayers()
3124 for (i = 0; i < MAX_PLAYERS; i++)
3125 if (stored_player[i].active)
3126 DrawPlayer(&stored_player[i]);
3129 void DrawPlayerField(int x, int y)
3131 if (!IS_PLAYER(x, y))
3134 DrawPlayer(PLAYERINFO(x, y));
3137 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3139 void DrawPlayer(struct PlayerInfo *player)
3141 int jx = player->jx;
3142 int jy = player->jy;
3143 int move_dir = player->MovDir;
3144 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3145 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3146 int last_jx = (player->is_moving ? jx - dx : jx);
3147 int last_jy = (player->is_moving ? jy - dy : jy);
3148 int next_jx = jx + dx;
3149 int next_jy = jy + dy;
3150 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3151 boolean player_is_opaque = FALSE;
3152 int sx = SCREENX(jx), sy = SCREENY(jy);
3153 int sxx = 0, syy = 0;
3154 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3156 int action = ACTION_DEFAULT;
3157 int last_player_graphic = getPlayerGraphic(player, move_dir);
3158 int last_player_frame = player->Frame;
3161 /* GfxElement[][] is set to the element the player is digging or collecting;
3162 remove also for off-screen player if the player is not moving anymore */
3163 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3164 GfxElement[jx][jy] = EL_UNDEFINED;
3166 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3170 if (!IN_LEV_FIELD(jx, jy))
3172 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3173 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3174 printf("DrawPlayerField(): This should never happen!\n");
3179 if (element == EL_EXPLOSION)
3182 action = (player->is_pushing ? ACTION_PUSHING :
3183 player->is_digging ? ACTION_DIGGING :
3184 player->is_collecting ? ACTION_COLLECTING :
3185 player->is_moving ? ACTION_MOVING :
3186 player->is_snapping ? ACTION_SNAPPING :
3187 player->is_dropping ? ACTION_DROPPING :
3188 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3190 if (player->is_waiting)
3191 move_dir = player->dir_waiting;
3193 InitPlayerGfxAnimation(player, action, move_dir);
3195 /* ----------------------------------------------------------------------- */
3196 /* draw things in the field the player is leaving, if needed */
3197 /* ----------------------------------------------------------------------- */
3199 if (player->is_moving)
3201 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3203 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3205 if (last_element == EL_DYNAMITE_ACTIVE ||
3206 last_element == EL_EM_DYNAMITE_ACTIVE ||
3207 last_element == EL_SP_DISK_RED_ACTIVE)
3208 DrawDynamite(last_jx, last_jy);
3210 DrawLevelFieldThruMask(last_jx, last_jy);
3212 else if (last_element == EL_DYNAMITE_ACTIVE ||
3213 last_element == EL_EM_DYNAMITE_ACTIVE ||
3214 last_element == EL_SP_DISK_RED_ACTIVE)
3215 DrawDynamite(last_jx, last_jy);
3217 /* !!! this is not enough to prevent flickering of players which are
3218 moving next to each others without a free tile between them -- this
3219 can only be solved by drawing all players layer by layer (first the
3220 background, then the foreground etc.) !!! => TODO */
3221 else if (!IS_PLAYER(last_jx, last_jy))
3222 DrawLevelField(last_jx, last_jy);
3225 DrawLevelField(last_jx, last_jy);
3228 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3229 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3232 if (!IN_SCR_FIELD(sx, sy))
3235 /* ----------------------------------------------------------------------- */
3236 /* draw things behind the player, if needed */
3237 /* ----------------------------------------------------------------------- */
3240 DrawLevelElement(jx, jy, Back[jx][jy]);
3241 else if (IS_ACTIVE_BOMB(element))
3242 DrawLevelElement(jx, jy, EL_EMPTY);
3245 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3247 int old_element = GfxElement[jx][jy];
3248 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3249 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3251 if (GFX_CRUMBLED(old_element))
3252 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3254 DrawGraphic(sx, sy, old_graphic, frame);
3256 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3257 player_is_opaque = TRUE;
3261 GfxElement[jx][jy] = EL_UNDEFINED;
3263 /* make sure that pushed elements are drawn with correct frame rate */
3264 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3266 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3267 GfxFrame[jx][jy] = player->StepFrame;
3269 DrawLevelField(jx, jy);
3273 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3274 /* ----------------------------------------------------------------------- */
3275 /* draw player himself */
3276 /* ----------------------------------------------------------------------- */
3278 graphic = getPlayerGraphic(player, move_dir);
3280 /* in the case of changed player action or direction, prevent the current
3281 animation frame from being restarted for identical animations */
3282 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3283 player->Frame = last_player_frame;
3285 frame = getGraphicAnimationFrame(graphic, player->Frame);
3289 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3290 sxx = player->GfxPos;
3292 syy = player->GfxPos;
3295 if (player_is_opaque)
3296 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3298 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3300 if (SHIELD_ON(player))
3302 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3303 IMG_SHIELD_NORMAL_ACTIVE);
3304 int frame = getGraphicAnimationFrame(graphic, -1);
3306 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3310 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3313 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3314 sxx = player->GfxPos;
3316 syy = player->GfxPos;
3320 /* ----------------------------------------------------------------------- */
3321 /* draw things the player is pushing, if needed */
3322 /* ----------------------------------------------------------------------- */
3324 if (player->is_pushing && player->is_moving)
3326 int px = SCREENX(jx), py = SCREENY(jy);
3327 int pxx = (TILEX - ABS(sxx)) * dx;
3328 int pyy = (TILEY - ABS(syy)) * dy;
3329 int gfx_frame = GfxFrame[jx][jy];
3335 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3337 element = Feld[next_jx][next_jy];
3338 gfx_frame = GfxFrame[next_jx][next_jy];
3341 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3343 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3344 frame = getGraphicAnimationFrame(graphic, sync_frame);
3346 /* draw background element under pushed element (like the Sokoban field) */
3347 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3349 /* this allows transparent pushing animation over non-black background */
3352 DrawLevelElement(jx, jy, Back[jx][jy]);
3354 DrawLevelElement(jx, jy, EL_EMPTY);
3356 if (Back[next_jx][next_jy])
3357 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3359 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3361 else if (Back[next_jx][next_jy])
3362 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3365 /* do not draw (EM style) pushing animation when pushing is finished */
3366 /* (two-tile animations usually do not contain start and end frame) */
3367 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3368 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3370 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3372 /* masked drawing is needed for EMC style (double) movement graphics */
3373 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3374 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3378 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3379 /* ----------------------------------------------------------------------- */
3380 /* draw player himself */
3381 /* ----------------------------------------------------------------------- */
3383 graphic = getPlayerGraphic(player, move_dir);
3385 /* in the case of changed player action or direction, prevent the current
3386 animation frame from being restarted for identical animations */
3387 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3388 player->Frame = last_player_frame;
3390 frame = getGraphicAnimationFrame(graphic, player->Frame);
3394 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3395 sxx = player->GfxPos;
3397 syy = player->GfxPos;
3400 if (player_is_opaque)
3401 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3403 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3405 if (SHIELD_ON(player))
3407 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3408 IMG_SHIELD_NORMAL_ACTIVE);
3409 int frame = getGraphicAnimationFrame(graphic, -1);
3411 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3415 /* ----------------------------------------------------------------------- */
3416 /* draw things in front of player (active dynamite or dynabombs) */
3417 /* ----------------------------------------------------------------------- */
3419 if (IS_ACTIVE_BOMB(element))
3421 graphic = el2img(element);
3422 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3424 if (game.emulation == EMU_SUPAPLEX)
3425 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3427 DrawGraphicThruMask(sx, sy, graphic, frame);
3430 if (player_is_moving && last_element == EL_EXPLOSION)
3432 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3433 GfxElement[last_jx][last_jy] : EL_EMPTY);
3434 int graphic = el_act2img(element, ACTION_EXPLODING);
3435 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3436 int phase = ExplodePhase[last_jx][last_jy] - 1;
3437 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3440 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3443 /* ----------------------------------------------------------------------- */
3444 /* draw elements the player is just walking/passing through/under */
3445 /* ----------------------------------------------------------------------- */
3447 if (player_is_moving)
3449 /* handle the field the player is leaving ... */
3450 if (IS_ACCESSIBLE_INSIDE(last_element))
3451 DrawLevelField(last_jx, last_jy);
3452 else if (IS_ACCESSIBLE_UNDER(last_element))
3453 DrawLevelFieldThruMask(last_jx, last_jy);
3456 /* do not redraw accessible elements if the player is just pushing them */
3457 if (!player_is_moving || !player->is_pushing)
3459 /* ... and the field the player is entering */
3460 if (IS_ACCESSIBLE_INSIDE(element))
3461 DrawLevelField(jx, jy);
3462 else if (IS_ACCESSIBLE_UNDER(element))
3463 DrawLevelFieldThruMask(jx, jy);
3466 MarkTileDirty(sx, sy);
3469 /* ------------------------------------------------------------------------- */
3471 void WaitForEventToContinue()
3473 boolean still_wait = TRUE;
3475 /* simulate releasing mouse button over last gadget, if still pressed */
3477 HandleGadgets(-1, -1, 0);
3479 button_status = MB_RELEASED;
3493 case EVENT_BUTTONPRESS:
3494 case EVENT_KEYPRESS:
3498 case EVENT_KEYRELEASE:
3499 ClearPlayerAction();
3503 HandleOtherEvents(&event);
3507 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3514 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3518 #define MAX_REQUEST_LINES 13
3519 #define MAX_REQUEST_LINE_FONT1_LEN 7
3520 #define MAX_REQUEST_LINE_FONT2_LEN 10
3522 static int RequestHandleEvents(unsigned int req_state)
3524 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3525 local_player->LevelSolved_GameEnd);
3526 int width = request.width;
3527 int height = request.height;
3531 setRequestPosition(&sx, &sy, FALSE);
3533 button_status = MB_RELEASED;
3535 request_gadget_id = -1;
3542 SetDrawtoField(DRAW_FIELDBUFFER);
3544 HandleGameActions();
3546 SetDrawtoField(DRAW_BACKBUFFER);
3548 if (global.use_envelope_request)
3550 /* copy current state of request area to middle of playfield area */
3551 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3559 while (NextValidEvent(&event))
3563 case EVENT_BUTTONPRESS:
3564 case EVENT_BUTTONRELEASE:
3565 case EVENT_MOTIONNOTIFY:
3569 if (event.type == EVENT_MOTIONNOTIFY)
3574 motion_status = TRUE;
3575 mx = ((MotionEvent *) &event)->x;
3576 my = ((MotionEvent *) &event)->y;
3580 motion_status = FALSE;
3581 mx = ((ButtonEvent *) &event)->x;
3582 my = ((ButtonEvent *) &event)->y;
3583 if (event.type == EVENT_BUTTONPRESS)
3584 button_status = ((ButtonEvent *) &event)->button;
3586 button_status = MB_RELEASED;
3589 /* this sets 'request_gadget_id' */
3590 HandleGadgets(mx, my, button_status);
3592 switch (request_gadget_id)
3594 case TOOL_CTRL_ID_YES:
3597 case TOOL_CTRL_ID_NO:
3600 case TOOL_CTRL_ID_CONFIRM:
3601 result = TRUE | FALSE;
3604 case TOOL_CTRL_ID_PLAYER_1:
3607 case TOOL_CTRL_ID_PLAYER_2:
3610 case TOOL_CTRL_ID_PLAYER_3:
3613 case TOOL_CTRL_ID_PLAYER_4:
3624 case EVENT_KEYPRESS:
3625 switch (GetEventKey((KeyEvent *)&event, TRUE))
3628 if (req_state & REQ_CONFIRM)
3633 #if defined(TARGET_SDL2)
3640 #if defined(TARGET_SDL2)
3650 if (req_state & REQ_PLAYER)
3654 case EVENT_KEYRELEASE:
3655 ClearPlayerAction();
3659 HandleOtherEvents(&event);
3664 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3666 int joy = AnyJoystick();
3668 if (joy & JOY_BUTTON_1)
3670 else if (joy & JOY_BUTTON_2)
3676 if (global.use_envelope_request)
3678 /* copy back current state of pressed buttons inside request area */
3679 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3689 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3695 static boolean RequestDoor(char *text, unsigned int req_state)
3697 unsigned int old_door_state;
3698 int last_game_status = game_status; /* save current game status */
3699 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3700 int font_nr = FONT_TEXT_2;
3705 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3707 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3708 font_nr = FONT_TEXT_1;
3711 if (game_status == GAME_MODE_PLAYING)
3712 BlitScreenToBitmap(backbuffer);
3714 /* disable deactivated drawing when quick-loading level tape recording */
3715 if (tape.playing && tape.deactivate_display)
3716 TapeDeactivateDisplayOff(TRUE);
3718 SetMouseCursor(CURSOR_DEFAULT);
3720 #if defined(NETWORK_AVALIABLE)
3721 /* pause network game while waiting for request to answer */
3722 if (options.network &&
3723 game_status == GAME_MODE_PLAYING &&
3724 req_state & REQUEST_WAIT_FOR_INPUT)
3725 SendToServer_PausePlaying();
3728 old_door_state = GetDoorState();
3730 /* simulate releasing mouse button over last gadget, if still pressed */
3732 HandleGadgets(-1, -1, 0);
3736 /* draw released gadget before proceeding */
3739 if (old_door_state & DOOR_OPEN_1)
3741 CloseDoor(DOOR_CLOSE_1);
3743 /* save old door content */
3744 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3745 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3748 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3749 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3751 /* clear door drawing field */
3752 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3754 /* force DOOR font inside door area */
3755 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
3757 /* write text for request */
3758 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3760 char text_line[max_request_line_len + 1];
3766 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3768 tc = *(text_ptr + tx);
3769 // if (!tc || tc == ' ')
3770 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3774 if ((tc == '?' || tc == '!') && tl == 0)
3784 strncpy(text_line, text_ptr, tl);
3787 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3788 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3789 text_line, font_nr);
3791 text_ptr += tl + (tc == ' ' ? 1 : 0);
3792 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3795 SetGameStatus(last_game_status); /* restore current game status */
3797 if (req_state & REQ_ASK)
3799 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3800 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3802 else if (req_state & REQ_CONFIRM)
3804 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3806 else if (req_state & REQ_PLAYER)
3808 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3809 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3810 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3811 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3814 /* copy request gadgets to door backbuffer */
3815 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3817 OpenDoor(DOOR_OPEN_1);
3819 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3821 if (game_status == GAME_MODE_PLAYING)
3823 SetPanelBackground();
3824 SetDrawBackgroundMask(REDRAW_DOOR_1);
3828 SetDrawBackgroundMask(REDRAW_FIELD);
3834 if (game_status != GAME_MODE_MAIN)
3837 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3839 // ---------- handle request buttons ----------
3840 result = RequestHandleEvents(req_state);
3842 if (game_status != GAME_MODE_MAIN)
3847 if (!(req_state & REQ_STAY_OPEN))
3849 CloseDoor(DOOR_CLOSE_1);
3851 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3852 (req_state & REQ_REOPEN))
3853 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3858 if (game_status == GAME_MODE_PLAYING)
3860 SetPanelBackground();
3861 SetDrawBackgroundMask(REDRAW_DOOR_1);
3865 SetDrawBackgroundMask(REDRAW_FIELD);
3868 #if defined(NETWORK_AVALIABLE)
3869 /* continue network game after request */
3870 if (options.network &&
3871 game_status == GAME_MODE_PLAYING &&
3872 req_state & REQUEST_WAIT_FOR_INPUT)
3873 SendToServer_ContinuePlaying();
3876 /* restore deactivated drawing when quick-loading level tape recording */
3877 if (tape.playing && tape.deactivate_display)
3878 TapeDeactivateDisplayOn();
3883 static boolean RequestEnvelope(char *text, unsigned int req_state)
3887 if (game_status == GAME_MODE_PLAYING)
3888 BlitScreenToBitmap(backbuffer);
3890 /* disable deactivated drawing when quick-loading level tape recording */
3891 if (tape.playing && tape.deactivate_display)
3892 TapeDeactivateDisplayOff(TRUE);
3894 SetMouseCursor(CURSOR_DEFAULT);
3896 #if defined(NETWORK_AVALIABLE)
3897 /* pause network game while waiting for request to answer */
3898 if (options.network &&
3899 game_status == GAME_MODE_PLAYING &&
3900 req_state & REQUEST_WAIT_FOR_INPUT)
3901 SendToServer_PausePlaying();
3904 /* simulate releasing mouse button over last gadget, if still pressed */
3906 HandleGadgets(-1, -1, 0);
3910 // (replace with setting corresponding request background)
3911 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3912 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3914 /* clear door drawing field */
3915 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3917 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3919 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3921 if (game_status == GAME_MODE_PLAYING)
3923 SetPanelBackground();
3924 SetDrawBackgroundMask(REDRAW_DOOR_1);
3928 SetDrawBackgroundMask(REDRAW_FIELD);
3934 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3936 // ---------- handle request buttons ----------
3937 result = RequestHandleEvents(req_state);
3939 if (game_status != GAME_MODE_MAIN)
3944 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3948 if (game_status == GAME_MODE_PLAYING)
3950 SetPanelBackground();
3951 SetDrawBackgroundMask(REDRAW_DOOR_1);
3955 SetDrawBackgroundMask(REDRAW_FIELD);
3958 #if defined(NETWORK_AVALIABLE)
3959 /* continue network game after request */
3960 if (options.network &&
3961 game_status == GAME_MODE_PLAYING &&
3962 req_state & REQUEST_WAIT_FOR_INPUT)
3963 SendToServer_ContinuePlaying();
3966 /* restore deactivated drawing when quick-loading level tape recording */
3967 if (tape.playing && tape.deactivate_display)
3968 TapeDeactivateDisplayOn();
3973 boolean Request(char *text, unsigned int req_state)
3975 if (global.use_envelope_request)
3976 return RequestEnvelope(text, req_state);
3978 return RequestDoor(text, req_state);
3981 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3983 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3984 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3987 if (dpo1->sort_priority != dpo2->sort_priority)
3988 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3990 compare_result = dpo1->nr - dpo2->nr;
3992 return compare_result;
3995 void InitGraphicCompatibilityInfo_Doors()
4001 struct DoorInfo *door;
4005 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
4006 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
4008 { -1, -1, -1, NULL }
4010 struct Rect door_rect_list[] =
4012 { DX, DY, DXSIZE, DYSIZE },
4013 { VX, VY, VXSIZE, VYSIZE }
4017 for (i = 0; doors[i].door_token != -1; i++)
4019 int door_token = doors[i].door_token;
4020 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4021 int part_1 = doors[i].part_1;
4022 int part_8 = doors[i].part_8;
4023 int part_2 = part_1 + 1;
4024 int part_3 = part_1 + 2;
4025 struct DoorInfo *door = doors[i].door;
4026 struct Rect *door_rect = &door_rect_list[door_index];
4027 boolean door_gfx_redefined = FALSE;
4029 /* check if any door part graphic definitions have been redefined */
4031 for (j = 0; door_part_controls[j].door_token != -1; j++)
4033 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4034 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4036 if (dpc->door_token == door_token && fi->redefined)
4037 door_gfx_redefined = TRUE;
4040 /* check for old-style door graphic/animation modifications */
4042 if (!door_gfx_redefined)
4044 if (door->anim_mode & ANIM_STATIC_PANEL)
4046 door->panel.step_xoffset = 0;
4047 door->panel.step_yoffset = 0;
4050 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4052 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4053 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4054 int num_door_steps, num_panel_steps;
4056 /* remove door part graphics other than the two default wings */
4058 for (j = 0; door_part_controls[j].door_token != -1; j++)
4060 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4061 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4063 if (dpc->graphic >= part_3 &&
4064 dpc->graphic <= part_8)
4068 /* set graphics and screen positions of the default wings */
4070 g_part_1->width = door_rect->width;
4071 g_part_1->height = door_rect->height;
4072 g_part_2->width = door_rect->width;
4073 g_part_2->height = door_rect->height;
4074 g_part_2->src_x = door_rect->width;
4075 g_part_2->src_y = g_part_1->src_y;
4077 door->part_2.x = door->part_1.x;
4078 door->part_2.y = door->part_1.y;
4080 if (door->width != -1)
4082 g_part_1->width = door->width;
4083 g_part_2->width = door->width;
4085 // special treatment for graphics and screen position of right wing
4086 g_part_2->src_x += door_rect->width - door->width;
4087 door->part_2.x += door_rect->width - door->width;
4090 if (door->height != -1)
4092 g_part_1->height = door->height;
4093 g_part_2->height = door->height;
4095 // special treatment for graphics and screen position of bottom wing
4096 g_part_2->src_y += door_rect->height - door->height;
4097 door->part_2.y += door_rect->height - door->height;
4100 /* set animation delays for the default wings and panels */
4102 door->part_1.step_delay = door->step_delay;
4103 door->part_2.step_delay = door->step_delay;
4104 door->panel.step_delay = door->step_delay;
4106 /* set animation draw order for the default wings */
4108 door->part_1.sort_priority = 2; /* draw left wing over ... */
4109 door->part_2.sort_priority = 1; /* ... right wing */
4111 /* set animation draw offset for the default wings */
4113 if (door->anim_mode & ANIM_HORIZONTAL)
4115 door->part_1.step_xoffset = door->step_offset;
4116 door->part_1.step_yoffset = 0;
4117 door->part_2.step_xoffset = door->step_offset * -1;
4118 door->part_2.step_yoffset = 0;
4120 num_door_steps = g_part_1->width / door->step_offset;
4122 else // ANIM_VERTICAL
4124 door->part_1.step_xoffset = 0;
4125 door->part_1.step_yoffset = door->step_offset;
4126 door->part_2.step_xoffset = 0;
4127 door->part_2.step_yoffset = door->step_offset * -1;
4129 num_door_steps = g_part_1->height / door->step_offset;
4132 /* set animation draw offset for the default panels */
4134 if (door->step_offset > 1)
4136 num_panel_steps = 2 * door_rect->height / door->step_offset;
4137 door->panel.start_step = num_panel_steps - num_door_steps;
4138 door->panel.start_step_closing = door->panel.start_step;
4142 num_panel_steps = door_rect->height / door->step_offset;
4143 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4144 door->panel.start_step_closing = door->panel.start_step;
4145 door->panel.step_delay *= 2;
4156 for (i = 0; door_part_controls[i].door_token != -1; i++)
4158 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4159 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4161 /* initialize "start_step_opening" and "start_step_closing", if needed */
4162 if (dpc->pos->start_step_opening == 0 &&
4163 dpc->pos->start_step_closing == 0)
4165 // dpc->pos->start_step_opening = dpc->pos->start_step;
4166 dpc->pos->start_step_closing = dpc->pos->start_step;
4169 /* fill structure for door part draw order (sorted below) */
4171 dpo->sort_priority = dpc->pos->sort_priority;
4174 /* sort door part controls according to sort_priority and graphic number */
4175 qsort(door_part_order, MAX_DOOR_PARTS,
4176 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4179 unsigned int OpenDoor(unsigned int door_state)
4181 if (door_state & DOOR_COPY_BACK)
4183 if (door_state & DOOR_OPEN_1)
4184 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4185 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4187 if (door_state & DOOR_OPEN_2)
4188 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4189 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4191 door_state &= ~DOOR_COPY_BACK;
4194 return MoveDoor(door_state);
4197 unsigned int CloseDoor(unsigned int door_state)
4199 unsigned int old_door_state = GetDoorState();
4201 if (!(door_state & DOOR_NO_COPY_BACK))
4203 if (old_door_state & DOOR_OPEN_1)
4204 BlitBitmap(backbuffer, bitmap_db_door_1,
4205 DX, DY, DXSIZE, DYSIZE, 0, 0);
4207 if (old_door_state & DOOR_OPEN_2)
4208 BlitBitmap(backbuffer, bitmap_db_door_2,
4209 VX, VY, VXSIZE, VYSIZE, 0, 0);
4211 door_state &= ~DOOR_NO_COPY_BACK;
4214 return MoveDoor(door_state);
4217 unsigned int GetDoorState()
4219 return MoveDoor(DOOR_GET_STATE);
4222 unsigned int SetDoorState(unsigned int door_state)
4224 return MoveDoor(door_state | DOOR_SET_STATE);
4227 int euclid(int a, int b)
4229 return (b ? euclid(b, a % b) : a);
4232 unsigned int MoveDoor(unsigned int door_state)
4234 struct Rect door_rect_list[] =
4236 { DX, DY, DXSIZE, DYSIZE },
4237 { VX, VY, VXSIZE, VYSIZE }
4239 static int door1 = DOOR_CLOSE_1;
4240 static int door2 = DOOR_CLOSE_2;
4241 unsigned int door_delay = 0;
4242 unsigned int door_delay_value;
4245 if (door_state == DOOR_GET_STATE)
4246 return (door1 | door2);
4248 if (door_state & DOOR_SET_STATE)
4250 if (door_state & DOOR_ACTION_1)
4251 door1 = door_state & DOOR_ACTION_1;
4252 if (door_state & DOOR_ACTION_2)
4253 door2 = door_state & DOOR_ACTION_2;
4255 return (door1 | door2);
4258 if (!(door_state & DOOR_FORCE_REDRAW))
4260 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4261 door_state &= ~DOOR_OPEN_1;
4262 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4263 door_state &= ~DOOR_CLOSE_1;
4264 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4265 door_state &= ~DOOR_OPEN_2;
4266 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4267 door_state &= ~DOOR_CLOSE_2;
4270 if (global.autoplay_leveldir)
4272 door_state |= DOOR_NO_DELAY;
4273 door_state &= ~DOOR_CLOSE_ALL;
4276 if (game_status == GAME_MODE_EDITOR)
4277 door_state |= DOOR_NO_DELAY;
4279 if (door_state & DOOR_ACTION)
4281 boolean door_panel_drawn[NUM_DOORS];
4282 boolean panel_has_doors[NUM_DOORS];
4283 boolean door_part_skip[MAX_DOOR_PARTS];
4284 boolean door_part_done[MAX_DOOR_PARTS];
4285 boolean door_part_done_all;
4286 int num_steps[MAX_DOOR_PARTS];
4287 int max_move_delay = 0; // delay for complete animations of all doors
4288 int max_step_delay = 0; // delay (ms) between two animation frames
4289 int num_move_steps = 0; // number of animation steps for all doors
4290 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4291 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4292 int current_move_delay = 0;
4296 for (i = 0; i < NUM_DOORS; i++)
4297 panel_has_doors[i] = FALSE;
4299 for (i = 0; i < MAX_DOOR_PARTS; i++)
4301 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4302 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4303 int door_token = dpc->door_token;
4305 door_part_done[i] = FALSE;
4306 door_part_skip[i] = (!(door_state & door_token) ||
4310 for (i = 0; i < MAX_DOOR_PARTS; i++)
4312 int nr = door_part_order[i].nr;
4313 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4314 struct DoorPartPosInfo *pos = dpc->pos;
4315 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4316 int door_token = dpc->door_token;
4317 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4318 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4319 int step_xoffset = ABS(pos->step_xoffset);
4320 int step_yoffset = ABS(pos->step_yoffset);
4321 int step_delay = pos->step_delay;
4322 int current_door_state = door_state & door_token;
4323 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4324 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4325 boolean part_opening = (is_panel ? door_closing : door_opening);
4326 int start_step = (part_opening ? pos->start_step_opening :
4327 pos->start_step_closing);
4328 float move_xsize = (step_xoffset ? g->width : 0);
4329 float move_ysize = (step_yoffset ? g->height : 0);
4330 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4331 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4332 int move_steps = (move_xsteps && move_ysteps ?
4333 MIN(move_xsteps, move_ysteps) :
4334 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4335 int move_delay = move_steps * step_delay;
4337 if (door_part_skip[nr])
4340 max_move_delay = MAX(max_move_delay, move_delay);
4341 max_step_delay = (max_step_delay == 0 ? step_delay :
4342 euclid(max_step_delay, step_delay));
4343 num_steps[nr] = move_steps;
4347 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4349 panel_has_doors[door_index] = TRUE;
4353 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4355 num_move_steps = max_move_delay / max_step_delay;
4356 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4358 door_delay_value = max_step_delay;
4360 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4362 start = num_move_steps - 1;
4366 /* opening door sound has priority over simultaneously closing door */
4367 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4368 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4369 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4370 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4373 for (k = start; k < num_move_steps; k++)
4375 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4377 door_part_done_all = TRUE;
4379 for (i = 0; i < NUM_DOORS; i++)
4380 door_panel_drawn[i] = FALSE;
4382 for (i = 0; i < MAX_DOOR_PARTS; i++)
4384 int nr = door_part_order[i].nr;
4385 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4386 struct DoorPartPosInfo *pos = dpc->pos;
4387 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4388 int door_token = dpc->door_token;
4389 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4390 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4391 boolean is_panel_and_door_has_closed = FALSE;
4392 struct Rect *door_rect = &door_rect_list[door_index];
4393 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4395 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4396 int current_door_state = door_state & door_token;
4397 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4398 boolean door_closing = !door_opening;
4399 boolean part_opening = (is_panel ? door_closing : door_opening);
4400 boolean part_closing = !part_opening;
4401 int start_step = (part_opening ? pos->start_step_opening :
4402 pos->start_step_closing);
4403 int step_delay = pos->step_delay;
4404 int step_factor = step_delay / max_step_delay;
4405 int k1 = (step_factor ? k / step_factor + 1 : k);
4406 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4407 int kk = MAX(0, k2);
4410 int src_x, src_y, src_xx, src_yy;
4411 int dst_x, dst_y, dst_xx, dst_yy;
4414 if (door_part_skip[nr])
4417 if (!(door_state & door_token))
4425 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4426 int kk_door = MAX(0, k2_door);
4427 int sync_frame = kk_door * door_delay_value;
4428 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4430 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4435 if (!door_panel_drawn[door_index])
4437 ClearRectangle(drawto, door_rect->x, door_rect->y,
4438 door_rect->width, door_rect->height);
4440 door_panel_drawn[door_index] = TRUE;
4443 // draw opening or closing door parts
4445 if (pos->step_xoffset < 0) // door part on right side
4448 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4451 if (dst_xx + width > door_rect->width)
4452 width = door_rect->width - dst_xx;
4454 else // door part on left side
4457 dst_xx = pos->x - kk * pos->step_xoffset;
4461 src_xx = ABS(dst_xx);
4465 width = g->width - src_xx;
4467 if (width > door_rect->width)
4468 width = door_rect->width;
4470 // printf("::: k == %d [%d] \n", k, start_step);
4473 if (pos->step_yoffset < 0) // door part on bottom side
4476 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4479 if (dst_yy + height > door_rect->height)
4480 height = door_rect->height - dst_yy;
4482 else // door part on top side
4485 dst_yy = pos->y - kk * pos->step_yoffset;
4489 src_yy = ABS(dst_yy);
4493 height = g->height - src_yy;
4496 src_x = g_src_x + src_xx;
4497 src_y = g_src_y + src_yy;
4499 dst_x = door_rect->x + dst_xx;
4500 dst_y = door_rect->y + dst_yy;
4502 is_panel_and_door_has_closed =
4505 panel_has_doors[door_index] &&
4506 k >= num_move_steps_doors_only - 1);
4508 if (width >= 0 && width <= g->width &&
4509 height >= 0 && height <= g->height &&
4510 !is_panel_and_door_has_closed)
4512 if (is_panel || !pos->draw_masked)
4513 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4516 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4520 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4522 if ((part_opening && (width < 0 || height < 0)) ||
4523 (part_closing && (width >= g->width && height >= g->height)))
4524 door_part_done[nr] = TRUE;
4526 // continue door part animations, but not panel after door has closed
4527 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4528 door_part_done_all = FALSE;
4531 if (!(door_state & DOOR_NO_DELAY))
4535 if (game_status == GAME_MODE_MAIN)
4538 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4540 current_move_delay += max_step_delay;
4543 if (door_part_done_all)
4548 if (door_state & DOOR_ACTION_1)
4549 door1 = door_state & DOOR_ACTION_1;
4550 if (door_state & DOOR_ACTION_2)
4551 door2 = door_state & DOOR_ACTION_2;
4553 // draw masked border over door area
4554 DrawMaskedBorder(REDRAW_DOOR_1);
4555 DrawMaskedBorder(REDRAW_DOOR_2);
4557 return (door1 | door2);
4560 static boolean useSpecialEditorDoor()
4562 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4563 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4565 // do not draw special editor door if editor border defined or redefined
4566 if (graphic_info[graphic].bitmap != NULL || redefined)
4569 // do not draw special editor door if global border defined to be empty
4570 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4573 // do not draw special editor door if viewport definitions do not match
4577 EY + EYSIZE != VY + VYSIZE)
4583 void DrawSpecialEditorDoor()
4585 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4586 int top_border_width = gfx1->width;
4587 int top_border_height = gfx1->height;
4588 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4589 int ex = EX - outer_border;
4590 int ey = EY - outer_border;
4591 int vy = VY - outer_border;
4592 int exsize = EXSIZE + 2 * outer_border;
4594 if (!useSpecialEditorDoor())
4597 /* draw bigger level editor toolbox window */
4598 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4599 top_border_width, top_border_height, ex, ey - top_border_height);
4600 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4601 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4603 redraw_mask |= REDRAW_ALL;
4606 void UndrawSpecialEditorDoor()
4608 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4609 int top_border_width = gfx1->width;
4610 int top_border_height = gfx1->height;
4611 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4612 int ex = EX - outer_border;
4613 int ey = EY - outer_border;
4614 int ey_top = ey - top_border_height;
4615 int exsize = EXSIZE + 2 * outer_border;
4616 int eysize = EYSIZE + 2 * outer_border;
4618 if (!useSpecialEditorDoor())
4621 /* draw normal tape recorder window */
4622 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4624 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4625 ex, ey_top, top_border_width, top_border_height,
4627 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4628 ex, ey, exsize, eysize, ex, ey);
4632 // if screen background is set to "[NONE]", clear editor toolbox window
4633 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4634 ClearRectangle(drawto, ex, ey, exsize, eysize);
4637 redraw_mask |= REDRAW_ALL;
4641 /* ---------- new tool button stuff ---------------------------------------- */
4646 struct TextPosInfo *pos;
4649 } toolbutton_info[NUM_TOOL_BUTTONS] =
4652 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4653 TOOL_CTRL_ID_YES, "yes"
4656 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4657 TOOL_CTRL_ID_NO, "no"
4660 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4661 TOOL_CTRL_ID_CONFIRM, "confirm"
4664 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4665 TOOL_CTRL_ID_PLAYER_1, "player 1"
4668 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4669 TOOL_CTRL_ID_PLAYER_2, "player 2"
4672 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4673 TOOL_CTRL_ID_PLAYER_3, "player 3"
4676 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4677 TOOL_CTRL_ID_PLAYER_4, "player 4"
4681 void CreateToolButtons()
4685 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4687 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4688 struct TextPosInfo *pos = toolbutton_info[i].pos;
4689 struct GadgetInfo *gi;
4690 Bitmap *deco_bitmap = None;
4691 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4692 unsigned int event_mask = GD_EVENT_RELEASED;
4695 int gd_x = gfx->src_x;
4696 int gd_y = gfx->src_y;
4697 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4698 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4701 if (global.use_envelope_request)
4702 setRequestPosition(&dx, &dy, TRUE);
4704 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4706 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4708 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4709 pos->size, &deco_bitmap, &deco_x, &deco_y);
4710 deco_xpos = (gfx->width - pos->size) / 2;
4711 deco_ypos = (gfx->height - pos->size) / 2;
4714 gi = CreateGadget(GDI_CUSTOM_ID, id,
4715 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4716 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4717 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4718 GDI_WIDTH, gfx->width,
4719 GDI_HEIGHT, gfx->height,
4720 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4721 GDI_STATE, GD_BUTTON_UNPRESSED,
4722 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4723 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4724 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4725 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4726 GDI_DECORATION_SIZE, pos->size, pos->size,
4727 GDI_DECORATION_SHIFTING, 1, 1,
4728 GDI_DIRECT_DRAW, FALSE,
4729 GDI_EVENT_MASK, event_mask,
4730 GDI_CALLBACK_ACTION, HandleToolButtons,
4734 Error(ERR_EXIT, "cannot create gadget");
4736 tool_gadget[id] = gi;
4740 void FreeToolButtons()
4744 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4745 FreeGadget(tool_gadget[i]);
4748 static void UnmapToolButtons()
4752 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4753 UnmapGadget(tool_gadget[i]);
4756 static void HandleToolButtons(struct GadgetInfo *gi)
4758 request_gadget_id = gi->custom_id;
4761 static struct Mapping_EM_to_RND_object
4764 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4765 boolean is_backside; /* backside of moving element */
4771 em_object_mapping_list[] =
4774 Xblank, TRUE, FALSE,
4778 Yacid_splash_eB, FALSE, FALSE,
4779 EL_ACID_SPLASH_RIGHT, -1, -1
4782 Yacid_splash_wB, FALSE, FALSE,
4783 EL_ACID_SPLASH_LEFT, -1, -1
4786 #ifdef EM_ENGINE_BAD_ROLL
4788 Xstone_force_e, FALSE, FALSE,
4789 EL_ROCK, -1, MV_BIT_RIGHT
4792 Xstone_force_w, FALSE, FALSE,
4793 EL_ROCK, -1, MV_BIT_LEFT
4796 Xnut_force_e, FALSE, FALSE,
4797 EL_NUT, -1, MV_BIT_RIGHT
4800 Xnut_force_w, FALSE, FALSE,
4801 EL_NUT, -1, MV_BIT_LEFT
4804 Xspring_force_e, FALSE, FALSE,
4805 EL_SPRING, -1, MV_BIT_RIGHT
4808 Xspring_force_w, FALSE, FALSE,
4809 EL_SPRING, -1, MV_BIT_LEFT
4812 Xemerald_force_e, FALSE, FALSE,
4813 EL_EMERALD, -1, MV_BIT_RIGHT
4816 Xemerald_force_w, FALSE, FALSE,
4817 EL_EMERALD, -1, MV_BIT_LEFT
4820 Xdiamond_force_e, FALSE, FALSE,
4821 EL_DIAMOND, -1, MV_BIT_RIGHT
4824 Xdiamond_force_w, FALSE, FALSE,
4825 EL_DIAMOND, -1, MV_BIT_LEFT
4828 Xbomb_force_e, FALSE, FALSE,
4829 EL_BOMB, -1, MV_BIT_RIGHT
4832 Xbomb_force_w, FALSE, FALSE,
4833 EL_BOMB, -1, MV_BIT_LEFT
4835 #endif /* EM_ENGINE_BAD_ROLL */
4838 Xstone, TRUE, FALSE,
4842 Xstone_pause, FALSE, FALSE,
4846 Xstone_fall, FALSE, FALSE,
4850 Ystone_s, FALSE, FALSE,
4851 EL_ROCK, ACTION_FALLING, -1
4854 Ystone_sB, FALSE, TRUE,
4855 EL_ROCK, ACTION_FALLING, -1
4858 Ystone_e, FALSE, FALSE,
4859 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4862 Ystone_eB, FALSE, TRUE,
4863 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4866 Ystone_w, FALSE, FALSE,
4867 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4870 Ystone_wB, FALSE, TRUE,
4871 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4878 Xnut_pause, FALSE, FALSE,
4882 Xnut_fall, FALSE, FALSE,
4886 Ynut_s, FALSE, FALSE,
4887 EL_NUT, ACTION_FALLING, -1
4890 Ynut_sB, FALSE, TRUE,
4891 EL_NUT, ACTION_FALLING, -1
4894 Ynut_e, FALSE, FALSE,
4895 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4898 Ynut_eB, FALSE, TRUE,
4899 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4902 Ynut_w, FALSE, FALSE,
4903 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4906 Ynut_wB, FALSE, TRUE,
4907 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4910 Xbug_n, TRUE, FALSE,
4914 Xbug_e, TRUE, FALSE,
4915 EL_BUG_RIGHT, -1, -1
4918 Xbug_s, TRUE, FALSE,
4922 Xbug_w, TRUE, FALSE,
4926 Xbug_gon, FALSE, FALSE,
4930 Xbug_goe, FALSE, FALSE,
4931 EL_BUG_RIGHT, -1, -1
4934 Xbug_gos, FALSE, FALSE,
4938 Xbug_gow, FALSE, FALSE,
4942 Ybug_n, FALSE, FALSE,
4943 EL_BUG, ACTION_MOVING, MV_BIT_UP
4946 Ybug_nB, FALSE, TRUE,
4947 EL_BUG, ACTION_MOVING, MV_BIT_UP
4950 Ybug_e, FALSE, FALSE,
4951 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4954 Ybug_eB, FALSE, TRUE,
4955 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4958 Ybug_s, FALSE, FALSE,
4959 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4962 Ybug_sB, FALSE, TRUE,
4963 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4966 Ybug_w, FALSE, FALSE,
4967 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4970 Ybug_wB, FALSE, TRUE,
4971 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4974 Ybug_w_n, FALSE, FALSE,
4975 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4978 Ybug_n_e, FALSE, FALSE,
4979 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4982 Ybug_e_s, FALSE, FALSE,
4983 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4986 Ybug_s_w, FALSE, FALSE,
4987 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4990 Ybug_e_n, FALSE, FALSE,
4991 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4994 Ybug_s_e, FALSE, FALSE,
4995 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4998 Ybug_w_s, FALSE, FALSE,
4999 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5002 Ybug_n_w, FALSE, FALSE,
5003 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5006 Ybug_stone, FALSE, FALSE,
5007 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5010 Ybug_spring, FALSE, FALSE,
5011 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5014 Xtank_n, TRUE, FALSE,
5015 EL_SPACESHIP_UP, -1, -1
5018 Xtank_e, TRUE, FALSE,
5019 EL_SPACESHIP_RIGHT, -1, -1
5022 Xtank_s, TRUE, FALSE,
5023 EL_SPACESHIP_DOWN, -1, -1
5026 Xtank_w, TRUE, FALSE,
5027 EL_SPACESHIP_LEFT, -1, -1
5030 Xtank_gon, FALSE, FALSE,
5031 EL_SPACESHIP_UP, -1, -1
5034 Xtank_goe, FALSE, FALSE,
5035 EL_SPACESHIP_RIGHT, -1, -1
5038 Xtank_gos, FALSE, FALSE,
5039 EL_SPACESHIP_DOWN, -1, -1
5042 Xtank_gow, FALSE, FALSE,
5043 EL_SPACESHIP_LEFT, -1, -1
5046 Ytank_n, FALSE, FALSE,
5047 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5050 Ytank_nB, FALSE, TRUE,
5051 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5054 Ytank_e, FALSE, FALSE,
5055 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5058 Ytank_eB, FALSE, TRUE,
5059 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5062 Ytank_s, FALSE, FALSE,
5063 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5066 Ytank_sB, FALSE, TRUE,
5067 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5070 Ytank_w, FALSE, FALSE,
5071 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5074 Ytank_wB, FALSE, TRUE,
5075 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5078 Ytank_w_n, FALSE, FALSE,
5079 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5082 Ytank_n_e, FALSE, FALSE,
5083 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5086 Ytank_e_s, FALSE, FALSE,
5087 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5090 Ytank_s_w, FALSE, FALSE,
5091 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5094 Ytank_e_n, FALSE, FALSE,
5095 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5098 Ytank_s_e, FALSE, FALSE,
5099 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5102 Ytank_w_s, FALSE, FALSE,
5103 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5106 Ytank_n_w, FALSE, FALSE,
5107 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5110 Ytank_stone, FALSE, FALSE,
5111 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5114 Ytank_spring, FALSE, FALSE,
5115 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5118 Xandroid, TRUE, FALSE,
5119 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5122 Xandroid_1_n, FALSE, FALSE,
5123 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5126 Xandroid_2_n, FALSE, FALSE,
5127 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5130 Xandroid_1_e, FALSE, FALSE,
5131 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5134 Xandroid_2_e, FALSE, FALSE,
5135 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5138 Xandroid_1_w, FALSE, FALSE,
5139 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5142 Xandroid_2_w, FALSE, FALSE,
5143 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5146 Xandroid_1_s, FALSE, FALSE,
5147 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5150 Xandroid_2_s, FALSE, FALSE,
5151 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5154 Yandroid_n, FALSE, FALSE,
5155 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5158 Yandroid_nB, FALSE, TRUE,
5159 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5162 Yandroid_ne, FALSE, FALSE,
5163 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5166 Yandroid_neB, FALSE, TRUE,
5167 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5170 Yandroid_e, FALSE, FALSE,
5171 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5174 Yandroid_eB, FALSE, TRUE,
5175 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5178 Yandroid_se, FALSE, FALSE,
5179 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5182 Yandroid_seB, FALSE, TRUE,
5183 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5186 Yandroid_s, FALSE, FALSE,
5187 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5190 Yandroid_sB, FALSE, TRUE,
5191 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5194 Yandroid_sw, FALSE, FALSE,
5195 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5198 Yandroid_swB, FALSE, TRUE,
5199 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5202 Yandroid_w, FALSE, FALSE,
5203 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5206 Yandroid_wB, FALSE, TRUE,
5207 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5210 Yandroid_nw, FALSE, FALSE,
5211 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5214 Yandroid_nwB, FALSE, TRUE,
5215 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5218 Xspring, TRUE, FALSE,
5222 Xspring_pause, FALSE, FALSE,
5226 Xspring_e, FALSE, FALSE,
5230 Xspring_w, FALSE, FALSE,
5234 Xspring_fall, FALSE, FALSE,
5238 Yspring_s, FALSE, FALSE,
5239 EL_SPRING, ACTION_FALLING, -1
5242 Yspring_sB, FALSE, TRUE,
5243 EL_SPRING, ACTION_FALLING, -1
5246 Yspring_e, FALSE, FALSE,
5247 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5250 Yspring_eB, FALSE, TRUE,
5251 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5254 Yspring_w, FALSE, FALSE,
5255 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5258 Yspring_wB, FALSE, TRUE,
5259 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5262 Yspring_kill_e, FALSE, FALSE,
5263 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5266 Yspring_kill_eB, FALSE, TRUE,
5267 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5270 Yspring_kill_w, FALSE, FALSE,
5271 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5274 Yspring_kill_wB, FALSE, TRUE,
5275 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5278 Xeater_n, TRUE, FALSE,
5279 EL_YAMYAM_UP, -1, -1
5282 Xeater_e, TRUE, FALSE,
5283 EL_YAMYAM_RIGHT, -1, -1
5286 Xeater_w, TRUE, FALSE,
5287 EL_YAMYAM_LEFT, -1, -1
5290 Xeater_s, TRUE, FALSE,
5291 EL_YAMYAM_DOWN, -1, -1
5294 Yeater_n, FALSE, FALSE,
5295 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5298 Yeater_nB, FALSE, TRUE,
5299 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5302 Yeater_e, FALSE, FALSE,
5303 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5306 Yeater_eB, FALSE, TRUE,
5307 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5310 Yeater_s, FALSE, FALSE,
5311 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5314 Yeater_sB, FALSE, TRUE,
5315 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5318 Yeater_w, FALSE, FALSE,
5319 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5322 Yeater_wB, FALSE, TRUE,
5323 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5326 Yeater_stone, FALSE, FALSE,
5327 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5330 Yeater_spring, FALSE, FALSE,
5331 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5334 Xalien, TRUE, FALSE,
5338 Xalien_pause, FALSE, FALSE,
5342 Yalien_n, FALSE, FALSE,
5343 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5346 Yalien_nB, FALSE, TRUE,
5347 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5350 Yalien_e, FALSE, FALSE,
5351 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5354 Yalien_eB, FALSE, TRUE,
5355 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5358 Yalien_s, FALSE, FALSE,
5359 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5362 Yalien_sB, FALSE, TRUE,
5363 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5366 Yalien_w, FALSE, FALSE,
5367 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5370 Yalien_wB, FALSE, TRUE,
5371 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5374 Yalien_stone, FALSE, FALSE,
5375 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5378 Yalien_spring, FALSE, FALSE,
5379 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5382 Xemerald, TRUE, FALSE,
5386 Xemerald_pause, FALSE, FALSE,
5390 Xemerald_fall, FALSE, FALSE,
5394 Xemerald_shine, FALSE, FALSE,
5395 EL_EMERALD, ACTION_TWINKLING, -1
5398 Yemerald_s, FALSE, FALSE,
5399 EL_EMERALD, ACTION_FALLING, -1
5402 Yemerald_sB, FALSE, TRUE,
5403 EL_EMERALD, ACTION_FALLING, -1
5406 Yemerald_e, FALSE, FALSE,
5407 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5410 Yemerald_eB, FALSE, TRUE,
5411 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5414 Yemerald_w, FALSE, FALSE,
5415 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5418 Yemerald_wB, FALSE, TRUE,
5419 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5422 Yemerald_eat, FALSE, FALSE,
5423 EL_EMERALD, ACTION_COLLECTING, -1
5426 Yemerald_stone, FALSE, FALSE,
5427 EL_NUT, ACTION_BREAKING, -1
5430 Xdiamond, TRUE, FALSE,
5434 Xdiamond_pause, FALSE, FALSE,
5438 Xdiamond_fall, FALSE, FALSE,
5442 Xdiamond_shine, FALSE, FALSE,
5443 EL_DIAMOND, ACTION_TWINKLING, -1
5446 Ydiamond_s, FALSE, FALSE,
5447 EL_DIAMOND, ACTION_FALLING, -1
5450 Ydiamond_sB, FALSE, TRUE,
5451 EL_DIAMOND, ACTION_FALLING, -1
5454 Ydiamond_e, FALSE, FALSE,
5455 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5458 Ydiamond_eB, FALSE, TRUE,
5459 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5462 Ydiamond_w, FALSE, FALSE,
5463 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5466 Ydiamond_wB, FALSE, TRUE,
5467 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5470 Ydiamond_eat, FALSE, FALSE,
5471 EL_DIAMOND, ACTION_COLLECTING, -1
5474 Ydiamond_stone, FALSE, FALSE,
5475 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5478 Xdrip_fall, TRUE, FALSE,
5479 EL_AMOEBA_DROP, -1, -1
5482 Xdrip_stretch, FALSE, FALSE,
5483 EL_AMOEBA_DROP, ACTION_FALLING, -1
5486 Xdrip_stretchB, FALSE, TRUE,
5487 EL_AMOEBA_DROP, ACTION_FALLING, -1
5490 Xdrip_eat, FALSE, FALSE,
5491 EL_AMOEBA_DROP, ACTION_GROWING, -1
5494 Ydrip_s1, FALSE, FALSE,
5495 EL_AMOEBA_DROP, ACTION_FALLING, -1
5498 Ydrip_s1B, FALSE, TRUE,
5499 EL_AMOEBA_DROP, ACTION_FALLING, -1
5502 Ydrip_s2, FALSE, FALSE,
5503 EL_AMOEBA_DROP, ACTION_FALLING, -1
5506 Ydrip_s2B, FALSE, TRUE,
5507 EL_AMOEBA_DROP, ACTION_FALLING, -1
5514 Xbomb_pause, FALSE, FALSE,
5518 Xbomb_fall, FALSE, FALSE,
5522 Ybomb_s, FALSE, FALSE,
5523 EL_BOMB, ACTION_FALLING, -1
5526 Ybomb_sB, FALSE, TRUE,
5527 EL_BOMB, ACTION_FALLING, -1
5530 Ybomb_e, FALSE, FALSE,
5531 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5534 Ybomb_eB, FALSE, TRUE,
5535 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5538 Ybomb_w, FALSE, FALSE,
5539 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5542 Ybomb_wB, FALSE, TRUE,
5543 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5546 Ybomb_eat, FALSE, FALSE,
5547 EL_BOMB, ACTION_ACTIVATING, -1
5550 Xballoon, TRUE, FALSE,
5554 Yballoon_n, FALSE, FALSE,
5555 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5558 Yballoon_nB, FALSE, TRUE,
5559 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5562 Yballoon_e, FALSE, FALSE,
5563 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5566 Yballoon_eB, FALSE, TRUE,
5567 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5570 Yballoon_s, FALSE, FALSE,
5571 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5574 Yballoon_sB, FALSE, TRUE,
5575 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5578 Yballoon_w, FALSE, FALSE,
5579 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5582 Yballoon_wB, FALSE, TRUE,
5583 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5586 Xgrass, TRUE, FALSE,
5587 EL_EMC_GRASS, -1, -1
5590 Ygrass_nB, FALSE, FALSE,
5591 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5594 Ygrass_eB, FALSE, FALSE,
5595 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5598 Ygrass_sB, FALSE, FALSE,
5599 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5602 Ygrass_wB, FALSE, FALSE,
5603 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5610 Ydirt_nB, FALSE, FALSE,
5611 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5614 Ydirt_eB, FALSE, FALSE,
5615 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5618 Ydirt_sB, FALSE, FALSE,
5619 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5622 Ydirt_wB, FALSE, FALSE,
5623 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5626 Xacid_ne, TRUE, FALSE,
5627 EL_ACID_POOL_TOPRIGHT, -1, -1
5630 Xacid_se, TRUE, FALSE,
5631 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5634 Xacid_s, TRUE, FALSE,
5635 EL_ACID_POOL_BOTTOM, -1, -1
5638 Xacid_sw, TRUE, FALSE,
5639 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5642 Xacid_nw, TRUE, FALSE,
5643 EL_ACID_POOL_TOPLEFT, -1, -1
5646 Xacid_1, TRUE, FALSE,
5650 Xacid_2, FALSE, FALSE,
5654 Xacid_3, FALSE, FALSE,
5658 Xacid_4, FALSE, FALSE,
5662 Xacid_5, FALSE, FALSE,
5666 Xacid_6, FALSE, FALSE,
5670 Xacid_7, FALSE, FALSE,
5674 Xacid_8, FALSE, FALSE,
5678 Xball_1, TRUE, FALSE,
5679 EL_EMC_MAGIC_BALL, -1, -1
5682 Xball_1B, FALSE, FALSE,
5683 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5686 Xball_2, FALSE, FALSE,
5687 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5690 Xball_2B, FALSE, FALSE,
5691 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5694 Yball_eat, FALSE, FALSE,
5695 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5698 Ykey_1_eat, FALSE, FALSE,
5699 EL_EM_KEY_1, ACTION_COLLECTING, -1
5702 Ykey_2_eat, FALSE, FALSE,
5703 EL_EM_KEY_2, ACTION_COLLECTING, -1
5706 Ykey_3_eat, FALSE, FALSE,
5707 EL_EM_KEY_3, ACTION_COLLECTING, -1
5710 Ykey_4_eat, FALSE, FALSE,
5711 EL_EM_KEY_4, ACTION_COLLECTING, -1
5714 Ykey_5_eat, FALSE, FALSE,
5715 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5718 Ykey_6_eat, FALSE, FALSE,
5719 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5722 Ykey_7_eat, FALSE, FALSE,
5723 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5726 Ykey_8_eat, FALSE, FALSE,
5727 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5730 Ylenses_eat, FALSE, FALSE,
5731 EL_EMC_LENSES, ACTION_COLLECTING, -1
5734 Ymagnify_eat, FALSE, FALSE,
5735 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5738 Ygrass_eat, FALSE, FALSE,
5739 EL_EMC_GRASS, ACTION_SNAPPING, -1
5742 Ydirt_eat, FALSE, FALSE,
5743 EL_SAND, ACTION_SNAPPING, -1
5746 Xgrow_ns, TRUE, FALSE,
5747 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5750 Ygrow_ns_eat, FALSE, FALSE,
5751 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5754 Xgrow_ew, TRUE, FALSE,
5755 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5758 Ygrow_ew_eat, FALSE, FALSE,
5759 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5762 Xwonderwall, TRUE, FALSE,
5763 EL_MAGIC_WALL, -1, -1
5766 XwonderwallB, FALSE, FALSE,
5767 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5770 Xamoeba_1, TRUE, FALSE,
5771 EL_AMOEBA_DRY, ACTION_OTHER, -1
5774 Xamoeba_2, FALSE, FALSE,
5775 EL_AMOEBA_DRY, ACTION_OTHER, -1
5778 Xamoeba_3, FALSE, FALSE,
5779 EL_AMOEBA_DRY, ACTION_OTHER, -1
5782 Xamoeba_4, FALSE, FALSE,
5783 EL_AMOEBA_DRY, ACTION_OTHER, -1
5786 Xamoeba_5, TRUE, FALSE,
5787 EL_AMOEBA_WET, ACTION_OTHER, -1
5790 Xamoeba_6, FALSE, FALSE,
5791 EL_AMOEBA_WET, ACTION_OTHER, -1
5794 Xamoeba_7, FALSE, FALSE,
5795 EL_AMOEBA_WET, ACTION_OTHER, -1
5798 Xamoeba_8, FALSE, FALSE,
5799 EL_AMOEBA_WET, ACTION_OTHER, -1
5802 Xdoor_1, TRUE, FALSE,
5803 EL_EM_GATE_1, -1, -1
5806 Xdoor_2, TRUE, FALSE,
5807 EL_EM_GATE_2, -1, -1
5810 Xdoor_3, TRUE, FALSE,
5811 EL_EM_GATE_3, -1, -1
5814 Xdoor_4, TRUE, FALSE,
5815 EL_EM_GATE_4, -1, -1
5818 Xdoor_5, TRUE, FALSE,
5819 EL_EMC_GATE_5, -1, -1
5822 Xdoor_6, TRUE, FALSE,
5823 EL_EMC_GATE_6, -1, -1
5826 Xdoor_7, TRUE, FALSE,
5827 EL_EMC_GATE_7, -1, -1
5830 Xdoor_8, TRUE, FALSE,
5831 EL_EMC_GATE_8, -1, -1
5834 Xkey_1, TRUE, FALSE,
5838 Xkey_2, TRUE, FALSE,
5842 Xkey_3, TRUE, FALSE,
5846 Xkey_4, TRUE, FALSE,
5850 Xkey_5, TRUE, FALSE,
5851 EL_EMC_KEY_5, -1, -1
5854 Xkey_6, TRUE, FALSE,
5855 EL_EMC_KEY_6, -1, -1
5858 Xkey_7, TRUE, FALSE,
5859 EL_EMC_KEY_7, -1, -1
5862 Xkey_8, TRUE, FALSE,
5863 EL_EMC_KEY_8, -1, -1
5866 Xwind_n, TRUE, FALSE,
5867 EL_BALLOON_SWITCH_UP, -1, -1
5870 Xwind_e, TRUE, FALSE,
5871 EL_BALLOON_SWITCH_RIGHT, -1, -1
5874 Xwind_s, TRUE, FALSE,
5875 EL_BALLOON_SWITCH_DOWN, -1, -1
5878 Xwind_w, TRUE, FALSE,
5879 EL_BALLOON_SWITCH_LEFT, -1, -1
5882 Xwind_nesw, TRUE, FALSE,
5883 EL_BALLOON_SWITCH_ANY, -1, -1
5886 Xwind_stop, TRUE, FALSE,
5887 EL_BALLOON_SWITCH_NONE, -1, -1
5891 EL_EM_EXIT_CLOSED, -1, -1
5894 Xexit_1, TRUE, FALSE,
5895 EL_EM_EXIT_OPEN, -1, -1
5898 Xexit_2, FALSE, FALSE,
5899 EL_EM_EXIT_OPEN, -1, -1
5902 Xexit_3, FALSE, FALSE,
5903 EL_EM_EXIT_OPEN, -1, -1
5906 Xdynamite, TRUE, FALSE,
5907 EL_EM_DYNAMITE, -1, -1
5910 Ydynamite_eat, FALSE, FALSE,
5911 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5914 Xdynamite_1, TRUE, FALSE,
5915 EL_EM_DYNAMITE_ACTIVE, -1, -1
5918 Xdynamite_2, FALSE, FALSE,
5919 EL_EM_DYNAMITE_ACTIVE, -1, -1
5922 Xdynamite_3, FALSE, FALSE,
5923 EL_EM_DYNAMITE_ACTIVE, -1, -1
5926 Xdynamite_4, FALSE, FALSE,
5927 EL_EM_DYNAMITE_ACTIVE, -1, -1
5930 Xbumper, TRUE, FALSE,
5931 EL_EMC_SPRING_BUMPER, -1, -1
5934 XbumperB, FALSE, FALSE,
5935 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5938 Xwheel, TRUE, FALSE,
5939 EL_ROBOT_WHEEL, -1, -1
5942 XwheelB, FALSE, FALSE,
5943 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5946 Xswitch, TRUE, FALSE,
5947 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5950 XswitchB, FALSE, FALSE,
5951 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5955 EL_QUICKSAND_EMPTY, -1, -1
5958 Xsand_stone, TRUE, FALSE,
5959 EL_QUICKSAND_FULL, -1, -1
5962 Xsand_stonein_1, FALSE, TRUE,
5963 EL_ROCK, ACTION_FILLING, -1
5966 Xsand_stonein_2, FALSE, TRUE,
5967 EL_ROCK, ACTION_FILLING, -1
5970 Xsand_stonein_3, FALSE, TRUE,
5971 EL_ROCK, ACTION_FILLING, -1
5974 Xsand_stonein_4, FALSE, TRUE,
5975 EL_ROCK, ACTION_FILLING, -1
5978 Xsand_stonesand_1, FALSE, FALSE,
5979 EL_QUICKSAND_EMPTYING, -1, -1
5982 Xsand_stonesand_2, FALSE, FALSE,
5983 EL_QUICKSAND_EMPTYING, -1, -1
5986 Xsand_stonesand_3, FALSE, FALSE,
5987 EL_QUICKSAND_EMPTYING, -1, -1
5990 Xsand_stonesand_4, FALSE, FALSE,
5991 EL_QUICKSAND_EMPTYING, -1, -1
5994 Xsand_stonesand_quickout_1, FALSE, FALSE,
5995 EL_QUICKSAND_EMPTYING, -1, -1
5998 Xsand_stonesand_quickout_2, FALSE, FALSE,
5999 EL_QUICKSAND_EMPTYING, -1, -1
6002 Xsand_stoneout_1, FALSE, FALSE,
6003 EL_ROCK, ACTION_EMPTYING, -1
6006 Xsand_stoneout_2, FALSE, FALSE,
6007 EL_ROCK, ACTION_EMPTYING, -1
6010 Xsand_sandstone_1, FALSE, FALSE,
6011 EL_QUICKSAND_FILLING, -1, -1
6014 Xsand_sandstone_2, FALSE, FALSE,
6015 EL_QUICKSAND_FILLING, -1, -1
6018 Xsand_sandstone_3, FALSE, FALSE,
6019 EL_QUICKSAND_FILLING, -1, -1
6022 Xsand_sandstone_4, FALSE, FALSE,
6023 EL_QUICKSAND_FILLING, -1, -1
6026 Xplant, TRUE, FALSE,
6027 EL_EMC_PLANT, -1, -1
6030 Yplant, FALSE, FALSE,
6031 EL_EMC_PLANT, -1, -1
6034 Xlenses, TRUE, FALSE,
6035 EL_EMC_LENSES, -1, -1
6038 Xmagnify, TRUE, FALSE,
6039 EL_EMC_MAGNIFIER, -1, -1
6042 Xdripper, TRUE, FALSE,
6043 EL_EMC_DRIPPER, -1, -1
6046 XdripperB, FALSE, FALSE,
6047 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6050 Xfake_blank, TRUE, FALSE,
6051 EL_INVISIBLE_WALL, -1, -1
6054 Xfake_blankB, FALSE, FALSE,
6055 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6058 Xfake_grass, TRUE, FALSE,
6059 EL_EMC_FAKE_GRASS, -1, -1
6062 Xfake_grassB, FALSE, FALSE,
6063 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6066 Xfake_door_1, TRUE, FALSE,
6067 EL_EM_GATE_1_GRAY, -1, -1
6070 Xfake_door_2, TRUE, FALSE,
6071 EL_EM_GATE_2_GRAY, -1, -1
6074 Xfake_door_3, TRUE, FALSE,
6075 EL_EM_GATE_3_GRAY, -1, -1
6078 Xfake_door_4, TRUE, FALSE,
6079 EL_EM_GATE_4_GRAY, -1, -1
6082 Xfake_door_5, TRUE, FALSE,
6083 EL_EMC_GATE_5_GRAY, -1, -1
6086 Xfake_door_6, TRUE, FALSE,
6087 EL_EMC_GATE_6_GRAY, -1, -1
6090 Xfake_door_7, TRUE, FALSE,
6091 EL_EMC_GATE_7_GRAY, -1, -1
6094 Xfake_door_8, TRUE, FALSE,
6095 EL_EMC_GATE_8_GRAY, -1, -1
6098 Xfake_acid_1, TRUE, FALSE,
6099 EL_EMC_FAKE_ACID, -1, -1
6102 Xfake_acid_2, FALSE, FALSE,
6103 EL_EMC_FAKE_ACID, -1, -1
6106 Xfake_acid_3, FALSE, FALSE,
6107 EL_EMC_FAKE_ACID, -1, -1
6110 Xfake_acid_4, FALSE, FALSE,
6111 EL_EMC_FAKE_ACID, -1, -1
6114 Xfake_acid_5, FALSE, FALSE,
6115 EL_EMC_FAKE_ACID, -1, -1
6118 Xfake_acid_6, FALSE, FALSE,
6119 EL_EMC_FAKE_ACID, -1, -1
6122 Xfake_acid_7, FALSE, FALSE,
6123 EL_EMC_FAKE_ACID, -1, -1
6126 Xfake_acid_8, FALSE, FALSE,
6127 EL_EMC_FAKE_ACID, -1, -1
6130 Xsteel_1, TRUE, FALSE,
6131 EL_STEELWALL, -1, -1
6134 Xsteel_2, TRUE, FALSE,
6135 EL_EMC_STEELWALL_2, -1, -1
6138 Xsteel_3, TRUE, FALSE,
6139 EL_EMC_STEELWALL_3, -1, -1
6142 Xsteel_4, TRUE, FALSE,
6143 EL_EMC_STEELWALL_4, -1, -1
6146 Xwall_1, TRUE, FALSE,
6150 Xwall_2, TRUE, FALSE,
6151 EL_EMC_WALL_14, -1, -1
6154 Xwall_3, TRUE, FALSE,
6155 EL_EMC_WALL_15, -1, -1
6158 Xwall_4, TRUE, FALSE,
6159 EL_EMC_WALL_16, -1, -1
6162 Xround_wall_1, TRUE, FALSE,
6163 EL_WALL_SLIPPERY, -1, -1
6166 Xround_wall_2, TRUE, FALSE,
6167 EL_EMC_WALL_SLIPPERY_2, -1, -1
6170 Xround_wall_3, TRUE, FALSE,
6171 EL_EMC_WALL_SLIPPERY_3, -1, -1
6174 Xround_wall_4, TRUE, FALSE,
6175 EL_EMC_WALL_SLIPPERY_4, -1, -1
6178 Xdecor_1, TRUE, FALSE,
6179 EL_EMC_WALL_8, -1, -1
6182 Xdecor_2, TRUE, FALSE,
6183 EL_EMC_WALL_6, -1, -1
6186 Xdecor_3, TRUE, FALSE,
6187 EL_EMC_WALL_4, -1, -1
6190 Xdecor_4, TRUE, FALSE,
6191 EL_EMC_WALL_7, -1, -1
6194 Xdecor_5, TRUE, FALSE,
6195 EL_EMC_WALL_5, -1, -1
6198 Xdecor_6, TRUE, FALSE,
6199 EL_EMC_WALL_9, -1, -1
6202 Xdecor_7, TRUE, FALSE,
6203 EL_EMC_WALL_10, -1, -1
6206 Xdecor_8, TRUE, FALSE,
6207 EL_EMC_WALL_1, -1, -1
6210 Xdecor_9, TRUE, FALSE,
6211 EL_EMC_WALL_2, -1, -1
6214 Xdecor_10, TRUE, FALSE,
6215 EL_EMC_WALL_3, -1, -1
6218 Xdecor_11, TRUE, FALSE,
6219 EL_EMC_WALL_11, -1, -1
6222 Xdecor_12, TRUE, FALSE,
6223 EL_EMC_WALL_12, -1, -1
6226 Xalpha_0, TRUE, FALSE,
6227 EL_CHAR('0'), -1, -1
6230 Xalpha_1, TRUE, FALSE,
6231 EL_CHAR('1'), -1, -1
6234 Xalpha_2, TRUE, FALSE,
6235 EL_CHAR('2'), -1, -1
6238 Xalpha_3, TRUE, FALSE,
6239 EL_CHAR('3'), -1, -1
6242 Xalpha_4, TRUE, FALSE,
6243 EL_CHAR('4'), -1, -1
6246 Xalpha_5, TRUE, FALSE,
6247 EL_CHAR('5'), -1, -1
6250 Xalpha_6, TRUE, FALSE,
6251 EL_CHAR('6'), -1, -1
6254 Xalpha_7, TRUE, FALSE,
6255 EL_CHAR('7'), -1, -1
6258 Xalpha_8, TRUE, FALSE,
6259 EL_CHAR('8'), -1, -1
6262 Xalpha_9, TRUE, FALSE,
6263 EL_CHAR('9'), -1, -1
6266 Xalpha_excla, TRUE, FALSE,
6267 EL_CHAR('!'), -1, -1
6270 Xalpha_quote, TRUE, FALSE,
6271 EL_CHAR('"'), -1, -1
6274 Xalpha_comma, TRUE, FALSE,
6275 EL_CHAR(','), -1, -1
6278 Xalpha_minus, TRUE, FALSE,
6279 EL_CHAR('-'), -1, -1
6282 Xalpha_perio, TRUE, FALSE,
6283 EL_CHAR('.'), -1, -1
6286 Xalpha_colon, TRUE, FALSE,
6287 EL_CHAR(':'), -1, -1
6290 Xalpha_quest, TRUE, FALSE,
6291 EL_CHAR('?'), -1, -1
6294 Xalpha_a, TRUE, FALSE,
6295 EL_CHAR('A'), -1, -1
6298 Xalpha_b, TRUE, FALSE,
6299 EL_CHAR('B'), -1, -1
6302 Xalpha_c, TRUE, FALSE,
6303 EL_CHAR('C'), -1, -1
6306 Xalpha_d, TRUE, FALSE,
6307 EL_CHAR('D'), -1, -1
6310 Xalpha_e, TRUE, FALSE,
6311 EL_CHAR('E'), -1, -1
6314 Xalpha_f, TRUE, FALSE,
6315 EL_CHAR('F'), -1, -1
6318 Xalpha_g, TRUE, FALSE,
6319 EL_CHAR('G'), -1, -1
6322 Xalpha_h, TRUE, FALSE,
6323 EL_CHAR('H'), -1, -1
6326 Xalpha_i, TRUE, FALSE,
6327 EL_CHAR('I'), -1, -1
6330 Xalpha_j, TRUE, FALSE,
6331 EL_CHAR('J'), -1, -1
6334 Xalpha_k, TRUE, FALSE,
6335 EL_CHAR('K'), -1, -1
6338 Xalpha_l, TRUE, FALSE,
6339 EL_CHAR('L'), -1, -1
6342 Xalpha_m, TRUE, FALSE,
6343 EL_CHAR('M'), -1, -1
6346 Xalpha_n, TRUE, FALSE,
6347 EL_CHAR('N'), -1, -1
6350 Xalpha_o, TRUE, FALSE,
6351 EL_CHAR('O'), -1, -1
6354 Xalpha_p, TRUE, FALSE,
6355 EL_CHAR('P'), -1, -1
6358 Xalpha_q, TRUE, FALSE,
6359 EL_CHAR('Q'), -1, -1
6362 Xalpha_r, TRUE, FALSE,
6363 EL_CHAR('R'), -1, -1
6366 Xalpha_s, TRUE, FALSE,
6367 EL_CHAR('S'), -1, -1
6370 Xalpha_t, TRUE, FALSE,
6371 EL_CHAR('T'), -1, -1
6374 Xalpha_u, TRUE, FALSE,
6375 EL_CHAR('U'), -1, -1
6378 Xalpha_v, TRUE, FALSE,
6379 EL_CHAR('V'), -1, -1
6382 Xalpha_w, TRUE, FALSE,
6383 EL_CHAR('W'), -1, -1
6386 Xalpha_x, TRUE, FALSE,
6387 EL_CHAR('X'), -1, -1
6390 Xalpha_y, TRUE, FALSE,
6391 EL_CHAR('Y'), -1, -1
6394 Xalpha_z, TRUE, FALSE,
6395 EL_CHAR('Z'), -1, -1
6398 Xalpha_arrow_e, TRUE, FALSE,
6399 EL_CHAR('>'), -1, -1
6402 Xalpha_arrow_w, TRUE, FALSE,
6403 EL_CHAR('<'), -1, -1
6406 Xalpha_copyr, TRUE, FALSE,
6407 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6411 Xboom_bug, FALSE, FALSE,
6412 EL_BUG, ACTION_EXPLODING, -1
6415 Xboom_bomb, FALSE, FALSE,
6416 EL_BOMB, ACTION_EXPLODING, -1
6419 Xboom_android, FALSE, FALSE,
6420 EL_EMC_ANDROID, ACTION_OTHER, -1
6423 Xboom_1, FALSE, FALSE,
6424 EL_DEFAULT, ACTION_EXPLODING, -1
6427 Xboom_2, FALSE, FALSE,
6428 EL_DEFAULT, ACTION_EXPLODING, -1
6431 Znormal, FALSE, FALSE,
6435 Zdynamite, FALSE, FALSE,
6439 Zplayer, FALSE, FALSE,
6443 ZBORDER, FALSE, FALSE,
6453 static struct Mapping_EM_to_RND_player
6462 em_player_mapping_list[] =
6466 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6470 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6474 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6478 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6482 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6486 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6490 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6494 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6498 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6502 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6506 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6510 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6514 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6518 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6522 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6526 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6530 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6534 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6538 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6542 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6546 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6550 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6554 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6558 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6562 EL_PLAYER_1, ACTION_DEFAULT, -1,
6566 EL_PLAYER_2, ACTION_DEFAULT, -1,
6570 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6574 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6578 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6582 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6586 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6590 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6594 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6598 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6602 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6606 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6610 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6614 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6618 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6622 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6626 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6630 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6634 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6638 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6642 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6646 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6650 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6654 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6658 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6662 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6666 EL_PLAYER_3, ACTION_DEFAULT, -1,
6670 EL_PLAYER_4, ACTION_DEFAULT, -1,
6679 int map_element_RND_to_EM(int element_rnd)
6681 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6682 static boolean mapping_initialized = FALSE;
6684 if (!mapping_initialized)
6688 /* return "Xalpha_quest" for all undefined elements in mapping array */
6689 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6690 mapping_RND_to_EM[i] = Xalpha_quest;
6692 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6693 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6694 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6695 em_object_mapping_list[i].element_em;
6697 mapping_initialized = TRUE;
6700 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6701 return mapping_RND_to_EM[element_rnd];
6703 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6708 int map_element_EM_to_RND(int element_em)
6710 static unsigned short mapping_EM_to_RND[TILE_MAX];
6711 static boolean mapping_initialized = FALSE;
6713 if (!mapping_initialized)
6717 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6718 for (i = 0; i < TILE_MAX; i++)
6719 mapping_EM_to_RND[i] = EL_UNKNOWN;
6721 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6722 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6723 em_object_mapping_list[i].element_rnd;
6725 mapping_initialized = TRUE;
6728 if (element_em >= 0 && element_em < TILE_MAX)
6729 return mapping_EM_to_RND[element_em];
6731 Error(ERR_WARN, "invalid EM level element %d", element_em);
6736 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6738 struct LevelInfo_EM *level_em = level->native_em_level;
6739 struct LEVEL *lev = level_em->lev;
6742 for (i = 0; i < TILE_MAX; i++)
6743 lev->android_array[i] = Xblank;
6745 for (i = 0; i < level->num_android_clone_elements; i++)
6747 int element_rnd = level->android_clone_element[i];
6748 int element_em = map_element_RND_to_EM(element_rnd);
6750 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6751 if (em_object_mapping_list[j].element_rnd == element_rnd)
6752 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6756 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6758 struct LevelInfo_EM *level_em = level->native_em_level;
6759 struct LEVEL *lev = level_em->lev;
6762 level->num_android_clone_elements = 0;
6764 for (i = 0; i < TILE_MAX; i++)
6766 int element_em = lev->android_array[i];
6768 boolean element_found = FALSE;
6770 if (element_em == Xblank)
6773 element_rnd = map_element_EM_to_RND(element_em);
6775 for (j = 0; j < level->num_android_clone_elements; j++)
6776 if (level->android_clone_element[j] == element_rnd)
6777 element_found = TRUE;
6781 level->android_clone_element[level->num_android_clone_elements++] =
6784 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6789 if (level->num_android_clone_elements == 0)
6791 level->num_android_clone_elements = 1;
6792 level->android_clone_element[0] = EL_EMPTY;
6796 int map_direction_RND_to_EM(int direction)
6798 return (direction == MV_UP ? 0 :
6799 direction == MV_RIGHT ? 1 :
6800 direction == MV_DOWN ? 2 :
6801 direction == MV_LEFT ? 3 :
6805 int map_direction_EM_to_RND(int direction)
6807 return (direction == 0 ? MV_UP :
6808 direction == 1 ? MV_RIGHT :
6809 direction == 2 ? MV_DOWN :
6810 direction == 3 ? MV_LEFT :
6814 int map_element_RND_to_SP(int element_rnd)
6816 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6818 if (element_rnd >= EL_SP_START &&
6819 element_rnd <= EL_SP_END)
6820 element_sp = element_rnd - EL_SP_START;
6821 else if (element_rnd == EL_EMPTY_SPACE)
6823 else if (element_rnd == EL_INVISIBLE_WALL)
6829 int map_element_SP_to_RND(int element_sp)
6831 int element_rnd = EL_UNKNOWN;
6833 if (element_sp >= 0x00 &&
6835 element_rnd = EL_SP_START + element_sp;
6836 else if (element_sp == 0x28)
6837 element_rnd = EL_INVISIBLE_WALL;
6842 int map_action_SP_to_RND(int action_sp)
6846 case actActive: return ACTION_ACTIVE;
6847 case actImpact: return ACTION_IMPACT;
6848 case actExploding: return ACTION_EXPLODING;
6849 case actDigging: return ACTION_DIGGING;
6850 case actSnapping: return ACTION_SNAPPING;
6851 case actCollecting: return ACTION_COLLECTING;
6852 case actPassing: return ACTION_PASSING;
6853 case actPushing: return ACTION_PUSHING;
6854 case actDropping: return ACTION_DROPPING;
6856 default: return ACTION_DEFAULT;
6860 int get_next_element(int element)
6864 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6865 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6866 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6867 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6868 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6869 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6870 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6871 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6872 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6873 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6874 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6876 default: return element;
6880 int el_act_dir2img(int element, int action, int direction)
6882 element = GFX_ELEMENT(element);
6883 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6885 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6886 return element_info[element].direction_graphic[action][direction];
6889 static int el_act_dir2crm(int element, int action, int direction)
6891 element = GFX_ELEMENT(element);
6892 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6894 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6895 return element_info[element].direction_crumbled[action][direction];
6898 int el_act2img(int element, int action)
6900 element = GFX_ELEMENT(element);
6902 return element_info[element].graphic[action];
6905 int el_act2crm(int element, int action)
6907 element = GFX_ELEMENT(element);
6909 return element_info[element].crumbled[action];
6912 int el_dir2img(int element, int direction)
6914 element = GFX_ELEMENT(element);
6916 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6919 int el2baseimg(int element)
6921 return element_info[element].graphic[ACTION_DEFAULT];
6924 int el2img(int element)
6926 element = GFX_ELEMENT(element);
6928 return element_info[element].graphic[ACTION_DEFAULT];
6931 int el2edimg(int element)
6933 element = GFX_ELEMENT(element);
6935 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6938 int el2preimg(int element)
6940 element = GFX_ELEMENT(element);
6942 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6945 int el2panelimg(int element)
6947 element = GFX_ELEMENT(element);
6949 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6952 int font2baseimg(int font_nr)
6954 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6957 int getBeltNrFromBeltElement(int element)
6959 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6960 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6961 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6964 int getBeltNrFromBeltActiveElement(int element)
6966 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6967 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6968 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6971 int getBeltNrFromBeltSwitchElement(int element)
6973 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6974 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6975 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6978 int getBeltDirNrFromBeltElement(int element)
6980 static int belt_base_element[4] =
6982 EL_CONVEYOR_BELT_1_LEFT,
6983 EL_CONVEYOR_BELT_2_LEFT,
6984 EL_CONVEYOR_BELT_3_LEFT,
6985 EL_CONVEYOR_BELT_4_LEFT
6988 int belt_nr = getBeltNrFromBeltElement(element);
6989 int belt_dir_nr = element - belt_base_element[belt_nr];
6991 return (belt_dir_nr % 3);
6994 int getBeltDirNrFromBeltSwitchElement(int element)
6996 static int belt_base_element[4] =
6998 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6999 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7000 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7001 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7004 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7005 int belt_dir_nr = element - belt_base_element[belt_nr];
7007 return (belt_dir_nr % 3);
7010 int getBeltDirFromBeltElement(int element)
7012 static int belt_move_dir[3] =
7019 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7021 return belt_move_dir[belt_dir_nr];
7024 int getBeltDirFromBeltSwitchElement(int element)
7026 static int belt_move_dir[3] =
7033 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7035 return belt_move_dir[belt_dir_nr];
7038 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7040 static int belt_base_element[4] =
7042 EL_CONVEYOR_BELT_1_LEFT,
7043 EL_CONVEYOR_BELT_2_LEFT,
7044 EL_CONVEYOR_BELT_3_LEFT,
7045 EL_CONVEYOR_BELT_4_LEFT
7048 return belt_base_element[belt_nr] + belt_dir_nr;
7051 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7053 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7055 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7058 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7060 static int belt_base_element[4] =
7062 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7063 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7064 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7065 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7068 return belt_base_element[belt_nr] + belt_dir_nr;
7071 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7073 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7075 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7078 boolean getTeamMode_EM()
7080 return game.team_mode;
7083 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7085 int game_frame_delay_value;
7087 game_frame_delay_value =
7088 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7089 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7092 if (tape.playing && tape.warp_forward && !tape.pausing)
7093 game_frame_delay_value = 0;
7095 return game_frame_delay_value;
7098 unsigned int InitRND(int seed)
7100 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7101 return InitEngineRandom_EM(seed);
7102 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7103 return InitEngineRandom_SP(seed);
7105 return InitEngineRandom_RND(seed);
7108 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7109 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7111 inline static int get_effective_element_EM(int tile, int frame_em)
7113 int element = object_mapping[tile].element_rnd;
7114 int action = object_mapping[tile].action;
7115 boolean is_backside = object_mapping[tile].is_backside;
7116 boolean action_removing = (action == ACTION_DIGGING ||
7117 action == ACTION_SNAPPING ||
7118 action == ACTION_COLLECTING);
7124 case Yacid_splash_eB:
7125 case Yacid_splash_wB:
7126 return (frame_em > 5 ? EL_EMPTY : element);
7132 else /* frame_em == 7 */
7136 case Yacid_splash_eB:
7137 case Yacid_splash_wB:
7140 case Yemerald_stone:
7143 case Ydiamond_stone:
7147 case Xdrip_stretchB:
7166 case Xsand_stonein_1:
7167 case Xsand_stonein_2:
7168 case Xsand_stonein_3:
7169 case Xsand_stonein_4:
7173 return (is_backside || action_removing ? EL_EMPTY : element);
7178 inline static boolean check_linear_animation_EM(int tile)
7182 case Xsand_stonesand_1:
7183 case Xsand_stonesand_quickout_1:
7184 case Xsand_sandstone_1:
7185 case Xsand_stonein_1:
7186 case Xsand_stoneout_1:
7205 case Yacid_splash_eB:
7206 case Yacid_splash_wB:
7207 case Yemerald_stone:
7214 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7215 boolean has_crumbled_graphics,
7216 int crumbled, int sync_frame)
7218 /* if element can be crumbled, but certain action graphics are just empty
7219 space (like instantly snapping sand to empty space in 1 frame), do not
7220 treat these empty space graphics as crumbled graphics in EMC engine */
7221 if (crumbled == IMG_EMPTY_SPACE)
7222 has_crumbled_graphics = FALSE;
7224 if (has_crumbled_graphics)
7226 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7227 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7228 g_crumbled->anim_delay,
7229 g_crumbled->anim_mode,
7230 g_crumbled->anim_start_frame,
7233 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7234 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7236 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7238 g_em->has_crumbled_graphics = TRUE;
7242 g_em->crumbled_bitmap = NULL;
7243 g_em->crumbled_src_x = 0;
7244 g_em->crumbled_src_y = 0;
7245 g_em->crumbled_border_size = 0;
7247 g_em->has_crumbled_graphics = FALSE;
7251 void ResetGfxAnimation_EM(int x, int y, int tile)
7256 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7257 int tile, int frame_em, int x, int y)
7259 int action = object_mapping[tile].action;
7260 int direction = object_mapping[tile].direction;
7261 int effective_element = get_effective_element_EM(tile, frame_em);
7262 int graphic = (direction == MV_NONE ?
7263 el_act2img(effective_element, action) :
7264 el_act_dir2img(effective_element, action, direction));
7265 struct GraphicInfo *g = &graphic_info[graphic];
7267 boolean action_removing = (action == ACTION_DIGGING ||
7268 action == ACTION_SNAPPING ||
7269 action == ACTION_COLLECTING);
7270 boolean action_moving = (action == ACTION_FALLING ||
7271 action == ACTION_MOVING ||
7272 action == ACTION_PUSHING ||
7273 action == ACTION_EATING ||
7274 action == ACTION_FILLING ||
7275 action == ACTION_EMPTYING);
7276 boolean action_falling = (action == ACTION_FALLING ||
7277 action == ACTION_FILLING ||
7278 action == ACTION_EMPTYING);
7280 /* special case: graphic uses "2nd movement tile" and has defined
7281 7 frames for movement animation (or less) => use default graphic
7282 for last (8th) frame which ends the movement animation */
7283 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7285 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7286 graphic = (direction == MV_NONE ?
7287 el_act2img(effective_element, action) :
7288 el_act_dir2img(effective_element, action, direction));
7290 g = &graphic_info[graphic];
7293 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7297 else if (action_moving)
7299 boolean is_backside = object_mapping[tile].is_backside;
7303 int direction = object_mapping[tile].direction;
7304 int move_dir = (action_falling ? MV_DOWN : direction);
7309 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7310 if (g->double_movement && frame_em == 0)
7314 if (move_dir == MV_LEFT)
7315 GfxFrame[x - 1][y] = GfxFrame[x][y];
7316 else if (move_dir == MV_RIGHT)
7317 GfxFrame[x + 1][y] = GfxFrame[x][y];
7318 else if (move_dir == MV_UP)
7319 GfxFrame[x][y - 1] = GfxFrame[x][y];
7320 else if (move_dir == MV_DOWN)
7321 GfxFrame[x][y + 1] = GfxFrame[x][y];
7328 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7329 if (tile == Xsand_stonesand_quickout_1 ||
7330 tile == Xsand_stonesand_quickout_2)
7334 if (graphic_info[graphic].anim_global_sync)
7335 sync_frame = FrameCounter;
7336 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7337 sync_frame = GfxFrame[x][y];
7339 sync_frame = 0; /* playfield border (pseudo steel) */
7341 SetRandomAnimationValue(x, y);
7343 int frame = getAnimationFrame(g->anim_frames,
7346 g->anim_start_frame,
7349 g_em->unique_identifier =
7350 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7353 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7354 int tile, int frame_em, int x, int y)
7356 int action = object_mapping[tile].action;
7357 int direction = object_mapping[tile].direction;
7358 boolean is_backside = object_mapping[tile].is_backside;
7359 int effective_element = get_effective_element_EM(tile, frame_em);
7360 int effective_action = action;
7361 int graphic = (direction == MV_NONE ?
7362 el_act2img(effective_element, effective_action) :
7363 el_act_dir2img(effective_element, effective_action,
7365 int crumbled = (direction == MV_NONE ?
7366 el_act2crm(effective_element, effective_action) :
7367 el_act_dir2crm(effective_element, effective_action,
7369 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7370 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7371 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7372 struct GraphicInfo *g = &graphic_info[graphic];
7375 /* special case: graphic uses "2nd movement tile" and has defined
7376 7 frames for movement animation (or less) => use default graphic
7377 for last (8th) frame which ends the movement animation */
7378 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7380 effective_action = ACTION_DEFAULT;
7381 graphic = (direction == MV_NONE ?
7382 el_act2img(effective_element, effective_action) :
7383 el_act_dir2img(effective_element, effective_action,
7385 crumbled = (direction == MV_NONE ?
7386 el_act2crm(effective_element, effective_action) :
7387 el_act_dir2crm(effective_element, effective_action,
7390 g = &graphic_info[graphic];
7393 if (graphic_info[graphic].anim_global_sync)
7394 sync_frame = FrameCounter;
7395 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7396 sync_frame = GfxFrame[x][y];
7398 sync_frame = 0; /* playfield border (pseudo steel) */
7400 SetRandomAnimationValue(x, y);
7402 int frame = getAnimationFrame(g->anim_frames,
7405 g->anim_start_frame,
7408 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7409 g->double_movement && is_backside);
7411 /* (updating the "crumbled" graphic definitions is probably not really needed,
7412 as animations for crumbled graphics can't be longer than one EMC cycle) */
7413 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7417 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7418 int player_nr, int anim, int frame_em)
7420 int element = player_mapping[player_nr][anim].element_rnd;
7421 int action = player_mapping[player_nr][anim].action;
7422 int direction = player_mapping[player_nr][anim].direction;
7423 int graphic = (direction == MV_NONE ?
7424 el_act2img(element, action) :
7425 el_act_dir2img(element, action, direction));
7426 struct GraphicInfo *g = &graphic_info[graphic];
7429 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7431 stored_player[player_nr].StepFrame = frame_em;
7433 sync_frame = stored_player[player_nr].Frame;
7435 int frame = getAnimationFrame(g->anim_frames,
7438 g->anim_start_frame,
7441 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7442 &g_em->src_x, &g_em->src_y, FALSE);
7445 void InitGraphicInfo_EM(void)
7450 int num_em_gfx_errors = 0;
7452 if (graphic_info_em_object[0][0].bitmap == NULL)
7454 /* EM graphics not yet initialized in em_open_all() */
7459 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7462 /* always start with reliable default values */
7463 for (i = 0; i < TILE_MAX; i++)
7465 object_mapping[i].element_rnd = EL_UNKNOWN;
7466 object_mapping[i].is_backside = FALSE;
7467 object_mapping[i].action = ACTION_DEFAULT;
7468 object_mapping[i].direction = MV_NONE;
7471 /* always start with reliable default values */
7472 for (p = 0; p < MAX_PLAYERS; p++)
7474 for (i = 0; i < SPR_MAX; i++)
7476 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7477 player_mapping[p][i].action = ACTION_DEFAULT;
7478 player_mapping[p][i].direction = MV_NONE;
7482 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7484 int e = em_object_mapping_list[i].element_em;
7486 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7487 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7489 if (em_object_mapping_list[i].action != -1)
7490 object_mapping[e].action = em_object_mapping_list[i].action;
7492 if (em_object_mapping_list[i].direction != -1)
7493 object_mapping[e].direction =
7494 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7497 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7499 int a = em_player_mapping_list[i].action_em;
7500 int p = em_player_mapping_list[i].player_nr;
7502 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7504 if (em_player_mapping_list[i].action != -1)
7505 player_mapping[p][a].action = em_player_mapping_list[i].action;
7507 if (em_player_mapping_list[i].direction != -1)
7508 player_mapping[p][a].direction =
7509 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7512 for (i = 0; i < TILE_MAX; i++)
7514 int element = object_mapping[i].element_rnd;
7515 int action = object_mapping[i].action;
7516 int direction = object_mapping[i].direction;
7517 boolean is_backside = object_mapping[i].is_backside;
7518 boolean action_exploding = ((action == ACTION_EXPLODING ||
7519 action == ACTION_SMASHED_BY_ROCK ||
7520 action == ACTION_SMASHED_BY_SPRING) &&
7521 element != EL_DIAMOND);
7522 boolean action_active = (action == ACTION_ACTIVE);
7523 boolean action_other = (action == ACTION_OTHER);
7525 for (j = 0; j < 8; j++)
7527 int effective_element = get_effective_element_EM(i, j);
7528 int effective_action = (j < 7 ? action :
7529 i == Xdrip_stretch ? action :
7530 i == Xdrip_stretchB ? action :
7531 i == Ydrip_s1 ? action :
7532 i == Ydrip_s1B ? action :
7533 i == Xball_1B ? action :
7534 i == Xball_2 ? action :
7535 i == Xball_2B ? action :
7536 i == Yball_eat ? action :
7537 i == Ykey_1_eat ? action :
7538 i == Ykey_2_eat ? action :
7539 i == Ykey_3_eat ? action :
7540 i == Ykey_4_eat ? action :
7541 i == Ykey_5_eat ? action :
7542 i == Ykey_6_eat ? action :
7543 i == Ykey_7_eat ? action :
7544 i == Ykey_8_eat ? action :
7545 i == Ylenses_eat ? action :
7546 i == Ymagnify_eat ? action :
7547 i == Ygrass_eat ? action :
7548 i == Ydirt_eat ? action :
7549 i == Xsand_stonein_1 ? action :
7550 i == Xsand_stonein_2 ? action :
7551 i == Xsand_stonein_3 ? action :
7552 i == Xsand_stonein_4 ? action :
7553 i == Xsand_stoneout_1 ? action :
7554 i == Xsand_stoneout_2 ? action :
7555 i == Xboom_android ? ACTION_EXPLODING :
7556 action_exploding ? ACTION_EXPLODING :
7557 action_active ? action :
7558 action_other ? action :
7560 int graphic = (el_act_dir2img(effective_element, effective_action,
7562 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7564 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7565 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7566 boolean has_action_graphics = (graphic != base_graphic);
7567 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7568 struct GraphicInfo *g = &graphic_info[graphic];
7569 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7572 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7573 boolean special_animation = (action != ACTION_DEFAULT &&
7574 g->anim_frames == 3 &&
7575 g->anim_delay == 2 &&
7576 g->anim_mode & ANIM_LINEAR);
7577 int sync_frame = (i == Xdrip_stretch ? 7 :
7578 i == Xdrip_stretchB ? 7 :
7579 i == Ydrip_s2 ? j + 8 :
7580 i == Ydrip_s2B ? j + 8 :
7589 i == Xfake_acid_1 ? 0 :
7590 i == Xfake_acid_2 ? 10 :
7591 i == Xfake_acid_3 ? 20 :
7592 i == Xfake_acid_4 ? 30 :
7593 i == Xfake_acid_5 ? 40 :
7594 i == Xfake_acid_6 ? 50 :
7595 i == Xfake_acid_7 ? 60 :
7596 i == Xfake_acid_8 ? 70 :
7598 i == Xball_2B ? j + 8 :
7599 i == Yball_eat ? j + 1 :
7600 i == Ykey_1_eat ? j + 1 :
7601 i == Ykey_2_eat ? j + 1 :
7602 i == Ykey_3_eat ? j + 1 :
7603 i == Ykey_4_eat ? j + 1 :
7604 i == Ykey_5_eat ? j + 1 :
7605 i == Ykey_6_eat ? j + 1 :
7606 i == Ykey_7_eat ? j + 1 :
7607 i == Ykey_8_eat ? j + 1 :
7608 i == Ylenses_eat ? j + 1 :
7609 i == Ymagnify_eat ? j + 1 :
7610 i == Ygrass_eat ? j + 1 :
7611 i == Ydirt_eat ? j + 1 :
7612 i == Xamoeba_1 ? 0 :
7613 i == Xamoeba_2 ? 1 :
7614 i == Xamoeba_3 ? 2 :
7615 i == Xamoeba_4 ? 3 :
7616 i == Xamoeba_5 ? 0 :
7617 i == Xamoeba_6 ? 1 :
7618 i == Xamoeba_7 ? 2 :
7619 i == Xamoeba_8 ? 3 :
7620 i == Xexit_2 ? j + 8 :
7621 i == Xexit_3 ? j + 16 :
7622 i == Xdynamite_1 ? 0 :
7623 i == Xdynamite_2 ? 8 :
7624 i == Xdynamite_3 ? 16 :
7625 i == Xdynamite_4 ? 24 :
7626 i == Xsand_stonein_1 ? j + 1 :
7627 i == Xsand_stonein_2 ? j + 9 :
7628 i == Xsand_stonein_3 ? j + 17 :
7629 i == Xsand_stonein_4 ? j + 25 :
7630 i == Xsand_stoneout_1 && j == 0 ? 0 :
7631 i == Xsand_stoneout_1 && j == 1 ? 0 :
7632 i == Xsand_stoneout_1 && j == 2 ? 1 :
7633 i == Xsand_stoneout_1 && j == 3 ? 2 :
7634 i == Xsand_stoneout_1 && j == 4 ? 2 :
7635 i == Xsand_stoneout_1 && j == 5 ? 3 :
7636 i == Xsand_stoneout_1 && j == 6 ? 4 :
7637 i == Xsand_stoneout_1 && j == 7 ? 4 :
7638 i == Xsand_stoneout_2 && j == 0 ? 5 :
7639 i == Xsand_stoneout_2 && j == 1 ? 6 :
7640 i == Xsand_stoneout_2 && j == 2 ? 7 :
7641 i == Xsand_stoneout_2 && j == 3 ? 8 :
7642 i == Xsand_stoneout_2 && j == 4 ? 9 :
7643 i == Xsand_stoneout_2 && j == 5 ? 11 :
7644 i == Xsand_stoneout_2 && j == 6 ? 13 :
7645 i == Xsand_stoneout_2 && j == 7 ? 15 :
7646 i == Xboom_bug && j == 1 ? 2 :
7647 i == Xboom_bug && j == 2 ? 2 :
7648 i == Xboom_bug && j == 3 ? 4 :
7649 i == Xboom_bug && j == 4 ? 4 :
7650 i == Xboom_bug && j == 5 ? 2 :
7651 i == Xboom_bug && j == 6 ? 2 :
7652 i == Xboom_bug && j == 7 ? 0 :
7653 i == Xboom_bomb && j == 1 ? 2 :
7654 i == Xboom_bomb && j == 2 ? 2 :
7655 i == Xboom_bomb && j == 3 ? 4 :
7656 i == Xboom_bomb && j == 4 ? 4 :
7657 i == Xboom_bomb && j == 5 ? 2 :
7658 i == Xboom_bomb && j == 6 ? 2 :
7659 i == Xboom_bomb && j == 7 ? 0 :
7660 i == Xboom_android && j == 7 ? 6 :
7661 i == Xboom_1 && j == 1 ? 2 :
7662 i == Xboom_1 && j == 2 ? 2 :
7663 i == Xboom_1 && j == 3 ? 4 :
7664 i == Xboom_1 && j == 4 ? 4 :
7665 i == Xboom_1 && j == 5 ? 6 :
7666 i == Xboom_1 && j == 6 ? 6 :
7667 i == Xboom_1 && j == 7 ? 8 :
7668 i == Xboom_2 && j == 0 ? 8 :
7669 i == Xboom_2 && j == 1 ? 8 :
7670 i == Xboom_2 && j == 2 ? 10 :
7671 i == Xboom_2 && j == 3 ? 10 :
7672 i == Xboom_2 && j == 4 ? 10 :
7673 i == Xboom_2 && j == 5 ? 12 :
7674 i == Xboom_2 && j == 6 ? 12 :
7675 i == Xboom_2 && j == 7 ? 12 :
7676 special_animation && j == 4 ? 3 :
7677 effective_action != action ? 0 :
7681 Bitmap *debug_bitmap = g_em->bitmap;
7682 int debug_src_x = g_em->src_x;
7683 int debug_src_y = g_em->src_y;
7686 int frame = getAnimationFrame(g->anim_frames,
7689 g->anim_start_frame,
7692 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7693 g->double_movement && is_backside);
7695 g_em->bitmap = src_bitmap;
7696 g_em->src_x = src_x;
7697 g_em->src_y = src_y;
7698 g_em->src_offset_x = 0;
7699 g_em->src_offset_y = 0;
7700 g_em->dst_offset_x = 0;
7701 g_em->dst_offset_y = 0;
7702 g_em->width = TILEX;
7703 g_em->height = TILEY;
7705 g_em->preserve_background = FALSE;
7707 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7710 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7711 effective_action == ACTION_MOVING ||
7712 effective_action == ACTION_PUSHING ||
7713 effective_action == ACTION_EATING)) ||
7714 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7715 effective_action == ACTION_EMPTYING)))
7718 (effective_action == ACTION_FALLING ||
7719 effective_action == ACTION_FILLING ||
7720 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7721 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7722 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7723 int num_steps = (i == Ydrip_s1 ? 16 :
7724 i == Ydrip_s1B ? 16 :
7725 i == Ydrip_s2 ? 16 :
7726 i == Ydrip_s2B ? 16 :
7727 i == Xsand_stonein_1 ? 32 :
7728 i == Xsand_stonein_2 ? 32 :
7729 i == Xsand_stonein_3 ? 32 :
7730 i == Xsand_stonein_4 ? 32 :
7731 i == Xsand_stoneout_1 ? 16 :
7732 i == Xsand_stoneout_2 ? 16 : 8);
7733 int cx = ABS(dx) * (TILEX / num_steps);
7734 int cy = ABS(dy) * (TILEY / num_steps);
7735 int step_frame = (i == Ydrip_s2 ? j + 8 :
7736 i == Ydrip_s2B ? j + 8 :
7737 i == Xsand_stonein_2 ? j + 8 :
7738 i == Xsand_stonein_3 ? j + 16 :
7739 i == Xsand_stonein_4 ? j + 24 :
7740 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7741 int step = (is_backside ? step_frame : num_steps - step_frame);
7743 if (is_backside) /* tile where movement starts */
7745 if (dx < 0 || dy < 0)
7747 g_em->src_offset_x = cx * step;
7748 g_em->src_offset_y = cy * step;
7752 g_em->dst_offset_x = cx * step;
7753 g_em->dst_offset_y = cy * step;
7756 else /* tile where movement ends */
7758 if (dx < 0 || dy < 0)
7760 g_em->dst_offset_x = cx * step;
7761 g_em->dst_offset_y = cy * step;
7765 g_em->src_offset_x = cx * step;
7766 g_em->src_offset_y = cy * step;
7770 g_em->width = TILEX - cx * step;
7771 g_em->height = TILEY - cy * step;
7774 /* create unique graphic identifier to decide if tile must be redrawn */
7775 /* bit 31 - 16 (16 bit): EM style graphic
7776 bit 15 - 12 ( 4 bit): EM style frame
7777 bit 11 - 6 ( 6 bit): graphic width
7778 bit 5 - 0 ( 6 bit): graphic height */
7779 g_em->unique_identifier =
7780 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7784 /* skip check for EMC elements not contained in original EMC artwork */
7785 if (element == EL_EMC_FAKE_ACID)
7788 if (g_em->bitmap != debug_bitmap ||
7789 g_em->src_x != debug_src_x ||
7790 g_em->src_y != debug_src_y ||
7791 g_em->src_offset_x != 0 ||
7792 g_em->src_offset_y != 0 ||
7793 g_em->dst_offset_x != 0 ||
7794 g_em->dst_offset_y != 0 ||
7795 g_em->width != TILEX ||
7796 g_em->height != TILEY)
7798 static int last_i = -1;
7806 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7807 i, element, element_info[element].token_name,
7808 element_action_info[effective_action].suffix, direction);
7810 if (element != effective_element)
7811 printf(" [%d ('%s')]",
7813 element_info[effective_element].token_name);
7817 if (g_em->bitmap != debug_bitmap)
7818 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7819 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7821 if (g_em->src_x != debug_src_x ||
7822 g_em->src_y != debug_src_y)
7823 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7824 j, (is_backside ? 'B' : 'F'),
7825 g_em->src_x, g_em->src_y,
7826 g_em->src_x / 32, g_em->src_y / 32,
7827 debug_src_x, debug_src_y,
7828 debug_src_x / 32, debug_src_y / 32);
7830 if (g_em->src_offset_x != 0 ||
7831 g_em->src_offset_y != 0 ||
7832 g_em->dst_offset_x != 0 ||
7833 g_em->dst_offset_y != 0)
7834 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7836 g_em->src_offset_x, g_em->src_offset_y,
7837 g_em->dst_offset_x, g_em->dst_offset_y);
7839 if (g_em->width != TILEX ||
7840 g_em->height != TILEY)
7841 printf(" %d (%d): size %d,%d should be %d,%d\n",
7843 g_em->width, g_em->height, TILEX, TILEY);
7845 num_em_gfx_errors++;
7852 for (i = 0; i < TILE_MAX; i++)
7854 for (j = 0; j < 8; j++)
7856 int element = object_mapping[i].element_rnd;
7857 int action = object_mapping[i].action;
7858 int direction = object_mapping[i].direction;
7859 boolean is_backside = object_mapping[i].is_backside;
7860 int graphic_action = el_act_dir2img(element, action, direction);
7861 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7863 if ((action == ACTION_SMASHED_BY_ROCK ||
7864 action == ACTION_SMASHED_BY_SPRING ||
7865 action == ACTION_EATING) &&
7866 graphic_action == graphic_default)
7868 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7869 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7870 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7871 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7874 /* no separate animation for "smashed by rock" -- use rock instead */
7875 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7876 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7878 g_em->bitmap = g_xx->bitmap;
7879 g_em->src_x = g_xx->src_x;
7880 g_em->src_y = g_xx->src_y;
7881 g_em->src_offset_x = g_xx->src_offset_x;
7882 g_em->src_offset_y = g_xx->src_offset_y;
7883 g_em->dst_offset_x = g_xx->dst_offset_x;
7884 g_em->dst_offset_y = g_xx->dst_offset_y;
7885 g_em->width = g_xx->width;
7886 g_em->height = g_xx->height;
7887 g_em->unique_identifier = g_xx->unique_identifier;
7890 g_em->preserve_background = TRUE;
7895 for (p = 0; p < MAX_PLAYERS; p++)
7897 for (i = 0; i < SPR_MAX; i++)
7899 int element = player_mapping[p][i].element_rnd;
7900 int action = player_mapping[p][i].action;
7901 int direction = player_mapping[p][i].direction;
7903 for (j = 0; j < 8; j++)
7905 int effective_element = element;
7906 int effective_action = action;
7907 int graphic = (direction == MV_NONE ?
7908 el_act2img(effective_element, effective_action) :
7909 el_act_dir2img(effective_element, effective_action,
7911 struct GraphicInfo *g = &graphic_info[graphic];
7912 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7918 Bitmap *debug_bitmap = g_em->bitmap;
7919 int debug_src_x = g_em->src_x;
7920 int debug_src_y = g_em->src_y;
7923 int frame = getAnimationFrame(g->anim_frames,
7926 g->anim_start_frame,
7929 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7931 g_em->bitmap = src_bitmap;
7932 g_em->src_x = src_x;
7933 g_em->src_y = src_y;
7934 g_em->src_offset_x = 0;
7935 g_em->src_offset_y = 0;
7936 g_em->dst_offset_x = 0;
7937 g_em->dst_offset_y = 0;
7938 g_em->width = TILEX;
7939 g_em->height = TILEY;
7943 /* skip check for EMC elements not contained in original EMC artwork */
7944 if (element == EL_PLAYER_3 ||
7945 element == EL_PLAYER_4)
7948 if (g_em->bitmap != debug_bitmap ||
7949 g_em->src_x != debug_src_x ||
7950 g_em->src_y != debug_src_y)
7952 static int last_i = -1;
7960 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7961 p, i, element, element_info[element].token_name,
7962 element_action_info[effective_action].suffix, direction);
7964 if (element != effective_element)
7965 printf(" [%d ('%s')]",
7967 element_info[effective_element].token_name);
7971 if (g_em->bitmap != debug_bitmap)
7972 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7973 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7975 if (g_em->src_x != debug_src_x ||
7976 g_em->src_y != debug_src_y)
7977 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7979 g_em->src_x, g_em->src_y,
7980 g_em->src_x / 32, g_em->src_y / 32,
7981 debug_src_x, debug_src_y,
7982 debug_src_x / 32, debug_src_y / 32);
7984 num_em_gfx_errors++;
7994 printf("::: [%d errors found]\n", num_em_gfx_errors);
8000 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8001 boolean any_player_moving,
8002 boolean any_player_snapping,
8003 boolean any_player_dropping)
8005 static boolean player_was_waiting = TRUE;
8007 if (frame == 0 && !any_player_dropping)
8009 if (!player_was_waiting)
8011 if (!SaveEngineSnapshotToList())
8014 player_was_waiting = TRUE;
8017 else if (any_player_moving || any_player_snapping || any_player_dropping)
8019 player_was_waiting = FALSE;
8023 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8024 boolean murphy_is_dropping)
8026 static boolean player_was_waiting = TRUE;
8028 if (murphy_is_waiting)
8030 if (!player_was_waiting)
8032 if (!SaveEngineSnapshotToList())
8035 player_was_waiting = TRUE;
8040 player_was_waiting = FALSE;
8044 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8045 boolean any_player_moving,
8046 boolean any_player_snapping,
8047 boolean any_player_dropping)
8049 if (tape.single_step && tape.recording && !tape.pausing)
8050 if (frame == 0 && !any_player_dropping)
8051 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8053 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8054 any_player_snapping, any_player_dropping);
8057 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8058 boolean murphy_is_dropping)
8060 if (tape.single_step && tape.recording && !tape.pausing)
8061 if (murphy_is_waiting)
8062 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8064 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8067 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8068 int graphic, int sync_frame, int x, int y)
8070 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8072 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8075 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8077 return (IS_NEXT_FRAME(sync_frame, graphic));
8080 int getGraphicInfo_Delay(int graphic)
8082 return graphic_info[graphic].anim_delay;
8085 void PlayMenuSoundExt(int sound)
8087 if (sound == SND_UNDEFINED)
8090 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8091 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8094 if (IS_LOOP_SOUND(sound))
8095 PlaySoundLoop(sound);
8100 void PlayMenuSound()
8102 PlayMenuSoundExt(menu.sound[game_status]);
8105 void PlayMenuSoundStereo(int sound, int stereo_position)
8107 if (sound == SND_UNDEFINED)
8110 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8111 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8114 if (IS_LOOP_SOUND(sound))
8115 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8117 PlaySoundStereo(sound, stereo_position);
8120 void PlayMenuSoundIfLoopExt(int sound)
8122 if (sound == SND_UNDEFINED)
8125 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8126 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8129 if (IS_LOOP_SOUND(sound))
8130 PlaySoundLoop(sound);
8133 void PlayMenuSoundIfLoop()
8135 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8138 void PlayMenuMusicExt(int music)
8140 if (music == MUS_UNDEFINED)
8143 if (!setup.sound_music)
8149 void PlayMenuMusic()
8151 PlayMenuMusicExt(menu.music[game_status]);
8154 void PlaySoundActivating()
8157 PlaySound(SND_MENU_ITEM_ACTIVATING);
8161 void PlaySoundSelecting()
8164 PlaySound(SND_MENU_ITEM_SELECTING);
8168 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8170 boolean change_fullscreen = (setup.fullscreen !=
8171 video.fullscreen_enabled);
8172 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8173 !strEqual(setup.fullscreen_mode,
8174 video.fullscreen_mode_current));
8175 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8176 setup.window_scaling_percent !=
8177 video.window_scaling_percent);
8179 if (change_window_scaling_percent && video.fullscreen_enabled)
8182 if (!change_window_scaling_percent && !video.fullscreen_available)
8185 #if defined(TARGET_SDL2)
8186 if (change_window_scaling_percent)
8188 SDLSetWindowScaling(setup.window_scaling_percent);
8192 else if (change_fullscreen)
8194 SDLSetWindowFullscreen(setup.fullscreen);
8196 /* set setup value according to successfully changed fullscreen mode */
8197 setup.fullscreen = video.fullscreen_enabled;
8203 if (change_fullscreen ||
8204 change_fullscreen_mode ||
8205 change_window_scaling_percent)
8207 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8209 /* save backbuffer content which gets lost when toggling fullscreen mode */
8210 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8212 if (change_fullscreen_mode)
8214 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8215 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8218 if (change_window_scaling_percent)
8220 /* keep window mode, but change window scaling */
8221 video.fullscreen_enabled = TRUE; /* force new window scaling */
8224 /* toggle fullscreen */
8225 ChangeVideoModeIfNeeded(setup.fullscreen);
8227 /* set setup value according to successfully changed fullscreen mode */
8228 setup.fullscreen = video.fullscreen_enabled;
8230 /* restore backbuffer content from temporary backbuffer backup bitmap */
8231 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8233 FreeBitmap(tmp_backbuffer);
8235 /* update visible window/screen */
8236 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8240 void JoinRectangles(int *x, int *y, int *width, int *height,
8241 int x2, int y2, int width2, int height2)
8243 // do not join with "off-screen" rectangle
8244 if (x2 == -1 || y2 == -1)
8249 *width = MAX(*width, width2);
8250 *height = MAX(*height, height2);
8253 void SetGameStatus(int game_status_new)
8255 game_status = game_status_new;
8257 global.anim_status_next = game_status;
8260 void ChangeViewportPropertiesIfNeeded()
8262 int gfx_game_mode = game_status;
8263 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8265 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8266 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8267 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8268 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8269 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8270 int new_win_xsize = vp_window->width;
8271 int new_win_ysize = vp_window->height;
8272 int border_size = vp_playfield->border_size;
8273 int new_sx = vp_playfield->x + border_size;
8274 int new_sy = vp_playfield->y + border_size;
8275 int new_sxsize = vp_playfield->width - 2 * border_size;
8276 int new_sysize = vp_playfield->height - 2 * border_size;
8277 int new_real_sx = vp_playfield->x;
8278 int new_real_sy = vp_playfield->y;
8279 int new_full_sxsize = vp_playfield->width;
8280 int new_full_sysize = vp_playfield->height;
8281 int new_dx = vp_door_1->x;
8282 int new_dy = vp_door_1->y;
8283 int new_dxsize = vp_door_1->width;
8284 int new_dysize = vp_door_1->height;
8285 int new_vx = vp_door_2->x;
8286 int new_vy = vp_door_2->y;
8287 int new_vxsize = vp_door_2->width;
8288 int new_vysize = vp_door_2->height;
8289 int new_ex = vp_door_3->x;
8290 int new_ey = vp_door_3->y;
8291 int new_exsize = vp_door_3->width;
8292 int new_eysize = vp_door_3->height;
8293 int new_tilesize_var =
8294 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8296 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8297 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8298 int new_scr_fieldx = new_sxsize / tilesize;
8299 int new_scr_fieldy = new_sysize / tilesize;
8300 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8301 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8302 boolean init_gfx_buffers = FALSE;
8303 boolean init_video_buffer = FALSE;
8304 boolean init_gadgets_and_toons = FALSE;
8305 boolean init_em_graphics = FALSE;
8307 if (new_win_xsize != WIN_XSIZE ||
8308 new_win_ysize != WIN_YSIZE)
8310 WIN_XSIZE = new_win_xsize;
8311 WIN_YSIZE = new_win_ysize;
8313 init_video_buffer = TRUE;
8314 init_gfx_buffers = TRUE;
8315 init_gadgets_and_toons = TRUE;
8317 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8320 if (new_scr_fieldx != SCR_FIELDX ||
8321 new_scr_fieldy != SCR_FIELDY)
8323 /* this always toggles between MAIN and GAME when using small tile size */
8325 SCR_FIELDX = new_scr_fieldx;
8326 SCR_FIELDY = new_scr_fieldy;
8328 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8339 new_sxsize != SXSIZE ||
8340 new_sysize != SYSIZE ||
8341 new_dxsize != DXSIZE ||
8342 new_dysize != DYSIZE ||
8343 new_vxsize != VXSIZE ||
8344 new_vysize != VYSIZE ||
8345 new_exsize != EXSIZE ||
8346 new_eysize != EYSIZE ||
8347 new_real_sx != REAL_SX ||
8348 new_real_sy != REAL_SY ||
8349 new_full_sxsize != FULL_SXSIZE ||
8350 new_full_sysize != FULL_SYSIZE ||
8351 new_tilesize_var != TILESIZE_VAR
8354 // ------------------------------------------------------------------------
8355 // determine next fading area for changed viewport definitions
8356 // ------------------------------------------------------------------------
8358 // start with current playfield area (default fading area)
8361 FADE_SXSIZE = FULL_SXSIZE;
8362 FADE_SYSIZE = FULL_SYSIZE;
8364 // add new playfield area if position or size has changed
8365 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8366 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8368 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8369 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8372 // add current and new door 1 area if position or size has changed
8373 if (new_dx != DX || new_dy != DY ||
8374 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8376 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8377 DX, DY, DXSIZE, DYSIZE);
8378 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8379 new_dx, new_dy, new_dxsize, new_dysize);
8382 // add current and new door 2 area if position or size has changed
8383 if (new_dx != VX || new_dy != VY ||
8384 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8386 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8387 VX, VY, VXSIZE, VYSIZE);
8388 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8389 new_vx, new_vy, new_vxsize, new_vysize);
8392 // ------------------------------------------------------------------------
8393 // handle changed tile size
8394 // ------------------------------------------------------------------------
8396 if (new_tilesize_var != TILESIZE_VAR)
8398 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8400 // changing tile size invalidates scroll values of engine snapshots
8401 FreeEngineSnapshotSingle();
8403 // changing tile size requires update of graphic mapping for EM engine
8404 init_em_graphics = TRUE;
8415 SXSIZE = new_sxsize;
8416 SYSIZE = new_sysize;
8417 DXSIZE = new_dxsize;
8418 DYSIZE = new_dysize;
8419 VXSIZE = new_vxsize;
8420 VYSIZE = new_vysize;
8421 EXSIZE = new_exsize;
8422 EYSIZE = new_eysize;
8423 REAL_SX = new_real_sx;
8424 REAL_SY = new_real_sy;
8425 FULL_SXSIZE = new_full_sxsize;
8426 FULL_SYSIZE = new_full_sysize;
8427 TILESIZE_VAR = new_tilesize_var;
8429 init_gfx_buffers = TRUE;
8430 init_gadgets_and_toons = TRUE;
8432 // printf("::: viewports: init_gfx_buffers\n");
8433 // printf("::: viewports: init_gadgets_and_toons\n");
8436 if (init_gfx_buffers)
8438 // printf("::: init_gfx_buffers\n");
8440 SCR_FIELDX = new_scr_fieldx_buffers;
8441 SCR_FIELDY = new_scr_fieldy_buffers;
8445 SCR_FIELDX = new_scr_fieldx;
8446 SCR_FIELDY = new_scr_fieldy;
8448 SetDrawDeactivationMask(REDRAW_NONE);
8449 SetDrawBackgroundMask(REDRAW_FIELD);
8452 if (init_video_buffer)
8454 // printf("::: init_video_buffer\n");
8456 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8457 InitImageTextures();
8460 if (init_gadgets_and_toons)
8462 // printf("::: init_gadgets_and_toons\n");
8466 InitGlobalAnimations();
8469 if (init_em_graphics)
8471 InitGraphicInfo_EM();