1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_DOOR_1_GFX_PART_1,
74 IMG_DOOR_1_GFX_PART_2,
79 IMG_DOOR_1_GFX_PART_3,
84 IMG_DOOR_1_GFX_PART_4,
89 IMG_DOOR_1_GFX_PART_5,
94 IMG_DOOR_1_GFX_PART_6,
99 IMG_DOOR_1_GFX_PART_7,
104 IMG_DOOR_1_GFX_PART_8,
110 IMG_DOOR_2_GFX_PART_1,
115 IMG_DOOR_2_GFX_PART_2,
120 IMG_DOOR_2_GFX_PART_3,
125 IMG_DOOR_2_GFX_PART_4,
130 IMG_DOOR_2_GFX_PART_5,
135 IMG_DOOR_2_GFX_PART_6,
140 IMG_DOOR_2_GFX_PART_7,
145 IMG_DOOR_2_GFX_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static unsigned int sync_frame_delay = 0;
178 static unsigned int sync_frame_delay_value = GAME_FRAME_DELAY;
180 static char *print_if_not_empty(int element)
182 static char *s = NULL;
183 char *token_name = element_info[element].token_name;
188 s = checked_malloc(strlen(token_name) + 10 + 1);
190 if (element != EL_EMPTY)
191 sprintf(s, "%d\t['%s']", element, token_name);
193 sprintf(s, "%d", element);
198 void DumpTile(int x, int y)
203 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
209 printf_line("-", 79);
210 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
211 printf_line("-", 79);
213 if (!IN_LEV_FIELD(x, y))
215 printf("(not in level field)\n");
221 printf(" Feld: %d\t['%s']\n", Feld[x][y],
222 element_info[Feld[x][y]].token_name);
223 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
224 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
225 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
226 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
227 printf(" MovPos: %d\n", MovPos[x][y]);
228 printf(" MovDir: %d\n", MovDir[x][y]);
229 printf(" MovDelay: %d\n", MovDelay[x][y]);
230 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
231 printf(" CustomValue: %d\n", CustomValue[x][y]);
232 printf(" GfxElement: %d\n", GfxElement[x][y]);
233 printf(" GfxAction: %d\n", GfxAction[x][y]);
234 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
238 void SetDrawtoField(int mode)
240 if (mode == DRAW_FIELDBUFFER)
246 BX2 = SCR_FIELDX + 1;
247 BY2 = SCR_FIELDY + 1;
249 drawto_field = fieldbuffer;
251 else /* DRAW_BACKBUFFER */
257 BX2 = SCR_FIELDX - 1;
258 BY2 = SCR_FIELDY - 1;
260 drawto_field = backbuffer;
264 static void RedrawPlayfield_RND()
266 if (game.envelope_active)
269 DrawLevel(REDRAW_ALL);
273 void RedrawPlayfield()
275 if (game_status != GAME_MODE_PLAYING)
278 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
279 RedrawPlayfield_EM(TRUE);
280 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
281 RedrawPlayfield_SP(TRUE);
282 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
283 RedrawPlayfield_RND();
285 BlitScreenToBitmap(backbuffer);
287 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
291 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
292 boolean blit_to_screen)
294 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
296 if (x == -1 && y == -1)
300 BlitToScreenMasked(bitmap, x, y, width, height, x, y);
302 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
305 static void DrawMaskedBorderExt_FIELD(boolean blit_to_screen)
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(boolean blit_to_screen)
316 // only draw border over closed doors when drawing to backbuffer
317 if (!blit_to_screen && (GetDoorState() & DOOR_OPEN_1))
320 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
321 (global.border_status != GAME_MODE_EDITOR ||
322 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
323 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, blit_to_screen);
326 static void DrawMaskedBorderExt_DOOR_2(boolean blit_to_screen)
328 // only draw border over closed doors when drawing to backbuffer
329 if (!blit_to_screen && (GetDoorState() & DOOR_OPEN_2))
332 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
333 global.border_status != GAME_MODE_EDITOR)
334 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, blit_to_screen);
337 static void DrawMaskedBorderExt_DOOR_3(boolean blit_to_screen)
339 /* currently not available */
342 static void DrawMaskedBorderExt_ALL(boolean blit_to_screen)
344 DrawMaskedBorderExt_FIELD(blit_to_screen);
345 DrawMaskedBorderExt_DOOR_1(blit_to_screen);
346 DrawMaskedBorderExt_DOOR_2(blit_to_screen);
347 DrawMaskedBorderExt_DOOR_3(blit_to_screen);
350 static void DrawMaskedBorderExt(int redraw_mask, boolean blit_to_screen)
352 /* never draw masked screen borders on borderless screens */
353 if (game_status == GAME_MODE_LOADING ||
354 game_status == GAME_MODE_TITLE)
357 if (redraw_mask & REDRAW_ALL)
358 DrawMaskedBorderExt_ALL(blit_to_screen);
361 if (redraw_mask & REDRAW_FIELD)
362 DrawMaskedBorderExt_FIELD(blit_to_screen);
363 if (redraw_mask & REDRAW_DOOR_1)
364 DrawMaskedBorderExt_DOOR_1(blit_to_screen);
365 if (redraw_mask & REDRAW_DOOR_2)
366 DrawMaskedBorderExt_DOOR_2(blit_to_screen);
367 if (redraw_mask & REDRAW_DOOR_3)
368 DrawMaskedBorderExt_DOOR_3(blit_to_screen);
372 void DrawMaskedBorder_FIELD()
374 DrawMaskedBorderExt_FIELD(FALSE);
377 void DrawMaskedBorder(int redraw_mask)
379 DrawMaskedBorderExt(redraw_mask, FALSE);
382 void DrawMaskedBorderToScreen(int redraw_mask)
384 DrawMaskedBorderExt(redraw_mask, TRUE);
387 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
389 int fx = FX, fy = FY;
390 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
391 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
393 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
394 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
395 int dx_var = dx * TILESIZE_VAR / TILESIZE;
396 int dy_var = dy * TILESIZE_VAR / TILESIZE;
399 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
400 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
402 if (EVEN(SCR_FIELDX))
404 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
405 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
407 fx += (dx_var > 0 ? TILEX_VAR : 0);
414 if (EVEN(SCR_FIELDY))
416 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
417 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
419 fy += (dy_var > 0 ? TILEY_VAR : 0);
426 if (full_lev_fieldx <= SCR_FIELDX)
428 if (EVEN(SCR_FIELDX))
429 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
431 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
434 if (full_lev_fieldy <= SCR_FIELDY)
436 if (EVEN(SCR_FIELDY))
437 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
439 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
442 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
445 void BlitScreenToBitmap(Bitmap *target_bitmap)
447 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
448 BlitScreenToBitmap_EM(target_bitmap);
449 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
450 BlitScreenToBitmap_SP(target_bitmap);
451 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
452 BlitScreenToBitmap_RND(target_bitmap);
454 redraw_mask |= REDRAW_FIELD;
457 void DrawFramesPerSecond()
460 int font_nr = FONT_TEXT_2;
461 int font_width = getFontWidth(font_nr);
463 sprintf(text, "%04.1f fps", global.frames_per_second);
465 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
466 font_nr, BLIT_OPAQUE);
470 static void PrintFrameTimeDebugging()
472 static unsigned int last_counter = 0;
473 unsigned int counter = Counter();
474 int diff_1 = counter - last_counter;
475 int diff_2 = diff_1 - GAME_FRAME_DELAY;
477 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
478 char diff_bar[2 * diff_2_max + 5];
482 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
484 for (i = 0; i < diff_2_max; i++)
485 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
486 i >= diff_2_max - diff_2_cut ? '-' : ' ');
488 diff_bar[pos++] = '|';
490 for (i = 0; i < diff_2_max; i++)
491 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
493 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
495 diff_bar[pos++] = '\0';
497 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
500 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
503 last_counter = counter;
509 static int last_redraw_mask = REDRAW_NONE;
511 // force screen redraw in every frame to continue drawing global animations
512 // (but always use the last redraw mask to prevent unwanted side effects)
513 if (redraw_mask == REDRAW_NONE)
514 redraw_mask = last_redraw_mask;
516 last_redraw_mask = redraw_mask;
519 // masked border now drawn immediately when blitting backbuffer to window
521 // draw masked border to all viewports, if defined
522 DrawMaskedBorder(redraw_mask);
525 // draw frames per second (only if debug mode is enabled)
526 if (redraw_mask & REDRAW_FPS)
527 DrawFramesPerSecond();
529 // redraw complete window if both playfield and (some) doors need redraw
530 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
531 redraw_mask = REDRAW_ALL;
533 /* although redrawing the whole window would be fine for normal gameplay,
534 being able to only redraw the playfield is required for deactivating
535 certain drawing areas (mainly playfield) to work, which is needed for
536 warp-forward to be fast enough (by skipping redraw of most frames) */
538 if (redraw_mask & REDRAW_ALL)
540 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
542 else if (redraw_mask & REDRAW_FIELD)
544 BlitBitmap(backbuffer, window,
545 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
547 else if (redraw_mask & REDRAW_DOORS)
549 if (redraw_mask & REDRAW_DOOR_1)
550 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
552 if (redraw_mask & REDRAW_DOOR_2)
553 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
555 if (redraw_mask & REDRAW_DOOR_3)
556 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
559 redraw_mask = REDRAW_NONE;
562 PrintFrameTimeDebugging();
566 static void FadeCrossSaveBackbuffer()
568 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
571 static void FadeCrossRestoreBackbuffer()
573 int redraw_mask_last = redraw_mask;
575 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
577 // do not change redraw mask when restoring backbuffer after cross-fading
578 redraw_mask = redraw_mask_last;
581 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
583 static int fade_type_skip = FADE_TYPE_NONE;
584 void (*draw_border_function)(void) = NULL;
585 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
586 int x, y, width, height;
587 int fade_delay, post_delay;
589 if (fade_type == FADE_TYPE_FADE_OUT)
591 if (fade_type_skip != FADE_TYPE_NONE)
593 /* skip all fade operations until specified fade operation */
594 if (fade_type & fade_type_skip)
595 fade_type_skip = FADE_TYPE_NONE;
601 FadeCrossSaveBackbuffer();
604 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
607 FadeCrossSaveBackbuffer();
614 redraw_mask |= fade_mask;
616 if (fade_type == FADE_TYPE_SKIP)
618 fade_type_skip = fade_mode;
623 fade_delay = fading.fade_delay;
624 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
626 if (fade_type_skip != FADE_TYPE_NONE)
628 /* skip all fade operations until specified fade operation */
629 if (fade_type & fade_type_skip)
630 fade_type_skip = FADE_TYPE_NONE;
635 if (global.autoplay_leveldir)
640 if (fade_mask == REDRAW_FIELD)
645 height = FADE_SYSIZE;
647 if (border.draw_masked_when_fading)
648 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
650 DrawMaskedBorder_FIELD(); /* draw once */
652 else /* REDRAW_ALL */
660 if (!setup.fade_screens ||
662 fading.fade_mode == FADE_MODE_NONE)
664 if (fade_mode == FADE_MODE_FADE_OUT)
667 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
669 redraw_mask &= ~fade_mask;
674 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
675 draw_border_function);
677 if (fade_type == FADE_TYPE_FADE_OUT)
678 FadeCrossRestoreBackbuffer();
680 redraw_mask &= ~fade_mask;
683 static void SetAnimStatus_BeforeFadingOut()
685 global.anim_status = GAME_MODE_PSEUDO_FADING;
688 static void SetAnimStatus_AfterFadingIn()
690 global.anim_status = global.anim_status_next;
692 // force update of global animation status in case of rapid screen changes
693 redraw_mask = REDRAW_ALL;
697 void FadeIn(int fade_mask)
700 DrawMaskedBorder(REDRAW_ALL);
703 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
704 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
706 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
710 FADE_SXSIZE = FULL_SXSIZE;
711 FADE_SYSIZE = FULL_SYSIZE;
713 SetAnimStatus_AfterFadingIn();
716 void FadeOut(int fade_mask)
718 SetAnimStatus_BeforeFadingOut();
721 DrawMaskedBorder(REDRAW_ALL);
724 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
725 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
727 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
729 global.border_status = game_status;
732 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
734 static struct TitleFadingInfo fading_leave_stored;
737 fading_leave_stored = fading_leave;
739 fading = fading_leave_stored;
742 void FadeSetEnterMenu()
744 fading = menu.enter_menu;
746 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
749 void FadeSetLeaveMenu()
751 fading = menu.leave_menu;
753 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
756 void FadeSetEnterScreen()
758 fading = menu.enter_screen[game_status];
760 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
763 void FadeSetNextScreen()
765 fading = menu.next_screen[game_status];
767 // (do not overwrite fade mode set by FadeSetEnterScreen)
768 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
771 void FadeSetLeaveScreen()
773 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
776 void FadeSetFromType(int type)
778 if (type & TYPE_ENTER_SCREEN)
779 FadeSetEnterScreen();
780 else if (type & TYPE_ENTER)
782 else if (type & TYPE_LEAVE)
786 void FadeSetDisabled()
788 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
790 fading = fading_none;
793 void FadeSkipNextFadeIn()
795 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
798 void FadeSkipNextFadeOut()
800 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
803 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
805 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
807 return (graphic == IMG_UNDEFINED ? NULL :
808 graphic_info[graphic].bitmap != NULL || redefined ?
809 graphic_info[graphic].bitmap :
810 graphic_info[default_graphic].bitmap);
813 Bitmap *getBackgroundBitmap(int graphic)
815 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
818 Bitmap *getGlobalBorderBitmap(int graphic)
820 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
823 Bitmap *getGlobalBorderBitmapFromGameStatus()
826 (game_status == GAME_MODE_MAIN ||
827 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
828 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
829 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
830 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
833 return getGlobalBorderBitmap(graphic);
836 void SetWindowBackgroundImageIfDefined(int graphic)
838 if (graphic_info[graphic].bitmap)
839 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
842 void SetMainBackgroundImageIfDefined(int graphic)
844 if (graphic_info[graphic].bitmap)
845 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
848 void SetDoorBackgroundImageIfDefined(int graphic)
850 if (graphic_info[graphic].bitmap)
851 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
854 void SetWindowBackgroundImage(int graphic)
856 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
859 void SetMainBackgroundImage(int graphic)
861 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
864 void SetDoorBackgroundImage(int graphic)
866 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
869 void SetPanelBackground()
871 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
873 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
874 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
876 SetDoorBackgroundBitmap(bitmap_db_panel);
879 void DrawBackground(int x, int y, int width, int height)
881 /* "drawto" might still point to playfield buffer here (hall of fame) */
882 ClearRectangleOnBackground(backbuffer, x, y, width, height);
884 if (IN_GFX_FIELD_FULL(x, y))
885 redraw_mask |= REDRAW_FIELD;
886 else if (IN_GFX_DOOR_1(x, y))
887 redraw_mask |= REDRAW_DOOR_1;
888 else if (IN_GFX_DOOR_2(x, y))
889 redraw_mask |= REDRAW_DOOR_2;
890 else if (IN_GFX_DOOR_3(x, y))
891 redraw_mask |= REDRAW_DOOR_3;
894 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
896 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
898 if (font->bitmap == NULL)
901 DrawBackground(x, y, width, height);
904 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
906 struct GraphicInfo *g = &graphic_info[graphic];
908 if (g->bitmap == NULL)
911 DrawBackground(x, y, width, height);
914 static int game_status_last = -1;
915 static Bitmap *global_border_bitmap_last = NULL;
916 static Bitmap *global_border_bitmap = NULL;
917 static int real_sx_last = -1, real_sy_last = -1;
918 static int full_sxsize_last = -1, full_sysize_last = -1;
919 static int dx_last = -1, dy_last = -1;
920 static int dxsize_last = -1, dysize_last = -1;
921 static int vx_last = -1, vy_last = -1;
922 static int vxsize_last = -1, vysize_last = -1;
924 boolean CheckIfGlobalBorderHasChanged()
926 // if game status has not changed, global border has not changed either
927 if (game_status == game_status_last)
930 // determine and store new global border bitmap for current game status
931 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
933 return (global_border_bitmap_last != global_border_bitmap);
936 boolean CheckIfGlobalBorderRedrawIsNeeded()
938 // if game status has not changed, nothing has to be redrawn
939 if (game_status == game_status_last)
942 // redraw if last screen was title screen
943 if (game_status_last == GAME_MODE_TITLE)
946 // redraw if global screen border has changed
947 if (CheckIfGlobalBorderHasChanged())
950 // redraw if position or size of playfield area has changed
951 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
952 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
955 // redraw if position or size of door area has changed
956 if (dx_last != DX || dy_last != DY ||
957 dxsize_last != DXSIZE || dysize_last != DYSIZE)
960 // redraw if position or size of tape area has changed
961 if (vx_last != VX || vy_last != VY ||
962 vxsize_last != VXSIZE || vysize_last != VYSIZE)
968 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
971 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
973 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
976 void RedrawGlobalBorder()
978 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
980 RedrawGlobalBorderFromBitmap(bitmap);
982 redraw_mask = REDRAW_ALL;
985 static void RedrawGlobalBorderIfNeeded()
987 if (game_status == game_status_last)
990 // copy current draw buffer to later copy back areas that have not changed
991 if (game_status_last != GAME_MODE_TITLE)
992 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
994 if (CheckIfGlobalBorderRedrawIsNeeded())
996 // redraw global screen border (or clear, if defined to be empty)
997 RedrawGlobalBorderFromBitmap(global_border_bitmap);
999 // copy previous playfield and door areas, if they are defined on both
1000 // previous and current screen and if they still have the same size
1002 if (real_sx_last != -1 && real_sy_last != -1 &&
1003 REAL_SX != -1 && REAL_SY != -1 &&
1004 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1005 BlitBitmap(bitmap_db_store, backbuffer,
1006 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1009 if (dx_last != -1 && dy_last != -1 &&
1010 DX != -1 && DY != -1 &&
1011 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1012 BlitBitmap(bitmap_db_store, backbuffer,
1013 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1015 if (vx_last != -1 && vy_last != -1 &&
1016 VX != -1 && VY != -1 &&
1017 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1018 BlitBitmap(bitmap_db_store, backbuffer,
1019 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1021 redraw_mask = REDRAW_ALL;
1024 game_status_last = game_status;
1026 global_border_bitmap_last = global_border_bitmap;
1028 real_sx_last = REAL_SX;
1029 real_sy_last = REAL_SY;
1030 full_sxsize_last = FULL_SXSIZE;
1031 full_sysize_last = FULL_SYSIZE;
1034 dxsize_last = DXSIZE;
1035 dysize_last = DYSIZE;
1038 vxsize_last = VXSIZE;
1039 vysize_last = VYSIZE;
1044 RedrawGlobalBorderIfNeeded();
1046 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1047 /* (when entering hall of fame after playing) */
1048 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1050 /* !!! maybe this should be done before clearing the background !!! */
1051 if (game_status == GAME_MODE_PLAYING)
1053 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1054 SetDrawtoField(DRAW_FIELDBUFFER);
1058 SetDrawtoField(DRAW_BACKBUFFER);
1062 void MarkTileDirty(int x, int y)
1064 redraw_mask |= REDRAW_FIELD;
1067 void SetBorderElement()
1071 BorderElement = EL_EMPTY;
1073 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1075 for (x = 0; x < lev_fieldx; x++)
1077 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1078 BorderElement = EL_STEELWALL;
1080 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1086 void FloodFillLevel(int from_x, int from_y, int fill_element,
1087 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1088 int max_fieldx, int max_fieldy)
1092 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1093 static int safety = 0;
1095 /* check if starting field still has the desired content */
1096 if (field[from_x][from_y] == fill_element)
1101 if (safety > max_fieldx * max_fieldy)
1102 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1104 old_element = field[from_x][from_y];
1105 field[from_x][from_y] = fill_element;
1107 for (i = 0; i < 4; i++)
1109 x = from_x + check[i][0];
1110 y = from_y + check[i][1];
1112 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1113 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1119 void SetRandomAnimationValue(int x, int y)
1121 gfx.anim_random_frame = GfxRandom[x][y];
1124 int getGraphicAnimationFrame(int graphic, int sync_frame)
1126 /* animation synchronized with global frame counter, not move position */
1127 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1128 sync_frame = FrameCounter;
1130 return getAnimationFrame(graphic_info[graphic].anim_frames,
1131 graphic_info[graphic].anim_delay,
1132 graphic_info[graphic].anim_mode,
1133 graphic_info[graphic].anim_start_frame,
1137 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1138 Bitmap **bitmap, int *x, int *y,
1139 boolean get_backside)
1141 struct GraphicInfo *g = &graphic_info[graphic];
1142 Bitmap *src_bitmap = g->bitmap;
1143 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1144 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1145 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1147 // if no in-game graphics defined, always use standard graphic size
1148 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1149 tilesize = TILESIZE;
1151 if (tilesize == gfx.standard_tile_size)
1152 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1153 else if (tilesize == game.tile_size)
1154 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1156 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1158 if (g->offset_y == 0) /* frames are ordered horizontally */
1160 int max_width = g->anim_frames_per_line * g->width;
1161 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1163 src_x = pos % max_width;
1164 src_y = src_y % g->height + pos / max_width * g->height;
1166 else if (g->offset_x == 0) /* frames are ordered vertically */
1168 int max_height = g->anim_frames_per_line * g->height;
1169 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1171 src_x = src_x % g->width + pos / max_height * g->width;
1172 src_y = pos % max_height;
1174 else /* frames are ordered diagonally */
1176 src_x = src_x + frame * g->offset_x;
1177 src_y = src_y + frame * g->offset_y;
1180 *bitmap = src_bitmap;
1181 *x = src_x * tilesize / g->tile_size;
1182 *y = src_y * tilesize / g->tile_size;
1185 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1186 int *x, int *y, boolean get_backside)
1188 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1192 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1193 Bitmap **bitmap, int *x, int *y)
1195 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1198 void getFixedGraphicSource(int graphic, int frame,
1199 Bitmap **bitmap, int *x, int *y)
1201 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1204 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1206 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1209 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1210 int *x, int *y, boolean get_backside)
1212 struct GraphicInfo *g = &graphic_info[graphic];
1213 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1214 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1216 if (TILESIZE_VAR != TILESIZE)
1217 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1220 *bitmap = g->bitmap;
1222 if (g->offset_y == 0) /* frames are ordered horizontally */
1224 int max_width = g->anim_frames_per_line * g->width;
1225 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1227 *x = pos % max_width;
1228 *y = src_y % g->height + pos / max_width * g->height;
1230 else if (g->offset_x == 0) /* frames are ordered vertically */
1232 int max_height = g->anim_frames_per_line * g->height;
1233 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1235 *x = src_x % g->width + pos / max_height * g->width;
1236 *y = pos % max_height;
1238 else /* frames are ordered diagonally */
1240 *x = src_x + frame * g->offset_x;
1241 *y = src_y + frame * g->offset_y;
1244 *x = *x * TILESIZE_VAR / g->tile_size;
1245 *y = *y * TILESIZE_VAR / g->tile_size;
1248 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1250 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1253 void DrawGraphic(int x, int y, int graphic, int frame)
1256 if (!IN_SCR_FIELD(x, y))
1258 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1259 printf("DrawGraphic(): This should never happen!\n");
1264 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1267 MarkTileDirty(x, y);
1270 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1273 if (!IN_SCR_FIELD(x, y))
1275 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1276 printf("DrawGraphic(): This should never happen!\n");
1281 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1283 MarkTileDirty(x, y);
1286 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1292 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1294 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1297 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1303 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1304 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1307 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1310 if (!IN_SCR_FIELD(x, y))
1312 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1313 printf("DrawGraphicThruMask(): This should never happen!\n");
1318 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1321 MarkTileDirty(x, y);
1324 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1327 if (!IN_SCR_FIELD(x, y))
1329 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1330 printf("DrawGraphicThruMask(): This should never happen!\n");
1335 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1337 MarkTileDirty(x, y);
1340 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1346 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1348 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1352 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1353 int graphic, int frame)
1358 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1360 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1364 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1366 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1368 MarkTileDirty(x / tilesize, y / tilesize);
1371 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1377 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1378 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1381 void DrawMiniGraphic(int x, int y, int graphic)
1383 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1384 MarkTileDirty(x / 2, y / 2);
1387 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1392 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1393 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1396 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1397 int graphic, int frame,
1398 int cut_mode, int mask_mode)
1403 int width = TILEX, height = TILEY;
1406 if (dx || dy) /* shifted graphic */
1408 if (x < BX1) /* object enters playfield from the left */
1415 else if (x > BX2) /* object enters playfield from the right */
1421 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1427 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1429 else if (dx) /* general horizontal movement */
1430 MarkTileDirty(x + SIGN(dx), y);
1432 if (y < BY1) /* object enters playfield from the top */
1434 if (cut_mode == CUT_BELOW) /* object completely above top border */
1442 else if (y > BY2) /* object enters playfield from the bottom */
1448 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1454 else if (dy > 0 && cut_mode == CUT_ABOVE)
1456 if (y == BY2) /* object completely above bottom border */
1462 MarkTileDirty(x, y + 1);
1463 } /* object leaves playfield to the bottom */
1464 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1466 else if (dy) /* general vertical movement */
1467 MarkTileDirty(x, y + SIGN(dy));
1471 if (!IN_SCR_FIELD(x, y))
1473 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1474 printf("DrawGraphicShifted(): This should never happen!\n");
1479 width = width * TILESIZE_VAR / TILESIZE;
1480 height = height * TILESIZE_VAR / TILESIZE;
1481 cx = cx * TILESIZE_VAR / TILESIZE;
1482 cy = cy * TILESIZE_VAR / TILESIZE;
1483 dx = dx * TILESIZE_VAR / TILESIZE;
1484 dy = dy * TILESIZE_VAR / TILESIZE;
1486 if (width > 0 && height > 0)
1488 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1493 dst_x = FX + x * TILEX_VAR + dx;
1494 dst_y = FY + y * TILEY_VAR + dy;
1496 if (mask_mode == USE_MASKING)
1497 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1500 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1503 MarkTileDirty(x, y);
1507 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1508 int graphic, int frame,
1509 int cut_mode, int mask_mode)
1514 int width = TILEX_VAR, height = TILEY_VAR;
1517 int x2 = x + SIGN(dx);
1518 int y2 = y + SIGN(dy);
1520 /* movement with two-tile animations must be sync'ed with movement position,
1521 not with current GfxFrame (which can be higher when using slow movement) */
1522 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1523 int anim_frames = graphic_info[graphic].anim_frames;
1525 /* (we also need anim_delay here for movement animations with less frames) */
1526 int anim_delay = graphic_info[graphic].anim_delay;
1527 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1529 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1530 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1532 /* re-calculate animation frame for two-tile movement animation */
1533 frame = getGraphicAnimationFrame(graphic, sync_frame);
1535 /* check if movement start graphic inside screen area and should be drawn */
1536 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1538 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1540 dst_x = FX + x1 * TILEX_VAR;
1541 dst_y = FY + y1 * TILEY_VAR;
1543 if (mask_mode == USE_MASKING)
1544 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1547 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1550 MarkTileDirty(x1, y1);
1553 /* check if movement end graphic inside screen area and should be drawn */
1554 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1556 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1558 dst_x = FX + x2 * TILEX_VAR;
1559 dst_y = FY + y2 * TILEY_VAR;
1561 if (mask_mode == USE_MASKING)
1562 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1565 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1568 MarkTileDirty(x2, y2);
1572 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1573 int graphic, int frame,
1574 int cut_mode, int mask_mode)
1578 DrawGraphic(x, y, graphic, frame);
1583 if (graphic_info[graphic].double_movement) /* EM style movement images */
1584 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1586 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1589 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1590 int frame, int cut_mode)
1592 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1595 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1596 int cut_mode, int mask_mode)
1598 int lx = LEVELX(x), ly = LEVELY(y);
1602 if (IN_LEV_FIELD(lx, ly))
1604 SetRandomAnimationValue(lx, ly);
1606 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1607 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1609 /* do not use double (EM style) movement graphic when not moving */
1610 if (graphic_info[graphic].double_movement && !dx && !dy)
1612 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1613 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1616 else /* border element */
1618 graphic = el2img(element);
1619 frame = getGraphicAnimationFrame(graphic, -1);
1622 if (element == EL_EXPANDABLE_WALL)
1624 boolean left_stopped = FALSE, right_stopped = FALSE;
1626 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1627 left_stopped = TRUE;
1628 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1629 right_stopped = TRUE;
1631 if (left_stopped && right_stopped)
1633 else if (left_stopped)
1635 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1636 frame = graphic_info[graphic].anim_frames - 1;
1638 else if (right_stopped)
1640 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1641 frame = graphic_info[graphic].anim_frames - 1;
1646 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1647 else if (mask_mode == USE_MASKING)
1648 DrawGraphicThruMask(x, y, graphic, frame);
1650 DrawGraphic(x, y, graphic, frame);
1653 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1654 int cut_mode, int mask_mode)
1656 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1657 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1658 cut_mode, mask_mode);
1661 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1664 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1667 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1670 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1673 void DrawLevelElementThruMask(int x, int y, int element)
1675 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1678 void DrawLevelFieldThruMask(int x, int y)
1680 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1683 /* !!! implementation of quicksand is totally broken !!! */
1684 #define IS_CRUMBLED_TILE(x, y, e) \
1685 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1686 !IS_MOVING(x, y) || \
1687 (e) == EL_QUICKSAND_EMPTYING || \
1688 (e) == EL_QUICKSAND_FAST_EMPTYING))
1690 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1695 int width, height, cx, cy;
1696 int sx = SCREENX(x), sy = SCREENY(y);
1697 int crumbled_border_size = graphic_info[graphic].border_size;
1700 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1702 for (i = 1; i < 4; i++)
1704 int dxx = (i & 1 ? dx : 0);
1705 int dyy = (i & 2 ? dy : 0);
1708 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1711 /* check if neighbour field is of same crumble type */
1712 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1713 graphic_info[graphic].class ==
1714 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1716 /* return if check prevents inner corner */
1717 if (same == (dxx == dx && dyy == dy))
1721 /* if we reach this point, we have an inner corner */
1723 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1725 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1726 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1727 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1728 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1730 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1731 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1734 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1739 int width, height, bx, by, cx, cy;
1740 int sx = SCREENX(x), sy = SCREENY(y);
1741 int crumbled_border_size = graphic_info[graphic].border_size;
1742 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1743 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1746 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1748 /* draw simple, sloppy, non-corner-accurate crumbled border */
1750 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1751 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1752 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1753 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1755 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1756 FX + sx * TILEX_VAR + cx,
1757 FY + sy * TILEY_VAR + cy);
1759 /* (remaining middle border part must be at least as big as corner part) */
1760 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1761 crumbled_border_size >= TILESIZE / 3)
1764 /* correct corners of crumbled border, if needed */
1766 for (i = -1; i <= 1; i += 2)
1768 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1769 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1770 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1773 /* check if neighbour field is of same crumble type */
1774 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1775 graphic_info[graphic].class ==
1776 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1778 /* no crumbled corner, but continued crumbled border */
1780 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1781 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1782 int b1 = (i == 1 ? crumbled_border_size_var :
1783 TILESIZE_VAR - 2 * crumbled_border_size_var);
1785 width = crumbled_border_size_var;
1786 height = crumbled_border_size_var;
1788 if (dir == 1 || dir == 2)
1803 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1805 FX + sx * TILEX_VAR + cx,
1806 FY + sy * TILEY_VAR + cy);
1811 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1813 int sx = SCREENX(x), sy = SCREENY(y);
1816 static int xy[4][2] =
1824 if (!IN_LEV_FIELD(x, y))
1827 element = TILE_GFX_ELEMENT(x, y);
1829 /* crumble field itself */
1830 if (IS_CRUMBLED_TILE(x, y, element))
1832 if (!IN_SCR_FIELD(sx, sy))
1835 for (i = 0; i < 4; i++)
1837 int xx = x + xy[i][0];
1838 int yy = y + xy[i][1];
1840 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1843 /* check if neighbour field is of same crumble type */
1844 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1845 graphic_info[graphic].class ==
1846 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1849 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1852 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1853 graphic_info[graphic].anim_frames == 2)
1855 for (i = 0; i < 4; i++)
1857 int dx = (i & 1 ? +1 : -1);
1858 int dy = (i & 2 ? +1 : -1);
1860 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1864 MarkTileDirty(sx, sy);
1866 else /* center field not crumbled -- crumble neighbour fields */
1868 for (i = 0; i < 4; i++)
1870 int xx = x + xy[i][0];
1871 int yy = y + xy[i][1];
1872 int sxx = sx + xy[i][0];
1873 int syy = sy + xy[i][1];
1875 if (!IN_LEV_FIELD(xx, yy) ||
1876 !IN_SCR_FIELD(sxx, syy))
1879 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1882 element = TILE_GFX_ELEMENT(xx, yy);
1884 if (!IS_CRUMBLED_TILE(xx, yy, element))
1887 graphic = el_act2crm(element, ACTION_DEFAULT);
1889 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1891 MarkTileDirty(sxx, syy);
1896 void DrawLevelFieldCrumbled(int x, int y)
1900 if (!IN_LEV_FIELD(x, y))
1903 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1904 GfxElement[x][y] != EL_UNDEFINED &&
1905 GFX_CRUMBLED(GfxElement[x][y]))
1907 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1912 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1914 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1917 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1920 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1921 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1922 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1923 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1924 int sx = SCREENX(x), sy = SCREENY(y);
1926 DrawGraphic(sx, sy, graphic1, frame1);
1927 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1930 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1932 int sx = SCREENX(x), sy = SCREENY(y);
1933 static int xy[4][2] =
1942 for (i = 0; i < 4; i++)
1944 int xx = x + xy[i][0];
1945 int yy = y + xy[i][1];
1946 int sxx = sx + xy[i][0];
1947 int syy = sy + xy[i][1];
1949 if (!IN_LEV_FIELD(xx, yy) ||
1950 !IN_SCR_FIELD(sxx, syy) ||
1951 !GFX_CRUMBLED(Feld[xx][yy]) ||
1955 DrawLevelField(xx, yy);
1959 static int getBorderElement(int x, int y)
1963 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1964 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1965 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1966 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1967 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1968 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1969 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1971 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1972 int steel_position = (x == -1 && y == -1 ? 0 :
1973 x == lev_fieldx && y == -1 ? 1 :
1974 x == -1 && y == lev_fieldy ? 2 :
1975 x == lev_fieldx && y == lev_fieldy ? 3 :
1976 x == -1 || x == lev_fieldx ? 4 :
1977 y == -1 || y == lev_fieldy ? 5 : 6);
1979 return border[steel_position][steel_type];
1982 void DrawScreenElement(int x, int y, int element)
1984 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1985 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1988 void DrawLevelElement(int x, int y, int element)
1990 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1991 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1994 void DrawScreenField(int x, int y)
1996 int lx = LEVELX(x), ly = LEVELY(y);
1997 int element, content;
1999 if (!IN_LEV_FIELD(lx, ly))
2001 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2004 element = getBorderElement(lx, ly);
2006 DrawScreenElement(x, y, element);
2011 element = Feld[lx][ly];
2012 content = Store[lx][ly];
2014 if (IS_MOVING(lx, ly))
2016 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2017 boolean cut_mode = NO_CUTTING;
2019 if (element == EL_QUICKSAND_EMPTYING ||
2020 element == EL_QUICKSAND_FAST_EMPTYING ||
2021 element == EL_MAGIC_WALL_EMPTYING ||
2022 element == EL_BD_MAGIC_WALL_EMPTYING ||
2023 element == EL_DC_MAGIC_WALL_EMPTYING ||
2024 element == EL_AMOEBA_DROPPING)
2025 cut_mode = CUT_ABOVE;
2026 else if (element == EL_QUICKSAND_FILLING ||
2027 element == EL_QUICKSAND_FAST_FILLING ||
2028 element == EL_MAGIC_WALL_FILLING ||
2029 element == EL_BD_MAGIC_WALL_FILLING ||
2030 element == EL_DC_MAGIC_WALL_FILLING)
2031 cut_mode = CUT_BELOW;
2033 if (cut_mode == CUT_ABOVE)
2034 DrawScreenElement(x, y, element);
2036 DrawScreenElement(x, y, EL_EMPTY);
2039 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2040 else if (cut_mode == NO_CUTTING)
2041 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2044 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2046 if (cut_mode == CUT_BELOW &&
2047 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2048 DrawLevelElement(lx, ly + 1, element);
2051 if (content == EL_ACID)
2053 int dir = MovDir[lx][ly];
2054 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2055 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2057 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2060 else if (IS_BLOCKED(lx, ly))
2065 boolean cut_mode = NO_CUTTING;
2066 int element_old, content_old;
2068 Blocked2Moving(lx, ly, &oldx, &oldy);
2071 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2072 MovDir[oldx][oldy] == MV_RIGHT);
2074 element_old = Feld[oldx][oldy];
2075 content_old = Store[oldx][oldy];
2077 if (element_old == EL_QUICKSAND_EMPTYING ||
2078 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2079 element_old == EL_MAGIC_WALL_EMPTYING ||
2080 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2081 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2082 element_old == EL_AMOEBA_DROPPING)
2083 cut_mode = CUT_ABOVE;
2085 DrawScreenElement(x, y, EL_EMPTY);
2088 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2090 else if (cut_mode == NO_CUTTING)
2091 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2094 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2097 else if (IS_DRAWABLE(element))
2098 DrawScreenElement(x, y, element);
2100 DrawScreenElement(x, y, EL_EMPTY);
2103 void DrawLevelField(int x, int y)
2105 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2106 DrawScreenField(SCREENX(x), SCREENY(y));
2107 else if (IS_MOVING(x, y))
2111 Moving2Blocked(x, y, &newx, &newy);
2112 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2113 DrawScreenField(SCREENX(newx), SCREENY(newy));
2115 else if (IS_BLOCKED(x, y))
2119 Blocked2Moving(x, y, &oldx, &oldy);
2120 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2121 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2125 void DrawSizedElement(int x, int y, int element, int tilesize)
2129 graphic = el2edimg(element);
2130 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2133 void DrawMiniElement(int x, int y, int element)
2137 graphic = el2edimg(element);
2138 DrawMiniGraphic(x, y, graphic);
2141 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2144 int x = sx + scroll_x, y = sy + scroll_y;
2146 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2147 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2148 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2149 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2151 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2154 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2156 int x = sx + scroll_x, y = sy + scroll_y;
2158 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2159 DrawMiniElement(sx, sy, EL_EMPTY);
2160 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2161 DrawMiniElement(sx, sy, Feld[x][y]);
2163 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2166 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2167 int x, int y, int xsize, int ysize,
2168 int tile_width, int tile_height)
2172 int dst_x = startx + x * tile_width;
2173 int dst_y = starty + y * tile_height;
2174 int width = graphic_info[graphic].width;
2175 int height = graphic_info[graphic].height;
2176 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2177 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2178 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2179 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2180 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2181 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2182 boolean draw_masked = graphic_info[graphic].draw_masked;
2184 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2186 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2188 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2192 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2193 inner_sx + (x - 1) * tile_width % inner_width);
2194 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2195 inner_sy + (y - 1) * tile_height % inner_height);
2198 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2201 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2205 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2206 int x, int y, int xsize, int ysize, int font_nr)
2208 int font_width = getFontWidth(font_nr);
2209 int font_height = getFontHeight(font_nr);
2211 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2212 font_width, font_height);
2215 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2217 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2218 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2219 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2220 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2221 boolean no_delay = (tape.warp_forward);
2222 unsigned int anim_delay = 0;
2223 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2224 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2225 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2226 int font_width = getFontWidth(font_nr);
2227 int font_height = getFontHeight(font_nr);
2228 int max_xsize = level.envelope[envelope_nr].xsize;
2229 int max_ysize = level.envelope[envelope_nr].ysize;
2230 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2231 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2232 int xend = max_xsize;
2233 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2234 int xstep = (xstart < xend ? 1 : 0);
2235 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2237 int end = MAX(xend - xstart, yend - ystart);
2240 for (i = start; i <= end; i++)
2242 int last_frame = end; // last frame of this "for" loop
2243 int x = xstart + i * xstep;
2244 int y = ystart + i * ystep;
2245 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2246 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2247 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2248 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2251 SetDrawtoField(DRAW_FIELDBUFFER);
2253 BlitScreenToBitmap(backbuffer);
2255 SetDrawtoField(DRAW_BACKBUFFER);
2257 for (yy = 0; yy < ysize; yy++)
2258 for (xx = 0; xx < xsize; xx++)
2259 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2261 DrawTextBuffer(sx + font_width, sy + font_height,
2262 level.envelope[envelope_nr].text, font_nr, max_xsize,
2263 xsize - 2, ysize - 2, 0, mask_mode,
2264 level.envelope[envelope_nr].autowrap,
2265 level.envelope[envelope_nr].centered, FALSE);
2267 redraw_mask |= REDRAW_FIELD;
2270 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2274 void ShowEnvelope(int envelope_nr)
2276 int element = EL_ENVELOPE_1 + envelope_nr;
2277 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2278 int sound_opening = element_info[element].sound[ACTION_OPENING];
2279 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2280 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2281 boolean no_delay = (tape.warp_forward);
2282 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2283 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2284 int anim_mode = graphic_info[graphic].anim_mode;
2285 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2286 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2288 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2290 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2292 if (anim_mode == ANIM_DEFAULT)
2293 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2295 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2298 Delay(wait_delay_value);
2300 WaitForEventToContinue();
2302 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2304 if (anim_mode != ANIM_NONE)
2305 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2307 if (anim_mode == ANIM_DEFAULT)
2308 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2310 game.envelope_active = FALSE;
2312 SetDrawtoField(DRAW_FIELDBUFFER);
2314 redraw_mask |= REDRAW_FIELD;
2318 static void setRequestBasePosition(int *x, int *y)
2320 int sx_base, sy_base;
2322 if (request.x != -1)
2323 sx_base = request.x;
2324 else if (request.align == ALIGN_LEFT)
2326 else if (request.align == ALIGN_RIGHT)
2327 sx_base = SX + SXSIZE;
2329 sx_base = SX + SXSIZE / 2;
2331 if (request.y != -1)
2332 sy_base = request.y;
2333 else if (request.valign == VALIGN_TOP)
2335 else if (request.valign == VALIGN_BOTTOM)
2336 sy_base = SY + SYSIZE;
2338 sy_base = SY + SYSIZE / 2;
2344 static void setRequestPositionExt(int *x, int *y, int width, int height,
2345 boolean add_border_size)
2347 int border_size = request.border_size;
2348 int sx_base, sy_base;
2351 setRequestBasePosition(&sx_base, &sy_base);
2353 if (request.align == ALIGN_LEFT)
2355 else if (request.align == ALIGN_RIGHT)
2356 sx = sx_base - width;
2358 sx = sx_base - width / 2;
2360 if (request.valign == VALIGN_TOP)
2362 else if (request.valign == VALIGN_BOTTOM)
2363 sy = sy_base - height;
2365 sy = sy_base - height / 2;
2367 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2368 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2370 if (add_border_size)
2380 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2382 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2385 void DrawEnvelopeRequest(char *text)
2387 int last_game_status = game_status; /* save current game status */
2388 char *text_final = text;
2389 char *text_door_style = NULL;
2390 int graphic = IMG_BACKGROUND_REQUEST;
2391 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2392 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2393 int font_nr = FONT_REQUEST;
2394 int font_width = getFontWidth(font_nr);
2395 int font_height = getFontHeight(font_nr);
2396 int border_size = request.border_size;
2397 int line_spacing = request.line_spacing;
2398 int line_height = font_height + line_spacing;
2399 int max_text_width = request.width - 2 * border_size;
2400 int max_text_height = request.height - 2 * border_size;
2401 int line_length = max_text_width / font_width;
2402 int max_lines = max_text_height / line_height;
2403 int text_width = line_length * font_width;
2404 int width = request.width;
2405 int height = request.height;
2406 int tile_size = MAX(request.step_offset, 1);
2407 int x_steps = width / tile_size;
2408 int y_steps = height / tile_size;
2409 int sx_offset = border_size;
2410 int sy_offset = border_size;
2414 if (request.centered)
2415 sx_offset = (request.width - text_width) / 2;
2417 if (request.wrap_single_words && !request.autowrap)
2419 char *src_text_ptr, *dst_text_ptr;
2421 text_door_style = checked_malloc(2 * strlen(text) + 1);
2423 src_text_ptr = text;
2424 dst_text_ptr = text_door_style;
2426 while (*src_text_ptr)
2428 if (*src_text_ptr == ' ' ||
2429 *src_text_ptr == '?' ||
2430 *src_text_ptr == '!')
2431 *dst_text_ptr++ = '\n';
2433 if (*src_text_ptr != ' ')
2434 *dst_text_ptr++ = *src_text_ptr;
2439 *dst_text_ptr = '\0';
2441 text_final = text_door_style;
2444 setRequestPosition(&sx, &sy, FALSE);
2446 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2448 for (y = 0; y < y_steps; y++)
2449 for (x = 0; x < x_steps; x++)
2450 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2451 x, y, x_steps, y_steps,
2452 tile_size, tile_size);
2454 /* force DOOR font inside door area */
2455 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
2457 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2458 line_length, -1, max_lines, line_spacing, mask_mode,
2459 request.autowrap, request.centered, FALSE);
2461 SetGameStatus(last_game_status); /* restore current game status */
2463 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2464 RedrawGadget(tool_gadget[i]);
2466 // store readily prepared envelope request for later use when animating
2467 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2469 if (text_door_style)
2470 free(text_door_style);
2473 void AnimateEnvelopeRequest(int anim_mode, int action)
2475 int graphic = IMG_BACKGROUND_REQUEST;
2476 boolean draw_masked = graphic_info[graphic].draw_masked;
2477 int delay_value_normal = request.step_delay;
2478 int delay_value_fast = delay_value_normal / 2;
2479 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2480 boolean no_delay = (tape.warp_forward);
2481 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2482 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2483 unsigned int anim_delay = 0;
2485 int tile_size = MAX(request.step_offset, 1);
2486 int max_xsize = request.width / tile_size;
2487 int max_ysize = request.height / tile_size;
2488 int max_xsize_inner = max_xsize - 2;
2489 int max_ysize_inner = max_ysize - 2;
2491 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2492 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2493 int xend = max_xsize_inner;
2494 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2495 int xstep = (xstart < xend ? 1 : 0);
2496 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2498 int end = MAX(xend - xstart, yend - ystart);
2501 if (setup.quick_doors)
2508 for (i = start; i <= end; i++)
2510 int last_frame = end; // last frame of this "for" loop
2511 int x = xstart + i * xstep;
2512 int y = ystart + i * ystep;
2513 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2514 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2515 int xsize_size_left = (xsize - 1) * tile_size;
2516 int ysize_size_top = (ysize - 1) * tile_size;
2517 int max_xsize_pos = (max_xsize - 1) * tile_size;
2518 int max_ysize_pos = (max_ysize - 1) * tile_size;
2519 int width = xsize * tile_size;
2520 int height = ysize * tile_size;
2525 setRequestPosition(&src_x, &src_y, FALSE);
2526 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2528 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2530 for (yy = 0; yy < 2; yy++)
2532 for (xx = 0; xx < 2; xx++)
2534 int src_xx = src_x + xx * max_xsize_pos;
2535 int src_yy = src_y + yy * max_ysize_pos;
2536 int dst_xx = dst_x + xx * xsize_size_left;
2537 int dst_yy = dst_y + yy * ysize_size_top;
2538 int xx_size = (xx ? tile_size : xsize_size_left);
2539 int yy_size = (yy ? tile_size : ysize_size_top);
2542 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2543 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2545 BlitBitmap(bitmap_db_cross, backbuffer,
2546 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2550 redraw_mask |= REDRAW_FIELD;
2555 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2559 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2561 int graphic = IMG_BACKGROUND_REQUEST;
2562 int sound_opening = SND_REQUEST_OPENING;
2563 int sound_closing = SND_REQUEST_CLOSING;
2564 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2565 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2566 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2567 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2568 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2570 if (game_status == GAME_MODE_PLAYING)
2571 BlitScreenToBitmap(backbuffer);
2573 SetDrawtoField(DRAW_BACKBUFFER);
2575 // SetDrawBackgroundMask(REDRAW_NONE);
2577 if (action == ACTION_OPENING)
2579 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2581 if (req_state & REQ_ASK)
2583 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2584 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2586 else if (req_state & REQ_CONFIRM)
2588 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2590 else if (req_state & REQ_PLAYER)
2592 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2593 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2594 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2595 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2598 DrawEnvelopeRequest(text);
2600 if (game_status != GAME_MODE_MAIN)
2604 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2606 if (action == ACTION_OPENING)
2608 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2610 if (anim_mode == ANIM_DEFAULT)
2611 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2613 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2617 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2619 if (anim_mode != ANIM_NONE)
2620 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2622 if (anim_mode == ANIM_DEFAULT)
2623 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2626 game.envelope_active = FALSE;
2628 if (action == ACTION_CLOSING)
2630 if (game_status != GAME_MODE_MAIN)
2633 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2636 // SetDrawBackgroundMask(last_draw_background_mask);
2638 redraw_mask |= REDRAW_FIELD;
2640 if (game_status == GAME_MODE_MAIN)
2645 if (action == ACTION_CLOSING &&
2646 game_status == GAME_MODE_PLAYING &&
2647 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2648 SetDrawtoField(DRAW_FIELDBUFFER);
2651 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2655 int graphic = el2preimg(element);
2657 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2658 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2661 void DrawLevel(int draw_background_mask)
2665 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2666 SetDrawBackgroundMask(draw_background_mask);
2670 for (x = BX1; x <= BX2; x++)
2671 for (y = BY1; y <= BY2; y++)
2672 DrawScreenField(x, y);
2674 redraw_mask |= REDRAW_FIELD;
2677 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2682 for (x = 0; x < size_x; x++)
2683 for (y = 0; y < size_y; y++)
2684 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2686 redraw_mask |= REDRAW_FIELD;
2689 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2693 for (x = 0; x < size_x; x++)
2694 for (y = 0; y < size_y; y++)
2695 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2697 redraw_mask |= REDRAW_FIELD;
2700 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2702 boolean show_level_border = (BorderElement != EL_EMPTY);
2703 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2704 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2705 int tile_size = preview.tile_size;
2706 int preview_width = preview.xsize * tile_size;
2707 int preview_height = preview.ysize * tile_size;
2708 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2709 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2710 int real_preview_width = real_preview_xsize * tile_size;
2711 int real_preview_height = real_preview_ysize * tile_size;
2712 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2713 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2716 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2719 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2721 dst_x += (preview_width - real_preview_width) / 2;
2722 dst_y += (preview_height - real_preview_height) / 2;
2724 for (x = 0; x < real_preview_xsize; x++)
2726 for (y = 0; y < real_preview_ysize; y++)
2728 int lx = from_x + x + (show_level_border ? -1 : 0);
2729 int ly = from_y + y + (show_level_border ? -1 : 0);
2730 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2731 getBorderElement(lx, ly));
2733 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2734 element, tile_size);
2738 redraw_mask |= REDRAW_FIELD;
2741 #define MICROLABEL_EMPTY 0
2742 #define MICROLABEL_LEVEL_NAME 1
2743 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2744 #define MICROLABEL_LEVEL_AUTHOR 3
2745 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2746 #define MICROLABEL_IMPORTED_FROM 5
2747 #define MICROLABEL_IMPORTED_BY_HEAD 6
2748 #define MICROLABEL_IMPORTED_BY 7
2750 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2752 int max_text_width = SXSIZE;
2753 int font_width = getFontWidth(font_nr);
2755 if (pos->align == ALIGN_CENTER)
2756 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2757 else if (pos->align == ALIGN_RIGHT)
2758 max_text_width = pos->x;
2760 max_text_width = SXSIZE - pos->x;
2762 return max_text_width / font_width;
2765 static void DrawPreviewLevelLabelExt(int mode)
2767 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2768 char label_text[MAX_OUTPUT_LINESIZE + 1];
2769 int max_len_label_text;
2770 int font_nr = pos->font;
2773 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2776 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2777 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2778 mode == MICROLABEL_IMPORTED_BY_HEAD)
2779 font_nr = pos->font_alt;
2781 max_len_label_text = getMaxTextLength(pos, font_nr);
2783 if (pos->size != -1)
2784 max_len_label_text = pos->size;
2786 for (i = 0; i < max_len_label_text; i++)
2787 label_text[i] = ' ';
2788 label_text[max_len_label_text] = '\0';
2790 if (strlen(label_text) > 0)
2791 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2794 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2795 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2796 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2797 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2798 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2799 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2800 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2801 max_len_label_text);
2802 label_text[max_len_label_text] = '\0';
2804 if (strlen(label_text) > 0)
2805 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2807 redraw_mask |= REDRAW_FIELD;
2810 static void DrawPreviewLevelExt(boolean restart)
2812 static unsigned int scroll_delay = 0;
2813 static unsigned int label_delay = 0;
2814 static int from_x, from_y, scroll_direction;
2815 static int label_state, label_counter;
2816 unsigned int scroll_delay_value = preview.step_delay;
2817 boolean show_level_border = (BorderElement != EL_EMPTY);
2818 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2819 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2820 int last_game_status = game_status; /* save current game status */
2827 if (preview.anim_mode == ANIM_CENTERED)
2829 if (level_xsize > preview.xsize)
2830 from_x = (level_xsize - preview.xsize) / 2;
2831 if (level_ysize > preview.ysize)
2832 from_y = (level_ysize - preview.ysize) / 2;
2835 from_x += preview.xoffset;
2836 from_y += preview.yoffset;
2838 scroll_direction = MV_RIGHT;
2842 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2843 DrawPreviewLevelLabelExt(label_state);
2845 /* initialize delay counters */
2846 DelayReached(&scroll_delay, 0);
2847 DelayReached(&label_delay, 0);
2849 if (leveldir_current->name)
2851 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2852 char label_text[MAX_OUTPUT_LINESIZE + 1];
2853 int font_nr = pos->font;
2854 int max_len_label_text = getMaxTextLength(pos, font_nr);
2856 if (pos->size != -1)
2857 max_len_label_text = pos->size;
2859 strncpy(label_text, leveldir_current->name, max_len_label_text);
2860 label_text[max_len_label_text] = '\0';
2862 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2863 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2866 SetGameStatus(last_game_status); /* restore current game status */
2871 /* scroll preview level, if needed */
2872 if (preview.anim_mode != ANIM_NONE &&
2873 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2874 DelayReached(&scroll_delay, scroll_delay_value))
2876 switch (scroll_direction)
2881 from_x -= preview.step_offset;
2882 from_x = (from_x < 0 ? 0 : from_x);
2885 scroll_direction = MV_UP;
2889 if (from_x < level_xsize - preview.xsize)
2891 from_x += preview.step_offset;
2892 from_x = (from_x > level_xsize - preview.xsize ?
2893 level_xsize - preview.xsize : from_x);
2896 scroll_direction = MV_DOWN;
2902 from_y -= preview.step_offset;
2903 from_y = (from_y < 0 ? 0 : from_y);
2906 scroll_direction = MV_RIGHT;
2910 if (from_y < level_ysize - preview.ysize)
2912 from_y += preview.step_offset;
2913 from_y = (from_y > level_ysize - preview.ysize ?
2914 level_ysize - preview.ysize : from_y);
2917 scroll_direction = MV_LEFT;
2924 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2927 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2928 /* redraw micro level label, if needed */
2929 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2930 !strEqual(level.author, ANONYMOUS_NAME) &&
2931 !strEqual(level.author, leveldir_current->name) &&
2932 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2934 int max_label_counter = 23;
2936 if (leveldir_current->imported_from != NULL &&
2937 strlen(leveldir_current->imported_from) > 0)
2938 max_label_counter += 14;
2939 if (leveldir_current->imported_by != NULL &&
2940 strlen(leveldir_current->imported_by) > 0)
2941 max_label_counter += 14;
2943 label_counter = (label_counter + 1) % max_label_counter;
2944 label_state = (label_counter >= 0 && label_counter <= 7 ?
2945 MICROLABEL_LEVEL_NAME :
2946 label_counter >= 9 && label_counter <= 12 ?
2947 MICROLABEL_LEVEL_AUTHOR_HEAD :
2948 label_counter >= 14 && label_counter <= 21 ?
2949 MICROLABEL_LEVEL_AUTHOR :
2950 label_counter >= 23 && label_counter <= 26 ?
2951 MICROLABEL_IMPORTED_FROM_HEAD :
2952 label_counter >= 28 && label_counter <= 35 ?
2953 MICROLABEL_IMPORTED_FROM :
2954 label_counter >= 37 && label_counter <= 40 ?
2955 MICROLABEL_IMPORTED_BY_HEAD :
2956 label_counter >= 42 && label_counter <= 49 ?
2957 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2959 if (leveldir_current->imported_from == NULL &&
2960 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2961 label_state == MICROLABEL_IMPORTED_FROM))
2962 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2963 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2965 DrawPreviewLevelLabelExt(label_state);
2968 SetGameStatus(last_game_status); /* restore current game status */
2971 void DrawPreviewLevelInitial()
2973 DrawPreviewLevelExt(TRUE);
2976 void DrawPreviewLevelAnimation()
2978 DrawPreviewLevelExt(FALSE);
2981 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2982 int graphic, int sync_frame,
2985 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2987 if (mask_mode == USE_MASKING)
2988 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2990 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2993 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2994 int graphic, int sync_frame, int mask_mode)
2996 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2998 if (mask_mode == USE_MASKING)
2999 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3001 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3004 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3006 int lx = LEVELX(x), ly = LEVELY(y);
3008 if (!IN_SCR_FIELD(x, y))
3011 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3012 graphic, GfxFrame[lx][ly], NO_MASKING);
3014 MarkTileDirty(x, y);
3017 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3019 int lx = LEVELX(x), ly = LEVELY(y);
3021 if (!IN_SCR_FIELD(x, y))
3024 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3025 graphic, GfxFrame[lx][ly], NO_MASKING);
3026 MarkTileDirty(x, y);
3029 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3031 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3034 void DrawLevelElementAnimation(int x, int y, int element)
3036 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3038 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3041 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3043 int sx = SCREENX(x), sy = SCREENY(y);
3045 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3048 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3051 DrawGraphicAnimation(sx, sy, graphic);
3054 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3055 DrawLevelFieldCrumbled(x, y);
3057 if (GFX_CRUMBLED(Feld[x][y]))
3058 DrawLevelFieldCrumbled(x, y);
3062 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3064 int sx = SCREENX(x), sy = SCREENY(y);
3067 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3070 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3072 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3075 DrawGraphicAnimation(sx, sy, graphic);
3077 if (GFX_CRUMBLED(element))
3078 DrawLevelFieldCrumbled(x, y);
3081 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3083 if (player->use_murphy)
3085 /* this works only because currently only one player can be "murphy" ... */
3086 static int last_horizontal_dir = MV_LEFT;
3087 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3089 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3090 last_horizontal_dir = move_dir;
3092 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3094 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3096 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3102 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3105 static boolean equalGraphics(int graphic1, int graphic2)
3107 struct GraphicInfo *g1 = &graphic_info[graphic1];
3108 struct GraphicInfo *g2 = &graphic_info[graphic2];
3110 return (g1->bitmap == g2->bitmap &&
3111 g1->src_x == g2->src_x &&
3112 g1->src_y == g2->src_y &&
3113 g1->anim_frames == g2->anim_frames &&
3114 g1->anim_delay == g2->anim_delay &&
3115 g1->anim_mode == g2->anim_mode);
3118 void DrawAllPlayers()
3122 for (i = 0; i < MAX_PLAYERS; i++)
3123 if (stored_player[i].active)
3124 DrawPlayer(&stored_player[i]);
3127 void DrawPlayerField(int x, int y)
3129 if (!IS_PLAYER(x, y))
3132 DrawPlayer(PLAYERINFO(x, y));
3135 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3137 void DrawPlayer(struct PlayerInfo *player)
3139 int jx = player->jx;
3140 int jy = player->jy;
3141 int move_dir = player->MovDir;
3142 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3143 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3144 int last_jx = (player->is_moving ? jx - dx : jx);
3145 int last_jy = (player->is_moving ? jy - dy : jy);
3146 int next_jx = jx + dx;
3147 int next_jy = jy + dy;
3148 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3149 boolean player_is_opaque = FALSE;
3150 int sx = SCREENX(jx), sy = SCREENY(jy);
3151 int sxx = 0, syy = 0;
3152 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3154 int action = ACTION_DEFAULT;
3155 int last_player_graphic = getPlayerGraphic(player, move_dir);
3156 int last_player_frame = player->Frame;
3159 /* GfxElement[][] is set to the element the player is digging or collecting;
3160 remove also for off-screen player if the player is not moving anymore */
3161 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3162 GfxElement[jx][jy] = EL_UNDEFINED;
3164 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3168 if (!IN_LEV_FIELD(jx, jy))
3170 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3171 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3172 printf("DrawPlayerField(): This should never happen!\n");
3177 if (element == EL_EXPLOSION)
3180 action = (player->is_pushing ? ACTION_PUSHING :
3181 player->is_digging ? ACTION_DIGGING :
3182 player->is_collecting ? ACTION_COLLECTING :
3183 player->is_moving ? ACTION_MOVING :
3184 player->is_snapping ? ACTION_SNAPPING :
3185 player->is_dropping ? ACTION_DROPPING :
3186 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3188 if (player->is_waiting)
3189 move_dir = player->dir_waiting;
3191 InitPlayerGfxAnimation(player, action, move_dir);
3193 /* ----------------------------------------------------------------------- */
3194 /* draw things in the field the player is leaving, if needed */
3195 /* ----------------------------------------------------------------------- */
3197 if (player->is_moving)
3199 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3201 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3203 if (last_element == EL_DYNAMITE_ACTIVE ||
3204 last_element == EL_EM_DYNAMITE_ACTIVE ||
3205 last_element == EL_SP_DISK_RED_ACTIVE)
3206 DrawDynamite(last_jx, last_jy);
3208 DrawLevelFieldThruMask(last_jx, last_jy);
3210 else if (last_element == EL_DYNAMITE_ACTIVE ||
3211 last_element == EL_EM_DYNAMITE_ACTIVE ||
3212 last_element == EL_SP_DISK_RED_ACTIVE)
3213 DrawDynamite(last_jx, last_jy);
3215 /* !!! this is not enough to prevent flickering of players which are
3216 moving next to each others without a free tile between them -- this
3217 can only be solved by drawing all players layer by layer (first the
3218 background, then the foreground etc.) !!! => TODO */
3219 else if (!IS_PLAYER(last_jx, last_jy))
3220 DrawLevelField(last_jx, last_jy);
3223 DrawLevelField(last_jx, last_jy);
3226 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3227 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3230 if (!IN_SCR_FIELD(sx, sy))
3233 /* ----------------------------------------------------------------------- */
3234 /* draw things behind the player, if needed */
3235 /* ----------------------------------------------------------------------- */
3238 DrawLevelElement(jx, jy, Back[jx][jy]);
3239 else if (IS_ACTIVE_BOMB(element))
3240 DrawLevelElement(jx, jy, EL_EMPTY);
3243 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3245 int old_element = GfxElement[jx][jy];
3246 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3247 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3249 if (GFX_CRUMBLED(old_element))
3250 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3252 DrawGraphic(sx, sy, old_graphic, frame);
3254 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3255 player_is_opaque = TRUE;
3259 GfxElement[jx][jy] = EL_UNDEFINED;
3261 /* make sure that pushed elements are drawn with correct frame rate */
3262 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3264 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3265 GfxFrame[jx][jy] = player->StepFrame;
3267 DrawLevelField(jx, jy);
3271 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3272 /* ----------------------------------------------------------------------- */
3273 /* draw player himself */
3274 /* ----------------------------------------------------------------------- */
3276 graphic = getPlayerGraphic(player, move_dir);
3278 /* in the case of changed player action or direction, prevent the current
3279 animation frame from being restarted for identical animations */
3280 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3281 player->Frame = last_player_frame;
3283 frame = getGraphicAnimationFrame(graphic, player->Frame);
3287 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3288 sxx = player->GfxPos;
3290 syy = player->GfxPos;
3293 if (player_is_opaque)
3294 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3296 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3298 if (SHIELD_ON(player))
3300 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3301 IMG_SHIELD_NORMAL_ACTIVE);
3302 int frame = getGraphicAnimationFrame(graphic, -1);
3304 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3308 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3311 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3312 sxx = player->GfxPos;
3314 syy = player->GfxPos;
3318 /* ----------------------------------------------------------------------- */
3319 /* draw things the player is pushing, if needed */
3320 /* ----------------------------------------------------------------------- */
3322 if (player->is_pushing && player->is_moving)
3324 int px = SCREENX(jx), py = SCREENY(jy);
3325 int pxx = (TILEX - ABS(sxx)) * dx;
3326 int pyy = (TILEY - ABS(syy)) * dy;
3327 int gfx_frame = GfxFrame[jx][jy];
3333 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3335 element = Feld[next_jx][next_jy];
3336 gfx_frame = GfxFrame[next_jx][next_jy];
3339 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3341 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3342 frame = getGraphicAnimationFrame(graphic, sync_frame);
3344 /* draw background element under pushed element (like the Sokoban field) */
3345 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3347 /* this allows transparent pushing animation over non-black background */
3350 DrawLevelElement(jx, jy, Back[jx][jy]);
3352 DrawLevelElement(jx, jy, EL_EMPTY);
3354 if (Back[next_jx][next_jy])
3355 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3357 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3359 else if (Back[next_jx][next_jy])
3360 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3363 /* do not draw (EM style) pushing animation when pushing is finished */
3364 /* (two-tile animations usually do not contain start and end frame) */
3365 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3366 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3368 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3370 /* masked drawing is needed for EMC style (double) movement graphics */
3371 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3372 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3376 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3377 /* ----------------------------------------------------------------------- */
3378 /* draw player himself */
3379 /* ----------------------------------------------------------------------- */
3381 graphic = getPlayerGraphic(player, move_dir);
3383 /* in the case of changed player action or direction, prevent the current
3384 animation frame from being restarted for identical animations */
3385 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3386 player->Frame = last_player_frame;
3388 frame = getGraphicAnimationFrame(graphic, player->Frame);
3392 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3393 sxx = player->GfxPos;
3395 syy = player->GfxPos;
3398 if (player_is_opaque)
3399 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3401 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3403 if (SHIELD_ON(player))
3405 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3406 IMG_SHIELD_NORMAL_ACTIVE);
3407 int frame = getGraphicAnimationFrame(graphic, -1);
3409 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3413 /* ----------------------------------------------------------------------- */
3414 /* draw things in front of player (active dynamite or dynabombs) */
3415 /* ----------------------------------------------------------------------- */
3417 if (IS_ACTIVE_BOMB(element))
3419 graphic = el2img(element);
3420 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3422 if (game.emulation == EMU_SUPAPLEX)
3423 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3425 DrawGraphicThruMask(sx, sy, graphic, frame);
3428 if (player_is_moving && last_element == EL_EXPLOSION)
3430 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3431 GfxElement[last_jx][last_jy] : EL_EMPTY);
3432 int graphic = el_act2img(element, ACTION_EXPLODING);
3433 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3434 int phase = ExplodePhase[last_jx][last_jy] - 1;
3435 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3438 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3441 /* ----------------------------------------------------------------------- */
3442 /* draw elements the player is just walking/passing through/under */
3443 /* ----------------------------------------------------------------------- */
3445 if (player_is_moving)
3447 /* handle the field the player is leaving ... */
3448 if (IS_ACCESSIBLE_INSIDE(last_element))
3449 DrawLevelField(last_jx, last_jy);
3450 else if (IS_ACCESSIBLE_UNDER(last_element))
3451 DrawLevelFieldThruMask(last_jx, last_jy);
3454 /* do not redraw accessible elements if the player is just pushing them */
3455 if (!player_is_moving || !player->is_pushing)
3457 /* ... and the field the player is entering */
3458 if (IS_ACCESSIBLE_INSIDE(element))
3459 DrawLevelField(jx, jy);
3460 else if (IS_ACCESSIBLE_UNDER(element))
3461 DrawLevelFieldThruMask(jx, jy);
3464 MarkTileDirty(sx, sy);
3467 /* ------------------------------------------------------------------------- */
3469 void WaitForEventToContinue()
3471 boolean still_wait = TRUE;
3473 /* simulate releasing mouse button over last gadget, if still pressed */
3475 HandleGadgets(-1, -1, 0);
3477 button_status = MB_RELEASED;
3491 case EVENT_BUTTONPRESS:
3492 case EVENT_KEYPRESS:
3496 case EVENT_KEYRELEASE:
3497 ClearPlayerAction();
3501 HandleOtherEvents(&event);
3505 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3512 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3516 #define MAX_REQUEST_LINES 13
3517 #define MAX_REQUEST_LINE_FONT1_LEN 7
3518 #define MAX_REQUEST_LINE_FONT2_LEN 10
3520 static int RequestHandleEvents(unsigned int req_state)
3522 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3523 local_player->LevelSolved_GameEnd);
3524 int width = request.width;
3525 int height = request.height;
3529 setRequestPosition(&sx, &sy, FALSE);
3531 button_status = MB_RELEASED;
3533 request_gadget_id = -1;
3540 SetDrawtoField(DRAW_FIELDBUFFER);
3542 HandleGameActions();
3544 SetDrawtoField(DRAW_BACKBUFFER);
3546 if (global.use_envelope_request)
3548 /* copy current state of request area to middle of playfield area */
3549 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3557 while (NextValidEvent(&event))
3561 case EVENT_BUTTONPRESS:
3562 case EVENT_BUTTONRELEASE:
3563 case EVENT_MOTIONNOTIFY:
3567 if (event.type == EVENT_MOTIONNOTIFY)
3572 motion_status = TRUE;
3573 mx = ((MotionEvent *) &event)->x;
3574 my = ((MotionEvent *) &event)->y;
3578 motion_status = FALSE;
3579 mx = ((ButtonEvent *) &event)->x;
3580 my = ((ButtonEvent *) &event)->y;
3581 if (event.type == EVENT_BUTTONPRESS)
3582 button_status = ((ButtonEvent *) &event)->button;
3584 button_status = MB_RELEASED;
3587 /* this sets 'request_gadget_id' */
3588 HandleGadgets(mx, my, button_status);
3590 switch (request_gadget_id)
3592 case TOOL_CTRL_ID_YES:
3595 case TOOL_CTRL_ID_NO:
3598 case TOOL_CTRL_ID_CONFIRM:
3599 result = TRUE | FALSE;
3602 case TOOL_CTRL_ID_PLAYER_1:
3605 case TOOL_CTRL_ID_PLAYER_2:
3608 case TOOL_CTRL_ID_PLAYER_3:
3611 case TOOL_CTRL_ID_PLAYER_4:
3622 case EVENT_KEYPRESS:
3623 switch (GetEventKey((KeyEvent *)&event, TRUE))
3626 if (req_state & REQ_CONFIRM)
3631 #if defined(TARGET_SDL2)
3638 #if defined(TARGET_SDL2)
3648 if (req_state & REQ_PLAYER)
3652 case EVENT_KEYRELEASE:
3653 ClearPlayerAction();
3657 HandleOtherEvents(&event);
3662 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3664 int joy = AnyJoystick();
3666 if (joy & JOY_BUTTON_1)
3668 else if (joy & JOY_BUTTON_2)
3674 if (global.use_envelope_request)
3676 /* copy back current state of pressed buttons inside request area */
3677 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3687 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3693 static boolean RequestDoor(char *text, unsigned int req_state)
3695 unsigned int old_door_state;
3696 int last_game_status = game_status; /* save current game status */
3697 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3698 int font_nr = FONT_TEXT_2;
3703 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3705 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3706 font_nr = FONT_TEXT_1;
3709 if (game_status == GAME_MODE_PLAYING)
3710 BlitScreenToBitmap(backbuffer);
3712 /* disable deactivated drawing when quick-loading level tape recording */
3713 if (tape.playing && tape.deactivate_display)
3714 TapeDeactivateDisplayOff(TRUE);
3716 SetMouseCursor(CURSOR_DEFAULT);
3718 #if defined(NETWORK_AVALIABLE)
3719 /* pause network game while waiting for request to answer */
3720 if (options.network &&
3721 game_status == GAME_MODE_PLAYING &&
3722 req_state & REQUEST_WAIT_FOR_INPUT)
3723 SendToServer_PausePlaying();
3726 old_door_state = GetDoorState();
3728 /* simulate releasing mouse button over last gadget, if still pressed */
3730 HandleGadgets(-1, -1, 0);
3734 /* draw released gadget before proceeding */
3737 if (old_door_state & DOOR_OPEN_1)
3739 CloseDoor(DOOR_CLOSE_1);
3741 /* save old door content */
3742 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3743 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3746 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3747 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3749 /* clear door drawing field */
3750 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3752 /* force DOOR font inside door area */
3753 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
3755 /* write text for request */
3756 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3758 char text_line[max_request_line_len + 1];
3764 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3766 tc = *(text_ptr + tx);
3767 // if (!tc || tc == ' ')
3768 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3772 if ((tc == '?' || tc == '!') && tl == 0)
3782 strncpy(text_line, text_ptr, tl);
3785 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3786 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3787 text_line, font_nr);
3789 text_ptr += tl + (tc == ' ' ? 1 : 0);
3790 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3793 SetGameStatus(last_game_status); /* restore current game status */
3795 if (req_state & REQ_ASK)
3797 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3798 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3800 else if (req_state & REQ_CONFIRM)
3802 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3804 else if (req_state & REQ_PLAYER)
3806 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3807 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3808 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3809 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3812 /* copy request gadgets to door backbuffer */
3813 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3815 OpenDoor(DOOR_OPEN_1);
3817 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3819 if (game_status == GAME_MODE_PLAYING)
3821 SetPanelBackground();
3822 SetDrawBackgroundMask(REDRAW_DOOR_1);
3826 SetDrawBackgroundMask(REDRAW_FIELD);
3832 if (game_status != GAME_MODE_MAIN)
3835 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3837 // ---------- handle request buttons ----------
3838 result = RequestHandleEvents(req_state);
3840 if (game_status != GAME_MODE_MAIN)
3845 if (!(req_state & REQ_STAY_OPEN))
3847 CloseDoor(DOOR_CLOSE_1);
3849 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3850 (req_state & REQ_REOPEN))
3851 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3856 if (game_status == GAME_MODE_PLAYING)
3858 SetPanelBackground();
3859 SetDrawBackgroundMask(REDRAW_DOOR_1);
3863 SetDrawBackgroundMask(REDRAW_FIELD);
3866 #if defined(NETWORK_AVALIABLE)
3867 /* continue network game after request */
3868 if (options.network &&
3869 game_status == GAME_MODE_PLAYING &&
3870 req_state & REQUEST_WAIT_FOR_INPUT)
3871 SendToServer_ContinuePlaying();
3874 /* restore deactivated drawing when quick-loading level tape recording */
3875 if (tape.playing && tape.deactivate_display)
3876 TapeDeactivateDisplayOn();
3881 static boolean RequestEnvelope(char *text, unsigned int req_state)
3885 if (game_status == GAME_MODE_PLAYING)
3886 BlitScreenToBitmap(backbuffer);
3888 /* disable deactivated drawing when quick-loading level tape recording */
3889 if (tape.playing && tape.deactivate_display)
3890 TapeDeactivateDisplayOff(TRUE);
3892 SetMouseCursor(CURSOR_DEFAULT);
3894 #if defined(NETWORK_AVALIABLE)
3895 /* pause network game while waiting for request to answer */
3896 if (options.network &&
3897 game_status == GAME_MODE_PLAYING &&
3898 req_state & REQUEST_WAIT_FOR_INPUT)
3899 SendToServer_PausePlaying();
3902 /* simulate releasing mouse button over last gadget, if still pressed */
3904 HandleGadgets(-1, -1, 0);
3908 // (replace with setting corresponding request background)
3909 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3910 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3912 /* clear door drawing field */
3913 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3915 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3917 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3919 if (game_status == GAME_MODE_PLAYING)
3921 SetPanelBackground();
3922 SetDrawBackgroundMask(REDRAW_DOOR_1);
3926 SetDrawBackgroundMask(REDRAW_FIELD);
3932 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3934 // ---------- handle request buttons ----------
3935 result = RequestHandleEvents(req_state);
3937 if (game_status != GAME_MODE_MAIN)
3942 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3946 if (game_status == GAME_MODE_PLAYING)
3948 SetPanelBackground();
3949 SetDrawBackgroundMask(REDRAW_DOOR_1);
3953 SetDrawBackgroundMask(REDRAW_FIELD);
3956 #if defined(NETWORK_AVALIABLE)
3957 /* continue network game after request */
3958 if (options.network &&
3959 game_status == GAME_MODE_PLAYING &&
3960 req_state & REQUEST_WAIT_FOR_INPUT)
3961 SendToServer_ContinuePlaying();
3964 /* restore deactivated drawing when quick-loading level tape recording */
3965 if (tape.playing && tape.deactivate_display)
3966 TapeDeactivateDisplayOn();
3971 boolean Request(char *text, unsigned int req_state)
3973 if (global.use_envelope_request)
3974 return RequestEnvelope(text, req_state);
3976 return RequestDoor(text, req_state);
3979 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3981 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3982 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3985 if (dpo1->sort_priority != dpo2->sort_priority)
3986 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3988 compare_result = dpo1->nr - dpo2->nr;
3990 return compare_result;
3993 void InitGraphicCompatibilityInfo_Doors()
3999 struct DoorInfo *door;
4003 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
4004 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
4006 { -1, -1, -1, NULL }
4008 struct Rect door_rect_list[] =
4010 { DX, DY, DXSIZE, DYSIZE },
4011 { VX, VY, VXSIZE, VYSIZE }
4015 for (i = 0; doors[i].door_token != -1; i++)
4017 int door_token = doors[i].door_token;
4018 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4019 int part_1 = doors[i].part_1;
4020 int part_8 = doors[i].part_8;
4021 int part_2 = part_1 + 1;
4022 int part_3 = part_1 + 2;
4023 struct DoorInfo *door = doors[i].door;
4024 struct Rect *door_rect = &door_rect_list[door_index];
4025 boolean door_gfx_redefined = FALSE;
4027 /* check if any door part graphic definitions have been redefined */
4029 for (j = 0; door_part_controls[j].door_token != -1; j++)
4031 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4032 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4034 if (dpc->door_token == door_token && fi->redefined)
4035 door_gfx_redefined = TRUE;
4038 /* check for old-style door graphic/animation modifications */
4040 if (!door_gfx_redefined)
4042 if (door->anim_mode & ANIM_STATIC_PANEL)
4044 door->panel.step_xoffset = 0;
4045 door->panel.step_yoffset = 0;
4048 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4050 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4051 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4052 int num_door_steps, num_panel_steps;
4054 /* remove door part graphics other than the two default wings */
4056 for (j = 0; door_part_controls[j].door_token != -1; j++)
4058 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4059 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4061 if (dpc->graphic >= part_3 &&
4062 dpc->graphic <= part_8)
4066 /* set graphics and screen positions of the default wings */
4068 g_part_1->width = door_rect->width;
4069 g_part_1->height = door_rect->height;
4070 g_part_2->width = door_rect->width;
4071 g_part_2->height = door_rect->height;
4072 g_part_2->src_x = door_rect->width;
4073 g_part_2->src_y = g_part_1->src_y;
4075 door->part_2.x = door->part_1.x;
4076 door->part_2.y = door->part_1.y;
4078 if (door->width != -1)
4080 g_part_1->width = door->width;
4081 g_part_2->width = door->width;
4083 // special treatment for graphics and screen position of right wing
4084 g_part_2->src_x += door_rect->width - door->width;
4085 door->part_2.x += door_rect->width - door->width;
4088 if (door->height != -1)
4090 g_part_1->height = door->height;
4091 g_part_2->height = door->height;
4093 // special treatment for graphics and screen position of bottom wing
4094 g_part_2->src_y += door_rect->height - door->height;
4095 door->part_2.y += door_rect->height - door->height;
4098 /* set animation delays for the default wings and panels */
4100 door->part_1.step_delay = door->step_delay;
4101 door->part_2.step_delay = door->step_delay;
4102 door->panel.step_delay = door->step_delay;
4104 /* set animation draw order for the default wings */
4106 door->part_1.sort_priority = 2; /* draw left wing over ... */
4107 door->part_2.sort_priority = 1; /* ... right wing */
4109 /* set animation draw offset for the default wings */
4111 if (door->anim_mode & ANIM_HORIZONTAL)
4113 door->part_1.step_xoffset = door->step_offset;
4114 door->part_1.step_yoffset = 0;
4115 door->part_2.step_xoffset = door->step_offset * -1;
4116 door->part_2.step_yoffset = 0;
4118 num_door_steps = g_part_1->width / door->step_offset;
4120 else // ANIM_VERTICAL
4122 door->part_1.step_xoffset = 0;
4123 door->part_1.step_yoffset = door->step_offset;
4124 door->part_2.step_xoffset = 0;
4125 door->part_2.step_yoffset = door->step_offset * -1;
4127 num_door_steps = g_part_1->height / door->step_offset;
4130 /* set animation draw offset for the default panels */
4132 if (door->step_offset > 1)
4134 num_panel_steps = 2 * door_rect->height / door->step_offset;
4135 door->panel.start_step = num_panel_steps - num_door_steps;
4136 door->panel.start_step_closing = door->panel.start_step;
4140 num_panel_steps = door_rect->height / door->step_offset;
4141 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4142 door->panel.start_step_closing = door->panel.start_step;
4143 door->panel.step_delay *= 2;
4154 for (i = 0; door_part_controls[i].door_token != -1; i++)
4156 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4157 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4159 /* initialize "start_step_opening" and "start_step_closing", if needed */
4160 if (dpc->pos->start_step_opening == 0 &&
4161 dpc->pos->start_step_closing == 0)
4163 // dpc->pos->start_step_opening = dpc->pos->start_step;
4164 dpc->pos->start_step_closing = dpc->pos->start_step;
4167 /* fill structure for door part draw order (sorted below) */
4169 dpo->sort_priority = dpc->pos->sort_priority;
4172 /* sort door part controls according to sort_priority and graphic number */
4173 qsort(door_part_order, MAX_DOOR_PARTS,
4174 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4177 unsigned int OpenDoor(unsigned int door_state)
4179 if (door_state & DOOR_COPY_BACK)
4181 if (door_state & DOOR_OPEN_1)
4182 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4183 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4185 if (door_state & DOOR_OPEN_2)
4186 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4187 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4189 door_state &= ~DOOR_COPY_BACK;
4192 return MoveDoor(door_state);
4195 unsigned int CloseDoor(unsigned int door_state)
4197 unsigned int old_door_state = GetDoorState();
4199 if (!(door_state & DOOR_NO_COPY_BACK))
4201 if (old_door_state & DOOR_OPEN_1)
4202 BlitBitmap(backbuffer, bitmap_db_door_1,
4203 DX, DY, DXSIZE, DYSIZE, 0, 0);
4205 if (old_door_state & DOOR_OPEN_2)
4206 BlitBitmap(backbuffer, bitmap_db_door_2,
4207 VX, VY, VXSIZE, VYSIZE, 0, 0);
4209 door_state &= ~DOOR_NO_COPY_BACK;
4212 return MoveDoor(door_state);
4215 unsigned int GetDoorState()
4217 return MoveDoor(DOOR_GET_STATE);
4220 unsigned int SetDoorState(unsigned int door_state)
4222 return MoveDoor(door_state | DOOR_SET_STATE);
4225 int euclid(int a, int b)
4227 return (b ? euclid(b, a % b) : a);
4230 unsigned int MoveDoor(unsigned int door_state)
4232 struct Rect door_rect_list[] =
4234 { DX, DY, DXSIZE, DYSIZE },
4235 { VX, VY, VXSIZE, VYSIZE }
4237 static int door1 = DOOR_CLOSE_1;
4238 static int door2 = DOOR_CLOSE_2;
4239 unsigned int door_delay = 0;
4240 unsigned int door_delay_value;
4243 if (door_state == DOOR_GET_STATE)
4244 return (door1 | door2);
4246 if (door_state & DOOR_SET_STATE)
4248 if (door_state & DOOR_ACTION_1)
4249 door1 = door_state & DOOR_ACTION_1;
4250 if (door_state & DOOR_ACTION_2)
4251 door2 = door_state & DOOR_ACTION_2;
4253 return (door1 | door2);
4256 if (!(door_state & DOOR_FORCE_REDRAW))
4258 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4259 door_state &= ~DOOR_OPEN_1;
4260 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4261 door_state &= ~DOOR_CLOSE_1;
4262 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4263 door_state &= ~DOOR_OPEN_2;
4264 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4265 door_state &= ~DOOR_CLOSE_2;
4268 if (global.autoplay_leveldir)
4270 door_state |= DOOR_NO_DELAY;
4271 door_state &= ~DOOR_CLOSE_ALL;
4274 if (game_status == GAME_MODE_EDITOR)
4275 door_state |= DOOR_NO_DELAY;
4277 if (door_state & DOOR_ACTION)
4279 boolean door_panel_drawn[NUM_DOORS];
4280 boolean panel_has_doors[NUM_DOORS];
4281 boolean door_part_skip[MAX_DOOR_PARTS];
4282 boolean door_part_done[MAX_DOOR_PARTS];
4283 boolean door_part_done_all;
4284 int num_steps[MAX_DOOR_PARTS];
4285 int max_move_delay = 0; // delay for complete animations of all doors
4286 int max_step_delay = 0; // delay (ms) between two animation frames
4287 int num_move_steps = 0; // number of animation steps for all doors
4288 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4289 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4290 int current_move_delay = 0;
4294 for (i = 0; i < NUM_DOORS; i++)
4295 panel_has_doors[i] = FALSE;
4297 for (i = 0; i < MAX_DOOR_PARTS; i++)
4299 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4300 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4301 int door_token = dpc->door_token;
4303 door_part_done[i] = FALSE;
4304 door_part_skip[i] = (!(door_state & door_token) ||
4308 for (i = 0; i < MAX_DOOR_PARTS; i++)
4310 int nr = door_part_order[i].nr;
4311 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4312 struct DoorPartPosInfo *pos = dpc->pos;
4313 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4314 int door_token = dpc->door_token;
4315 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4316 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4317 int step_xoffset = ABS(pos->step_xoffset);
4318 int step_yoffset = ABS(pos->step_yoffset);
4319 int step_delay = pos->step_delay;
4320 int current_door_state = door_state & door_token;
4321 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4322 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4323 boolean part_opening = (is_panel ? door_closing : door_opening);
4324 int start_step = (part_opening ? pos->start_step_opening :
4325 pos->start_step_closing);
4326 float move_xsize = (step_xoffset ? g->width : 0);
4327 float move_ysize = (step_yoffset ? g->height : 0);
4328 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4329 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4330 int move_steps = (move_xsteps && move_ysteps ?
4331 MIN(move_xsteps, move_ysteps) :
4332 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4333 int move_delay = move_steps * step_delay;
4335 if (door_part_skip[nr])
4338 max_move_delay = MAX(max_move_delay, move_delay);
4339 max_step_delay = (max_step_delay == 0 ? step_delay :
4340 euclid(max_step_delay, step_delay));
4341 num_steps[nr] = move_steps;
4345 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4347 panel_has_doors[door_index] = TRUE;
4351 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4353 num_move_steps = max_move_delay / max_step_delay;
4354 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4356 door_delay_value = max_step_delay;
4358 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4360 start = num_move_steps - 1;
4364 /* opening door sound has priority over simultaneously closing door */
4365 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4366 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4367 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4368 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4371 for (k = start; k < num_move_steps; k++)
4373 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4375 door_part_done_all = TRUE;
4377 for (i = 0; i < NUM_DOORS; i++)
4378 door_panel_drawn[i] = FALSE;
4380 for (i = 0; i < MAX_DOOR_PARTS; i++)
4382 int nr = door_part_order[i].nr;
4383 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4384 struct DoorPartPosInfo *pos = dpc->pos;
4385 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4386 int door_token = dpc->door_token;
4387 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4388 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4389 boolean is_panel_and_door_has_closed = FALSE;
4390 struct Rect *door_rect = &door_rect_list[door_index];
4391 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4393 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4394 int current_door_state = door_state & door_token;
4395 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4396 boolean door_closing = !door_opening;
4397 boolean part_opening = (is_panel ? door_closing : door_opening);
4398 boolean part_closing = !part_opening;
4399 int start_step = (part_opening ? pos->start_step_opening :
4400 pos->start_step_closing);
4401 int step_delay = pos->step_delay;
4402 int step_factor = step_delay / max_step_delay;
4403 int k1 = (step_factor ? k / step_factor + 1 : k);
4404 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4405 int kk = MAX(0, k2);
4408 int src_x, src_y, src_xx, src_yy;
4409 int dst_x, dst_y, dst_xx, dst_yy;
4412 if (door_part_skip[nr])
4415 if (!(door_state & door_token))
4423 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4424 int kk_door = MAX(0, k2_door);
4425 int sync_frame = kk_door * door_delay_value;
4426 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4428 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4433 if (!door_panel_drawn[door_index])
4435 ClearRectangle(drawto, door_rect->x, door_rect->y,
4436 door_rect->width, door_rect->height);
4438 door_panel_drawn[door_index] = TRUE;
4441 // draw opening or closing door parts
4443 if (pos->step_xoffset < 0) // door part on right side
4446 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4449 if (dst_xx + width > door_rect->width)
4450 width = door_rect->width - dst_xx;
4452 else // door part on left side
4455 dst_xx = pos->x - kk * pos->step_xoffset;
4459 src_xx = ABS(dst_xx);
4463 width = g->width - src_xx;
4465 if (width > door_rect->width)
4466 width = door_rect->width;
4468 // printf("::: k == %d [%d] \n", k, start_step);
4471 if (pos->step_yoffset < 0) // door part on bottom side
4474 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4477 if (dst_yy + height > door_rect->height)
4478 height = door_rect->height - dst_yy;
4480 else // door part on top side
4483 dst_yy = pos->y - kk * pos->step_yoffset;
4487 src_yy = ABS(dst_yy);
4491 height = g->height - src_yy;
4494 src_x = g_src_x + src_xx;
4495 src_y = g_src_y + src_yy;
4497 dst_x = door_rect->x + dst_xx;
4498 dst_y = door_rect->y + dst_yy;
4500 is_panel_and_door_has_closed =
4503 panel_has_doors[door_index] &&
4504 k >= num_move_steps_doors_only - 1);
4506 if (width >= 0 && width <= g->width &&
4507 height >= 0 && height <= g->height &&
4508 !is_panel_and_door_has_closed)
4510 if (is_panel || !pos->draw_masked)
4511 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4514 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4518 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4520 if ((part_opening && (width < 0 || height < 0)) ||
4521 (part_closing && (width >= g->width && height >= g->height)))
4522 door_part_done[nr] = TRUE;
4524 // continue door part animations, but not panel after door has closed
4525 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4526 door_part_done_all = FALSE;
4529 if (!(door_state & DOOR_NO_DELAY))
4533 if (game_status == GAME_MODE_MAIN)
4536 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4538 current_move_delay += max_step_delay;
4541 if (door_part_done_all)
4546 if (door_state & DOOR_ACTION_1)
4547 door1 = door_state & DOOR_ACTION_1;
4548 if (door_state & DOOR_ACTION_2)
4549 door2 = door_state & DOOR_ACTION_2;
4551 // draw masked border over door area
4552 DrawMaskedBorder(REDRAW_DOOR_1);
4553 DrawMaskedBorder(REDRAW_DOOR_2);
4555 return (door1 | door2);
4558 static boolean useSpecialEditorDoor()
4560 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4561 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4563 // do not draw special editor door if editor border defined or redefined
4564 if (graphic_info[graphic].bitmap != NULL || redefined)
4567 // do not draw special editor door if global border defined to be empty
4568 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4571 // do not draw special editor door if viewport definitions do not match
4575 EY + EYSIZE != VY + VYSIZE)
4581 void DrawSpecialEditorDoor()
4583 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4584 int top_border_width = gfx1->width;
4585 int top_border_height = gfx1->height;
4586 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4587 int ex = EX - outer_border;
4588 int ey = EY - outer_border;
4589 int vy = VY - outer_border;
4590 int exsize = EXSIZE + 2 * outer_border;
4592 if (!useSpecialEditorDoor())
4595 /* draw bigger level editor toolbox window */
4596 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4597 top_border_width, top_border_height, ex, ey - top_border_height);
4598 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4599 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4601 redraw_mask |= REDRAW_ALL;
4604 void UndrawSpecialEditorDoor()
4606 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4607 int top_border_width = gfx1->width;
4608 int top_border_height = gfx1->height;
4609 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4610 int ex = EX - outer_border;
4611 int ey = EY - outer_border;
4612 int ey_top = ey - top_border_height;
4613 int exsize = EXSIZE + 2 * outer_border;
4614 int eysize = EYSIZE + 2 * outer_border;
4616 if (!useSpecialEditorDoor())
4619 /* draw normal tape recorder window */
4620 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4622 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4623 ex, ey_top, top_border_width, top_border_height,
4625 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4626 ex, ey, exsize, eysize, ex, ey);
4630 // if screen background is set to "[NONE]", clear editor toolbox window
4631 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4632 ClearRectangle(drawto, ex, ey, exsize, eysize);
4635 redraw_mask |= REDRAW_ALL;
4639 /* ---------- new tool button stuff ---------------------------------------- */
4644 struct TextPosInfo *pos;
4647 } toolbutton_info[NUM_TOOL_BUTTONS] =
4650 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4651 TOOL_CTRL_ID_YES, "yes"
4654 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4655 TOOL_CTRL_ID_NO, "no"
4658 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4659 TOOL_CTRL_ID_CONFIRM, "confirm"
4662 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4663 TOOL_CTRL_ID_PLAYER_1, "player 1"
4666 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4667 TOOL_CTRL_ID_PLAYER_2, "player 2"
4670 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4671 TOOL_CTRL_ID_PLAYER_3, "player 3"
4674 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4675 TOOL_CTRL_ID_PLAYER_4, "player 4"
4679 void CreateToolButtons()
4683 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4685 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4686 struct TextPosInfo *pos = toolbutton_info[i].pos;
4687 struct GadgetInfo *gi;
4688 Bitmap *deco_bitmap = None;
4689 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4690 unsigned int event_mask = GD_EVENT_RELEASED;
4693 int gd_x = gfx->src_x;
4694 int gd_y = gfx->src_y;
4695 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4696 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4699 if (global.use_envelope_request)
4700 setRequestPosition(&dx, &dy, TRUE);
4702 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4704 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4706 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4707 pos->size, &deco_bitmap, &deco_x, &deco_y);
4708 deco_xpos = (gfx->width - pos->size) / 2;
4709 deco_ypos = (gfx->height - pos->size) / 2;
4712 gi = CreateGadget(GDI_CUSTOM_ID, id,
4713 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4714 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4715 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4716 GDI_WIDTH, gfx->width,
4717 GDI_HEIGHT, gfx->height,
4718 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4719 GDI_STATE, GD_BUTTON_UNPRESSED,
4720 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4721 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4722 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4723 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4724 GDI_DECORATION_SIZE, pos->size, pos->size,
4725 GDI_DECORATION_SHIFTING, 1, 1,
4726 GDI_DIRECT_DRAW, FALSE,
4727 GDI_EVENT_MASK, event_mask,
4728 GDI_CALLBACK_ACTION, HandleToolButtons,
4732 Error(ERR_EXIT, "cannot create gadget");
4734 tool_gadget[id] = gi;
4738 void FreeToolButtons()
4742 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4743 FreeGadget(tool_gadget[i]);
4746 static void UnmapToolButtons()
4750 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4751 UnmapGadget(tool_gadget[i]);
4754 static void HandleToolButtons(struct GadgetInfo *gi)
4756 request_gadget_id = gi->custom_id;
4759 static struct Mapping_EM_to_RND_object
4762 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4763 boolean is_backside; /* backside of moving element */
4769 em_object_mapping_list[] =
4772 Xblank, TRUE, FALSE,
4776 Yacid_splash_eB, FALSE, FALSE,
4777 EL_ACID_SPLASH_RIGHT, -1, -1
4780 Yacid_splash_wB, FALSE, FALSE,
4781 EL_ACID_SPLASH_LEFT, -1, -1
4784 #ifdef EM_ENGINE_BAD_ROLL
4786 Xstone_force_e, FALSE, FALSE,
4787 EL_ROCK, -1, MV_BIT_RIGHT
4790 Xstone_force_w, FALSE, FALSE,
4791 EL_ROCK, -1, MV_BIT_LEFT
4794 Xnut_force_e, FALSE, FALSE,
4795 EL_NUT, -1, MV_BIT_RIGHT
4798 Xnut_force_w, FALSE, FALSE,
4799 EL_NUT, -1, MV_BIT_LEFT
4802 Xspring_force_e, FALSE, FALSE,
4803 EL_SPRING, -1, MV_BIT_RIGHT
4806 Xspring_force_w, FALSE, FALSE,
4807 EL_SPRING, -1, MV_BIT_LEFT
4810 Xemerald_force_e, FALSE, FALSE,
4811 EL_EMERALD, -1, MV_BIT_RIGHT
4814 Xemerald_force_w, FALSE, FALSE,
4815 EL_EMERALD, -1, MV_BIT_LEFT
4818 Xdiamond_force_e, FALSE, FALSE,
4819 EL_DIAMOND, -1, MV_BIT_RIGHT
4822 Xdiamond_force_w, FALSE, FALSE,
4823 EL_DIAMOND, -1, MV_BIT_LEFT
4826 Xbomb_force_e, FALSE, FALSE,
4827 EL_BOMB, -1, MV_BIT_RIGHT
4830 Xbomb_force_w, FALSE, FALSE,
4831 EL_BOMB, -1, MV_BIT_LEFT
4833 #endif /* EM_ENGINE_BAD_ROLL */
4836 Xstone, TRUE, FALSE,
4840 Xstone_pause, FALSE, FALSE,
4844 Xstone_fall, FALSE, FALSE,
4848 Ystone_s, FALSE, FALSE,
4849 EL_ROCK, ACTION_FALLING, -1
4852 Ystone_sB, FALSE, TRUE,
4853 EL_ROCK, ACTION_FALLING, -1
4856 Ystone_e, FALSE, FALSE,
4857 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4860 Ystone_eB, FALSE, TRUE,
4861 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4864 Ystone_w, FALSE, FALSE,
4865 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4868 Ystone_wB, FALSE, TRUE,
4869 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4876 Xnut_pause, FALSE, FALSE,
4880 Xnut_fall, FALSE, FALSE,
4884 Ynut_s, FALSE, FALSE,
4885 EL_NUT, ACTION_FALLING, -1
4888 Ynut_sB, FALSE, TRUE,
4889 EL_NUT, ACTION_FALLING, -1
4892 Ynut_e, FALSE, FALSE,
4893 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4896 Ynut_eB, FALSE, TRUE,
4897 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4900 Ynut_w, FALSE, FALSE,
4901 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4904 Ynut_wB, FALSE, TRUE,
4905 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4908 Xbug_n, TRUE, FALSE,
4912 Xbug_e, TRUE, FALSE,
4913 EL_BUG_RIGHT, -1, -1
4916 Xbug_s, TRUE, FALSE,
4920 Xbug_w, TRUE, FALSE,
4924 Xbug_gon, FALSE, FALSE,
4928 Xbug_goe, FALSE, FALSE,
4929 EL_BUG_RIGHT, -1, -1
4932 Xbug_gos, FALSE, FALSE,
4936 Xbug_gow, FALSE, FALSE,
4940 Ybug_n, FALSE, FALSE,
4941 EL_BUG, ACTION_MOVING, MV_BIT_UP
4944 Ybug_nB, FALSE, TRUE,
4945 EL_BUG, ACTION_MOVING, MV_BIT_UP
4948 Ybug_e, FALSE, FALSE,
4949 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4952 Ybug_eB, FALSE, TRUE,
4953 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4956 Ybug_s, FALSE, FALSE,
4957 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4960 Ybug_sB, FALSE, TRUE,
4961 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4964 Ybug_w, FALSE, FALSE,
4965 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4968 Ybug_wB, FALSE, TRUE,
4969 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4972 Ybug_w_n, FALSE, FALSE,
4973 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4976 Ybug_n_e, FALSE, FALSE,
4977 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4980 Ybug_e_s, FALSE, FALSE,
4981 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4984 Ybug_s_w, FALSE, FALSE,
4985 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4988 Ybug_e_n, FALSE, FALSE,
4989 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4992 Ybug_s_e, FALSE, FALSE,
4993 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4996 Ybug_w_s, FALSE, FALSE,
4997 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5000 Ybug_n_w, FALSE, FALSE,
5001 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5004 Ybug_stone, FALSE, FALSE,
5005 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5008 Ybug_spring, FALSE, FALSE,
5009 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5012 Xtank_n, TRUE, FALSE,
5013 EL_SPACESHIP_UP, -1, -1
5016 Xtank_e, TRUE, FALSE,
5017 EL_SPACESHIP_RIGHT, -1, -1
5020 Xtank_s, TRUE, FALSE,
5021 EL_SPACESHIP_DOWN, -1, -1
5024 Xtank_w, TRUE, FALSE,
5025 EL_SPACESHIP_LEFT, -1, -1
5028 Xtank_gon, FALSE, FALSE,
5029 EL_SPACESHIP_UP, -1, -1
5032 Xtank_goe, FALSE, FALSE,
5033 EL_SPACESHIP_RIGHT, -1, -1
5036 Xtank_gos, FALSE, FALSE,
5037 EL_SPACESHIP_DOWN, -1, -1
5040 Xtank_gow, FALSE, FALSE,
5041 EL_SPACESHIP_LEFT, -1, -1
5044 Ytank_n, FALSE, FALSE,
5045 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5048 Ytank_nB, FALSE, TRUE,
5049 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5052 Ytank_e, FALSE, FALSE,
5053 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5056 Ytank_eB, FALSE, TRUE,
5057 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5060 Ytank_s, FALSE, FALSE,
5061 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5064 Ytank_sB, FALSE, TRUE,
5065 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5068 Ytank_w, FALSE, FALSE,
5069 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5072 Ytank_wB, FALSE, TRUE,
5073 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5076 Ytank_w_n, FALSE, FALSE,
5077 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5080 Ytank_n_e, FALSE, FALSE,
5081 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5084 Ytank_e_s, FALSE, FALSE,
5085 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5088 Ytank_s_w, FALSE, FALSE,
5089 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5092 Ytank_e_n, FALSE, FALSE,
5093 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5096 Ytank_s_e, FALSE, FALSE,
5097 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5100 Ytank_w_s, FALSE, FALSE,
5101 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5104 Ytank_n_w, FALSE, FALSE,
5105 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5108 Ytank_stone, FALSE, FALSE,
5109 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5112 Ytank_spring, FALSE, FALSE,
5113 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5116 Xandroid, TRUE, FALSE,
5117 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5120 Xandroid_1_n, FALSE, FALSE,
5121 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5124 Xandroid_2_n, FALSE, FALSE,
5125 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5128 Xandroid_1_e, FALSE, FALSE,
5129 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5132 Xandroid_2_e, FALSE, FALSE,
5133 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5136 Xandroid_1_w, FALSE, FALSE,
5137 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5140 Xandroid_2_w, FALSE, FALSE,
5141 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5144 Xandroid_1_s, FALSE, FALSE,
5145 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5148 Xandroid_2_s, FALSE, FALSE,
5149 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5152 Yandroid_n, FALSE, FALSE,
5153 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5156 Yandroid_nB, FALSE, TRUE,
5157 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5160 Yandroid_ne, FALSE, FALSE,
5161 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5164 Yandroid_neB, FALSE, TRUE,
5165 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5168 Yandroid_e, FALSE, FALSE,
5169 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5172 Yandroid_eB, FALSE, TRUE,
5173 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5176 Yandroid_se, FALSE, FALSE,
5177 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5180 Yandroid_seB, FALSE, TRUE,
5181 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5184 Yandroid_s, FALSE, FALSE,
5185 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5188 Yandroid_sB, FALSE, TRUE,
5189 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5192 Yandroid_sw, FALSE, FALSE,
5193 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5196 Yandroid_swB, FALSE, TRUE,
5197 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5200 Yandroid_w, FALSE, FALSE,
5201 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5204 Yandroid_wB, FALSE, TRUE,
5205 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5208 Yandroid_nw, FALSE, FALSE,
5209 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5212 Yandroid_nwB, FALSE, TRUE,
5213 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5216 Xspring, TRUE, FALSE,
5220 Xspring_pause, FALSE, FALSE,
5224 Xspring_e, FALSE, FALSE,
5228 Xspring_w, FALSE, FALSE,
5232 Xspring_fall, FALSE, FALSE,
5236 Yspring_s, FALSE, FALSE,
5237 EL_SPRING, ACTION_FALLING, -1
5240 Yspring_sB, FALSE, TRUE,
5241 EL_SPRING, ACTION_FALLING, -1
5244 Yspring_e, FALSE, FALSE,
5245 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5248 Yspring_eB, FALSE, TRUE,
5249 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5252 Yspring_w, FALSE, FALSE,
5253 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5256 Yspring_wB, FALSE, TRUE,
5257 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5260 Yspring_kill_e, FALSE, FALSE,
5261 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5264 Yspring_kill_eB, FALSE, TRUE,
5265 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5268 Yspring_kill_w, FALSE, FALSE,
5269 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5272 Yspring_kill_wB, FALSE, TRUE,
5273 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5276 Xeater_n, TRUE, FALSE,
5277 EL_YAMYAM_UP, -1, -1
5280 Xeater_e, TRUE, FALSE,
5281 EL_YAMYAM_RIGHT, -1, -1
5284 Xeater_w, TRUE, FALSE,
5285 EL_YAMYAM_LEFT, -1, -1
5288 Xeater_s, TRUE, FALSE,
5289 EL_YAMYAM_DOWN, -1, -1
5292 Yeater_n, FALSE, FALSE,
5293 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5296 Yeater_nB, FALSE, TRUE,
5297 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5300 Yeater_e, FALSE, FALSE,
5301 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5304 Yeater_eB, FALSE, TRUE,
5305 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5308 Yeater_s, FALSE, FALSE,
5309 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5312 Yeater_sB, FALSE, TRUE,
5313 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5316 Yeater_w, FALSE, FALSE,
5317 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5320 Yeater_wB, FALSE, TRUE,
5321 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5324 Yeater_stone, FALSE, FALSE,
5325 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5328 Yeater_spring, FALSE, FALSE,
5329 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5332 Xalien, TRUE, FALSE,
5336 Xalien_pause, FALSE, FALSE,
5340 Yalien_n, FALSE, FALSE,
5341 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5344 Yalien_nB, FALSE, TRUE,
5345 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5348 Yalien_e, FALSE, FALSE,
5349 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5352 Yalien_eB, FALSE, TRUE,
5353 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5356 Yalien_s, FALSE, FALSE,
5357 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5360 Yalien_sB, FALSE, TRUE,
5361 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5364 Yalien_w, FALSE, FALSE,
5365 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5368 Yalien_wB, FALSE, TRUE,
5369 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5372 Yalien_stone, FALSE, FALSE,
5373 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5376 Yalien_spring, FALSE, FALSE,
5377 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5380 Xemerald, TRUE, FALSE,
5384 Xemerald_pause, FALSE, FALSE,
5388 Xemerald_fall, FALSE, FALSE,
5392 Xemerald_shine, FALSE, FALSE,
5393 EL_EMERALD, ACTION_TWINKLING, -1
5396 Yemerald_s, FALSE, FALSE,
5397 EL_EMERALD, ACTION_FALLING, -1
5400 Yemerald_sB, FALSE, TRUE,
5401 EL_EMERALD, ACTION_FALLING, -1
5404 Yemerald_e, FALSE, FALSE,
5405 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5408 Yemerald_eB, FALSE, TRUE,
5409 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5412 Yemerald_w, FALSE, FALSE,
5413 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5416 Yemerald_wB, FALSE, TRUE,
5417 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5420 Yemerald_eat, FALSE, FALSE,
5421 EL_EMERALD, ACTION_COLLECTING, -1
5424 Yemerald_stone, FALSE, FALSE,
5425 EL_NUT, ACTION_BREAKING, -1
5428 Xdiamond, TRUE, FALSE,
5432 Xdiamond_pause, FALSE, FALSE,
5436 Xdiamond_fall, FALSE, FALSE,
5440 Xdiamond_shine, FALSE, FALSE,
5441 EL_DIAMOND, ACTION_TWINKLING, -1
5444 Ydiamond_s, FALSE, FALSE,
5445 EL_DIAMOND, ACTION_FALLING, -1
5448 Ydiamond_sB, FALSE, TRUE,
5449 EL_DIAMOND, ACTION_FALLING, -1
5452 Ydiamond_e, FALSE, FALSE,
5453 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5456 Ydiamond_eB, FALSE, TRUE,
5457 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5460 Ydiamond_w, FALSE, FALSE,
5461 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5464 Ydiamond_wB, FALSE, TRUE,
5465 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5468 Ydiamond_eat, FALSE, FALSE,
5469 EL_DIAMOND, ACTION_COLLECTING, -1
5472 Ydiamond_stone, FALSE, FALSE,
5473 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5476 Xdrip_fall, TRUE, FALSE,
5477 EL_AMOEBA_DROP, -1, -1
5480 Xdrip_stretch, FALSE, FALSE,
5481 EL_AMOEBA_DROP, ACTION_FALLING, -1
5484 Xdrip_stretchB, FALSE, TRUE,
5485 EL_AMOEBA_DROP, ACTION_FALLING, -1
5488 Xdrip_eat, FALSE, FALSE,
5489 EL_AMOEBA_DROP, ACTION_GROWING, -1
5492 Ydrip_s1, FALSE, FALSE,
5493 EL_AMOEBA_DROP, ACTION_FALLING, -1
5496 Ydrip_s1B, FALSE, TRUE,
5497 EL_AMOEBA_DROP, ACTION_FALLING, -1
5500 Ydrip_s2, FALSE, FALSE,
5501 EL_AMOEBA_DROP, ACTION_FALLING, -1
5504 Ydrip_s2B, FALSE, TRUE,
5505 EL_AMOEBA_DROP, ACTION_FALLING, -1
5512 Xbomb_pause, FALSE, FALSE,
5516 Xbomb_fall, FALSE, FALSE,
5520 Ybomb_s, FALSE, FALSE,
5521 EL_BOMB, ACTION_FALLING, -1
5524 Ybomb_sB, FALSE, TRUE,
5525 EL_BOMB, ACTION_FALLING, -1
5528 Ybomb_e, FALSE, FALSE,
5529 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5532 Ybomb_eB, FALSE, TRUE,
5533 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5536 Ybomb_w, FALSE, FALSE,
5537 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5540 Ybomb_wB, FALSE, TRUE,
5541 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5544 Ybomb_eat, FALSE, FALSE,
5545 EL_BOMB, ACTION_ACTIVATING, -1
5548 Xballoon, TRUE, FALSE,
5552 Yballoon_n, FALSE, FALSE,
5553 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5556 Yballoon_nB, FALSE, TRUE,
5557 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5560 Yballoon_e, FALSE, FALSE,
5561 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5564 Yballoon_eB, FALSE, TRUE,
5565 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5568 Yballoon_s, FALSE, FALSE,
5569 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5572 Yballoon_sB, FALSE, TRUE,
5573 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5576 Yballoon_w, FALSE, FALSE,
5577 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5580 Yballoon_wB, FALSE, TRUE,
5581 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5584 Xgrass, TRUE, FALSE,
5585 EL_EMC_GRASS, -1, -1
5588 Ygrass_nB, FALSE, FALSE,
5589 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5592 Ygrass_eB, FALSE, FALSE,
5593 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5596 Ygrass_sB, FALSE, FALSE,
5597 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5600 Ygrass_wB, FALSE, FALSE,
5601 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5608 Ydirt_nB, FALSE, FALSE,
5609 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5612 Ydirt_eB, FALSE, FALSE,
5613 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5616 Ydirt_sB, FALSE, FALSE,
5617 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5620 Ydirt_wB, FALSE, FALSE,
5621 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5624 Xacid_ne, TRUE, FALSE,
5625 EL_ACID_POOL_TOPRIGHT, -1, -1
5628 Xacid_se, TRUE, FALSE,
5629 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5632 Xacid_s, TRUE, FALSE,
5633 EL_ACID_POOL_BOTTOM, -1, -1
5636 Xacid_sw, TRUE, FALSE,
5637 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5640 Xacid_nw, TRUE, FALSE,
5641 EL_ACID_POOL_TOPLEFT, -1, -1
5644 Xacid_1, TRUE, FALSE,
5648 Xacid_2, FALSE, FALSE,
5652 Xacid_3, FALSE, FALSE,
5656 Xacid_4, FALSE, FALSE,
5660 Xacid_5, FALSE, FALSE,
5664 Xacid_6, FALSE, FALSE,
5668 Xacid_7, FALSE, FALSE,
5672 Xacid_8, FALSE, FALSE,
5676 Xball_1, TRUE, FALSE,
5677 EL_EMC_MAGIC_BALL, -1, -1
5680 Xball_1B, FALSE, FALSE,
5681 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5684 Xball_2, FALSE, FALSE,
5685 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5688 Xball_2B, FALSE, FALSE,
5689 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5692 Yball_eat, FALSE, FALSE,
5693 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5696 Ykey_1_eat, FALSE, FALSE,
5697 EL_EM_KEY_1, ACTION_COLLECTING, -1
5700 Ykey_2_eat, FALSE, FALSE,
5701 EL_EM_KEY_2, ACTION_COLLECTING, -1
5704 Ykey_3_eat, FALSE, FALSE,
5705 EL_EM_KEY_3, ACTION_COLLECTING, -1
5708 Ykey_4_eat, FALSE, FALSE,
5709 EL_EM_KEY_4, ACTION_COLLECTING, -1
5712 Ykey_5_eat, FALSE, FALSE,
5713 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5716 Ykey_6_eat, FALSE, FALSE,
5717 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5720 Ykey_7_eat, FALSE, FALSE,
5721 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5724 Ykey_8_eat, FALSE, FALSE,
5725 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5728 Ylenses_eat, FALSE, FALSE,
5729 EL_EMC_LENSES, ACTION_COLLECTING, -1
5732 Ymagnify_eat, FALSE, FALSE,
5733 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5736 Ygrass_eat, FALSE, FALSE,
5737 EL_EMC_GRASS, ACTION_SNAPPING, -1
5740 Ydirt_eat, FALSE, FALSE,
5741 EL_SAND, ACTION_SNAPPING, -1
5744 Xgrow_ns, TRUE, FALSE,
5745 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5748 Ygrow_ns_eat, FALSE, FALSE,
5749 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5752 Xgrow_ew, TRUE, FALSE,
5753 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5756 Ygrow_ew_eat, FALSE, FALSE,
5757 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5760 Xwonderwall, TRUE, FALSE,
5761 EL_MAGIC_WALL, -1, -1
5764 XwonderwallB, FALSE, FALSE,
5765 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5768 Xamoeba_1, TRUE, FALSE,
5769 EL_AMOEBA_DRY, ACTION_OTHER, -1
5772 Xamoeba_2, FALSE, FALSE,
5773 EL_AMOEBA_DRY, ACTION_OTHER, -1
5776 Xamoeba_3, FALSE, FALSE,
5777 EL_AMOEBA_DRY, ACTION_OTHER, -1
5780 Xamoeba_4, FALSE, FALSE,
5781 EL_AMOEBA_DRY, ACTION_OTHER, -1
5784 Xamoeba_5, TRUE, FALSE,
5785 EL_AMOEBA_WET, ACTION_OTHER, -1
5788 Xamoeba_6, FALSE, FALSE,
5789 EL_AMOEBA_WET, ACTION_OTHER, -1
5792 Xamoeba_7, FALSE, FALSE,
5793 EL_AMOEBA_WET, ACTION_OTHER, -1
5796 Xamoeba_8, FALSE, FALSE,
5797 EL_AMOEBA_WET, ACTION_OTHER, -1
5800 Xdoor_1, TRUE, FALSE,
5801 EL_EM_GATE_1, -1, -1
5804 Xdoor_2, TRUE, FALSE,
5805 EL_EM_GATE_2, -1, -1
5808 Xdoor_3, TRUE, FALSE,
5809 EL_EM_GATE_3, -1, -1
5812 Xdoor_4, TRUE, FALSE,
5813 EL_EM_GATE_4, -1, -1
5816 Xdoor_5, TRUE, FALSE,
5817 EL_EMC_GATE_5, -1, -1
5820 Xdoor_6, TRUE, FALSE,
5821 EL_EMC_GATE_6, -1, -1
5824 Xdoor_7, TRUE, FALSE,
5825 EL_EMC_GATE_7, -1, -1
5828 Xdoor_8, TRUE, FALSE,
5829 EL_EMC_GATE_8, -1, -1
5832 Xkey_1, TRUE, FALSE,
5836 Xkey_2, TRUE, FALSE,
5840 Xkey_3, TRUE, FALSE,
5844 Xkey_4, TRUE, FALSE,
5848 Xkey_5, TRUE, FALSE,
5849 EL_EMC_KEY_5, -1, -1
5852 Xkey_6, TRUE, FALSE,
5853 EL_EMC_KEY_6, -1, -1
5856 Xkey_7, TRUE, FALSE,
5857 EL_EMC_KEY_7, -1, -1
5860 Xkey_8, TRUE, FALSE,
5861 EL_EMC_KEY_8, -1, -1
5864 Xwind_n, TRUE, FALSE,
5865 EL_BALLOON_SWITCH_UP, -1, -1
5868 Xwind_e, TRUE, FALSE,
5869 EL_BALLOON_SWITCH_RIGHT, -1, -1
5872 Xwind_s, TRUE, FALSE,
5873 EL_BALLOON_SWITCH_DOWN, -1, -1
5876 Xwind_w, TRUE, FALSE,
5877 EL_BALLOON_SWITCH_LEFT, -1, -1
5880 Xwind_nesw, TRUE, FALSE,
5881 EL_BALLOON_SWITCH_ANY, -1, -1
5884 Xwind_stop, TRUE, FALSE,
5885 EL_BALLOON_SWITCH_NONE, -1, -1
5889 EL_EM_EXIT_CLOSED, -1, -1
5892 Xexit_1, TRUE, FALSE,
5893 EL_EM_EXIT_OPEN, -1, -1
5896 Xexit_2, FALSE, FALSE,
5897 EL_EM_EXIT_OPEN, -1, -1
5900 Xexit_3, FALSE, FALSE,
5901 EL_EM_EXIT_OPEN, -1, -1
5904 Xdynamite, TRUE, FALSE,
5905 EL_EM_DYNAMITE, -1, -1
5908 Ydynamite_eat, FALSE, FALSE,
5909 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5912 Xdynamite_1, TRUE, FALSE,
5913 EL_EM_DYNAMITE_ACTIVE, -1, -1
5916 Xdynamite_2, FALSE, FALSE,
5917 EL_EM_DYNAMITE_ACTIVE, -1, -1
5920 Xdynamite_3, FALSE, FALSE,
5921 EL_EM_DYNAMITE_ACTIVE, -1, -1
5924 Xdynamite_4, FALSE, FALSE,
5925 EL_EM_DYNAMITE_ACTIVE, -1, -1
5928 Xbumper, TRUE, FALSE,
5929 EL_EMC_SPRING_BUMPER, -1, -1
5932 XbumperB, FALSE, FALSE,
5933 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5936 Xwheel, TRUE, FALSE,
5937 EL_ROBOT_WHEEL, -1, -1
5940 XwheelB, FALSE, FALSE,
5941 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5944 Xswitch, TRUE, FALSE,
5945 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5948 XswitchB, FALSE, FALSE,
5949 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5953 EL_QUICKSAND_EMPTY, -1, -1
5956 Xsand_stone, TRUE, FALSE,
5957 EL_QUICKSAND_FULL, -1, -1
5960 Xsand_stonein_1, FALSE, TRUE,
5961 EL_ROCK, ACTION_FILLING, -1
5964 Xsand_stonein_2, FALSE, TRUE,
5965 EL_ROCK, ACTION_FILLING, -1
5968 Xsand_stonein_3, FALSE, TRUE,
5969 EL_ROCK, ACTION_FILLING, -1
5972 Xsand_stonein_4, FALSE, TRUE,
5973 EL_ROCK, ACTION_FILLING, -1
5976 Xsand_stonesand_1, FALSE, FALSE,
5977 EL_QUICKSAND_EMPTYING, -1, -1
5980 Xsand_stonesand_2, FALSE, FALSE,
5981 EL_QUICKSAND_EMPTYING, -1, -1
5984 Xsand_stonesand_3, FALSE, FALSE,
5985 EL_QUICKSAND_EMPTYING, -1, -1
5988 Xsand_stonesand_4, FALSE, FALSE,
5989 EL_QUICKSAND_EMPTYING, -1, -1
5992 Xsand_stonesand_quickout_1, FALSE, FALSE,
5993 EL_QUICKSAND_EMPTYING, -1, -1
5996 Xsand_stonesand_quickout_2, FALSE, FALSE,
5997 EL_QUICKSAND_EMPTYING, -1, -1
6000 Xsand_stoneout_1, FALSE, FALSE,
6001 EL_ROCK, ACTION_EMPTYING, -1
6004 Xsand_stoneout_2, FALSE, FALSE,
6005 EL_ROCK, ACTION_EMPTYING, -1
6008 Xsand_sandstone_1, FALSE, FALSE,
6009 EL_QUICKSAND_FILLING, -1, -1
6012 Xsand_sandstone_2, FALSE, FALSE,
6013 EL_QUICKSAND_FILLING, -1, -1
6016 Xsand_sandstone_3, FALSE, FALSE,
6017 EL_QUICKSAND_FILLING, -1, -1
6020 Xsand_sandstone_4, FALSE, FALSE,
6021 EL_QUICKSAND_FILLING, -1, -1
6024 Xplant, TRUE, FALSE,
6025 EL_EMC_PLANT, -1, -1
6028 Yplant, FALSE, FALSE,
6029 EL_EMC_PLANT, -1, -1
6032 Xlenses, TRUE, FALSE,
6033 EL_EMC_LENSES, -1, -1
6036 Xmagnify, TRUE, FALSE,
6037 EL_EMC_MAGNIFIER, -1, -1
6040 Xdripper, TRUE, FALSE,
6041 EL_EMC_DRIPPER, -1, -1
6044 XdripperB, FALSE, FALSE,
6045 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6048 Xfake_blank, TRUE, FALSE,
6049 EL_INVISIBLE_WALL, -1, -1
6052 Xfake_blankB, FALSE, FALSE,
6053 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6056 Xfake_grass, TRUE, FALSE,
6057 EL_EMC_FAKE_GRASS, -1, -1
6060 Xfake_grassB, FALSE, FALSE,
6061 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6064 Xfake_door_1, TRUE, FALSE,
6065 EL_EM_GATE_1_GRAY, -1, -1
6068 Xfake_door_2, TRUE, FALSE,
6069 EL_EM_GATE_2_GRAY, -1, -1
6072 Xfake_door_3, TRUE, FALSE,
6073 EL_EM_GATE_3_GRAY, -1, -1
6076 Xfake_door_4, TRUE, FALSE,
6077 EL_EM_GATE_4_GRAY, -1, -1
6080 Xfake_door_5, TRUE, FALSE,
6081 EL_EMC_GATE_5_GRAY, -1, -1
6084 Xfake_door_6, TRUE, FALSE,
6085 EL_EMC_GATE_6_GRAY, -1, -1
6088 Xfake_door_7, TRUE, FALSE,
6089 EL_EMC_GATE_7_GRAY, -1, -1
6092 Xfake_door_8, TRUE, FALSE,
6093 EL_EMC_GATE_8_GRAY, -1, -1
6096 Xfake_acid_1, TRUE, FALSE,
6097 EL_EMC_FAKE_ACID, -1, -1
6100 Xfake_acid_2, FALSE, FALSE,
6101 EL_EMC_FAKE_ACID, -1, -1
6104 Xfake_acid_3, FALSE, FALSE,
6105 EL_EMC_FAKE_ACID, -1, -1
6108 Xfake_acid_4, FALSE, FALSE,
6109 EL_EMC_FAKE_ACID, -1, -1
6112 Xfake_acid_5, FALSE, FALSE,
6113 EL_EMC_FAKE_ACID, -1, -1
6116 Xfake_acid_6, FALSE, FALSE,
6117 EL_EMC_FAKE_ACID, -1, -1
6120 Xfake_acid_7, FALSE, FALSE,
6121 EL_EMC_FAKE_ACID, -1, -1
6124 Xfake_acid_8, FALSE, FALSE,
6125 EL_EMC_FAKE_ACID, -1, -1
6128 Xsteel_1, TRUE, FALSE,
6129 EL_STEELWALL, -1, -1
6132 Xsteel_2, TRUE, FALSE,
6133 EL_EMC_STEELWALL_2, -1, -1
6136 Xsteel_3, TRUE, FALSE,
6137 EL_EMC_STEELWALL_3, -1, -1
6140 Xsteel_4, TRUE, FALSE,
6141 EL_EMC_STEELWALL_4, -1, -1
6144 Xwall_1, TRUE, FALSE,
6148 Xwall_2, TRUE, FALSE,
6149 EL_EMC_WALL_14, -1, -1
6152 Xwall_3, TRUE, FALSE,
6153 EL_EMC_WALL_15, -1, -1
6156 Xwall_4, TRUE, FALSE,
6157 EL_EMC_WALL_16, -1, -1
6160 Xround_wall_1, TRUE, FALSE,
6161 EL_WALL_SLIPPERY, -1, -1
6164 Xround_wall_2, TRUE, FALSE,
6165 EL_EMC_WALL_SLIPPERY_2, -1, -1
6168 Xround_wall_3, TRUE, FALSE,
6169 EL_EMC_WALL_SLIPPERY_3, -1, -1
6172 Xround_wall_4, TRUE, FALSE,
6173 EL_EMC_WALL_SLIPPERY_4, -1, -1
6176 Xdecor_1, TRUE, FALSE,
6177 EL_EMC_WALL_8, -1, -1
6180 Xdecor_2, TRUE, FALSE,
6181 EL_EMC_WALL_6, -1, -1
6184 Xdecor_3, TRUE, FALSE,
6185 EL_EMC_WALL_4, -1, -1
6188 Xdecor_4, TRUE, FALSE,
6189 EL_EMC_WALL_7, -1, -1
6192 Xdecor_5, TRUE, FALSE,
6193 EL_EMC_WALL_5, -1, -1
6196 Xdecor_6, TRUE, FALSE,
6197 EL_EMC_WALL_9, -1, -1
6200 Xdecor_7, TRUE, FALSE,
6201 EL_EMC_WALL_10, -1, -1
6204 Xdecor_8, TRUE, FALSE,
6205 EL_EMC_WALL_1, -1, -1
6208 Xdecor_9, TRUE, FALSE,
6209 EL_EMC_WALL_2, -1, -1
6212 Xdecor_10, TRUE, FALSE,
6213 EL_EMC_WALL_3, -1, -1
6216 Xdecor_11, TRUE, FALSE,
6217 EL_EMC_WALL_11, -1, -1
6220 Xdecor_12, TRUE, FALSE,
6221 EL_EMC_WALL_12, -1, -1
6224 Xalpha_0, TRUE, FALSE,
6225 EL_CHAR('0'), -1, -1
6228 Xalpha_1, TRUE, FALSE,
6229 EL_CHAR('1'), -1, -1
6232 Xalpha_2, TRUE, FALSE,
6233 EL_CHAR('2'), -1, -1
6236 Xalpha_3, TRUE, FALSE,
6237 EL_CHAR('3'), -1, -1
6240 Xalpha_4, TRUE, FALSE,
6241 EL_CHAR('4'), -1, -1
6244 Xalpha_5, TRUE, FALSE,
6245 EL_CHAR('5'), -1, -1
6248 Xalpha_6, TRUE, FALSE,
6249 EL_CHAR('6'), -1, -1
6252 Xalpha_7, TRUE, FALSE,
6253 EL_CHAR('7'), -1, -1
6256 Xalpha_8, TRUE, FALSE,
6257 EL_CHAR('8'), -1, -1
6260 Xalpha_9, TRUE, FALSE,
6261 EL_CHAR('9'), -1, -1
6264 Xalpha_excla, TRUE, FALSE,
6265 EL_CHAR('!'), -1, -1
6268 Xalpha_quote, TRUE, FALSE,
6269 EL_CHAR('"'), -1, -1
6272 Xalpha_comma, TRUE, FALSE,
6273 EL_CHAR(','), -1, -1
6276 Xalpha_minus, TRUE, FALSE,
6277 EL_CHAR('-'), -1, -1
6280 Xalpha_perio, TRUE, FALSE,
6281 EL_CHAR('.'), -1, -1
6284 Xalpha_colon, TRUE, FALSE,
6285 EL_CHAR(':'), -1, -1
6288 Xalpha_quest, TRUE, FALSE,
6289 EL_CHAR('?'), -1, -1
6292 Xalpha_a, TRUE, FALSE,
6293 EL_CHAR('A'), -1, -1
6296 Xalpha_b, TRUE, FALSE,
6297 EL_CHAR('B'), -1, -1
6300 Xalpha_c, TRUE, FALSE,
6301 EL_CHAR('C'), -1, -1
6304 Xalpha_d, TRUE, FALSE,
6305 EL_CHAR('D'), -1, -1
6308 Xalpha_e, TRUE, FALSE,
6309 EL_CHAR('E'), -1, -1
6312 Xalpha_f, TRUE, FALSE,
6313 EL_CHAR('F'), -1, -1
6316 Xalpha_g, TRUE, FALSE,
6317 EL_CHAR('G'), -1, -1
6320 Xalpha_h, TRUE, FALSE,
6321 EL_CHAR('H'), -1, -1
6324 Xalpha_i, TRUE, FALSE,
6325 EL_CHAR('I'), -1, -1
6328 Xalpha_j, TRUE, FALSE,
6329 EL_CHAR('J'), -1, -1
6332 Xalpha_k, TRUE, FALSE,
6333 EL_CHAR('K'), -1, -1
6336 Xalpha_l, TRUE, FALSE,
6337 EL_CHAR('L'), -1, -1
6340 Xalpha_m, TRUE, FALSE,
6341 EL_CHAR('M'), -1, -1
6344 Xalpha_n, TRUE, FALSE,
6345 EL_CHAR('N'), -1, -1
6348 Xalpha_o, TRUE, FALSE,
6349 EL_CHAR('O'), -1, -1
6352 Xalpha_p, TRUE, FALSE,
6353 EL_CHAR('P'), -1, -1
6356 Xalpha_q, TRUE, FALSE,
6357 EL_CHAR('Q'), -1, -1
6360 Xalpha_r, TRUE, FALSE,
6361 EL_CHAR('R'), -1, -1
6364 Xalpha_s, TRUE, FALSE,
6365 EL_CHAR('S'), -1, -1
6368 Xalpha_t, TRUE, FALSE,
6369 EL_CHAR('T'), -1, -1
6372 Xalpha_u, TRUE, FALSE,
6373 EL_CHAR('U'), -1, -1
6376 Xalpha_v, TRUE, FALSE,
6377 EL_CHAR('V'), -1, -1
6380 Xalpha_w, TRUE, FALSE,
6381 EL_CHAR('W'), -1, -1
6384 Xalpha_x, TRUE, FALSE,
6385 EL_CHAR('X'), -1, -1
6388 Xalpha_y, TRUE, FALSE,
6389 EL_CHAR('Y'), -1, -1
6392 Xalpha_z, TRUE, FALSE,
6393 EL_CHAR('Z'), -1, -1
6396 Xalpha_arrow_e, TRUE, FALSE,
6397 EL_CHAR('>'), -1, -1
6400 Xalpha_arrow_w, TRUE, FALSE,
6401 EL_CHAR('<'), -1, -1
6404 Xalpha_copyr, TRUE, FALSE,
6405 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6409 Xboom_bug, FALSE, FALSE,
6410 EL_BUG, ACTION_EXPLODING, -1
6413 Xboom_bomb, FALSE, FALSE,
6414 EL_BOMB, ACTION_EXPLODING, -1
6417 Xboom_android, FALSE, FALSE,
6418 EL_EMC_ANDROID, ACTION_OTHER, -1
6421 Xboom_1, FALSE, FALSE,
6422 EL_DEFAULT, ACTION_EXPLODING, -1
6425 Xboom_2, FALSE, FALSE,
6426 EL_DEFAULT, ACTION_EXPLODING, -1
6429 Znormal, FALSE, FALSE,
6433 Zdynamite, FALSE, FALSE,
6437 Zplayer, FALSE, FALSE,
6441 ZBORDER, FALSE, FALSE,
6451 static struct Mapping_EM_to_RND_player
6460 em_player_mapping_list[] =
6464 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6468 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6472 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6476 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6480 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6484 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6488 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6492 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6496 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6500 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6504 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6508 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6512 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6516 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6520 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6524 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6528 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6532 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6536 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6540 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6544 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6548 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6552 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6556 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6560 EL_PLAYER_1, ACTION_DEFAULT, -1,
6564 EL_PLAYER_2, ACTION_DEFAULT, -1,
6568 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6572 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6576 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6580 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6584 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6588 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6592 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6596 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6600 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6604 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6608 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6612 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6616 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6620 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6624 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6628 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6632 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6636 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6640 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6644 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6648 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6652 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6656 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6660 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6664 EL_PLAYER_3, ACTION_DEFAULT, -1,
6668 EL_PLAYER_4, ACTION_DEFAULT, -1,
6677 int map_element_RND_to_EM(int element_rnd)
6679 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6680 static boolean mapping_initialized = FALSE;
6682 if (!mapping_initialized)
6686 /* return "Xalpha_quest" for all undefined elements in mapping array */
6687 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6688 mapping_RND_to_EM[i] = Xalpha_quest;
6690 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6691 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6692 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6693 em_object_mapping_list[i].element_em;
6695 mapping_initialized = TRUE;
6698 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6699 return mapping_RND_to_EM[element_rnd];
6701 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6706 int map_element_EM_to_RND(int element_em)
6708 static unsigned short mapping_EM_to_RND[TILE_MAX];
6709 static boolean mapping_initialized = FALSE;
6711 if (!mapping_initialized)
6715 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6716 for (i = 0; i < TILE_MAX; i++)
6717 mapping_EM_to_RND[i] = EL_UNKNOWN;
6719 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6720 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6721 em_object_mapping_list[i].element_rnd;
6723 mapping_initialized = TRUE;
6726 if (element_em >= 0 && element_em < TILE_MAX)
6727 return mapping_EM_to_RND[element_em];
6729 Error(ERR_WARN, "invalid EM level element %d", element_em);
6734 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6736 struct LevelInfo_EM *level_em = level->native_em_level;
6737 struct LEVEL *lev = level_em->lev;
6740 for (i = 0; i < TILE_MAX; i++)
6741 lev->android_array[i] = Xblank;
6743 for (i = 0; i < level->num_android_clone_elements; i++)
6745 int element_rnd = level->android_clone_element[i];
6746 int element_em = map_element_RND_to_EM(element_rnd);
6748 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6749 if (em_object_mapping_list[j].element_rnd == element_rnd)
6750 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6754 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6756 struct LevelInfo_EM *level_em = level->native_em_level;
6757 struct LEVEL *lev = level_em->lev;
6760 level->num_android_clone_elements = 0;
6762 for (i = 0; i < TILE_MAX; i++)
6764 int element_em = lev->android_array[i];
6766 boolean element_found = FALSE;
6768 if (element_em == Xblank)
6771 element_rnd = map_element_EM_to_RND(element_em);
6773 for (j = 0; j < level->num_android_clone_elements; j++)
6774 if (level->android_clone_element[j] == element_rnd)
6775 element_found = TRUE;
6779 level->android_clone_element[level->num_android_clone_elements++] =
6782 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6787 if (level->num_android_clone_elements == 0)
6789 level->num_android_clone_elements = 1;
6790 level->android_clone_element[0] = EL_EMPTY;
6794 int map_direction_RND_to_EM(int direction)
6796 return (direction == MV_UP ? 0 :
6797 direction == MV_RIGHT ? 1 :
6798 direction == MV_DOWN ? 2 :
6799 direction == MV_LEFT ? 3 :
6803 int map_direction_EM_to_RND(int direction)
6805 return (direction == 0 ? MV_UP :
6806 direction == 1 ? MV_RIGHT :
6807 direction == 2 ? MV_DOWN :
6808 direction == 3 ? MV_LEFT :
6812 int map_element_RND_to_SP(int element_rnd)
6814 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6816 if (element_rnd >= EL_SP_START &&
6817 element_rnd <= EL_SP_END)
6818 element_sp = element_rnd - EL_SP_START;
6819 else if (element_rnd == EL_EMPTY_SPACE)
6821 else if (element_rnd == EL_INVISIBLE_WALL)
6827 int map_element_SP_to_RND(int element_sp)
6829 int element_rnd = EL_UNKNOWN;
6831 if (element_sp >= 0x00 &&
6833 element_rnd = EL_SP_START + element_sp;
6834 else if (element_sp == 0x28)
6835 element_rnd = EL_INVISIBLE_WALL;
6840 int map_action_SP_to_RND(int action_sp)
6844 case actActive: return ACTION_ACTIVE;
6845 case actImpact: return ACTION_IMPACT;
6846 case actExploding: return ACTION_EXPLODING;
6847 case actDigging: return ACTION_DIGGING;
6848 case actSnapping: return ACTION_SNAPPING;
6849 case actCollecting: return ACTION_COLLECTING;
6850 case actPassing: return ACTION_PASSING;
6851 case actPushing: return ACTION_PUSHING;
6852 case actDropping: return ACTION_DROPPING;
6854 default: return ACTION_DEFAULT;
6858 int get_next_element(int element)
6862 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6863 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6864 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6865 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6866 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6867 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6868 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6869 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6870 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6871 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6872 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6874 default: return element;
6878 int el_act_dir2img(int element, int action, int direction)
6880 element = GFX_ELEMENT(element);
6881 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6883 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6884 return element_info[element].direction_graphic[action][direction];
6887 static int el_act_dir2crm(int element, int action, int direction)
6889 element = GFX_ELEMENT(element);
6890 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6892 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6893 return element_info[element].direction_crumbled[action][direction];
6896 int el_act2img(int element, int action)
6898 element = GFX_ELEMENT(element);
6900 return element_info[element].graphic[action];
6903 int el_act2crm(int element, int action)
6905 element = GFX_ELEMENT(element);
6907 return element_info[element].crumbled[action];
6910 int el_dir2img(int element, int direction)
6912 element = GFX_ELEMENT(element);
6914 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6917 int el2baseimg(int element)
6919 return element_info[element].graphic[ACTION_DEFAULT];
6922 int el2img(int element)
6924 element = GFX_ELEMENT(element);
6926 return element_info[element].graphic[ACTION_DEFAULT];
6929 int el2edimg(int element)
6931 element = GFX_ELEMENT(element);
6933 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6936 int el2preimg(int element)
6938 element = GFX_ELEMENT(element);
6940 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6943 int el2panelimg(int element)
6945 element = GFX_ELEMENT(element);
6947 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6950 int font2baseimg(int font_nr)
6952 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6955 int getBeltNrFromBeltElement(int element)
6957 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6958 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6959 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6962 int getBeltNrFromBeltActiveElement(int element)
6964 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6965 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6966 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6969 int getBeltNrFromBeltSwitchElement(int element)
6971 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6972 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6973 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6976 int getBeltDirNrFromBeltElement(int element)
6978 static int belt_base_element[4] =
6980 EL_CONVEYOR_BELT_1_LEFT,
6981 EL_CONVEYOR_BELT_2_LEFT,
6982 EL_CONVEYOR_BELT_3_LEFT,
6983 EL_CONVEYOR_BELT_4_LEFT
6986 int belt_nr = getBeltNrFromBeltElement(element);
6987 int belt_dir_nr = element - belt_base_element[belt_nr];
6989 return (belt_dir_nr % 3);
6992 int getBeltDirNrFromBeltSwitchElement(int element)
6994 static int belt_base_element[4] =
6996 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6997 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6998 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6999 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7002 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7003 int belt_dir_nr = element - belt_base_element[belt_nr];
7005 return (belt_dir_nr % 3);
7008 int getBeltDirFromBeltElement(int element)
7010 static int belt_move_dir[3] =
7017 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7019 return belt_move_dir[belt_dir_nr];
7022 int getBeltDirFromBeltSwitchElement(int element)
7024 static int belt_move_dir[3] =
7031 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7033 return belt_move_dir[belt_dir_nr];
7036 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7038 static int belt_base_element[4] =
7040 EL_CONVEYOR_BELT_1_LEFT,
7041 EL_CONVEYOR_BELT_2_LEFT,
7042 EL_CONVEYOR_BELT_3_LEFT,
7043 EL_CONVEYOR_BELT_4_LEFT
7046 return belt_base_element[belt_nr] + belt_dir_nr;
7049 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7051 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7053 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7056 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7058 static int belt_base_element[4] =
7060 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7061 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7062 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7063 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7066 return belt_base_element[belt_nr] + belt_dir_nr;
7069 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7071 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7073 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7076 boolean getTeamMode_EM()
7078 return game.team_mode;
7081 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7083 int game_frame_delay_value;
7085 game_frame_delay_value =
7086 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7087 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7090 if (tape.playing && tape.warp_forward && !tape.pausing)
7091 game_frame_delay_value = 0;
7093 return game_frame_delay_value;
7096 unsigned int InitRND(int seed)
7098 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7099 return InitEngineRandom_EM(seed);
7100 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7101 return InitEngineRandom_SP(seed);
7103 return InitEngineRandom_RND(seed);
7106 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7107 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7109 inline static int get_effective_element_EM(int tile, int frame_em)
7111 int element = object_mapping[tile].element_rnd;
7112 int action = object_mapping[tile].action;
7113 boolean is_backside = object_mapping[tile].is_backside;
7114 boolean action_removing = (action == ACTION_DIGGING ||
7115 action == ACTION_SNAPPING ||
7116 action == ACTION_COLLECTING);
7122 case Yacid_splash_eB:
7123 case Yacid_splash_wB:
7124 return (frame_em > 5 ? EL_EMPTY : element);
7130 else /* frame_em == 7 */
7134 case Yacid_splash_eB:
7135 case Yacid_splash_wB:
7138 case Yemerald_stone:
7141 case Ydiamond_stone:
7145 case Xdrip_stretchB:
7164 case Xsand_stonein_1:
7165 case Xsand_stonein_2:
7166 case Xsand_stonein_3:
7167 case Xsand_stonein_4:
7171 return (is_backside || action_removing ? EL_EMPTY : element);
7176 inline static boolean check_linear_animation_EM(int tile)
7180 case Xsand_stonesand_1:
7181 case Xsand_stonesand_quickout_1:
7182 case Xsand_sandstone_1:
7183 case Xsand_stonein_1:
7184 case Xsand_stoneout_1:
7203 case Yacid_splash_eB:
7204 case Yacid_splash_wB:
7205 case Yemerald_stone:
7212 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7213 boolean has_crumbled_graphics,
7214 int crumbled, int sync_frame)
7216 /* if element can be crumbled, but certain action graphics are just empty
7217 space (like instantly snapping sand to empty space in 1 frame), do not
7218 treat these empty space graphics as crumbled graphics in EMC engine */
7219 if (crumbled == IMG_EMPTY_SPACE)
7220 has_crumbled_graphics = FALSE;
7222 if (has_crumbled_graphics)
7224 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7225 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7226 g_crumbled->anim_delay,
7227 g_crumbled->anim_mode,
7228 g_crumbled->anim_start_frame,
7231 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7232 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7234 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7236 g_em->has_crumbled_graphics = TRUE;
7240 g_em->crumbled_bitmap = NULL;
7241 g_em->crumbled_src_x = 0;
7242 g_em->crumbled_src_y = 0;
7243 g_em->crumbled_border_size = 0;
7245 g_em->has_crumbled_graphics = FALSE;
7249 void ResetGfxAnimation_EM(int x, int y, int tile)
7254 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7255 int tile, int frame_em, int x, int y)
7257 int action = object_mapping[tile].action;
7258 int direction = object_mapping[tile].direction;
7259 int effective_element = get_effective_element_EM(tile, frame_em);
7260 int graphic = (direction == MV_NONE ?
7261 el_act2img(effective_element, action) :
7262 el_act_dir2img(effective_element, action, direction));
7263 struct GraphicInfo *g = &graphic_info[graphic];
7265 boolean action_removing = (action == ACTION_DIGGING ||
7266 action == ACTION_SNAPPING ||
7267 action == ACTION_COLLECTING);
7268 boolean action_moving = (action == ACTION_FALLING ||
7269 action == ACTION_MOVING ||
7270 action == ACTION_PUSHING ||
7271 action == ACTION_EATING ||
7272 action == ACTION_FILLING ||
7273 action == ACTION_EMPTYING);
7274 boolean action_falling = (action == ACTION_FALLING ||
7275 action == ACTION_FILLING ||
7276 action == ACTION_EMPTYING);
7278 /* special case: graphic uses "2nd movement tile" and has defined
7279 7 frames for movement animation (or less) => use default graphic
7280 for last (8th) frame which ends the movement animation */
7281 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7283 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7284 graphic = (direction == MV_NONE ?
7285 el_act2img(effective_element, action) :
7286 el_act_dir2img(effective_element, action, direction));
7288 g = &graphic_info[graphic];
7291 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7295 else if (action_moving)
7297 boolean is_backside = object_mapping[tile].is_backside;
7301 int direction = object_mapping[tile].direction;
7302 int move_dir = (action_falling ? MV_DOWN : direction);
7307 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7308 if (g->double_movement && frame_em == 0)
7312 if (move_dir == MV_LEFT)
7313 GfxFrame[x - 1][y] = GfxFrame[x][y];
7314 else if (move_dir == MV_RIGHT)
7315 GfxFrame[x + 1][y] = GfxFrame[x][y];
7316 else if (move_dir == MV_UP)
7317 GfxFrame[x][y - 1] = GfxFrame[x][y];
7318 else if (move_dir == MV_DOWN)
7319 GfxFrame[x][y + 1] = GfxFrame[x][y];
7326 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7327 if (tile == Xsand_stonesand_quickout_1 ||
7328 tile == Xsand_stonesand_quickout_2)
7332 if (graphic_info[graphic].anim_global_sync)
7333 sync_frame = FrameCounter;
7334 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7335 sync_frame = GfxFrame[x][y];
7337 sync_frame = 0; /* playfield border (pseudo steel) */
7339 SetRandomAnimationValue(x, y);
7341 int frame = getAnimationFrame(g->anim_frames,
7344 g->anim_start_frame,
7347 g_em->unique_identifier =
7348 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7351 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7352 int tile, int frame_em, int x, int y)
7354 int action = object_mapping[tile].action;
7355 int direction = object_mapping[tile].direction;
7356 boolean is_backside = object_mapping[tile].is_backside;
7357 int effective_element = get_effective_element_EM(tile, frame_em);
7358 int effective_action = action;
7359 int graphic = (direction == MV_NONE ?
7360 el_act2img(effective_element, effective_action) :
7361 el_act_dir2img(effective_element, effective_action,
7363 int crumbled = (direction == MV_NONE ?
7364 el_act2crm(effective_element, effective_action) :
7365 el_act_dir2crm(effective_element, effective_action,
7367 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7368 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7369 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7370 struct GraphicInfo *g = &graphic_info[graphic];
7373 /* special case: graphic uses "2nd movement tile" and has defined
7374 7 frames for movement animation (or less) => use default graphic
7375 for last (8th) frame which ends the movement animation */
7376 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7378 effective_action = ACTION_DEFAULT;
7379 graphic = (direction == MV_NONE ?
7380 el_act2img(effective_element, effective_action) :
7381 el_act_dir2img(effective_element, effective_action,
7383 crumbled = (direction == MV_NONE ?
7384 el_act2crm(effective_element, effective_action) :
7385 el_act_dir2crm(effective_element, effective_action,
7388 g = &graphic_info[graphic];
7391 if (graphic_info[graphic].anim_global_sync)
7392 sync_frame = FrameCounter;
7393 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7394 sync_frame = GfxFrame[x][y];
7396 sync_frame = 0; /* playfield border (pseudo steel) */
7398 SetRandomAnimationValue(x, y);
7400 int frame = getAnimationFrame(g->anim_frames,
7403 g->anim_start_frame,
7406 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7407 g->double_movement && is_backside);
7409 /* (updating the "crumbled" graphic definitions is probably not really needed,
7410 as animations for crumbled graphics can't be longer than one EMC cycle) */
7411 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7415 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7416 int player_nr, int anim, int frame_em)
7418 int element = player_mapping[player_nr][anim].element_rnd;
7419 int action = player_mapping[player_nr][anim].action;
7420 int direction = player_mapping[player_nr][anim].direction;
7421 int graphic = (direction == MV_NONE ?
7422 el_act2img(element, action) :
7423 el_act_dir2img(element, action, direction));
7424 struct GraphicInfo *g = &graphic_info[graphic];
7427 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7429 stored_player[player_nr].StepFrame = frame_em;
7431 sync_frame = stored_player[player_nr].Frame;
7433 int frame = getAnimationFrame(g->anim_frames,
7436 g->anim_start_frame,
7439 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7440 &g_em->src_x, &g_em->src_y, FALSE);
7443 void InitGraphicInfo_EM(void)
7448 int num_em_gfx_errors = 0;
7450 if (graphic_info_em_object[0][0].bitmap == NULL)
7452 /* EM graphics not yet initialized in em_open_all() */
7457 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7460 /* always start with reliable default values */
7461 for (i = 0; i < TILE_MAX; i++)
7463 object_mapping[i].element_rnd = EL_UNKNOWN;
7464 object_mapping[i].is_backside = FALSE;
7465 object_mapping[i].action = ACTION_DEFAULT;
7466 object_mapping[i].direction = MV_NONE;
7469 /* always start with reliable default values */
7470 for (p = 0; p < MAX_PLAYERS; p++)
7472 for (i = 0; i < SPR_MAX; i++)
7474 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7475 player_mapping[p][i].action = ACTION_DEFAULT;
7476 player_mapping[p][i].direction = MV_NONE;
7480 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7482 int e = em_object_mapping_list[i].element_em;
7484 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7485 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7487 if (em_object_mapping_list[i].action != -1)
7488 object_mapping[e].action = em_object_mapping_list[i].action;
7490 if (em_object_mapping_list[i].direction != -1)
7491 object_mapping[e].direction =
7492 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7495 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7497 int a = em_player_mapping_list[i].action_em;
7498 int p = em_player_mapping_list[i].player_nr;
7500 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7502 if (em_player_mapping_list[i].action != -1)
7503 player_mapping[p][a].action = em_player_mapping_list[i].action;
7505 if (em_player_mapping_list[i].direction != -1)
7506 player_mapping[p][a].direction =
7507 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7510 for (i = 0; i < TILE_MAX; i++)
7512 int element = object_mapping[i].element_rnd;
7513 int action = object_mapping[i].action;
7514 int direction = object_mapping[i].direction;
7515 boolean is_backside = object_mapping[i].is_backside;
7516 boolean action_exploding = ((action == ACTION_EXPLODING ||
7517 action == ACTION_SMASHED_BY_ROCK ||
7518 action == ACTION_SMASHED_BY_SPRING) &&
7519 element != EL_DIAMOND);
7520 boolean action_active = (action == ACTION_ACTIVE);
7521 boolean action_other = (action == ACTION_OTHER);
7523 for (j = 0; j < 8; j++)
7525 int effective_element = get_effective_element_EM(i, j);
7526 int effective_action = (j < 7 ? action :
7527 i == Xdrip_stretch ? action :
7528 i == Xdrip_stretchB ? action :
7529 i == Ydrip_s1 ? action :
7530 i == Ydrip_s1B ? action :
7531 i == Xball_1B ? action :
7532 i == Xball_2 ? action :
7533 i == Xball_2B ? action :
7534 i == Yball_eat ? action :
7535 i == Ykey_1_eat ? action :
7536 i == Ykey_2_eat ? action :
7537 i == Ykey_3_eat ? action :
7538 i == Ykey_4_eat ? action :
7539 i == Ykey_5_eat ? action :
7540 i == Ykey_6_eat ? action :
7541 i == Ykey_7_eat ? action :
7542 i == Ykey_8_eat ? action :
7543 i == Ylenses_eat ? action :
7544 i == Ymagnify_eat ? action :
7545 i == Ygrass_eat ? action :
7546 i == Ydirt_eat ? action :
7547 i == Xsand_stonein_1 ? action :
7548 i == Xsand_stonein_2 ? action :
7549 i == Xsand_stonein_3 ? action :
7550 i == Xsand_stonein_4 ? action :
7551 i == Xsand_stoneout_1 ? action :
7552 i == Xsand_stoneout_2 ? action :
7553 i == Xboom_android ? ACTION_EXPLODING :
7554 action_exploding ? ACTION_EXPLODING :
7555 action_active ? action :
7556 action_other ? action :
7558 int graphic = (el_act_dir2img(effective_element, effective_action,
7560 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7562 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7563 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7564 boolean has_action_graphics = (graphic != base_graphic);
7565 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7566 struct GraphicInfo *g = &graphic_info[graphic];
7567 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7570 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7571 boolean special_animation = (action != ACTION_DEFAULT &&
7572 g->anim_frames == 3 &&
7573 g->anim_delay == 2 &&
7574 g->anim_mode & ANIM_LINEAR);
7575 int sync_frame = (i == Xdrip_stretch ? 7 :
7576 i == Xdrip_stretchB ? 7 :
7577 i == Ydrip_s2 ? j + 8 :
7578 i == Ydrip_s2B ? j + 8 :
7587 i == Xfake_acid_1 ? 0 :
7588 i == Xfake_acid_2 ? 10 :
7589 i == Xfake_acid_3 ? 20 :
7590 i == Xfake_acid_4 ? 30 :
7591 i == Xfake_acid_5 ? 40 :
7592 i == Xfake_acid_6 ? 50 :
7593 i == Xfake_acid_7 ? 60 :
7594 i == Xfake_acid_8 ? 70 :
7596 i == Xball_2B ? j + 8 :
7597 i == Yball_eat ? j + 1 :
7598 i == Ykey_1_eat ? j + 1 :
7599 i == Ykey_2_eat ? j + 1 :
7600 i == Ykey_3_eat ? j + 1 :
7601 i == Ykey_4_eat ? j + 1 :
7602 i == Ykey_5_eat ? j + 1 :
7603 i == Ykey_6_eat ? j + 1 :
7604 i == Ykey_7_eat ? j + 1 :
7605 i == Ykey_8_eat ? j + 1 :
7606 i == Ylenses_eat ? j + 1 :
7607 i == Ymagnify_eat ? j + 1 :
7608 i == Ygrass_eat ? j + 1 :
7609 i == Ydirt_eat ? j + 1 :
7610 i == Xamoeba_1 ? 0 :
7611 i == Xamoeba_2 ? 1 :
7612 i == Xamoeba_3 ? 2 :
7613 i == Xamoeba_4 ? 3 :
7614 i == Xamoeba_5 ? 0 :
7615 i == Xamoeba_6 ? 1 :
7616 i == Xamoeba_7 ? 2 :
7617 i == Xamoeba_8 ? 3 :
7618 i == Xexit_2 ? j + 8 :
7619 i == Xexit_3 ? j + 16 :
7620 i == Xdynamite_1 ? 0 :
7621 i == Xdynamite_2 ? 8 :
7622 i == Xdynamite_3 ? 16 :
7623 i == Xdynamite_4 ? 24 :
7624 i == Xsand_stonein_1 ? j + 1 :
7625 i == Xsand_stonein_2 ? j + 9 :
7626 i == Xsand_stonein_3 ? j + 17 :
7627 i == Xsand_stonein_4 ? j + 25 :
7628 i == Xsand_stoneout_1 && j == 0 ? 0 :
7629 i == Xsand_stoneout_1 && j == 1 ? 0 :
7630 i == Xsand_stoneout_1 && j == 2 ? 1 :
7631 i == Xsand_stoneout_1 && j == 3 ? 2 :
7632 i == Xsand_stoneout_1 && j == 4 ? 2 :
7633 i == Xsand_stoneout_1 && j == 5 ? 3 :
7634 i == Xsand_stoneout_1 && j == 6 ? 4 :
7635 i == Xsand_stoneout_1 && j == 7 ? 4 :
7636 i == Xsand_stoneout_2 && j == 0 ? 5 :
7637 i == Xsand_stoneout_2 && j == 1 ? 6 :
7638 i == Xsand_stoneout_2 && j == 2 ? 7 :
7639 i == Xsand_stoneout_2 && j == 3 ? 8 :
7640 i == Xsand_stoneout_2 && j == 4 ? 9 :
7641 i == Xsand_stoneout_2 && j == 5 ? 11 :
7642 i == Xsand_stoneout_2 && j == 6 ? 13 :
7643 i == Xsand_stoneout_2 && j == 7 ? 15 :
7644 i == Xboom_bug && j == 1 ? 2 :
7645 i == Xboom_bug && j == 2 ? 2 :
7646 i == Xboom_bug && j == 3 ? 4 :
7647 i == Xboom_bug && j == 4 ? 4 :
7648 i == Xboom_bug && j == 5 ? 2 :
7649 i == Xboom_bug && j == 6 ? 2 :
7650 i == Xboom_bug && j == 7 ? 0 :
7651 i == Xboom_bomb && j == 1 ? 2 :
7652 i == Xboom_bomb && j == 2 ? 2 :
7653 i == Xboom_bomb && j == 3 ? 4 :
7654 i == Xboom_bomb && j == 4 ? 4 :
7655 i == Xboom_bomb && j == 5 ? 2 :
7656 i == Xboom_bomb && j == 6 ? 2 :
7657 i == Xboom_bomb && j == 7 ? 0 :
7658 i == Xboom_android && j == 7 ? 6 :
7659 i == Xboom_1 && j == 1 ? 2 :
7660 i == Xboom_1 && j == 2 ? 2 :
7661 i == Xboom_1 && j == 3 ? 4 :
7662 i == Xboom_1 && j == 4 ? 4 :
7663 i == Xboom_1 && j == 5 ? 6 :
7664 i == Xboom_1 && j == 6 ? 6 :
7665 i == Xboom_1 && j == 7 ? 8 :
7666 i == Xboom_2 && j == 0 ? 8 :
7667 i == Xboom_2 && j == 1 ? 8 :
7668 i == Xboom_2 && j == 2 ? 10 :
7669 i == Xboom_2 && j == 3 ? 10 :
7670 i == Xboom_2 && j == 4 ? 10 :
7671 i == Xboom_2 && j == 5 ? 12 :
7672 i == Xboom_2 && j == 6 ? 12 :
7673 i == Xboom_2 && j == 7 ? 12 :
7674 special_animation && j == 4 ? 3 :
7675 effective_action != action ? 0 :
7679 Bitmap *debug_bitmap = g_em->bitmap;
7680 int debug_src_x = g_em->src_x;
7681 int debug_src_y = g_em->src_y;
7684 int frame = getAnimationFrame(g->anim_frames,
7687 g->anim_start_frame,
7690 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7691 g->double_movement && is_backside);
7693 g_em->bitmap = src_bitmap;
7694 g_em->src_x = src_x;
7695 g_em->src_y = src_y;
7696 g_em->src_offset_x = 0;
7697 g_em->src_offset_y = 0;
7698 g_em->dst_offset_x = 0;
7699 g_em->dst_offset_y = 0;
7700 g_em->width = TILEX;
7701 g_em->height = TILEY;
7703 g_em->preserve_background = FALSE;
7705 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7708 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7709 effective_action == ACTION_MOVING ||
7710 effective_action == ACTION_PUSHING ||
7711 effective_action == ACTION_EATING)) ||
7712 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7713 effective_action == ACTION_EMPTYING)))
7716 (effective_action == ACTION_FALLING ||
7717 effective_action == ACTION_FILLING ||
7718 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7719 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7720 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7721 int num_steps = (i == Ydrip_s1 ? 16 :
7722 i == Ydrip_s1B ? 16 :
7723 i == Ydrip_s2 ? 16 :
7724 i == Ydrip_s2B ? 16 :
7725 i == Xsand_stonein_1 ? 32 :
7726 i == Xsand_stonein_2 ? 32 :
7727 i == Xsand_stonein_3 ? 32 :
7728 i == Xsand_stonein_4 ? 32 :
7729 i == Xsand_stoneout_1 ? 16 :
7730 i == Xsand_stoneout_2 ? 16 : 8);
7731 int cx = ABS(dx) * (TILEX / num_steps);
7732 int cy = ABS(dy) * (TILEY / num_steps);
7733 int step_frame = (i == Ydrip_s2 ? j + 8 :
7734 i == Ydrip_s2B ? j + 8 :
7735 i == Xsand_stonein_2 ? j + 8 :
7736 i == Xsand_stonein_3 ? j + 16 :
7737 i == Xsand_stonein_4 ? j + 24 :
7738 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7739 int step = (is_backside ? step_frame : num_steps - step_frame);
7741 if (is_backside) /* tile where movement starts */
7743 if (dx < 0 || dy < 0)
7745 g_em->src_offset_x = cx * step;
7746 g_em->src_offset_y = cy * step;
7750 g_em->dst_offset_x = cx * step;
7751 g_em->dst_offset_y = cy * step;
7754 else /* tile where movement ends */
7756 if (dx < 0 || dy < 0)
7758 g_em->dst_offset_x = cx * step;
7759 g_em->dst_offset_y = cy * step;
7763 g_em->src_offset_x = cx * step;
7764 g_em->src_offset_y = cy * step;
7768 g_em->width = TILEX - cx * step;
7769 g_em->height = TILEY - cy * step;
7772 /* create unique graphic identifier to decide if tile must be redrawn */
7773 /* bit 31 - 16 (16 bit): EM style graphic
7774 bit 15 - 12 ( 4 bit): EM style frame
7775 bit 11 - 6 ( 6 bit): graphic width
7776 bit 5 - 0 ( 6 bit): graphic height */
7777 g_em->unique_identifier =
7778 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7782 /* skip check for EMC elements not contained in original EMC artwork */
7783 if (element == EL_EMC_FAKE_ACID)
7786 if (g_em->bitmap != debug_bitmap ||
7787 g_em->src_x != debug_src_x ||
7788 g_em->src_y != debug_src_y ||
7789 g_em->src_offset_x != 0 ||
7790 g_em->src_offset_y != 0 ||
7791 g_em->dst_offset_x != 0 ||
7792 g_em->dst_offset_y != 0 ||
7793 g_em->width != TILEX ||
7794 g_em->height != TILEY)
7796 static int last_i = -1;
7804 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7805 i, element, element_info[element].token_name,
7806 element_action_info[effective_action].suffix, direction);
7808 if (element != effective_element)
7809 printf(" [%d ('%s')]",
7811 element_info[effective_element].token_name);
7815 if (g_em->bitmap != debug_bitmap)
7816 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7817 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7819 if (g_em->src_x != debug_src_x ||
7820 g_em->src_y != debug_src_y)
7821 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7822 j, (is_backside ? 'B' : 'F'),
7823 g_em->src_x, g_em->src_y,
7824 g_em->src_x / 32, g_em->src_y / 32,
7825 debug_src_x, debug_src_y,
7826 debug_src_x / 32, debug_src_y / 32);
7828 if (g_em->src_offset_x != 0 ||
7829 g_em->src_offset_y != 0 ||
7830 g_em->dst_offset_x != 0 ||
7831 g_em->dst_offset_y != 0)
7832 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7834 g_em->src_offset_x, g_em->src_offset_y,
7835 g_em->dst_offset_x, g_em->dst_offset_y);
7837 if (g_em->width != TILEX ||
7838 g_em->height != TILEY)
7839 printf(" %d (%d): size %d,%d should be %d,%d\n",
7841 g_em->width, g_em->height, TILEX, TILEY);
7843 num_em_gfx_errors++;
7850 for (i = 0; i < TILE_MAX; i++)
7852 for (j = 0; j < 8; j++)
7854 int element = object_mapping[i].element_rnd;
7855 int action = object_mapping[i].action;
7856 int direction = object_mapping[i].direction;
7857 boolean is_backside = object_mapping[i].is_backside;
7858 int graphic_action = el_act_dir2img(element, action, direction);
7859 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7861 if ((action == ACTION_SMASHED_BY_ROCK ||
7862 action == ACTION_SMASHED_BY_SPRING ||
7863 action == ACTION_EATING) &&
7864 graphic_action == graphic_default)
7866 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7867 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7868 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7869 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7872 /* no separate animation for "smashed by rock" -- use rock instead */
7873 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7874 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7876 g_em->bitmap = g_xx->bitmap;
7877 g_em->src_x = g_xx->src_x;
7878 g_em->src_y = g_xx->src_y;
7879 g_em->src_offset_x = g_xx->src_offset_x;
7880 g_em->src_offset_y = g_xx->src_offset_y;
7881 g_em->dst_offset_x = g_xx->dst_offset_x;
7882 g_em->dst_offset_y = g_xx->dst_offset_y;
7883 g_em->width = g_xx->width;
7884 g_em->height = g_xx->height;
7885 g_em->unique_identifier = g_xx->unique_identifier;
7888 g_em->preserve_background = TRUE;
7893 for (p = 0; p < MAX_PLAYERS; p++)
7895 for (i = 0; i < SPR_MAX; i++)
7897 int element = player_mapping[p][i].element_rnd;
7898 int action = player_mapping[p][i].action;
7899 int direction = player_mapping[p][i].direction;
7901 for (j = 0; j < 8; j++)
7903 int effective_element = element;
7904 int effective_action = action;
7905 int graphic = (direction == MV_NONE ?
7906 el_act2img(effective_element, effective_action) :
7907 el_act_dir2img(effective_element, effective_action,
7909 struct GraphicInfo *g = &graphic_info[graphic];
7910 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7916 Bitmap *debug_bitmap = g_em->bitmap;
7917 int debug_src_x = g_em->src_x;
7918 int debug_src_y = g_em->src_y;
7921 int frame = getAnimationFrame(g->anim_frames,
7924 g->anim_start_frame,
7927 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7929 g_em->bitmap = src_bitmap;
7930 g_em->src_x = src_x;
7931 g_em->src_y = src_y;
7932 g_em->src_offset_x = 0;
7933 g_em->src_offset_y = 0;
7934 g_em->dst_offset_x = 0;
7935 g_em->dst_offset_y = 0;
7936 g_em->width = TILEX;
7937 g_em->height = TILEY;
7941 /* skip check for EMC elements not contained in original EMC artwork */
7942 if (element == EL_PLAYER_3 ||
7943 element == EL_PLAYER_4)
7946 if (g_em->bitmap != debug_bitmap ||
7947 g_em->src_x != debug_src_x ||
7948 g_em->src_y != debug_src_y)
7950 static int last_i = -1;
7958 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7959 p, i, element, element_info[element].token_name,
7960 element_action_info[effective_action].suffix, direction);
7962 if (element != effective_element)
7963 printf(" [%d ('%s')]",
7965 element_info[effective_element].token_name);
7969 if (g_em->bitmap != debug_bitmap)
7970 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7971 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7973 if (g_em->src_x != debug_src_x ||
7974 g_em->src_y != debug_src_y)
7975 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7977 g_em->src_x, g_em->src_y,
7978 g_em->src_x / 32, g_em->src_y / 32,
7979 debug_src_x, debug_src_y,
7980 debug_src_x / 32, debug_src_y / 32);
7982 num_em_gfx_errors++;
7992 printf("::: [%d errors found]\n", num_em_gfx_errors);
7998 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7999 boolean any_player_moving,
8000 boolean any_player_snapping,
8001 boolean any_player_dropping)
8003 static boolean player_was_waiting = TRUE;
8005 if (frame == 0 && !any_player_dropping)
8007 if (!player_was_waiting)
8009 if (!SaveEngineSnapshotToList())
8012 player_was_waiting = TRUE;
8015 else if (any_player_moving || any_player_snapping || any_player_dropping)
8017 player_was_waiting = FALSE;
8021 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8022 boolean murphy_is_dropping)
8024 static boolean player_was_waiting = TRUE;
8026 if (murphy_is_waiting)
8028 if (!player_was_waiting)
8030 if (!SaveEngineSnapshotToList())
8033 player_was_waiting = TRUE;
8038 player_was_waiting = FALSE;
8042 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8043 boolean any_player_moving,
8044 boolean any_player_snapping,
8045 boolean any_player_dropping)
8047 if (tape.single_step && tape.recording && !tape.pausing)
8048 if (frame == 0 && !any_player_dropping)
8049 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8051 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8052 any_player_snapping, any_player_dropping);
8055 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8056 boolean murphy_is_dropping)
8058 if (tape.single_step && tape.recording && !tape.pausing)
8059 if (murphy_is_waiting)
8060 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8062 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8065 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8066 int graphic, int sync_frame, int x, int y)
8068 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8070 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8073 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8075 return (IS_NEXT_FRAME(sync_frame, graphic));
8078 int getGraphicInfo_Delay(int graphic)
8080 return graphic_info[graphic].anim_delay;
8083 void PlayMenuSoundExt(int sound)
8085 if (sound == SND_UNDEFINED)
8088 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8089 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8092 if (IS_LOOP_SOUND(sound))
8093 PlaySoundLoop(sound);
8098 void PlayMenuSound()
8100 PlayMenuSoundExt(menu.sound[game_status]);
8103 void PlayMenuSoundStereo(int sound, int stereo_position)
8105 if (sound == SND_UNDEFINED)
8108 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8109 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8112 if (IS_LOOP_SOUND(sound))
8113 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8115 PlaySoundStereo(sound, stereo_position);
8118 void PlayMenuSoundIfLoopExt(int sound)
8120 if (sound == SND_UNDEFINED)
8123 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8124 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8127 if (IS_LOOP_SOUND(sound))
8128 PlaySoundLoop(sound);
8131 void PlayMenuSoundIfLoop()
8133 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8136 void PlayMenuMusicExt(int music)
8138 if (music == MUS_UNDEFINED)
8141 if (!setup.sound_music)
8147 void PlayMenuMusic()
8149 PlayMenuMusicExt(menu.music[game_status]);
8152 void PlaySoundActivating()
8155 PlaySound(SND_MENU_ITEM_ACTIVATING);
8159 void PlaySoundSelecting()
8162 PlaySound(SND_MENU_ITEM_SELECTING);
8166 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8168 boolean change_fullscreen = (setup.fullscreen !=
8169 video.fullscreen_enabled);
8170 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8171 !strEqual(setup.fullscreen_mode,
8172 video.fullscreen_mode_current));
8173 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8174 setup.window_scaling_percent !=
8175 video.window_scaling_percent);
8177 if (change_window_scaling_percent && video.fullscreen_enabled)
8180 if (!change_window_scaling_percent && !video.fullscreen_available)
8183 #if defined(TARGET_SDL2)
8184 if (change_window_scaling_percent)
8186 SDLSetWindowScaling(setup.window_scaling_percent);
8190 else if (change_fullscreen)
8192 SDLSetWindowFullscreen(setup.fullscreen);
8194 /* set setup value according to successfully changed fullscreen mode */
8195 setup.fullscreen = video.fullscreen_enabled;
8201 if (change_fullscreen ||
8202 change_fullscreen_mode ||
8203 change_window_scaling_percent)
8205 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8207 /* save backbuffer content which gets lost when toggling fullscreen mode */
8208 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8210 if (change_fullscreen_mode)
8212 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8213 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8216 if (change_window_scaling_percent)
8218 /* keep window mode, but change window scaling */
8219 video.fullscreen_enabled = TRUE; /* force new window scaling */
8222 /* toggle fullscreen */
8223 ChangeVideoModeIfNeeded(setup.fullscreen);
8225 /* set setup value according to successfully changed fullscreen mode */
8226 setup.fullscreen = video.fullscreen_enabled;
8228 /* restore backbuffer content from temporary backbuffer backup bitmap */
8229 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8231 FreeBitmap(tmp_backbuffer);
8233 /* update visible window/screen */
8234 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8238 void JoinRectangles(int *x, int *y, int *width, int *height,
8239 int x2, int y2, int width2, int height2)
8241 // do not join with "off-screen" rectangle
8242 if (x2 == -1 || y2 == -1)
8247 *width = MAX(*width, width2);
8248 *height = MAX(*height, height2);
8251 void SetGameStatus(int game_status_new)
8253 game_status = game_status_new;
8255 global.anim_status_next = game_status;
8258 void ChangeViewportPropertiesIfNeeded()
8260 int gfx_game_mode = game_status;
8261 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8263 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8264 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8265 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8266 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8267 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8268 int new_win_xsize = vp_window->width;
8269 int new_win_ysize = vp_window->height;
8270 int border_size = vp_playfield->border_size;
8271 int new_sx = vp_playfield->x + border_size;
8272 int new_sy = vp_playfield->y + border_size;
8273 int new_sxsize = vp_playfield->width - 2 * border_size;
8274 int new_sysize = vp_playfield->height - 2 * border_size;
8275 int new_real_sx = vp_playfield->x;
8276 int new_real_sy = vp_playfield->y;
8277 int new_full_sxsize = vp_playfield->width;
8278 int new_full_sysize = vp_playfield->height;
8279 int new_dx = vp_door_1->x;
8280 int new_dy = vp_door_1->y;
8281 int new_dxsize = vp_door_1->width;
8282 int new_dysize = vp_door_1->height;
8283 int new_vx = vp_door_2->x;
8284 int new_vy = vp_door_2->y;
8285 int new_vxsize = vp_door_2->width;
8286 int new_vysize = vp_door_2->height;
8287 int new_ex = vp_door_3->x;
8288 int new_ey = vp_door_3->y;
8289 int new_exsize = vp_door_3->width;
8290 int new_eysize = vp_door_3->height;
8291 int new_tilesize_var =
8292 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8294 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8295 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8296 int new_scr_fieldx = new_sxsize / tilesize;
8297 int new_scr_fieldy = new_sysize / tilesize;
8298 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8299 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8300 boolean init_gfx_buffers = FALSE;
8301 boolean init_video_buffer = FALSE;
8302 boolean init_gadgets_and_toons = FALSE;
8303 boolean init_em_graphics = FALSE;
8305 if (new_win_xsize != WIN_XSIZE ||
8306 new_win_ysize != WIN_YSIZE)
8308 WIN_XSIZE = new_win_xsize;
8309 WIN_YSIZE = new_win_ysize;
8311 init_video_buffer = TRUE;
8312 init_gfx_buffers = TRUE;
8313 init_gadgets_and_toons = TRUE;
8315 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8318 if (new_scr_fieldx != SCR_FIELDX ||
8319 new_scr_fieldy != SCR_FIELDY)
8321 /* this always toggles between MAIN and GAME when using small tile size */
8323 SCR_FIELDX = new_scr_fieldx;
8324 SCR_FIELDY = new_scr_fieldy;
8326 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8337 new_sxsize != SXSIZE ||
8338 new_sysize != SYSIZE ||
8339 new_dxsize != DXSIZE ||
8340 new_dysize != DYSIZE ||
8341 new_vxsize != VXSIZE ||
8342 new_vysize != VYSIZE ||
8343 new_exsize != EXSIZE ||
8344 new_eysize != EYSIZE ||
8345 new_real_sx != REAL_SX ||
8346 new_real_sy != REAL_SY ||
8347 new_full_sxsize != FULL_SXSIZE ||
8348 new_full_sysize != FULL_SYSIZE ||
8349 new_tilesize_var != TILESIZE_VAR
8352 // ------------------------------------------------------------------------
8353 // determine next fading area for changed viewport definitions
8354 // ------------------------------------------------------------------------
8356 // start with current playfield area (default fading area)
8359 FADE_SXSIZE = FULL_SXSIZE;
8360 FADE_SYSIZE = FULL_SYSIZE;
8362 // add new playfield area if position or size has changed
8363 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8364 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8366 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8367 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8370 // add current and new door 1 area if position or size has changed
8371 if (new_dx != DX || new_dy != DY ||
8372 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8374 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8375 DX, DY, DXSIZE, DYSIZE);
8376 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8377 new_dx, new_dy, new_dxsize, new_dysize);
8380 // add current and new door 2 area if position or size has changed
8381 if (new_dx != VX || new_dy != VY ||
8382 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8384 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8385 VX, VY, VXSIZE, VYSIZE);
8386 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8387 new_vx, new_vy, new_vxsize, new_vysize);
8390 // ------------------------------------------------------------------------
8391 // handle changed tile size
8392 // ------------------------------------------------------------------------
8394 if (new_tilesize_var != TILESIZE_VAR)
8396 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8398 // changing tile size invalidates scroll values of engine snapshots
8399 FreeEngineSnapshotSingle();
8401 // changing tile size requires update of graphic mapping for EM engine
8402 init_em_graphics = TRUE;
8413 SXSIZE = new_sxsize;
8414 SYSIZE = new_sysize;
8415 DXSIZE = new_dxsize;
8416 DYSIZE = new_dysize;
8417 VXSIZE = new_vxsize;
8418 VYSIZE = new_vysize;
8419 EXSIZE = new_exsize;
8420 EYSIZE = new_eysize;
8421 REAL_SX = new_real_sx;
8422 REAL_SY = new_real_sy;
8423 FULL_SXSIZE = new_full_sxsize;
8424 FULL_SYSIZE = new_full_sysize;
8425 TILESIZE_VAR = new_tilesize_var;
8427 init_gfx_buffers = TRUE;
8428 init_gadgets_and_toons = TRUE;
8430 // printf("::: viewports: init_gfx_buffers\n");
8431 // printf("::: viewports: init_gadgets_and_toons\n");
8434 if (init_gfx_buffers)
8436 // printf("::: init_gfx_buffers\n");
8438 SCR_FIELDX = new_scr_fieldx_buffers;
8439 SCR_FIELDY = new_scr_fieldy_buffers;
8443 SCR_FIELDX = new_scr_fieldx;
8444 SCR_FIELDY = new_scr_fieldy;
8446 SetDrawDeactivationMask(REDRAW_NONE);
8447 SetDrawBackgroundMask(REDRAW_FIELD);
8450 if (init_video_buffer)
8452 // printf("::: init_video_buffer\n");
8454 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8455 InitImageTextures();
8458 if (init_gadgets_and_toons)
8460 // printf("::: init_gadgets_and_toons\n");
8464 InitGlobalAnimations();
8467 if (init_em_graphics)
8469 InitGraphicInfo_EM();