1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static char *print_if_not_empty(int element)
178 static char *s = NULL;
179 char *token_name = element_info[element].token_name;
184 s = checked_malloc(strlen(token_name) + 10 + 1);
186 if (element != EL_EMPTY)
187 sprintf(s, "%d\t['%s']", element, token_name);
189 sprintf(s, "%d", element);
194 void DumpTile(int x, int y)
199 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
205 printf_line("-", 79);
206 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
207 printf_line("-", 79);
209 if (!IN_LEV_FIELD(x, y))
211 printf("(not in level field)\n");
217 printf(" Feld: %d\t['%s']\n", Feld[x][y],
218 element_info[Feld[x][y]].token_name);
219 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
220 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
221 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
222 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
223 printf(" MovPos: %d\n", MovPos[x][y]);
224 printf(" MovDir: %d\n", MovDir[x][y]);
225 printf(" MovDelay: %d\n", MovDelay[x][y]);
226 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
227 printf(" CustomValue: %d\n", CustomValue[x][y]);
228 printf(" GfxElement: %d\n", GfxElement[x][y]);
229 printf(" GfxAction: %d\n", GfxAction[x][y]);
230 printf(" GfxFrame: %d\n", GfxFrame[x][y]);
234 void SetDrawtoField(int mode)
236 if (mode == DRAW_FIELDBUFFER)
242 BX2 = SCR_FIELDX + 1;
243 BY2 = SCR_FIELDY + 1;
247 drawto_field = fieldbuffer;
249 else /* DRAW_BACKBUFFER */
255 BX2 = SCR_FIELDX - 1;
256 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 void DrawMaskedBorder_Rect(int x, int y, int width, int height)
293 Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
295 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
298 void DrawMaskedBorder_FIELD()
300 if (global.border_status >= GAME_MODE_TITLE &&
301 global.border_status <= GAME_MODE_PLAYING &&
302 border.draw_masked[global.border_status])
303 DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
306 void DrawMaskedBorder_DOOR_1()
308 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
309 (global.border_status != GAME_MODE_EDITOR ||
310 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
311 DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
314 void DrawMaskedBorder_DOOR_2()
316 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
317 global.border_status != GAME_MODE_EDITOR)
318 DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
321 void DrawMaskedBorder_DOOR_3()
323 /* currently not available */
326 void DrawMaskedBorder_ALL()
328 DrawMaskedBorder_FIELD();
329 DrawMaskedBorder_DOOR_1();
330 DrawMaskedBorder_DOOR_2();
331 DrawMaskedBorder_DOOR_3();
334 void DrawMaskedBorder(int redraw_mask)
336 /* never draw masked screen borders on borderless screens */
337 if (effectiveGameStatus() == GAME_MODE_LOADING ||
338 effectiveGameStatus() == GAME_MODE_TITLE)
341 /* never draw masked screen borders when displaying request outside door */
342 if (effectiveGameStatus() == GAME_MODE_PSEUDO_DOOR &&
343 global.use_envelope_request)
346 if (redraw_mask & REDRAW_ALL)
347 DrawMaskedBorder_ALL();
350 if (redraw_mask & REDRAW_FIELD)
351 DrawMaskedBorder_FIELD();
352 if (redraw_mask & REDRAW_DOOR_1)
353 DrawMaskedBorder_DOOR_1();
354 if (redraw_mask & REDRAW_DOOR_2)
355 DrawMaskedBorder_DOOR_2();
356 if (redraw_mask & REDRAW_DOOR_3)
357 DrawMaskedBorder_DOOR_3();
361 static void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
363 DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
364 int fx = FX, fy = FY;
365 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
366 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
368 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
369 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
370 int dx_var = dx * TILESIZE_VAR / TILESIZE;
371 int dy_var = dy * TILESIZE_VAR / TILESIZE;
374 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
375 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
377 if (EVEN(SCR_FIELDX))
379 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
380 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
382 fx += (dx_var > 0 ? TILEX_VAR : 0);
389 if (EVEN(SCR_FIELDY))
391 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
392 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
394 fy += (dy_var > 0 ? TILEY_VAR : 0);
401 if (full_lev_fieldx <= SCR_FIELDX)
403 if (EVEN(SCR_FIELDX))
404 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
406 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
409 if (full_lev_fieldy <= SCR_FIELDY)
411 if (EVEN(SCR_FIELDY))
412 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
414 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
417 if (border.draw_masked[GAME_MODE_PLAYING])
419 if (buffer != backbuffer)
421 /* copy playfield buffer to backbuffer to add masked border */
422 BlitBitmap(buffer, backbuffer, fx, fy, SXSIZE, SYSIZE, SX, SY);
423 DrawMaskedBorder(REDRAW_FIELD);
426 BlitBitmap(backbuffer, target_bitmap,
427 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
432 BlitBitmap(buffer, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
436 void BlitScreenToBitmap(Bitmap *target_bitmap)
438 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
439 BlitScreenToBitmap_EM(target_bitmap);
440 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
441 BlitScreenToBitmap_SP(target_bitmap);
442 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
443 BlitScreenToBitmap_RND(target_bitmap);
449 DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
451 // never redraw single tiles, always redraw the whole field
452 if (redraw_mask & REDRAW_TILES)
453 redraw_mask |= REDRAW_FIELD;
456 /* !!! TEST ONLY !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */
457 /* (force full redraw) */
458 if (game_status == GAME_MODE_PLAYING)
459 redraw_mask |= REDRAW_FIELD;
462 if (redraw_mask & REDRAW_FIELD)
463 redraw_mask &= ~REDRAW_TILES;
465 if (redraw_mask == REDRAW_NONE)
470 if (redraw_mask & REDRAW_ALL)
471 printf("[REDRAW_ALL]");
472 if (redraw_mask & REDRAW_FIELD)
473 printf("[REDRAW_FIELD]");
474 if (redraw_mask & REDRAW_TILES)
475 printf("[REDRAW_TILES]");
476 if (redraw_mask & REDRAW_DOOR_1)
477 printf("[REDRAW_DOOR_1]");
478 if (redraw_mask & REDRAW_DOOR_2)
479 printf("[REDRAW_DOOR_2]");
480 if (redraw_mask & REDRAW_FROM_BACKBUFFER)
481 printf("[REDRAW_FROM_BACKBUFFER]");
482 printf(" [%d]\n", FrameCounter);
485 if (redraw_mask & REDRAW_TILES &&
486 game_status == GAME_MODE_PLAYING &&
487 border.draw_masked[GAME_MODE_PLAYING])
488 redraw_mask |= REDRAW_FIELD;
490 if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
492 static boolean last_frame_skipped = FALSE;
493 boolean skip_even_when_not_scrolling = TRUE;
494 boolean just_scrolling = (ScreenMovDir != 0);
495 boolean verbose = FALSE;
497 if (global.fps_slowdown_factor > 1 &&
498 (FrameCounter % global.fps_slowdown_factor) &&
499 (just_scrolling || skip_even_when_not_scrolling))
501 redraw_mask &= ~REDRAW_MAIN;
503 last_frame_skipped = TRUE;
506 printf("FRAME SKIPPED\n");
510 if (last_frame_skipped)
511 redraw_mask |= REDRAW_FIELD;
513 last_frame_skipped = FALSE;
516 printf("frame not skipped\n");
520 /* synchronize X11 graphics at this point; if we would synchronize the
521 display immediately after the buffer switching (after the XFlush),
522 this could mean that we have to wait for the graphics to complete,
523 although we could go on doing calculations for the next frame */
527 /* never draw masked border to backbuffer when using playfield buffer */
528 if (game_status != GAME_MODE_PLAYING ||
529 redraw_mask & REDRAW_FROM_BACKBUFFER ||
530 buffer == backbuffer)
531 DrawMaskedBorder(redraw_mask);
533 DrawMaskedBorder(redraw_mask & REDRAW_DOORS);
535 if (redraw_mask & REDRAW_ALL)
537 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
539 redraw_mask = REDRAW_NONE;
542 if (redraw_mask & REDRAW_FIELD)
544 if (game_status != GAME_MODE_PLAYING ||
545 redraw_mask & REDRAW_FROM_BACKBUFFER)
547 BlitBitmap(backbuffer, window,
548 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
552 BlitScreenToBitmap_RND(window);
555 redraw_mask &= ~REDRAW_MAIN;
558 if (redraw_mask & REDRAW_DOORS)
560 if (redraw_mask & REDRAW_DOOR_1)
561 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
563 if (redraw_mask & REDRAW_DOOR_2)
564 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
566 if (redraw_mask & REDRAW_DOOR_3)
567 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
569 redraw_mask &= ~REDRAW_DOORS;
572 if (redraw_mask & REDRAW_MICROLEVEL)
574 BlitBitmap(backbuffer, window, SX, SY + 10 * TILEY, SXSIZE, 7 * TILEY,
575 SX, SY + 10 * TILEY);
577 redraw_mask &= ~REDRAW_MICROLEVEL;
580 if (redraw_mask & REDRAW_FPS) /* display frames per second */
585 sprintf(info1, " (only every %d. frame)", global.fps_slowdown_factor);
586 if (!global.fps_slowdown)
589 sprintf(text, "%04.1f fps%s", global.frames_per_second, info1);
591 DrawTextExt(window, SX + SXSIZE + SX, 0, text, FONT_TEXT_2, BLIT_OPAQUE);
594 for (x = 0; x < MAX_BUF_XSIZE; x++)
595 for (y = 0; y < MAX_BUF_YSIZE; y++)
598 redraw_mask = REDRAW_NONE;
601 static void FadeCrossSaveBackbuffer()
603 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
606 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
608 static int fade_type_skip = FADE_TYPE_NONE;
609 void (*draw_border_function)(void) = NULL;
610 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
611 int x, y, width, height;
612 int fade_delay, post_delay;
614 if (fade_type == FADE_TYPE_FADE_OUT)
616 if (fade_type_skip != FADE_TYPE_NONE)
618 /* skip all fade operations until specified fade operation */
619 if (fade_type & fade_type_skip)
620 fade_type_skip = FADE_TYPE_NONE;
625 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
627 FadeCrossSaveBackbuffer();
633 redraw_mask |= fade_mask;
635 if (fade_type == FADE_TYPE_SKIP)
637 fade_type_skip = fade_mode;
642 fade_delay = fading.fade_delay;
643 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
645 if (fade_type_skip != FADE_TYPE_NONE)
647 /* skip all fade operations until specified fade operation */
648 if (fade_type & fade_type_skip)
649 fade_type_skip = FADE_TYPE_NONE;
654 if (global.autoplay_leveldir)
659 if (fade_mask == REDRAW_FIELD)
664 height = FULL_SYSIZE;
666 if (border.draw_masked_when_fading)
667 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
669 DrawMaskedBorder_FIELD(); /* draw once */
671 else /* REDRAW_ALL */
679 if (!setup.fade_screens ||
681 fading.fade_mode == FADE_MODE_NONE)
683 if (fade_mode == FADE_MODE_FADE_OUT)
686 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
688 redraw_mask &= ~fade_mask;
693 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
694 draw_border_function);
696 redraw_mask &= ~fade_mask;
699 void FadeIn(int fade_mask)
701 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
702 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
704 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
707 void FadeOut(int fade_mask)
709 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
710 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
712 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
714 global.border_status = game_status;
717 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
719 static struct TitleFadingInfo fading_leave_stored;
722 fading_leave_stored = fading_leave;
724 fading = fading_leave_stored;
727 void FadeSetEnterMenu()
729 fading = menu.enter_menu;
731 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
734 void FadeSetLeaveMenu()
736 fading = menu.leave_menu;
738 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
741 void FadeSetEnterScreen()
743 fading = menu.enter_screen[game_status];
745 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
748 void FadeSetNextScreen()
750 fading = menu.next_screen;
752 // (do not overwrite fade mode set by FadeSetEnterScreen)
753 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
756 void FadeSetLeaveScreen()
758 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
761 void FadeSetFromType(int type)
763 if (type & TYPE_ENTER_SCREEN)
764 FadeSetEnterScreen();
765 else if (type & TYPE_ENTER)
767 else if (type & TYPE_LEAVE)
771 void FadeSetDisabled()
773 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
775 fading = fading_none;
778 void FadeSkipNextFadeIn()
780 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
783 void FadeSkipNextFadeOut()
785 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
788 void SetWindowBackgroundImageIfDefined(int graphic)
790 if (graphic_info[graphic].bitmap)
791 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
794 void SetMainBackgroundImageIfDefined(int graphic)
796 if (graphic_info[graphic].bitmap)
797 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
800 void SetDoorBackgroundImageIfDefined(int graphic)
802 if (graphic_info[graphic].bitmap)
803 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
806 void SetWindowBackgroundImage(int graphic)
808 SetWindowBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
809 graphic_info[graphic].bitmap ?
810 graphic_info[graphic].bitmap :
811 graphic_info[IMG_BACKGROUND].bitmap);
814 void SetMainBackgroundImage(int graphic)
816 SetMainBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
817 graphic_info[graphic].bitmap ?
818 graphic_info[graphic].bitmap :
819 graphic_info[IMG_BACKGROUND].bitmap);
822 void SetDoorBackgroundImage(int graphic)
824 SetDoorBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
825 graphic_info[graphic].bitmap ?
826 graphic_info[graphic].bitmap :
827 graphic_info[IMG_BACKGROUND].bitmap);
830 void SetPanelBackground()
832 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
834 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
835 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
837 SetDoorBackgroundBitmap(bitmap_db_panel);
840 void DrawBackground(int x, int y, int width, int height)
842 /* "drawto" might still point to playfield buffer here (hall of fame) */
843 ClearRectangleOnBackground(backbuffer, x, y, width, height);
845 if (IN_GFX_FIELD_FULL(x, y))
846 redraw_mask |= REDRAW_FIELD;
847 else if (IN_GFX_DOOR_1(x, y))
848 redraw_mask |= REDRAW_DOOR_1;
849 else if (IN_GFX_DOOR_2(x, y))
850 redraw_mask |= REDRAW_DOOR_2;
851 else if (IN_GFX_DOOR_3(x, y))
852 redraw_mask |= REDRAW_DOOR_3;
855 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
857 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
859 if (font->bitmap == NULL)
862 DrawBackground(x, y, width, height);
865 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
867 struct GraphicInfo *g = &graphic_info[graphic];
869 if (g->bitmap == NULL)
872 DrawBackground(x, y, width, height);
877 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
878 /* (when entering hall of fame after playing) */
879 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
881 /* !!! maybe this should be done before clearing the background !!! */
882 if (game_status == GAME_MODE_PLAYING)
884 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
885 SetDrawtoField(DRAW_FIELDBUFFER);
888 SetDrawtoField(DRAW_BACKBUFFER);
891 void MarkTileDirty(int x, int y)
893 int xx = redraw_x1 + x;
894 int yy = redraw_y1 + y;
899 redraw[xx][yy] = TRUE;
900 redraw_mask |= REDRAW_TILES;
903 void SetBorderElement()
907 BorderElement = EL_EMPTY;
909 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
911 for (x = 0; x < lev_fieldx; x++)
913 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
914 BorderElement = EL_STEELWALL;
916 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
922 void FloodFillLevel(int from_x, int from_y, int fill_element,
923 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
924 int max_fieldx, int max_fieldy)
928 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
929 static int safety = 0;
931 /* check if starting field still has the desired content */
932 if (field[from_x][from_y] == fill_element)
937 if (safety > max_fieldx * max_fieldy)
938 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
940 old_element = field[from_x][from_y];
941 field[from_x][from_y] = fill_element;
943 for (i = 0; i < 4; i++)
945 x = from_x + check[i][0];
946 y = from_y + check[i][1];
948 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
949 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
955 void SetRandomAnimationValue(int x, int y)
957 gfx.anim_random_frame = GfxRandom[x][y];
960 inline int getGraphicAnimationFrame(int graphic, int sync_frame)
962 /* animation synchronized with global frame counter, not move position */
963 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
964 sync_frame = FrameCounter;
966 return getAnimationFrame(graphic_info[graphic].anim_frames,
967 graphic_info[graphic].anim_delay,
968 graphic_info[graphic].anim_mode,
969 graphic_info[graphic].anim_start_frame,
973 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
974 Bitmap **bitmap, int *x, int *y,
975 boolean get_backside)
977 struct GraphicInfo *g = &graphic_info[graphic];
978 Bitmap *src_bitmap = g->bitmap;
979 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
980 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
981 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
983 // if no in-game graphics defined, always use standard graphic size
984 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
987 if (tilesize == gfx.standard_tile_size)
988 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
989 else if (tilesize == game.tile_size)
990 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
992 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
994 if (g->offset_y == 0) /* frames are ordered horizontally */
996 int max_width = g->anim_frames_per_line * g->width;
997 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
999 src_x = pos % max_width;
1000 src_y = src_y % g->height + pos / max_width * g->height;
1002 else if (g->offset_x == 0) /* frames are ordered vertically */
1004 int max_height = g->anim_frames_per_line * g->height;
1005 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1007 src_x = src_x % g->width + pos / max_height * g->width;
1008 src_y = pos % max_height;
1010 else /* frames are ordered diagonally */
1012 src_x = src_x + frame * g->offset_x;
1013 src_y = src_y + frame * g->offset_y;
1016 *bitmap = src_bitmap;
1017 *x = src_x * tilesize / TILESIZE;
1018 *y = src_y * tilesize / TILESIZE;
1021 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1022 int *x, int *y, boolean get_backside)
1024 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1028 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1029 Bitmap **bitmap, int *x, int *y)
1031 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1034 void getFixedGraphicSource(int graphic, int frame,
1035 Bitmap **bitmap, int *x, int *y)
1037 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1040 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1042 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1045 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1046 int *x, int *y, boolean get_backside)
1048 struct GraphicInfo *g = &graphic_info[graphic];
1049 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1050 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1052 if (TILESIZE_VAR != TILESIZE)
1053 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1056 *bitmap = g->bitmap;
1058 if (g->offset_y == 0) /* frames are ordered horizontally */
1060 int max_width = g->anim_frames_per_line * g->width;
1061 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1063 *x = pos % max_width;
1064 *y = src_y % g->height + pos / max_width * g->height;
1066 else if (g->offset_x == 0) /* frames are ordered vertically */
1068 int max_height = g->anim_frames_per_line * g->height;
1069 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1071 *x = src_x % g->width + pos / max_height * g->width;
1072 *y = pos % max_height;
1074 else /* frames are ordered diagonally */
1076 *x = src_x + frame * g->offset_x;
1077 *y = src_y + frame * g->offset_y;
1081 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1083 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1086 void DrawGraphic(int x, int y, int graphic, int frame)
1089 if (!IN_SCR_FIELD(x, y))
1091 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1092 printf("DrawGraphic(): This should never happen!\n");
1097 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1100 MarkTileDirty(x, y);
1103 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1106 if (!IN_SCR_FIELD(x, y))
1108 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1109 printf("DrawGraphic(): This should never happen!\n");
1114 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1116 MarkTileDirty(x, y);
1119 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1125 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1127 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1130 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1136 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1137 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1140 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1143 if (!IN_SCR_FIELD(x, y))
1145 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1146 printf("DrawGraphicThruMask(): This should never happen!\n");
1151 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1154 MarkTileDirty(x, y);
1157 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1160 if (!IN_SCR_FIELD(x, y))
1162 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1163 printf("DrawGraphicThruMask(): This should never happen!\n");
1168 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1170 MarkTileDirty(x, y);
1173 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1179 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1181 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1185 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1186 int graphic, int frame)
1188 struct GraphicInfo *g = &graphic_info[graphic];
1192 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1194 BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
1198 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1200 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1202 MarkTileDirty(x / tilesize, y / tilesize);
1205 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1211 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1212 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1215 void DrawMiniGraphic(int x, int y, int graphic)
1217 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1218 MarkTileDirty(x / 2, y / 2);
1221 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1226 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1227 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1230 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1231 int graphic, int frame,
1232 int cut_mode, int mask_mode)
1237 int width = TILEX, height = TILEY;
1240 if (dx || dy) /* shifted graphic */
1242 if (x < BX1) /* object enters playfield from the left */
1249 else if (x > BX2) /* object enters playfield from the right */
1255 else if (x==BX1 && dx < 0) /* object leaves playfield to the left */
1261 else if (x==BX2 && dx > 0) /* object leaves playfield to the right */
1263 else if (dx) /* general horizontal movement */
1264 MarkTileDirty(x + SIGN(dx), y);
1266 if (y < BY1) /* object enters playfield from the top */
1268 if (cut_mode==CUT_BELOW) /* object completely above top border */
1276 else if (y > BY2) /* object enters playfield from the bottom */
1282 else if (y==BY1 && dy < 0) /* object leaves playfield to the top */
1288 else if (dy > 0 && cut_mode == CUT_ABOVE)
1290 if (y == BY2) /* object completely above bottom border */
1296 MarkTileDirty(x, y + 1);
1297 } /* object leaves playfield to the bottom */
1298 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1300 else if (dy) /* general vertical movement */
1301 MarkTileDirty(x, y + SIGN(dy));
1305 if (!IN_SCR_FIELD(x, y))
1307 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1308 printf("DrawGraphicShifted(): This should never happen!\n");
1313 width = width * TILESIZE_VAR / TILESIZE;
1314 height = height * TILESIZE_VAR / TILESIZE;
1315 cx = cx * TILESIZE_VAR / TILESIZE;
1316 cy = cy * TILESIZE_VAR / TILESIZE;
1317 dx = dx * TILESIZE_VAR / TILESIZE;
1318 dy = dy * TILESIZE_VAR / TILESIZE;
1320 if (width > 0 && height > 0)
1322 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1327 dst_x = FX + x * TILEX_VAR + dx;
1328 dst_y = FY + y * TILEY_VAR + dy;
1330 if (mask_mode == USE_MASKING)
1331 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1334 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1337 MarkTileDirty(x, y);
1341 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1342 int graphic, int frame,
1343 int cut_mode, int mask_mode)
1348 int width = TILEX_VAR, height = TILEY_VAR;
1351 int x2 = x + SIGN(dx);
1352 int y2 = y + SIGN(dy);
1354 /* movement with two-tile animations must be sync'ed with movement position,
1355 not with current GfxFrame (which can be higher when using slow movement) */
1356 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1357 int anim_frames = graphic_info[graphic].anim_frames;
1359 /* (we also need anim_delay here for movement animations with less frames) */
1360 int anim_delay = graphic_info[graphic].anim_delay;
1361 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1363 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1364 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1366 /* re-calculate animation frame for two-tile movement animation */
1367 frame = getGraphicAnimationFrame(graphic, sync_frame);
1369 /* check if movement start graphic inside screen area and should be drawn */
1370 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1372 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1374 dst_x = FX + x1 * TILEX_VAR;
1375 dst_y = FY + y1 * TILEY_VAR;
1377 if (mask_mode == USE_MASKING)
1378 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1381 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1384 MarkTileDirty(x1, y1);
1387 /* check if movement end graphic inside screen area and should be drawn */
1388 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1390 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1392 dst_x = FX + x2 * TILEX_VAR;
1393 dst_y = FY + y2 * TILEY_VAR;
1395 if (mask_mode == USE_MASKING)
1396 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1399 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1402 MarkTileDirty(x2, y2);
1406 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1407 int graphic, int frame,
1408 int cut_mode, int mask_mode)
1412 DrawGraphic(x, y, graphic, frame);
1417 if (graphic_info[graphic].double_movement) /* EM style movement images */
1418 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1420 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1423 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1424 int frame, int cut_mode)
1426 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1429 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1430 int cut_mode, int mask_mode)
1432 int lx = LEVELX(x), ly = LEVELY(y);
1436 if (IN_LEV_FIELD(lx, ly))
1438 SetRandomAnimationValue(lx, ly);
1440 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1441 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1443 /* do not use double (EM style) movement graphic when not moving */
1444 if (graphic_info[graphic].double_movement && !dx && !dy)
1446 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1447 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1450 else /* border element */
1452 graphic = el2img(element);
1453 frame = getGraphicAnimationFrame(graphic, -1);
1456 if (element == EL_EXPANDABLE_WALL)
1458 boolean left_stopped = FALSE, right_stopped = FALSE;
1460 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1461 left_stopped = TRUE;
1462 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1463 right_stopped = TRUE;
1465 if (left_stopped && right_stopped)
1467 else if (left_stopped)
1469 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1470 frame = graphic_info[graphic].anim_frames - 1;
1472 else if (right_stopped)
1474 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1475 frame = graphic_info[graphic].anim_frames - 1;
1480 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1481 else if (mask_mode == USE_MASKING)
1482 DrawGraphicThruMask(x, y, graphic, frame);
1484 DrawGraphic(x, y, graphic, frame);
1487 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1488 int cut_mode, int mask_mode)
1490 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1491 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1492 cut_mode, mask_mode);
1495 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1498 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1501 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1504 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1507 void DrawLevelElementThruMask(int x, int y, int element)
1509 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1512 void DrawLevelFieldThruMask(int x, int y)
1514 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1517 /* !!! implementation of quicksand is totally broken !!! */
1518 #define IS_CRUMBLED_TILE(x, y, e) \
1519 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1520 !IS_MOVING(x, y) || \
1521 (e) == EL_QUICKSAND_EMPTYING || \
1522 (e) == EL_QUICKSAND_FAST_EMPTYING))
1524 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1529 int width, height, cx, cy;
1530 int sx = SCREENX(x), sy = SCREENY(y);
1531 int crumbled_border_size = graphic_info[graphic].border_size;
1534 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1536 for (i = 1; i < 4; i++)
1538 int dxx = (i & 1 ? dx : 0);
1539 int dyy = (i & 2 ? dy : 0);
1542 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1545 /* check if neighbour field is of same crumble type */
1546 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1547 graphic_info[graphic].class ==
1548 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1550 /* return if check prevents inner corner */
1551 if (same == (dxx == dx && dyy == dy))
1555 /* if we reach this point, we have an inner corner */
1557 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1559 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1560 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1561 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1562 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1564 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1565 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1568 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1573 int width, height, bx, by, cx, cy;
1574 int sx = SCREENX(x), sy = SCREENY(y);
1575 int crumbled_border_size = graphic_info[graphic].border_size;
1576 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1577 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1580 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1582 /* draw simple, sloppy, non-corner-accurate crumbled border */
1584 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1585 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1586 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1587 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1589 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1590 FX + sx * TILEX_VAR + cx,
1591 FY + sy * TILEY_VAR + cy);
1593 /* (remaining middle border part must be at least as big as corner part) */
1594 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1595 crumbled_border_size >= TILESIZE / 3)
1598 /* correct corners of crumbled border, if needed */
1600 for (i = -1; i <= 1; i += 2)
1602 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1603 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1604 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1607 /* check if neighbour field is of same crumble type */
1608 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1609 graphic_info[graphic].class ==
1610 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1612 /* no crumbled corner, but continued crumbled border */
1614 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1615 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1616 int b1 = (i == 1 ? crumbled_border_size_var :
1617 TILESIZE_VAR - 2 * crumbled_border_size_var);
1619 width = crumbled_border_size_var;
1620 height = crumbled_border_size_var;
1622 if (dir == 1 || dir == 2)
1637 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1639 FX + sx * TILEX_VAR + cx,
1640 FY + sy * TILEY_VAR + cy);
1645 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1647 int sx = SCREENX(x), sy = SCREENY(y);
1650 static int xy[4][2] =
1658 if (!IN_LEV_FIELD(x, y))
1661 element = TILE_GFX_ELEMENT(x, y);
1663 /* crumble field itself */
1664 if (IS_CRUMBLED_TILE(x, y, element))
1666 if (!IN_SCR_FIELD(sx, sy))
1669 for (i = 0; i < 4; i++)
1671 int xx = x + xy[i][0];
1672 int yy = y + xy[i][1];
1674 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1677 /* check if neighbour field is of same crumble type */
1678 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1679 graphic_info[graphic].class ==
1680 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1683 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1686 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1687 graphic_info[graphic].anim_frames == 2)
1689 for (i = 0; i < 4; i++)
1691 int dx = (i & 1 ? +1 : -1);
1692 int dy = (i & 2 ? +1 : -1);
1694 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1698 MarkTileDirty(sx, sy);
1700 else /* center field not crumbled -- crumble neighbour fields */
1702 for (i = 0; i < 4; i++)
1704 int xx = x + xy[i][0];
1705 int yy = y + xy[i][1];
1706 int sxx = sx + xy[i][0];
1707 int syy = sy + xy[i][1];
1709 if (!IN_LEV_FIELD(xx, yy) ||
1710 !IN_SCR_FIELD(sxx, syy))
1713 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1716 element = TILE_GFX_ELEMENT(xx, yy);
1718 if (!IS_CRUMBLED_TILE(xx, yy, element))
1721 graphic = el_act2crm(element, ACTION_DEFAULT);
1723 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1725 MarkTileDirty(sxx, syy);
1730 void DrawLevelFieldCrumbled(int x, int y)
1734 if (!IN_LEV_FIELD(x, y))
1737 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1738 GfxElement[x][y] != EL_UNDEFINED &&
1739 GFX_CRUMBLED(GfxElement[x][y]))
1741 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1746 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1748 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1751 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1754 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1755 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1756 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1757 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1758 int sx = SCREENX(x), sy = SCREENY(y);
1760 DrawGraphic(sx, sy, graphic1, frame1);
1761 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1764 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1766 int sx = SCREENX(x), sy = SCREENY(y);
1767 static int xy[4][2] =
1776 for (i = 0; i < 4; i++)
1778 int xx = x + xy[i][0];
1779 int yy = y + xy[i][1];
1780 int sxx = sx + xy[i][0];
1781 int syy = sy + xy[i][1];
1783 if (!IN_LEV_FIELD(xx, yy) ||
1784 !IN_SCR_FIELD(sxx, syy) ||
1785 !GFX_CRUMBLED(Feld[xx][yy]) ||
1789 DrawLevelField(xx, yy);
1793 static int getBorderElement(int x, int y)
1797 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1798 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1799 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1800 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1801 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1802 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1803 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1805 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1806 int steel_position = (x == -1 && y == -1 ? 0 :
1807 x == lev_fieldx && y == -1 ? 1 :
1808 x == -1 && y == lev_fieldy ? 2 :
1809 x == lev_fieldx && y == lev_fieldy ? 3 :
1810 x == -1 || x == lev_fieldx ? 4 :
1811 y == -1 || y == lev_fieldy ? 5 : 6);
1813 return border[steel_position][steel_type];
1816 void DrawScreenElement(int x, int y, int element)
1818 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1819 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1822 void DrawLevelElement(int x, int y, int element)
1824 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1825 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1828 void DrawScreenField(int x, int y)
1830 int lx = LEVELX(x), ly = LEVELY(y);
1831 int element, content;
1833 if (!IN_LEV_FIELD(lx, ly))
1835 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1838 element = getBorderElement(lx, ly);
1840 DrawScreenElement(x, y, element);
1845 element = Feld[lx][ly];
1846 content = Store[lx][ly];
1848 if (IS_MOVING(lx, ly))
1850 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1851 boolean cut_mode = NO_CUTTING;
1853 if (element == EL_QUICKSAND_EMPTYING ||
1854 element == EL_QUICKSAND_FAST_EMPTYING ||
1855 element == EL_MAGIC_WALL_EMPTYING ||
1856 element == EL_BD_MAGIC_WALL_EMPTYING ||
1857 element == EL_DC_MAGIC_WALL_EMPTYING ||
1858 element == EL_AMOEBA_DROPPING)
1859 cut_mode = CUT_ABOVE;
1860 else if (element == EL_QUICKSAND_FILLING ||
1861 element == EL_QUICKSAND_FAST_FILLING ||
1862 element == EL_MAGIC_WALL_FILLING ||
1863 element == EL_BD_MAGIC_WALL_FILLING ||
1864 element == EL_DC_MAGIC_WALL_FILLING)
1865 cut_mode = CUT_BELOW;
1867 if (cut_mode == CUT_ABOVE)
1868 DrawScreenElement(x, y, element);
1870 DrawScreenElement(x, y, EL_EMPTY);
1873 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1874 else if (cut_mode == NO_CUTTING)
1875 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1878 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1880 if (cut_mode == CUT_BELOW &&
1881 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1882 DrawLevelElement(lx, ly + 1, element);
1885 if (content == EL_ACID)
1887 int dir = MovDir[lx][ly];
1888 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1889 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1891 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1894 else if (IS_BLOCKED(lx, ly))
1899 boolean cut_mode = NO_CUTTING;
1900 int element_old, content_old;
1902 Blocked2Moving(lx, ly, &oldx, &oldy);
1905 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
1906 MovDir[oldx][oldy] == MV_RIGHT);
1908 element_old = Feld[oldx][oldy];
1909 content_old = Store[oldx][oldy];
1911 if (element_old == EL_QUICKSAND_EMPTYING ||
1912 element_old == EL_QUICKSAND_FAST_EMPTYING ||
1913 element_old == EL_MAGIC_WALL_EMPTYING ||
1914 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
1915 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
1916 element_old == EL_AMOEBA_DROPPING)
1917 cut_mode = CUT_ABOVE;
1919 DrawScreenElement(x, y, EL_EMPTY);
1922 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
1924 else if (cut_mode == NO_CUTTING)
1925 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
1928 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
1931 else if (IS_DRAWABLE(element))
1932 DrawScreenElement(x, y, element);
1934 DrawScreenElement(x, y, EL_EMPTY);
1937 void DrawLevelField(int x, int y)
1939 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1940 DrawScreenField(SCREENX(x), SCREENY(y));
1941 else if (IS_MOVING(x, y))
1945 Moving2Blocked(x, y, &newx, &newy);
1946 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
1947 DrawScreenField(SCREENX(newx), SCREENY(newy));
1949 else if (IS_BLOCKED(x, y))
1953 Blocked2Moving(x, y, &oldx, &oldy);
1954 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
1955 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
1959 void DrawSizedElement(int x, int y, int element, int tilesize)
1963 graphic = el2edimg(element);
1964 DrawSizedGraphic(x, y, graphic, 0, tilesize);
1967 void DrawMiniElement(int x, int y, int element)
1971 graphic = el2edimg(element);
1972 DrawMiniGraphic(x, y, graphic);
1975 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
1978 int x = sx + scroll_x, y = sy + scroll_y;
1980 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
1981 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
1982 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
1983 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
1985 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
1988 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
1990 int x = sx + scroll_x, y = sy + scroll_y;
1992 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
1993 DrawMiniElement(sx, sy, EL_EMPTY);
1994 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
1995 DrawMiniElement(sx, sy, Feld[x][y]);
1997 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2000 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2001 int x, int y, int xsize, int ysize,
2002 int tile_width, int tile_height)
2006 int dst_x = startx + x * tile_width;
2007 int dst_y = starty + y * tile_height;
2008 int width = graphic_info[graphic].width;
2009 int height = graphic_info[graphic].height;
2010 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2011 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2012 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2013 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2014 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2015 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2016 boolean draw_masked = graphic_info[graphic].draw_masked;
2018 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2020 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2022 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2026 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2027 inner_sx + (x - 1) * tile_width % inner_width);
2028 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2029 inner_sy + (y - 1) * tile_height % inner_height);
2032 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2035 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2039 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2040 int x, int y, int xsize, int ysize, int font_nr)
2042 int font_width = getFontWidth(font_nr);
2043 int font_height = getFontHeight(font_nr);
2045 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2046 font_width, font_height);
2049 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2051 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2052 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2053 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2054 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2055 boolean no_delay = (tape.warp_forward);
2056 unsigned int anim_delay = 0;
2057 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2058 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2059 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2060 int font_width = getFontWidth(font_nr);
2061 int font_height = getFontHeight(font_nr);
2062 int max_xsize = level.envelope[envelope_nr].xsize;
2063 int max_ysize = level.envelope[envelope_nr].ysize;
2064 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2065 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2066 int xend = max_xsize;
2067 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2068 int xstep = (xstart < xend ? 1 : 0);
2069 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2071 int end = MAX(xend - xstart, yend - ystart);
2074 for (i = start; i <= end; i++)
2076 int last_frame = end; // last frame of this "for" loop
2077 int x = xstart + i * xstep;
2078 int y = ystart + i * ystep;
2079 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2080 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2081 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2082 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2085 SetDrawtoField(DRAW_FIELDBUFFER);
2087 BlitScreenToBitmap(backbuffer);
2089 SetDrawtoField(DRAW_BACKBUFFER);
2091 for (yy = 0; yy < ysize; yy++)
2092 for (xx = 0; xx < xsize; xx++)
2093 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2095 DrawTextBuffer(sx + font_width, sy + font_height,
2096 level.envelope[envelope_nr].text, font_nr, max_xsize,
2097 xsize - 2, ysize - 2, 0, mask_mode,
2098 level.envelope[envelope_nr].autowrap,
2099 level.envelope[envelope_nr].centered, FALSE);
2101 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2104 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2108 void ShowEnvelope(int envelope_nr)
2110 int element = EL_ENVELOPE_1 + envelope_nr;
2111 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2112 int sound_opening = element_info[element].sound[ACTION_OPENING];
2113 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2114 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2115 boolean no_delay = (tape.warp_forward);
2116 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2117 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2118 int anim_mode = graphic_info[graphic].anim_mode;
2119 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2120 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2122 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2124 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2126 if (anim_mode == ANIM_DEFAULT)
2127 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2129 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2132 Delay(wait_delay_value);
2134 WaitForEventToContinue();
2136 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2138 if (anim_mode != ANIM_NONE)
2139 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2141 if (anim_mode == ANIM_DEFAULT)
2142 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2144 game.envelope_active = FALSE;
2146 SetDrawtoField(DRAW_FIELDBUFFER);
2148 redraw_mask |= REDRAW_FIELD;
2152 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2154 int border_size = request.border_size;
2155 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2156 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2157 int sx = sx_center - request.width / 2;
2158 int sy = sy_center - request.height / 2;
2160 if (add_border_size)
2170 void DrawEnvelopeRequest(char *text)
2172 char *text_final = text;
2173 char *text_door_style = NULL;
2174 int graphic = IMG_BACKGROUND_REQUEST;
2175 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2176 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2177 int font_nr = FONT_REQUEST;
2178 int font_width = getFontWidth(font_nr);
2179 int font_height = getFontHeight(font_nr);
2180 int border_size = request.border_size;
2181 int line_spacing = request.line_spacing;
2182 int line_height = font_height + line_spacing;
2183 int text_width = request.width - 2 * border_size;
2184 int text_height = request.height - 2 * border_size;
2185 int line_length = text_width / font_width;
2186 int max_lines = text_height / line_height;
2187 int width = request.width;
2188 int height = request.height;
2189 int tile_size = request.step_offset;
2190 int x_steps = width / tile_size;
2191 int y_steps = height / tile_size;
2195 if (request.wrap_single_words)
2197 char *src_text_ptr, *dst_text_ptr;
2199 text_door_style = checked_malloc(2 * strlen(text) + 1);
2201 src_text_ptr = text;
2202 dst_text_ptr = text_door_style;
2204 while (*src_text_ptr)
2206 if (*src_text_ptr == ' ' ||
2207 *src_text_ptr == '?' ||
2208 *src_text_ptr == '!')
2209 *dst_text_ptr++ = '\n';
2211 if (*src_text_ptr != ' ')
2212 *dst_text_ptr++ = *src_text_ptr;
2217 *dst_text_ptr = '\0';
2219 text_final = text_door_style;
2222 setRequestPosition(&sx, &sy, FALSE);
2224 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2226 for (y = 0; y < y_steps; y++)
2227 for (x = 0; x < x_steps; x++)
2228 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2229 x, y, x_steps, y_steps,
2230 tile_size, tile_size);
2232 DrawTextBuffer(sx + border_size, sy + border_size, text_final, font_nr,
2233 line_length, -1, max_lines, line_spacing, mask_mode,
2234 request.autowrap, request.centered, FALSE);
2236 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2237 RedrawGadget(tool_gadget[i]);
2239 // store readily prepared envelope request for later use when animating
2240 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2242 if (text_door_style)
2243 free(text_door_style);
2246 void AnimateEnvelopeRequest(int anim_mode, int action)
2248 int graphic = IMG_BACKGROUND_REQUEST;
2249 boolean draw_masked = graphic_info[graphic].draw_masked;
2250 int delay_value_normal = request.step_delay;
2251 int delay_value_fast = delay_value_normal / 2;
2252 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2253 boolean no_delay = (tape.warp_forward);
2254 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2255 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2256 unsigned int anim_delay = 0;
2258 int width = request.width;
2259 int height = request.height;
2260 int tile_size = request.step_offset;
2261 int max_xsize = width / tile_size;
2262 int max_ysize = height / tile_size;
2263 int max_xsize_inner = max_xsize - 2;
2264 int max_ysize_inner = max_ysize - 2;
2266 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2267 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2268 int xend = max_xsize_inner;
2269 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2270 int xstep = (xstart < xend ? 1 : 0);
2271 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2273 int end = MAX(xend - xstart, yend - ystart);
2276 if (setup.quick_doors)
2284 if (action == ACTION_OPENING)
2285 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
2286 else if (action == ACTION_CLOSING)
2287 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
2290 for (i = start; i <= end; i++)
2292 int last_frame = end; // last frame of this "for" loop
2293 int x = xstart + i * xstep;
2294 int y = ystart + i * ystep;
2295 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2296 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2297 int sx_center = (request.x != -1 ? request.x : SX + SXSIZE / 2);
2298 int sy_center = (request.y != -1 ? request.y : SY + SYSIZE / 2);
2299 int src_x = sx_center - width / 2;
2300 int src_y = sy_center - height / 2;
2301 int dst_x = sx_center - xsize * tile_size / 2;
2302 int dst_y = sy_center - ysize * tile_size / 2;
2303 int xsize_size_left = (xsize - 1) * tile_size;
2304 int ysize_size_top = (ysize - 1) * tile_size;
2305 int max_xsize_pos = (max_xsize - 1) * tile_size;
2306 int max_ysize_pos = (max_ysize - 1) * tile_size;
2309 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2311 for (yy = 0; yy < 2; yy++)
2313 for (xx = 0; xx < 2; xx++)
2315 int src_xx = src_x + xx * max_xsize_pos;
2316 int src_yy = src_y + yy * max_ysize_pos;
2317 int dst_xx = dst_x + xx * xsize_size_left;
2318 int dst_yy = dst_y + yy * ysize_size_top;
2319 int xx_size = (xx ? tile_size : xsize_size_left);
2320 int yy_size = (yy ? tile_size : ysize_size_top);
2323 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2324 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2326 BlitBitmap(bitmap_db_cross, backbuffer,
2327 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2331 redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
2336 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2341 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2343 int last_game_status = game_status; /* save current game status */
2344 int graphic = IMG_BACKGROUND_REQUEST;
2345 int sound_opening = SND_REQUEST_OPENING;
2346 int sound_closing = SND_REQUEST_CLOSING;
2347 int anim_mode = graphic_info[graphic].anim_mode;
2348 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2349 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2351 if (game_status == GAME_MODE_PLAYING)
2352 BlitScreenToBitmap(backbuffer);
2354 SetDrawtoField(DRAW_BACKBUFFER);
2356 // SetDrawBackgroundMask(REDRAW_NONE);
2358 if (action == ACTION_OPENING)
2360 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2362 if (req_state & REQ_ASK)
2364 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2365 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2367 else if (req_state & REQ_CONFIRM)
2369 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2371 else if (req_state & REQ_PLAYER)
2373 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2374 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2375 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2376 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2379 DrawEnvelopeRequest(text);
2381 if (game_status != GAME_MODE_MAIN)
2385 /* force DOOR font inside door area */
2386 game_status = GAME_MODE_PSEUDO_DOOR;
2388 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2390 if (action == ACTION_OPENING)
2392 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2394 if (anim_mode == ANIM_DEFAULT)
2395 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2397 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2401 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2403 if (anim_mode != ANIM_NONE)
2404 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2406 if (anim_mode == ANIM_DEFAULT)
2407 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2410 game.envelope_active = FALSE;
2412 game_status = last_game_status; /* restore current game status */
2414 if (action == ACTION_CLOSING)
2416 if (game_status != GAME_MODE_MAIN)
2419 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2422 // SetDrawBackgroundMask(last_draw_background_mask);
2424 redraw_mask |= REDRAW_FIELD;
2426 if (game_status == GAME_MODE_MAIN)
2431 if (action == ACTION_CLOSING &&
2432 game_status == GAME_MODE_PLAYING &&
2433 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2434 SetDrawtoField(DRAW_FIELDBUFFER);
2437 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2441 int graphic = el2preimg(element);
2443 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2444 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2447 void DrawLevel(int draw_background_mask)
2451 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2452 SetDrawBackgroundMask(draw_background_mask);
2456 for (x = BX1; x <= BX2; x++)
2457 for (y = BY1; y <= BY2; y++)
2458 DrawScreenField(x, y);
2460 redraw_mask |= REDRAW_FIELD;
2463 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2468 for (x = 0; x < size_x; x++)
2469 for (y = 0; y < size_y; y++)
2470 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2472 redraw_mask |= REDRAW_FIELD;
2475 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2479 for (x = 0; x < size_x; x++)
2480 for (y = 0; y < size_y; y++)
2481 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2483 redraw_mask |= REDRAW_FIELD;
2486 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2488 boolean show_level_border = (BorderElement != EL_EMPTY);
2489 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2490 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2491 int tile_size = preview.tile_size;
2492 int preview_width = preview.xsize * tile_size;
2493 int preview_height = preview.ysize * tile_size;
2494 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2495 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2496 int real_preview_width = real_preview_xsize * tile_size;
2497 int real_preview_height = real_preview_ysize * tile_size;
2498 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2499 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2502 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2505 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2507 dst_x += (preview_width - real_preview_width) / 2;
2508 dst_y += (preview_height - real_preview_height) / 2;
2510 for (x = 0; x < real_preview_xsize; x++)
2512 for (y = 0; y < real_preview_ysize; y++)
2514 int lx = from_x + x + (show_level_border ? -1 : 0);
2515 int ly = from_y + y + (show_level_border ? -1 : 0);
2516 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2517 getBorderElement(lx, ly));
2519 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2520 element, tile_size);
2524 redraw_mask |= REDRAW_MICROLEVEL;
2527 #define MICROLABEL_EMPTY 0
2528 #define MICROLABEL_LEVEL_NAME 1
2529 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2530 #define MICROLABEL_LEVEL_AUTHOR 3
2531 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2532 #define MICROLABEL_IMPORTED_FROM 5
2533 #define MICROLABEL_IMPORTED_BY_HEAD 6
2534 #define MICROLABEL_IMPORTED_BY 7
2536 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2538 int max_text_width = SXSIZE;
2539 int font_width = getFontWidth(font_nr);
2541 if (pos->align == ALIGN_CENTER)
2542 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2543 else if (pos->align == ALIGN_RIGHT)
2544 max_text_width = pos->x;
2546 max_text_width = SXSIZE - pos->x;
2548 return max_text_width / font_width;
2551 static void DrawPreviewLevelLabelExt(int mode)
2553 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2554 char label_text[MAX_OUTPUT_LINESIZE + 1];
2555 int max_len_label_text;
2556 int font_nr = pos->font;
2559 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2562 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2563 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2564 mode == MICROLABEL_IMPORTED_BY_HEAD)
2565 font_nr = pos->font_alt;
2567 max_len_label_text = getMaxTextLength(pos, font_nr);
2569 if (pos->size != -1)
2570 max_len_label_text = pos->size;
2572 for (i = 0; i < max_len_label_text; i++)
2573 label_text[i] = ' ';
2574 label_text[max_len_label_text] = '\0';
2576 if (strlen(label_text) > 0)
2577 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2580 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2581 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2582 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2583 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2584 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2585 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2586 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2587 max_len_label_text);
2588 label_text[max_len_label_text] = '\0';
2590 if (strlen(label_text) > 0)
2591 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2593 redraw_mask |= REDRAW_MICROLEVEL;
2596 static void DrawPreviewLevelExt(boolean restart)
2598 static unsigned int scroll_delay = 0;
2599 static unsigned int label_delay = 0;
2600 static int from_x, from_y, scroll_direction;
2601 static int label_state, label_counter;
2602 unsigned int scroll_delay_value = preview.step_delay;
2603 boolean show_level_border = (BorderElement != EL_EMPTY);
2604 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2605 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2606 int last_game_status = game_status; /* save current game status */
2613 if (preview.anim_mode == ANIM_CENTERED)
2615 if (level_xsize > preview.xsize)
2616 from_x = (level_xsize - preview.xsize) / 2;
2617 if (level_ysize > preview.ysize)
2618 from_y = (level_ysize - preview.ysize) / 2;
2621 from_x += preview.xoffset;
2622 from_y += preview.yoffset;
2624 scroll_direction = MV_RIGHT;
2628 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2629 DrawPreviewLevelLabelExt(label_state);
2631 /* initialize delay counters */
2632 DelayReached(&scroll_delay, 0);
2633 DelayReached(&label_delay, 0);
2635 if (leveldir_current->name)
2637 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2638 char label_text[MAX_OUTPUT_LINESIZE + 1];
2639 int font_nr = pos->font;
2640 int max_len_label_text = getMaxTextLength(pos, font_nr);
2642 if (pos->size != -1)
2643 max_len_label_text = pos->size;
2645 strncpy(label_text, leveldir_current->name, max_len_label_text);
2646 label_text[max_len_label_text] = '\0';
2648 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2649 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2652 game_status = last_game_status; /* restore current game status */
2657 /* scroll preview level, if needed */
2658 if (preview.anim_mode != ANIM_NONE &&
2659 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2660 DelayReached(&scroll_delay, scroll_delay_value))
2662 switch (scroll_direction)
2667 from_x -= preview.step_offset;
2668 from_x = (from_x < 0 ? 0 : from_x);
2671 scroll_direction = MV_UP;
2675 if (from_x < level_xsize - preview.xsize)
2677 from_x += preview.step_offset;
2678 from_x = (from_x > level_xsize - preview.xsize ?
2679 level_xsize - preview.xsize : from_x);
2682 scroll_direction = MV_DOWN;
2688 from_y -= preview.step_offset;
2689 from_y = (from_y < 0 ? 0 : from_y);
2692 scroll_direction = MV_RIGHT;
2696 if (from_y < level_ysize - preview.ysize)
2698 from_y += preview.step_offset;
2699 from_y = (from_y > level_ysize - preview.ysize ?
2700 level_ysize - preview.ysize : from_y);
2703 scroll_direction = MV_LEFT;
2710 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2713 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2714 /* redraw micro level label, if needed */
2715 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2716 !strEqual(level.author, ANONYMOUS_NAME) &&
2717 !strEqual(level.author, leveldir_current->name) &&
2718 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2720 int max_label_counter = 23;
2722 if (leveldir_current->imported_from != NULL &&
2723 strlen(leveldir_current->imported_from) > 0)
2724 max_label_counter += 14;
2725 if (leveldir_current->imported_by != NULL &&
2726 strlen(leveldir_current->imported_by) > 0)
2727 max_label_counter += 14;
2729 label_counter = (label_counter + 1) % max_label_counter;
2730 label_state = (label_counter >= 0 && label_counter <= 7 ?
2731 MICROLABEL_LEVEL_NAME :
2732 label_counter >= 9 && label_counter <= 12 ?
2733 MICROLABEL_LEVEL_AUTHOR_HEAD :
2734 label_counter >= 14 && label_counter <= 21 ?
2735 MICROLABEL_LEVEL_AUTHOR :
2736 label_counter >= 23 && label_counter <= 26 ?
2737 MICROLABEL_IMPORTED_FROM_HEAD :
2738 label_counter >= 28 && label_counter <= 35 ?
2739 MICROLABEL_IMPORTED_FROM :
2740 label_counter >= 37 && label_counter <= 40 ?
2741 MICROLABEL_IMPORTED_BY_HEAD :
2742 label_counter >= 42 && label_counter <= 49 ?
2743 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2745 if (leveldir_current->imported_from == NULL &&
2746 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2747 label_state == MICROLABEL_IMPORTED_FROM))
2748 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2749 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2751 DrawPreviewLevelLabelExt(label_state);
2754 game_status = last_game_status; /* restore current game status */
2757 void DrawPreviewLevelInitial()
2759 DrawPreviewLevelExt(TRUE);
2762 void DrawPreviewLevelAnimation()
2764 DrawPreviewLevelExt(FALSE);
2767 inline void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2768 int graphic, int sync_frame, int mask_mode)
2770 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2772 if (mask_mode == USE_MASKING)
2773 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2775 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2778 inline void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2779 int graphic, int sync_frame,
2782 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2784 if (mask_mode == USE_MASKING)
2785 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2787 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2790 inline void DrawGraphicAnimation(int x, int y, int graphic)
2792 int lx = LEVELX(x), ly = LEVELY(y);
2794 if (!IN_SCR_FIELD(x, y))
2797 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2798 graphic, GfxFrame[lx][ly], NO_MASKING);
2800 MarkTileDirty(x, y);
2803 inline void DrawFixedGraphicAnimation(int x, int y, int graphic)
2805 int lx = LEVELX(x), ly = LEVELY(y);
2807 if (!IN_SCR_FIELD(x, y))
2810 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2811 graphic, GfxFrame[lx][ly], NO_MASKING);
2812 MarkTileDirty(x, y);
2815 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2817 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2820 void DrawLevelElementAnimation(int x, int y, int element)
2822 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2824 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2827 inline void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2829 int sx = SCREENX(x), sy = SCREENY(y);
2831 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2834 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2837 DrawGraphicAnimation(sx, sy, graphic);
2840 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2841 DrawLevelFieldCrumbled(x, y);
2843 if (GFX_CRUMBLED(Feld[x][y]))
2844 DrawLevelFieldCrumbled(x, y);
2848 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2850 int sx = SCREENX(x), sy = SCREENY(y);
2853 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2856 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2858 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2861 DrawGraphicAnimation(sx, sy, graphic);
2863 if (GFX_CRUMBLED(element))
2864 DrawLevelFieldCrumbled(x, y);
2867 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
2869 if (player->use_murphy)
2871 /* this works only because currently only one player can be "murphy" ... */
2872 static int last_horizontal_dir = MV_LEFT;
2873 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
2875 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
2876 last_horizontal_dir = move_dir;
2878 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
2880 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
2882 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
2888 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
2891 static boolean equalGraphics(int graphic1, int graphic2)
2893 struct GraphicInfo *g1 = &graphic_info[graphic1];
2894 struct GraphicInfo *g2 = &graphic_info[graphic2];
2896 return (g1->bitmap == g2->bitmap &&
2897 g1->src_x == g2->src_x &&
2898 g1->src_y == g2->src_y &&
2899 g1->anim_frames == g2->anim_frames &&
2900 g1->anim_delay == g2->anim_delay &&
2901 g1->anim_mode == g2->anim_mode);
2904 void DrawAllPlayers()
2908 for (i = 0; i < MAX_PLAYERS; i++)
2909 if (stored_player[i].active)
2910 DrawPlayer(&stored_player[i]);
2913 void DrawPlayerField(int x, int y)
2915 if (!IS_PLAYER(x, y))
2918 DrawPlayer(PLAYERINFO(x, y));
2921 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
2923 void DrawPlayer(struct PlayerInfo *player)
2925 int jx = player->jx;
2926 int jy = player->jy;
2927 int move_dir = player->MovDir;
2928 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
2929 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
2930 int last_jx = (player->is_moving ? jx - dx : jx);
2931 int last_jy = (player->is_moving ? jy - dy : jy);
2932 int next_jx = jx + dx;
2933 int next_jy = jy + dy;
2934 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
2935 boolean player_is_opaque = FALSE;
2936 int sx = SCREENX(jx), sy = SCREENY(jy);
2937 int sxx = 0, syy = 0;
2938 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
2940 int action = ACTION_DEFAULT;
2941 int last_player_graphic = getPlayerGraphic(player, move_dir);
2942 int last_player_frame = player->Frame;
2945 /* GfxElement[][] is set to the element the player is digging or collecting;
2946 remove also for off-screen player if the player is not moving anymore */
2947 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
2948 GfxElement[jx][jy] = EL_UNDEFINED;
2950 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
2954 if (!IN_LEV_FIELD(jx, jy))
2956 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
2957 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
2958 printf("DrawPlayerField(): This should never happen!\n");
2963 if (element == EL_EXPLOSION)
2966 action = (player->is_pushing ? ACTION_PUSHING :
2967 player->is_digging ? ACTION_DIGGING :
2968 player->is_collecting ? ACTION_COLLECTING :
2969 player->is_moving ? ACTION_MOVING :
2970 player->is_snapping ? ACTION_SNAPPING :
2971 player->is_dropping ? ACTION_DROPPING :
2972 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
2974 if (player->is_waiting)
2975 move_dir = player->dir_waiting;
2977 InitPlayerGfxAnimation(player, action, move_dir);
2979 /* ----------------------------------------------------------------------- */
2980 /* draw things in the field the player is leaving, if needed */
2981 /* ----------------------------------------------------------------------- */
2983 if (player->is_moving)
2985 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
2987 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
2989 if (last_element == EL_DYNAMITE_ACTIVE ||
2990 last_element == EL_EM_DYNAMITE_ACTIVE ||
2991 last_element == EL_SP_DISK_RED_ACTIVE)
2992 DrawDynamite(last_jx, last_jy);
2994 DrawLevelFieldThruMask(last_jx, last_jy);
2996 else if (last_element == EL_DYNAMITE_ACTIVE ||
2997 last_element == EL_EM_DYNAMITE_ACTIVE ||
2998 last_element == EL_SP_DISK_RED_ACTIVE)
2999 DrawDynamite(last_jx, last_jy);
3001 /* !!! this is not enough to prevent flickering of players which are
3002 moving next to each others without a free tile between them -- this
3003 can only be solved by drawing all players layer by layer (first the
3004 background, then the foreground etc.) !!! => TODO */
3005 else if (!IS_PLAYER(last_jx, last_jy))
3006 DrawLevelField(last_jx, last_jy);
3009 DrawLevelField(last_jx, last_jy);
3012 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3013 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3016 if (!IN_SCR_FIELD(sx, sy))
3019 /* ----------------------------------------------------------------------- */
3020 /* draw things behind the player, if needed */
3021 /* ----------------------------------------------------------------------- */
3024 DrawLevelElement(jx, jy, Back[jx][jy]);
3025 else if (IS_ACTIVE_BOMB(element))
3026 DrawLevelElement(jx, jy, EL_EMPTY);
3029 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3031 int old_element = GfxElement[jx][jy];
3032 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3033 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3035 if (GFX_CRUMBLED(old_element))
3036 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3038 DrawGraphic(sx, sy, old_graphic, frame);
3040 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3041 player_is_opaque = TRUE;
3045 GfxElement[jx][jy] = EL_UNDEFINED;
3047 /* make sure that pushed elements are drawn with correct frame rate */
3048 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3050 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3051 GfxFrame[jx][jy] = player->StepFrame;
3053 DrawLevelField(jx, jy);
3057 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3058 /* ----------------------------------------------------------------------- */
3059 /* draw player himself */
3060 /* ----------------------------------------------------------------------- */
3062 graphic = getPlayerGraphic(player, move_dir);
3064 /* in the case of changed player action or direction, prevent the current
3065 animation frame from being restarted for identical animations */
3066 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3067 player->Frame = last_player_frame;
3069 frame = getGraphicAnimationFrame(graphic, player->Frame);
3073 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3074 sxx = player->GfxPos;
3076 syy = player->GfxPos;
3079 if (player_is_opaque)
3080 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3082 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3084 if (SHIELD_ON(player))
3086 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3087 IMG_SHIELD_NORMAL_ACTIVE);
3088 int frame = getGraphicAnimationFrame(graphic, -1);
3090 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3094 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3097 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3098 sxx = player->GfxPos;
3100 syy = player->GfxPos;
3104 /* ----------------------------------------------------------------------- */
3105 /* draw things the player is pushing, if needed */
3106 /* ----------------------------------------------------------------------- */
3108 if (player->is_pushing && player->is_moving)
3110 int px = SCREENX(jx), py = SCREENY(jy);
3111 int pxx = (TILEX - ABS(sxx)) * dx;
3112 int pyy = (TILEY - ABS(syy)) * dy;
3113 int gfx_frame = GfxFrame[jx][jy];
3119 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3121 element = Feld[next_jx][next_jy];
3122 gfx_frame = GfxFrame[next_jx][next_jy];
3125 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3127 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3128 frame = getGraphicAnimationFrame(graphic, sync_frame);
3130 /* draw background element under pushed element (like the Sokoban field) */
3131 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3133 /* this allows transparent pushing animation over non-black background */
3136 DrawLevelElement(jx, jy, Back[jx][jy]);
3138 DrawLevelElement(jx, jy, EL_EMPTY);
3140 if (Back[next_jx][next_jy])
3141 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3143 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3145 else if (Back[next_jx][next_jy])
3146 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3149 /* do not draw (EM style) pushing animation when pushing is finished */
3150 /* (two-tile animations usually do not contain start and end frame) */
3151 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3152 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3154 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3156 /* masked drawing is needed for EMC style (double) movement graphics */
3157 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3158 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3162 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3163 /* ----------------------------------------------------------------------- */
3164 /* draw player himself */
3165 /* ----------------------------------------------------------------------- */
3167 graphic = getPlayerGraphic(player, move_dir);
3169 /* in the case of changed player action or direction, prevent the current
3170 animation frame from being restarted for identical animations */
3171 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3172 player->Frame = last_player_frame;
3174 frame = getGraphicAnimationFrame(graphic, player->Frame);
3178 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3179 sxx = player->GfxPos;
3181 syy = player->GfxPos;
3184 if (player_is_opaque)
3185 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3187 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3189 if (SHIELD_ON(player))
3191 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3192 IMG_SHIELD_NORMAL_ACTIVE);
3193 int frame = getGraphicAnimationFrame(graphic, -1);
3195 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3199 /* ----------------------------------------------------------------------- */
3200 /* draw things in front of player (active dynamite or dynabombs) */
3201 /* ----------------------------------------------------------------------- */
3203 if (IS_ACTIVE_BOMB(element))
3205 graphic = el2img(element);
3206 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3208 if (game.emulation == EMU_SUPAPLEX)
3209 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3211 DrawGraphicThruMask(sx, sy, graphic, frame);
3214 if (player_is_moving && last_element == EL_EXPLOSION)
3216 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3217 GfxElement[last_jx][last_jy] : EL_EMPTY);
3218 int graphic = el_act2img(element, ACTION_EXPLODING);
3219 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3220 int phase = ExplodePhase[last_jx][last_jy] - 1;
3221 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3224 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3227 /* ----------------------------------------------------------------------- */
3228 /* draw elements the player is just walking/passing through/under */
3229 /* ----------------------------------------------------------------------- */
3231 if (player_is_moving)
3233 /* handle the field the player is leaving ... */
3234 if (IS_ACCESSIBLE_INSIDE(last_element))
3235 DrawLevelField(last_jx, last_jy);
3236 else if (IS_ACCESSIBLE_UNDER(last_element))
3237 DrawLevelFieldThruMask(last_jx, last_jy);
3240 /* do not redraw accessible elements if the player is just pushing them */
3241 if (!player_is_moving || !player->is_pushing)
3243 /* ... and the field the player is entering */
3244 if (IS_ACCESSIBLE_INSIDE(element))
3245 DrawLevelField(jx, jy);
3246 else if (IS_ACCESSIBLE_UNDER(element))
3247 DrawLevelFieldThruMask(jx, jy);
3250 MarkTileDirty(sx, sy);
3253 /* ------------------------------------------------------------------------- */
3255 void WaitForEventToContinue()
3257 boolean still_wait = TRUE;
3259 /* simulate releasing mouse button over last gadget, if still pressed */
3261 HandleGadgets(-1, -1, 0);
3263 button_status = MB_RELEASED;
3277 case EVENT_BUTTONPRESS:
3278 case EVENT_KEYPRESS:
3282 case EVENT_KEYRELEASE:
3283 ClearPlayerAction();
3287 HandleOtherEvents(&event);
3291 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3298 /* don't eat all CPU time */
3303 #define MAX_REQUEST_LINES 13
3304 #define MAX_REQUEST_LINE_FONT1_LEN 7
3305 #define MAX_REQUEST_LINE_FONT2_LEN 10
3307 static int RequestHandleEvents(unsigned int req_state)
3309 int last_game_status = game_status; /* save current game status */
3313 button_status = MB_RELEASED;
3315 request_gadget_id = -1;
3324 while (NextValidEvent(&event))
3328 case EVENT_BUTTONPRESS:
3329 case EVENT_BUTTONRELEASE:
3330 case EVENT_MOTIONNOTIFY:
3332 if (event.type == EVENT_MOTIONNOTIFY)
3334 if (!PointerInWindow(window))
3335 continue; /* window and pointer on different screens */
3340 motion_status = TRUE;
3341 mx = ((MotionEvent *) &event)->x;
3342 my = ((MotionEvent *) &event)->y;
3346 motion_status = FALSE;
3347 mx = ((ButtonEvent *) &event)->x;
3348 my = ((ButtonEvent *) &event)->y;
3349 if (event.type == EVENT_BUTTONPRESS)
3350 button_status = ((ButtonEvent *) &event)->button;
3352 button_status = MB_RELEASED;
3355 /* this sets 'request_gadget_id' */
3356 HandleGadgets(mx, my, button_status);
3358 switch (request_gadget_id)
3360 case TOOL_CTRL_ID_YES:
3363 case TOOL_CTRL_ID_NO:
3366 case TOOL_CTRL_ID_CONFIRM:
3367 result = TRUE | FALSE;
3370 case TOOL_CTRL_ID_PLAYER_1:
3373 case TOOL_CTRL_ID_PLAYER_2:
3376 case TOOL_CTRL_ID_PLAYER_3:
3379 case TOOL_CTRL_ID_PLAYER_4:
3390 case EVENT_KEYPRESS:
3391 switch (GetEventKey((KeyEvent *)&event, TRUE))
3394 if (req_state & REQ_CONFIRM)
3399 #if defined(TARGET_SDL2)
3406 #if defined(TARGET_SDL2)
3416 if (req_state & REQ_PLAYER)
3420 case EVENT_KEYRELEASE:
3421 ClearPlayerAction();
3425 HandleOtherEvents(&event);
3430 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3432 int joy = AnyJoystick();
3434 if (joy & JOY_BUTTON_1)
3436 else if (joy & JOY_BUTTON_2)
3440 if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
3442 HandleGameActions();
3448 if (!PendingEvent()) /* delay only if no pending events */
3452 game_status = GAME_MODE_PSEUDO_DOOR;
3456 game_status = last_game_status; /* restore current game status */
3462 static boolean RequestDoor(char *text, unsigned int req_state)
3464 unsigned int old_door_state;
3465 int last_game_status = game_status; /* save current game status */
3466 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3467 int font_nr = FONT_TEXT_2;
3472 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3474 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3475 font_nr = FONT_TEXT_1;
3478 if (game_status == GAME_MODE_PLAYING)
3479 BlitScreenToBitmap(backbuffer);
3481 /* disable deactivated drawing when quick-loading level tape recording */
3482 if (tape.playing && tape.deactivate_display)
3483 TapeDeactivateDisplayOff(TRUE);
3485 SetMouseCursor(CURSOR_DEFAULT);
3487 #if defined(NETWORK_AVALIABLE)
3488 /* pause network game while waiting for request to answer */
3489 if (options.network &&
3490 game_status == GAME_MODE_PLAYING &&
3491 req_state & REQUEST_WAIT_FOR_INPUT)
3492 SendToServer_PausePlaying();
3495 old_door_state = GetDoorState();
3497 /* simulate releasing mouse button over last gadget, if still pressed */
3499 HandleGadgets(-1, -1, 0);
3503 /* draw released gadget before proceeding */
3506 if (old_door_state & DOOR_OPEN_1)
3508 CloseDoor(DOOR_CLOSE_1);
3510 /* save old door content */
3511 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3512 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3515 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3516 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3518 /* clear door drawing field */
3519 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3521 /* force DOOR font inside door area */
3522 game_status = GAME_MODE_PSEUDO_DOOR;
3524 /* write text for request */
3525 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3527 char text_line[max_request_line_len + 1];
3533 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3535 tc = *(text_ptr + tx);
3536 // if (!tc || tc == ' ')
3537 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3541 if ((tc == '?' || tc == '!') && tl == 0)
3551 strncpy(text_line, text_ptr, tl);
3554 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3555 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3556 text_line, font_nr);
3558 text_ptr += tl + (tc == ' ' ? 1 : 0);
3559 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3562 game_status = last_game_status; /* restore current game status */
3564 if (req_state & REQ_ASK)
3566 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3567 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3569 else if (req_state & REQ_CONFIRM)
3571 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3573 else if (req_state & REQ_PLAYER)
3575 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3576 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3577 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3578 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3581 /* copy request gadgets to door backbuffer */
3582 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3584 OpenDoor(DOOR_OPEN_1);
3586 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3588 if (game_status == GAME_MODE_PLAYING)
3590 SetPanelBackground();
3591 SetDrawBackgroundMask(REDRAW_DOOR_1);
3595 SetDrawBackgroundMask(REDRAW_FIELD);
3601 if (game_status != GAME_MODE_MAIN)
3604 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3606 // ---------- handle request buttons ----------
3607 result = RequestHandleEvents(req_state);
3609 if (game_status != GAME_MODE_MAIN)
3614 if (!(req_state & REQ_STAY_OPEN))
3616 CloseDoor(DOOR_CLOSE_1);
3618 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3619 (req_state & REQ_REOPEN))
3620 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3625 if (game_status == GAME_MODE_PLAYING)
3627 SetPanelBackground();
3628 SetDrawBackgroundMask(REDRAW_DOOR_1);
3632 SetDrawBackgroundMask(REDRAW_FIELD);
3635 #if defined(NETWORK_AVALIABLE)
3636 /* continue network game after request */
3637 if (options.network &&
3638 game_status == GAME_MODE_PLAYING &&
3639 req_state & REQUEST_WAIT_FOR_INPUT)
3640 SendToServer_ContinuePlaying();
3643 /* restore deactivated drawing when quick-loading level tape recording */
3644 if (tape.playing && tape.deactivate_display)
3645 TapeDeactivateDisplayOn();
3650 static boolean RequestEnvelope(char *text, unsigned int req_state)
3654 if (game_status == GAME_MODE_PLAYING)
3655 BlitScreenToBitmap(backbuffer);
3657 /* disable deactivated drawing when quick-loading level tape recording */
3658 if (tape.playing && tape.deactivate_display)
3659 TapeDeactivateDisplayOff(TRUE);
3661 SetMouseCursor(CURSOR_DEFAULT);
3663 #if defined(NETWORK_AVALIABLE)
3664 /* pause network game while waiting for request to answer */
3665 if (options.network &&
3666 game_status == GAME_MODE_PLAYING &&
3667 req_state & REQUEST_WAIT_FOR_INPUT)
3668 SendToServer_PausePlaying();
3671 /* simulate releasing mouse button over last gadget, if still pressed */
3673 HandleGadgets(-1, -1, 0);
3677 // (replace with setting corresponding request background)
3678 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3679 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3681 /* clear door drawing field */
3682 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3684 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3686 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3688 if (game_status == GAME_MODE_PLAYING)
3690 SetPanelBackground();
3691 SetDrawBackgroundMask(REDRAW_DOOR_1);
3695 SetDrawBackgroundMask(REDRAW_FIELD);
3701 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3703 // ---------- handle request buttons ----------
3704 result = RequestHandleEvents(req_state);
3706 if (game_status != GAME_MODE_MAIN)
3711 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3715 if (game_status == GAME_MODE_PLAYING)
3717 SetPanelBackground();
3718 SetDrawBackgroundMask(REDRAW_DOOR_1);
3722 SetDrawBackgroundMask(REDRAW_FIELD);
3725 #if defined(NETWORK_AVALIABLE)
3726 /* continue network game after request */
3727 if (options.network &&
3728 game_status == GAME_MODE_PLAYING &&
3729 req_state & REQUEST_WAIT_FOR_INPUT)
3730 SendToServer_ContinuePlaying();
3733 /* restore deactivated drawing when quick-loading level tape recording */
3734 if (tape.playing && tape.deactivate_display)
3735 TapeDeactivateDisplayOn();
3740 boolean Request(char *text, unsigned int req_state)
3742 if (global.use_envelope_request)
3743 return RequestEnvelope(text, req_state);
3745 return RequestDoor(text, req_state);
3748 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3750 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3751 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3754 if (dpo1->sort_priority != dpo2->sort_priority)
3755 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3757 compare_result = dpo1->nr - dpo2->nr;
3759 return compare_result;
3762 void InitGraphicCompatibilityInfo_Doors()
3768 struct DoorInfo *door;
3772 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3773 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3775 { -1, -1, -1, NULL }
3777 struct Rect door_rect_list[] =
3779 { DX, DY, DXSIZE, DYSIZE },
3780 { VX, VY, VXSIZE, VYSIZE }
3784 for (i = 0; doors[i].door_token != -1; i++)
3786 int door_token = doors[i].door_token;
3787 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3788 int part_1 = doors[i].part_1;
3789 int part_8 = doors[i].part_8;
3790 int part_2 = part_1 + 1;
3791 int part_3 = part_1 + 2;
3792 struct DoorInfo *door = doors[i].door;
3793 struct Rect *door_rect = &door_rect_list[door_index];
3794 boolean door_gfx_redefined = FALSE;
3796 /* check if any door part graphic definitions have been redefined */
3798 for (j = 0; door_part_controls[j].door_token != -1; j++)
3800 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3801 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3803 if (dpc->door_token == door_token && fi->redefined)
3804 door_gfx_redefined = TRUE;
3807 /* check for old-style door graphic/animation modifications */
3809 if (!door_gfx_redefined)
3811 if (door->anim_mode & ANIM_STATIC_PANEL)
3813 door->panel.step_xoffset = 0;
3814 door->panel.step_yoffset = 0;
3817 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3819 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3820 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3821 int num_door_steps, num_panel_steps;
3823 /* remove door part graphics other than the two default wings */
3825 for (j = 0; door_part_controls[j].door_token != -1; j++)
3827 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3828 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3830 if (dpc->graphic >= part_3 &&
3831 dpc->graphic <= part_8)
3835 /* set graphics and screen positions of the default wings */
3837 g_part_1->width = door_rect->width;
3838 g_part_1->height = door_rect->height;
3839 g_part_2->width = door_rect->width;
3840 g_part_2->height = door_rect->height;
3841 g_part_2->src_x = door_rect->width;
3842 g_part_2->src_y = g_part_1->src_y;
3844 door->part_2.x = door->part_1.x;
3845 door->part_2.y = door->part_1.y;
3847 if (door->width != -1)
3849 g_part_1->width = door->width;
3850 g_part_2->width = door->width;
3852 // special treatment for graphics and screen position of right wing
3853 g_part_2->src_x += door_rect->width - door->width;
3854 door->part_2.x += door_rect->width - door->width;
3857 if (door->height != -1)
3859 g_part_1->height = door->height;
3860 g_part_2->height = door->height;
3862 // special treatment for graphics and screen position of bottom wing
3863 g_part_2->src_y += door_rect->height - door->height;
3864 door->part_2.y += door_rect->height - door->height;
3867 /* set animation delays for the default wings and panels */
3869 door->part_1.step_delay = door->step_delay;
3870 door->part_2.step_delay = door->step_delay;
3871 door->panel.step_delay = door->step_delay;
3873 /* set animation draw order for the default wings */
3875 door->part_1.sort_priority = 2; /* draw left wing over ... */
3876 door->part_2.sort_priority = 1; /* ... right wing */
3878 /* set animation draw offset for the default wings */
3880 if (door->anim_mode & ANIM_HORIZONTAL)
3882 door->part_1.step_xoffset = door->step_offset;
3883 door->part_1.step_yoffset = 0;
3884 door->part_2.step_xoffset = door->step_offset * -1;
3885 door->part_2.step_yoffset = 0;
3887 num_door_steps = g_part_1->width / door->step_offset;
3889 else // ANIM_VERTICAL
3891 door->part_1.step_xoffset = 0;
3892 door->part_1.step_yoffset = door->step_offset;
3893 door->part_2.step_xoffset = 0;
3894 door->part_2.step_yoffset = door->step_offset * -1;
3896 num_door_steps = g_part_1->height / door->step_offset;
3899 /* set animation draw offset for the default panels */
3901 if (door->step_offset > 1)
3903 num_panel_steps = 2 * door_rect->height / door->step_offset;
3904 door->panel.start_step = num_panel_steps - num_door_steps;
3905 door->panel.start_step_closing = door->panel.start_step;
3909 num_panel_steps = door_rect->height / door->step_offset;
3910 door->panel.start_step = num_panel_steps - num_door_steps / 2;
3911 door->panel.start_step_closing = door->panel.start_step;
3912 door->panel.step_delay *= 2;
3923 for (i = 0; door_part_controls[i].door_token != -1; i++)
3925 struct DoorPartControlInfo *dpc = &door_part_controls[i];
3926 struct DoorPartOrderInfo *dpo = &door_part_order[i];
3928 /* initialize "start_step_opening" and "start_step_closing", if needed */
3929 if (dpc->pos->start_step_opening == 0 &&
3930 dpc->pos->start_step_closing == 0)
3932 // dpc->pos->start_step_opening = dpc->pos->start_step;
3933 dpc->pos->start_step_closing = dpc->pos->start_step;
3936 /* fill structure for door part draw order (sorted below) */
3938 dpo->sort_priority = dpc->pos->sort_priority;
3941 /* sort door part controls according to sort_priority and graphic number */
3942 qsort(door_part_order, MAX_DOOR_PARTS,
3943 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
3946 unsigned int OpenDoor(unsigned int door_state)
3948 if (door_state & DOOR_COPY_BACK)
3950 if (door_state & DOOR_OPEN_1)
3951 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3952 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
3954 if (door_state & DOOR_OPEN_2)
3955 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
3956 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
3958 door_state &= ~DOOR_COPY_BACK;
3961 return MoveDoor(door_state);
3964 unsigned int CloseDoor(unsigned int door_state)
3966 unsigned int old_door_state = GetDoorState();
3968 if (!(door_state & DOOR_NO_COPY_BACK))
3970 if (old_door_state & DOOR_OPEN_1)
3971 BlitBitmap(backbuffer, bitmap_db_door_1,
3972 DX, DY, DXSIZE, DYSIZE, 0, 0);
3974 if (old_door_state & DOOR_OPEN_2)
3975 BlitBitmap(backbuffer, bitmap_db_door_2,
3976 VX, VY, VXSIZE, VYSIZE, 0, 0);
3978 door_state &= ~DOOR_NO_COPY_BACK;
3981 return MoveDoor(door_state);
3984 unsigned int GetDoorState()
3986 return MoveDoor(DOOR_GET_STATE);
3989 unsigned int SetDoorState(unsigned int door_state)
3991 return MoveDoor(door_state | DOOR_SET_STATE);
3994 int euclid(int a, int b)
3996 return (b ? euclid(b, a % b) : a);
3999 unsigned int MoveDoor(unsigned int door_state)
4001 struct Rect door_rect_list[] =
4003 { DX, DY, DXSIZE, DYSIZE },
4004 { VX, VY, VXSIZE, VYSIZE }
4006 static int door1 = DOOR_OPEN_1;
4007 static int door2 = DOOR_CLOSE_2;
4008 unsigned int door_delay = 0;
4009 unsigned int door_delay_value;
4012 if (door_state == DOOR_GET_STATE)
4013 return (door1 | door2);
4015 if (door_state & DOOR_SET_STATE)
4017 if (door_state & DOOR_ACTION_1)
4018 door1 = door_state & DOOR_ACTION_1;
4019 if (door_state & DOOR_ACTION_2)
4020 door2 = door_state & DOOR_ACTION_2;
4022 return (door1 | door2);
4025 if (!(door_state & DOOR_FORCE_REDRAW))
4027 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4028 door_state &= ~DOOR_OPEN_1;
4029 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4030 door_state &= ~DOOR_CLOSE_1;
4031 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4032 door_state &= ~DOOR_OPEN_2;
4033 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4034 door_state &= ~DOOR_CLOSE_2;
4037 if (global.autoplay_leveldir)
4039 door_state |= DOOR_NO_DELAY;
4040 door_state &= ~DOOR_CLOSE_ALL;
4043 if (game_status == GAME_MODE_EDITOR)
4044 door_state |= DOOR_NO_DELAY;
4046 if (door_state & DOOR_ACTION)
4048 boolean door_panel_drawn[NUM_DOORS];
4049 boolean panel_has_doors[NUM_DOORS];
4050 boolean door_part_skip[MAX_DOOR_PARTS];
4051 boolean door_part_done[MAX_DOOR_PARTS];
4052 boolean door_part_done_all;
4053 int num_steps[MAX_DOOR_PARTS];
4054 int max_move_delay = 0; // delay for complete animations of all doors
4055 int max_step_delay = 0; // delay (ms) between two animation frames
4056 int num_move_steps = 0; // number of animation steps for all doors
4057 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4058 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4059 int current_move_delay = 0;
4063 for (i = 0; i < NUM_DOORS; i++)
4064 panel_has_doors[i] = FALSE;
4066 for (i = 0; i < MAX_DOOR_PARTS; i++)
4068 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4069 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4070 int door_token = dpc->door_token;
4072 door_part_done[i] = FALSE;
4073 door_part_skip[i] = (!(door_state & door_token) ||
4077 for (i = 0; i < MAX_DOOR_PARTS; i++)
4079 int nr = door_part_order[i].nr;
4080 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4081 struct DoorPartPosInfo *pos = dpc->pos;
4082 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4083 int door_token = dpc->door_token;
4084 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4085 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4086 int step_xoffset = ABS(pos->step_xoffset);
4087 int step_yoffset = ABS(pos->step_yoffset);
4088 int step_delay = pos->step_delay;
4089 int current_door_state = door_state & door_token;
4090 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4091 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4092 boolean part_opening = (is_panel ? door_closing : door_opening);
4093 int start_step = (part_opening ? pos->start_step_opening :
4094 pos->start_step_closing);
4095 float move_xsize = (step_xoffset ? g->width : 0);
4096 float move_ysize = (step_yoffset ? g->height : 0);
4097 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4098 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4099 int move_steps = (move_xsteps && move_ysteps ?
4100 MIN(move_xsteps, move_ysteps) :
4101 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4102 int move_delay = move_steps * step_delay;
4104 if (door_part_skip[nr])
4107 max_move_delay = MAX(max_move_delay, move_delay);
4108 max_step_delay = (max_step_delay == 0 ? step_delay :
4109 euclid(max_step_delay, step_delay));
4110 num_steps[nr] = move_steps;
4114 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4116 panel_has_doors[door_index] = TRUE;
4120 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4122 num_move_steps = max_move_delay / max_step_delay;
4123 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4125 door_delay_value = max_step_delay;
4127 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4129 start = num_move_steps - 1;
4133 /* opening door sound has priority over simultaneously closing door */
4134 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4135 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4136 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4137 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4140 for (k = start; k < num_move_steps; k++)
4142 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4144 door_part_done_all = TRUE;
4146 for (i = 0; i < NUM_DOORS; i++)
4147 door_panel_drawn[i] = FALSE;
4149 for (i = 0; i < MAX_DOOR_PARTS; i++)
4151 int nr = door_part_order[i].nr;
4152 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4153 struct DoorPartPosInfo *pos = dpc->pos;
4154 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4155 int door_token = dpc->door_token;
4156 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4157 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4158 boolean is_panel_and_door_has_closed = FALSE;
4159 struct Rect *door_rect = &door_rect_list[door_index];
4160 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4162 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4163 int current_door_state = door_state & door_token;
4164 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4165 boolean door_closing = !door_opening;
4166 boolean part_opening = (is_panel ? door_closing : door_opening);
4167 boolean part_closing = !part_opening;
4168 int start_step = (part_opening ? pos->start_step_opening :
4169 pos->start_step_closing);
4170 int step_delay = pos->step_delay;
4171 int step_factor = step_delay / max_step_delay;
4172 int k1 = (step_factor ? k / step_factor + 1 : k);
4173 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4174 int kk = MAX(0, k2);
4177 int src_x, src_y, src_xx, src_yy;
4178 int dst_x, dst_y, dst_xx, dst_yy;
4181 if (door_part_skip[nr])
4184 if (!(door_state & door_token))
4192 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4193 int kk_door = MAX(0, k2_door);
4194 int sync_frame = kk_door * door_delay_value;
4195 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4197 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4202 if (!door_panel_drawn[door_index])
4204 ClearRectangle(drawto, door_rect->x, door_rect->y,
4205 door_rect->width, door_rect->height);
4207 door_panel_drawn[door_index] = TRUE;
4210 // draw opening or closing door parts
4212 if (pos->step_xoffset < 0) // door part on right side
4215 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4218 if (dst_xx + width > door_rect->width)
4219 width = door_rect->width - dst_xx;
4221 else // door part on left side
4224 dst_xx = pos->x - kk * pos->step_xoffset;
4228 src_xx = ABS(dst_xx);
4232 width = g->width - src_xx;
4234 // printf("::: k == %d [%d] \n", k, start_step);
4237 if (pos->step_yoffset < 0) // door part on bottom side
4240 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4243 if (dst_yy + height > door_rect->height)
4244 height = door_rect->height - dst_yy;
4246 else // door part on top side
4249 dst_yy = pos->y - kk * pos->step_yoffset;
4253 src_yy = ABS(dst_yy);
4257 height = g->height - src_yy;
4260 src_x = g_src_x + src_xx;
4261 src_y = g_src_y + src_yy;
4263 dst_x = door_rect->x + dst_xx;
4264 dst_y = door_rect->y + dst_yy;
4266 is_panel_and_door_has_closed =
4269 panel_has_doors[door_index] &&
4270 k >= num_move_steps_doors_only - 1);
4272 if (width >= 0 && width <= g->width &&
4273 height >= 0 && height <= g->height &&
4274 !is_panel_and_door_has_closed)
4276 if (is_panel || !pos->draw_masked)
4277 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4280 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4284 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4286 if ((part_opening && (width < 0 || height < 0)) ||
4287 (part_closing && (width >= g->width && height >= g->height)))
4288 door_part_done[nr] = TRUE;
4290 // continue door part animations, but not panel after door has closed
4291 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4292 door_part_done_all = FALSE;
4295 if (!(door_state & DOOR_NO_DELAY))
4299 if (game_status == GAME_MODE_MAIN)
4302 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4304 current_move_delay += max_step_delay;
4307 if (door_part_done_all)
4312 if (door_state & DOOR_ACTION_1)
4313 door1 = door_state & DOOR_ACTION_1;
4314 if (door_state & DOOR_ACTION_2)
4315 door2 = door_state & DOOR_ACTION_2;
4317 return (door1 | door2);
4320 void DrawSpecialEditorDoor()
4322 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4323 int top_border_width = gfx1->width;
4324 int top_border_height = gfx1->height;
4325 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4326 int ex = EX - outer_border;
4327 int ey = EY - outer_border;
4328 int vy = VY - outer_border;
4329 int exsize = EXSIZE + 2 * outer_border;
4331 /* draw bigger level editor toolbox window */
4332 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4333 top_border_width, top_border_height, ex, ey - top_border_height);
4334 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4335 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4337 redraw_mask |= REDRAW_ALL;
4340 void UndrawSpecialEditorDoor()
4342 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4343 int top_border_width = gfx1->width;
4344 int top_border_height = gfx1->height;
4345 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4346 int ex = EX - outer_border;
4347 int ey = EY - outer_border;
4348 int ey_top = ey - top_border_height;
4349 int exsize = EXSIZE + 2 * outer_border;
4350 int eysize = EYSIZE + 2 * outer_border;
4352 /* draw normal tape recorder window */
4353 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4355 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4356 ex, ey_top, top_border_width, top_border_height,
4358 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4359 ex, ey, exsize, eysize, ex, ey);
4363 // if screen background is set to "[NONE]", clear editor toolbox window
4364 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4365 ClearRectangle(drawto, ex, ey, exsize, eysize);
4368 redraw_mask |= REDRAW_ALL;
4372 /* ---------- new tool button stuff ---------------------------------------- */
4377 struct TextPosInfo *pos;
4380 } toolbutton_info[NUM_TOOL_BUTTONS] =
4383 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4384 TOOL_CTRL_ID_YES, "yes"
4387 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4388 TOOL_CTRL_ID_NO, "no"
4391 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4392 TOOL_CTRL_ID_CONFIRM, "confirm"
4395 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4396 TOOL_CTRL_ID_PLAYER_1, "player 1"
4399 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4400 TOOL_CTRL_ID_PLAYER_2, "player 2"
4403 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4404 TOOL_CTRL_ID_PLAYER_3, "player 3"
4407 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4408 TOOL_CTRL_ID_PLAYER_4, "player 4"
4412 void CreateToolButtons()
4416 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4418 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4419 struct TextPosInfo *pos = toolbutton_info[i].pos;
4420 struct GadgetInfo *gi;
4421 Bitmap *deco_bitmap = None;
4422 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4423 unsigned int event_mask = GD_EVENT_RELEASED;
4426 int gd_x = gfx->src_x;
4427 int gd_y = gfx->src_y;
4428 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4429 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4432 if (global.use_envelope_request)
4433 setRequestPosition(&dx, &dy, TRUE);
4435 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4437 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4439 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4440 pos->size, &deco_bitmap, &deco_x, &deco_y);
4441 deco_xpos = (gfx->width - pos->size) / 2;
4442 deco_ypos = (gfx->height - pos->size) / 2;
4445 gi = CreateGadget(GDI_CUSTOM_ID, id,
4446 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4447 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4448 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4449 GDI_WIDTH, gfx->width,
4450 GDI_HEIGHT, gfx->height,
4451 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4452 GDI_STATE, GD_BUTTON_UNPRESSED,
4453 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4454 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4455 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4456 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4457 GDI_DECORATION_SIZE, pos->size, pos->size,
4458 GDI_DECORATION_SHIFTING, 1, 1,
4459 GDI_DIRECT_DRAW, FALSE,
4460 GDI_EVENT_MASK, event_mask,
4461 GDI_CALLBACK_ACTION, HandleToolButtons,
4465 Error(ERR_EXIT, "cannot create gadget");
4467 tool_gadget[id] = gi;
4471 void FreeToolButtons()
4475 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4476 FreeGadget(tool_gadget[i]);
4479 static void UnmapToolButtons()
4483 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4484 UnmapGadget(tool_gadget[i]);
4487 static void HandleToolButtons(struct GadgetInfo *gi)
4489 request_gadget_id = gi->custom_id;
4492 static struct Mapping_EM_to_RND_object
4495 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4496 boolean is_backside; /* backside of moving element */
4502 em_object_mapping_list[] =
4505 Xblank, TRUE, FALSE,
4509 Yacid_splash_eB, FALSE, FALSE,
4510 EL_ACID_SPLASH_RIGHT, -1, -1
4513 Yacid_splash_wB, FALSE, FALSE,
4514 EL_ACID_SPLASH_LEFT, -1, -1
4517 #ifdef EM_ENGINE_BAD_ROLL
4519 Xstone_force_e, FALSE, FALSE,
4520 EL_ROCK, -1, MV_BIT_RIGHT
4523 Xstone_force_w, FALSE, FALSE,
4524 EL_ROCK, -1, MV_BIT_LEFT
4527 Xnut_force_e, FALSE, FALSE,
4528 EL_NUT, -1, MV_BIT_RIGHT
4531 Xnut_force_w, FALSE, FALSE,
4532 EL_NUT, -1, MV_BIT_LEFT
4535 Xspring_force_e, FALSE, FALSE,
4536 EL_SPRING, -1, MV_BIT_RIGHT
4539 Xspring_force_w, FALSE, FALSE,
4540 EL_SPRING, -1, MV_BIT_LEFT
4543 Xemerald_force_e, FALSE, FALSE,
4544 EL_EMERALD, -1, MV_BIT_RIGHT
4547 Xemerald_force_w, FALSE, FALSE,
4548 EL_EMERALD, -1, MV_BIT_LEFT
4551 Xdiamond_force_e, FALSE, FALSE,
4552 EL_DIAMOND, -1, MV_BIT_RIGHT
4555 Xdiamond_force_w, FALSE, FALSE,
4556 EL_DIAMOND, -1, MV_BIT_LEFT
4559 Xbomb_force_e, FALSE, FALSE,
4560 EL_BOMB, -1, MV_BIT_RIGHT
4563 Xbomb_force_w, FALSE, FALSE,
4564 EL_BOMB, -1, MV_BIT_LEFT
4566 #endif /* EM_ENGINE_BAD_ROLL */
4569 Xstone, TRUE, FALSE,
4573 Xstone_pause, FALSE, FALSE,
4577 Xstone_fall, FALSE, FALSE,
4581 Ystone_s, FALSE, FALSE,
4582 EL_ROCK, ACTION_FALLING, -1
4585 Ystone_sB, FALSE, TRUE,
4586 EL_ROCK, ACTION_FALLING, -1
4589 Ystone_e, FALSE, FALSE,
4590 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4593 Ystone_eB, FALSE, TRUE,
4594 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4597 Ystone_w, FALSE, FALSE,
4598 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4601 Ystone_wB, FALSE, TRUE,
4602 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4609 Xnut_pause, FALSE, FALSE,
4613 Xnut_fall, FALSE, FALSE,
4617 Ynut_s, FALSE, FALSE,
4618 EL_NUT, ACTION_FALLING, -1
4621 Ynut_sB, FALSE, TRUE,
4622 EL_NUT, ACTION_FALLING, -1
4625 Ynut_e, FALSE, FALSE,
4626 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4629 Ynut_eB, FALSE, TRUE,
4630 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4633 Ynut_w, FALSE, FALSE,
4634 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4637 Ynut_wB, FALSE, TRUE,
4638 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4641 Xbug_n, TRUE, FALSE,
4645 Xbug_e, TRUE, FALSE,
4646 EL_BUG_RIGHT, -1, -1
4649 Xbug_s, TRUE, FALSE,
4653 Xbug_w, TRUE, FALSE,
4657 Xbug_gon, FALSE, FALSE,
4661 Xbug_goe, FALSE, FALSE,
4662 EL_BUG_RIGHT, -1, -1
4665 Xbug_gos, FALSE, FALSE,
4669 Xbug_gow, FALSE, FALSE,
4673 Ybug_n, FALSE, FALSE,
4674 EL_BUG, ACTION_MOVING, MV_BIT_UP
4677 Ybug_nB, FALSE, TRUE,
4678 EL_BUG, ACTION_MOVING, MV_BIT_UP
4681 Ybug_e, FALSE, FALSE,
4682 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4685 Ybug_eB, FALSE, TRUE,
4686 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4689 Ybug_s, FALSE, FALSE,
4690 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4693 Ybug_sB, FALSE, TRUE,
4694 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4697 Ybug_w, FALSE, FALSE,
4698 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4701 Ybug_wB, FALSE, TRUE,
4702 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4705 Ybug_w_n, FALSE, FALSE,
4706 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4709 Ybug_n_e, FALSE, FALSE,
4710 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4713 Ybug_e_s, FALSE, FALSE,
4714 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4717 Ybug_s_w, FALSE, FALSE,
4718 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4721 Ybug_e_n, FALSE, FALSE,
4722 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4725 Ybug_s_e, FALSE, FALSE,
4726 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4729 Ybug_w_s, FALSE, FALSE,
4730 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4733 Ybug_n_w, FALSE, FALSE,
4734 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4737 Ybug_stone, FALSE, FALSE,
4738 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4741 Ybug_spring, FALSE, FALSE,
4742 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4745 Xtank_n, TRUE, FALSE,
4746 EL_SPACESHIP_UP, -1, -1
4749 Xtank_e, TRUE, FALSE,
4750 EL_SPACESHIP_RIGHT, -1, -1
4753 Xtank_s, TRUE, FALSE,
4754 EL_SPACESHIP_DOWN, -1, -1
4757 Xtank_w, TRUE, FALSE,
4758 EL_SPACESHIP_LEFT, -1, -1
4761 Xtank_gon, FALSE, FALSE,
4762 EL_SPACESHIP_UP, -1, -1
4765 Xtank_goe, FALSE, FALSE,
4766 EL_SPACESHIP_RIGHT, -1, -1
4769 Xtank_gos, FALSE, FALSE,
4770 EL_SPACESHIP_DOWN, -1, -1
4773 Xtank_gow, FALSE, FALSE,
4774 EL_SPACESHIP_LEFT, -1, -1
4777 Ytank_n, FALSE, FALSE,
4778 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4781 Ytank_nB, FALSE, TRUE,
4782 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4785 Ytank_e, FALSE, FALSE,
4786 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4789 Ytank_eB, FALSE, TRUE,
4790 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4793 Ytank_s, FALSE, FALSE,
4794 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4797 Ytank_sB, FALSE, TRUE,
4798 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4801 Ytank_w, FALSE, FALSE,
4802 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4805 Ytank_wB, FALSE, TRUE,
4806 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
4809 Ytank_w_n, FALSE, FALSE,
4810 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4813 Ytank_n_e, FALSE, FALSE,
4814 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4817 Ytank_e_s, FALSE, FALSE,
4818 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4821 Ytank_s_w, FALSE, FALSE,
4822 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4825 Ytank_e_n, FALSE, FALSE,
4826 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4829 Ytank_s_e, FALSE, FALSE,
4830 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4833 Ytank_w_s, FALSE, FALSE,
4834 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4837 Ytank_n_w, FALSE, FALSE,
4838 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4841 Ytank_stone, FALSE, FALSE,
4842 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
4845 Ytank_spring, FALSE, FALSE,
4846 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
4849 Xandroid, TRUE, FALSE,
4850 EL_EMC_ANDROID, ACTION_ACTIVE, -1
4853 Xandroid_1_n, FALSE, FALSE,
4854 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4857 Xandroid_2_n, FALSE, FALSE,
4858 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
4861 Xandroid_1_e, FALSE, FALSE,
4862 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4865 Xandroid_2_e, FALSE, FALSE,
4866 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
4869 Xandroid_1_w, FALSE, FALSE,
4870 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4873 Xandroid_2_w, FALSE, FALSE,
4874 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
4877 Xandroid_1_s, FALSE, FALSE,
4878 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4881 Xandroid_2_s, FALSE, FALSE,
4882 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
4885 Yandroid_n, FALSE, FALSE,
4886 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4889 Yandroid_nB, FALSE, TRUE,
4890 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
4893 Yandroid_ne, FALSE, FALSE,
4894 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
4897 Yandroid_neB, FALSE, TRUE,
4898 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
4901 Yandroid_e, FALSE, FALSE,
4902 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4905 Yandroid_eB, FALSE, TRUE,
4906 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
4909 Yandroid_se, FALSE, FALSE,
4910 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
4913 Yandroid_seB, FALSE, TRUE,
4914 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
4917 Yandroid_s, FALSE, FALSE,
4918 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4921 Yandroid_sB, FALSE, TRUE,
4922 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
4925 Yandroid_sw, FALSE, FALSE,
4926 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
4929 Yandroid_swB, FALSE, TRUE,
4930 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
4933 Yandroid_w, FALSE, FALSE,
4934 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
4937 Yandroid_wB, FALSE, TRUE,
4938 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
4941 Yandroid_nw, FALSE, FALSE,
4942 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
4945 Yandroid_nwB, FALSE, TRUE,
4946 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
4949 Xspring, TRUE, FALSE,
4953 Xspring_pause, FALSE, FALSE,
4957 Xspring_e, FALSE, FALSE,
4961 Xspring_w, FALSE, FALSE,
4965 Xspring_fall, FALSE, FALSE,
4969 Yspring_s, FALSE, FALSE,
4970 EL_SPRING, ACTION_FALLING, -1
4973 Yspring_sB, FALSE, TRUE,
4974 EL_SPRING, ACTION_FALLING, -1
4977 Yspring_e, FALSE, FALSE,
4978 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
4981 Yspring_eB, FALSE, TRUE,
4982 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
4985 Yspring_w, FALSE, FALSE,
4986 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
4989 Yspring_wB, FALSE, TRUE,
4990 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
4993 Yspring_kill_e, FALSE, FALSE,
4994 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
4997 Yspring_kill_eB, FALSE, TRUE,
4998 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5001 Yspring_kill_w, FALSE, FALSE,
5002 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5005 Yspring_kill_wB, FALSE, TRUE,
5006 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5009 Xeater_n, TRUE, FALSE,
5010 EL_YAMYAM_UP, -1, -1
5013 Xeater_e, TRUE, FALSE,
5014 EL_YAMYAM_RIGHT, -1, -1
5017 Xeater_w, TRUE, FALSE,
5018 EL_YAMYAM_LEFT, -1, -1
5021 Xeater_s, TRUE, FALSE,
5022 EL_YAMYAM_DOWN, -1, -1
5025 Yeater_n, FALSE, FALSE,
5026 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5029 Yeater_nB, FALSE, TRUE,
5030 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5033 Yeater_e, FALSE, FALSE,
5034 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5037 Yeater_eB, FALSE, TRUE,
5038 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5041 Yeater_s, FALSE, FALSE,
5042 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5045 Yeater_sB, FALSE, TRUE,
5046 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5049 Yeater_w, FALSE, FALSE,
5050 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5053 Yeater_wB, FALSE, TRUE,
5054 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5057 Yeater_stone, FALSE, FALSE,
5058 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5061 Yeater_spring, FALSE, FALSE,
5062 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5065 Xalien, TRUE, FALSE,
5069 Xalien_pause, FALSE, FALSE,
5073 Yalien_n, FALSE, FALSE,
5074 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5077 Yalien_nB, FALSE, TRUE,
5078 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5081 Yalien_e, FALSE, FALSE,
5082 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5085 Yalien_eB, FALSE, TRUE,
5086 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5089 Yalien_s, FALSE, FALSE,
5090 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5093 Yalien_sB, FALSE, TRUE,
5094 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5097 Yalien_w, FALSE, FALSE,
5098 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5101 Yalien_wB, FALSE, TRUE,
5102 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5105 Yalien_stone, FALSE, FALSE,
5106 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5109 Yalien_spring, FALSE, FALSE,
5110 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5113 Xemerald, TRUE, FALSE,
5117 Xemerald_pause, FALSE, FALSE,
5121 Xemerald_fall, FALSE, FALSE,
5125 Xemerald_shine, FALSE, FALSE,
5126 EL_EMERALD, ACTION_TWINKLING, -1
5129 Yemerald_s, FALSE, FALSE,
5130 EL_EMERALD, ACTION_FALLING, -1
5133 Yemerald_sB, FALSE, TRUE,
5134 EL_EMERALD, ACTION_FALLING, -1
5137 Yemerald_e, FALSE, FALSE,
5138 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5141 Yemerald_eB, FALSE, TRUE,
5142 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5145 Yemerald_w, FALSE, FALSE,
5146 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5149 Yemerald_wB, FALSE, TRUE,
5150 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5153 Yemerald_eat, FALSE, FALSE,
5154 EL_EMERALD, ACTION_COLLECTING, -1
5157 Yemerald_stone, FALSE, FALSE,
5158 EL_NUT, ACTION_BREAKING, -1
5161 Xdiamond, TRUE, FALSE,
5165 Xdiamond_pause, FALSE, FALSE,
5169 Xdiamond_fall, FALSE, FALSE,
5173 Xdiamond_shine, FALSE, FALSE,
5174 EL_DIAMOND, ACTION_TWINKLING, -1
5177 Ydiamond_s, FALSE, FALSE,
5178 EL_DIAMOND, ACTION_FALLING, -1
5181 Ydiamond_sB, FALSE, TRUE,
5182 EL_DIAMOND, ACTION_FALLING, -1
5185 Ydiamond_e, FALSE, FALSE,
5186 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5189 Ydiamond_eB, FALSE, TRUE,
5190 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5193 Ydiamond_w, FALSE, FALSE,
5194 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5197 Ydiamond_wB, FALSE, TRUE,
5198 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5201 Ydiamond_eat, FALSE, FALSE,
5202 EL_DIAMOND, ACTION_COLLECTING, -1
5205 Ydiamond_stone, FALSE, FALSE,
5206 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5209 Xdrip_fall, TRUE, FALSE,
5210 EL_AMOEBA_DROP, -1, -1
5213 Xdrip_stretch, FALSE, FALSE,
5214 EL_AMOEBA_DROP, ACTION_FALLING, -1
5217 Xdrip_stretchB, FALSE, TRUE,
5218 EL_AMOEBA_DROP, ACTION_FALLING, -1
5221 Xdrip_eat, FALSE, FALSE,
5222 EL_AMOEBA_DROP, ACTION_GROWING, -1
5225 Ydrip_s1, FALSE, FALSE,
5226 EL_AMOEBA_DROP, ACTION_FALLING, -1
5229 Ydrip_s1B, FALSE, TRUE,
5230 EL_AMOEBA_DROP, ACTION_FALLING, -1
5233 Ydrip_s2, FALSE, FALSE,
5234 EL_AMOEBA_DROP, ACTION_FALLING, -1
5237 Ydrip_s2B, FALSE, TRUE,
5238 EL_AMOEBA_DROP, ACTION_FALLING, -1
5245 Xbomb_pause, FALSE, FALSE,
5249 Xbomb_fall, FALSE, FALSE,
5253 Ybomb_s, FALSE, FALSE,
5254 EL_BOMB, ACTION_FALLING, -1
5257 Ybomb_sB, FALSE, TRUE,
5258 EL_BOMB, ACTION_FALLING, -1
5261 Ybomb_e, FALSE, FALSE,
5262 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5265 Ybomb_eB, FALSE, TRUE,
5266 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5269 Ybomb_w, FALSE, FALSE,
5270 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5273 Ybomb_wB, FALSE, TRUE,
5274 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5277 Ybomb_eat, FALSE, FALSE,
5278 EL_BOMB, ACTION_ACTIVATING, -1
5281 Xballoon, TRUE, FALSE,
5285 Yballoon_n, FALSE, FALSE,
5286 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5289 Yballoon_nB, FALSE, TRUE,
5290 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5293 Yballoon_e, FALSE, FALSE,
5294 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5297 Yballoon_eB, FALSE, TRUE,
5298 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5301 Yballoon_s, FALSE, FALSE,
5302 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5305 Yballoon_sB, FALSE, TRUE,
5306 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5309 Yballoon_w, FALSE, FALSE,
5310 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5313 Yballoon_wB, FALSE, TRUE,
5314 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5317 Xgrass, TRUE, FALSE,
5318 EL_EMC_GRASS, -1, -1
5321 Ygrass_nB, FALSE, FALSE,
5322 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5325 Ygrass_eB, FALSE, FALSE,
5326 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5329 Ygrass_sB, FALSE, FALSE,
5330 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5333 Ygrass_wB, FALSE, FALSE,
5334 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5341 Ydirt_nB, FALSE, FALSE,
5342 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5345 Ydirt_eB, FALSE, FALSE,
5346 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5349 Ydirt_sB, FALSE, FALSE,
5350 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5353 Ydirt_wB, FALSE, FALSE,
5354 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5357 Xacid_ne, TRUE, FALSE,
5358 EL_ACID_POOL_TOPRIGHT, -1, -1
5361 Xacid_se, TRUE, FALSE,
5362 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5365 Xacid_s, TRUE, FALSE,
5366 EL_ACID_POOL_BOTTOM, -1, -1
5369 Xacid_sw, TRUE, FALSE,
5370 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5373 Xacid_nw, TRUE, FALSE,
5374 EL_ACID_POOL_TOPLEFT, -1, -1
5377 Xacid_1, TRUE, FALSE,
5381 Xacid_2, FALSE, FALSE,
5385 Xacid_3, FALSE, FALSE,
5389 Xacid_4, FALSE, FALSE,
5393 Xacid_5, FALSE, FALSE,
5397 Xacid_6, FALSE, FALSE,
5401 Xacid_7, FALSE, FALSE,
5405 Xacid_8, FALSE, FALSE,
5409 Xball_1, TRUE, FALSE,
5410 EL_EMC_MAGIC_BALL, -1, -1
5413 Xball_1B, FALSE, FALSE,
5414 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5417 Xball_2, FALSE, FALSE,
5418 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5421 Xball_2B, FALSE, FALSE,
5422 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5425 Yball_eat, FALSE, FALSE,
5426 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5429 Ykey_1_eat, FALSE, FALSE,
5430 EL_EM_KEY_1, ACTION_COLLECTING, -1
5433 Ykey_2_eat, FALSE, FALSE,
5434 EL_EM_KEY_2, ACTION_COLLECTING, -1
5437 Ykey_3_eat, FALSE, FALSE,
5438 EL_EM_KEY_3, ACTION_COLLECTING, -1
5441 Ykey_4_eat, FALSE, FALSE,
5442 EL_EM_KEY_4, ACTION_COLLECTING, -1
5445 Ykey_5_eat, FALSE, FALSE,
5446 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5449 Ykey_6_eat, FALSE, FALSE,
5450 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5453 Ykey_7_eat, FALSE, FALSE,
5454 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5457 Ykey_8_eat, FALSE, FALSE,
5458 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5461 Ylenses_eat, FALSE, FALSE,
5462 EL_EMC_LENSES, ACTION_COLLECTING, -1
5465 Ymagnify_eat, FALSE, FALSE,
5466 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5469 Ygrass_eat, FALSE, FALSE,
5470 EL_EMC_GRASS, ACTION_SNAPPING, -1
5473 Ydirt_eat, FALSE, FALSE,
5474 EL_SAND, ACTION_SNAPPING, -1
5477 Xgrow_ns, TRUE, FALSE,
5478 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5481 Ygrow_ns_eat, FALSE, FALSE,
5482 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5485 Xgrow_ew, TRUE, FALSE,
5486 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5489 Ygrow_ew_eat, FALSE, FALSE,
5490 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5493 Xwonderwall, TRUE, FALSE,
5494 EL_MAGIC_WALL, -1, -1
5497 XwonderwallB, FALSE, FALSE,
5498 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5501 Xamoeba_1, TRUE, FALSE,
5502 EL_AMOEBA_DRY, ACTION_OTHER, -1
5505 Xamoeba_2, FALSE, FALSE,
5506 EL_AMOEBA_DRY, ACTION_OTHER, -1
5509 Xamoeba_3, FALSE, FALSE,
5510 EL_AMOEBA_DRY, ACTION_OTHER, -1
5513 Xamoeba_4, FALSE, FALSE,
5514 EL_AMOEBA_DRY, ACTION_OTHER, -1
5517 Xamoeba_5, TRUE, FALSE,
5518 EL_AMOEBA_WET, ACTION_OTHER, -1
5521 Xamoeba_6, FALSE, FALSE,
5522 EL_AMOEBA_WET, ACTION_OTHER, -1
5525 Xamoeba_7, FALSE, FALSE,
5526 EL_AMOEBA_WET, ACTION_OTHER, -1
5529 Xamoeba_8, FALSE, FALSE,
5530 EL_AMOEBA_WET, ACTION_OTHER, -1
5533 Xdoor_1, TRUE, FALSE,
5534 EL_EM_GATE_1, -1, -1
5537 Xdoor_2, TRUE, FALSE,
5538 EL_EM_GATE_2, -1, -1
5541 Xdoor_3, TRUE, FALSE,
5542 EL_EM_GATE_3, -1, -1
5545 Xdoor_4, TRUE, FALSE,
5546 EL_EM_GATE_4, -1, -1
5549 Xdoor_5, TRUE, FALSE,
5550 EL_EMC_GATE_5, -1, -1
5553 Xdoor_6, TRUE, FALSE,
5554 EL_EMC_GATE_6, -1, -1
5557 Xdoor_7, TRUE, FALSE,
5558 EL_EMC_GATE_7, -1, -1
5561 Xdoor_8, TRUE, FALSE,
5562 EL_EMC_GATE_8, -1, -1
5565 Xkey_1, TRUE, FALSE,
5569 Xkey_2, TRUE, FALSE,
5573 Xkey_3, TRUE, FALSE,
5577 Xkey_4, TRUE, FALSE,
5581 Xkey_5, TRUE, FALSE,
5582 EL_EMC_KEY_5, -1, -1
5585 Xkey_6, TRUE, FALSE,
5586 EL_EMC_KEY_6, -1, -1
5589 Xkey_7, TRUE, FALSE,
5590 EL_EMC_KEY_7, -1, -1
5593 Xkey_8, TRUE, FALSE,
5594 EL_EMC_KEY_8, -1, -1
5597 Xwind_n, TRUE, FALSE,
5598 EL_BALLOON_SWITCH_UP, -1, -1
5601 Xwind_e, TRUE, FALSE,
5602 EL_BALLOON_SWITCH_RIGHT, -1, -1
5605 Xwind_s, TRUE, FALSE,
5606 EL_BALLOON_SWITCH_DOWN, -1, -1
5609 Xwind_w, TRUE, FALSE,
5610 EL_BALLOON_SWITCH_LEFT, -1, -1
5613 Xwind_nesw, TRUE, FALSE,
5614 EL_BALLOON_SWITCH_ANY, -1, -1
5617 Xwind_stop, TRUE, FALSE,
5618 EL_BALLOON_SWITCH_NONE, -1, -1
5622 EL_EM_EXIT_CLOSED, -1, -1
5625 Xexit_1, TRUE, FALSE,
5626 EL_EM_EXIT_OPEN, -1, -1
5629 Xexit_2, FALSE, FALSE,
5630 EL_EM_EXIT_OPEN, -1, -1
5633 Xexit_3, FALSE, FALSE,
5634 EL_EM_EXIT_OPEN, -1, -1
5637 Xdynamite, TRUE, FALSE,
5638 EL_EM_DYNAMITE, -1, -1
5641 Ydynamite_eat, FALSE, FALSE,
5642 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5645 Xdynamite_1, TRUE, FALSE,
5646 EL_EM_DYNAMITE_ACTIVE, -1, -1
5649 Xdynamite_2, FALSE, FALSE,
5650 EL_EM_DYNAMITE_ACTIVE, -1, -1
5653 Xdynamite_3, FALSE, FALSE,
5654 EL_EM_DYNAMITE_ACTIVE, -1, -1
5657 Xdynamite_4, FALSE, FALSE,
5658 EL_EM_DYNAMITE_ACTIVE, -1, -1
5661 Xbumper, TRUE, FALSE,
5662 EL_EMC_SPRING_BUMPER, -1, -1
5665 XbumperB, FALSE, FALSE,
5666 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5669 Xwheel, TRUE, FALSE,
5670 EL_ROBOT_WHEEL, -1, -1
5673 XwheelB, FALSE, FALSE,
5674 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5677 Xswitch, TRUE, FALSE,
5678 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5681 XswitchB, FALSE, FALSE,
5682 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5686 EL_QUICKSAND_EMPTY, -1, -1
5689 Xsand_stone, TRUE, FALSE,
5690 EL_QUICKSAND_FULL, -1, -1
5693 Xsand_stonein_1, FALSE, TRUE,
5694 EL_ROCK, ACTION_FILLING, -1
5697 Xsand_stonein_2, FALSE, TRUE,
5698 EL_ROCK, ACTION_FILLING, -1
5701 Xsand_stonein_3, FALSE, TRUE,
5702 EL_ROCK, ACTION_FILLING, -1
5705 Xsand_stonein_4, FALSE, TRUE,
5706 EL_ROCK, ACTION_FILLING, -1
5709 Xsand_stonesand_1, FALSE, FALSE,
5710 EL_QUICKSAND_EMPTYING, -1, -1
5713 Xsand_stonesand_2, FALSE, FALSE,
5714 EL_QUICKSAND_EMPTYING, -1, -1
5717 Xsand_stonesand_3, FALSE, FALSE,
5718 EL_QUICKSAND_EMPTYING, -1, -1
5721 Xsand_stonesand_4, FALSE, FALSE,
5722 EL_QUICKSAND_EMPTYING, -1, -1
5725 Xsand_stonesand_quickout_1, FALSE, FALSE,
5726 EL_QUICKSAND_EMPTYING, -1, -1
5729 Xsand_stonesand_quickout_2, FALSE, FALSE,
5730 EL_QUICKSAND_EMPTYING, -1, -1
5733 Xsand_stoneout_1, FALSE, FALSE,
5734 EL_ROCK, ACTION_EMPTYING, -1
5737 Xsand_stoneout_2, FALSE, FALSE,
5738 EL_ROCK, ACTION_EMPTYING, -1
5741 Xsand_sandstone_1, FALSE, FALSE,
5742 EL_QUICKSAND_FILLING, -1, -1
5745 Xsand_sandstone_2, FALSE, FALSE,
5746 EL_QUICKSAND_FILLING, -1, -1
5749 Xsand_sandstone_3, FALSE, FALSE,
5750 EL_QUICKSAND_FILLING, -1, -1
5753 Xsand_sandstone_4, FALSE, FALSE,
5754 EL_QUICKSAND_FILLING, -1, -1
5757 Xplant, TRUE, FALSE,
5758 EL_EMC_PLANT, -1, -1
5761 Yplant, FALSE, FALSE,
5762 EL_EMC_PLANT, -1, -1
5765 Xlenses, TRUE, FALSE,
5766 EL_EMC_LENSES, -1, -1
5769 Xmagnify, TRUE, FALSE,
5770 EL_EMC_MAGNIFIER, -1, -1
5773 Xdripper, TRUE, FALSE,
5774 EL_EMC_DRIPPER, -1, -1
5777 XdripperB, FALSE, FALSE,
5778 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5781 Xfake_blank, TRUE, FALSE,
5782 EL_INVISIBLE_WALL, -1, -1
5785 Xfake_blankB, FALSE, FALSE,
5786 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5789 Xfake_grass, TRUE, FALSE,
5790 EL_EMC_FAKE_GRASS, -1, -1
5793 Xfake_grassB, FALSE, FALSE,
5794 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5797 Xfake_door_1, TRUE, FALSE,
5798 EL_EM_GATE_1_GRAY, -1, -1
5801 Xfake_door_2, TRUE, FALSE,
5802 EL_EM_GATE_2_GRAY, -1, -1
5805 Xfake_door_3, TRUE, FALSE,
5806 EL_EM_GATE_3_GRAY, -1, -1
5809 Xfake_door_4, TRUE, FALSE,
5810 EL_EM_GATE_4_GRAY, -1, -1
5813 Xfake_door_5, TRUE, FALSE,
5814 EL_EMC_GATE_5_GRAY, -1, -1
5817 Xfake_door_6, TRUE, FALSE,
5818 EL_EMC_GATE_6_GRAY, -1, -1
5821 Xfake_door_7, TRUE, FALSE,
5822 EL_EMC_GATE_7_GRAY, -1, -1
5825 Xfake_door_8, TRUE, FALSE,
5826 EL_EMC_GATE_8_GRAY, -1, -1
5829 Xfake_acid_1, TRUE, FALSE,
5830 EL_EMC_FAKE_ACID, -1, -1
5833 Xfake_acid_2, FALSE, FALSE,
5834 EL_EMC_FAKE_ACID, -1, -1
5837 Xfake_acid_3, FALSE, FALSE,
5838 EL_EMC_FAKE_ACID, -1, -1
5841 Xfake_acid_4, FALSE, FALSE,
5842 EL_EMC_FAKE_ACID, -1, -1
5845 Xfake_acid_5, FALSE, FALSE,
5846 EL_EMC_FAKE_ACID, -1, -1
5849 Xfake_acid_6, FALSE, FALSE,
5850 EL_EMC_FAKE_ACID, -1, -1
5853 Xfake_acid_7, FALSE, FALSE,
5854 EL_EMC_FAKE_ACID, -1, -1
5857 Xfake_acid_8, FALSE, FALSE,
5858 EL_EMC_FAKE_ACID, -1, -1
5861 Xsteel_1, TRUE, FALSE,
5862 EL_STEELWALL, -1, -1
5865 Xsteel_2, TRUE, FALSE,
5866 EL_EMC_STEELWALL_2, -1, -1
5869 Xsteel_3, TRUE, FALSE,
5870 EL_EMC_STEELWALL_3, -1, -1
5873 Xsteel_4, TRUE, FALSE,
5874 EL_EMC_STEELWALL_4, -1, -1
5877 Xwall_1, TRUE, FALSE,
5881 Xwall_2, TRUE, FALSE,
5882 EL_EMC_WALL_14, -1, -1
5885 Xwall_3, TRUE, FALSE,
5886 EL_EMC_WALL_15, -1, -1
5889 Xwall_4, TRUE, FALSE,
5890 EL_EMC_WALL_16, -1, -1
5893 Xround_wall_1, TRUE, FALSE,
5894 EL_WALL_SLIPPERY, -1, -1
5897 Xround_wall_2, TRUE, FALSE,
5898 EL_EMC_WALL_SLIPPERY_2, -1, -1
5901 Xround_wall_3, TRUE, FALSE,
5902 EL_EMC_WALL_SLIPPERY_3, -1, -1
5905 Xround_wall_4, TRUE, FALSE,
5906 EL_EMC_WALL_SLIPPERY_4, -1, -1
5909 Xdecor_1, TRUE, FALSE,
5910 EL_EMC_WALL_8, -1, -1
5913 Xdecor_2, TRUE, FALSE,
5914 EL_EMC_WALL_6, -1, -1
5917 Xdecor_3, TRUE, FALSE,
5918 EL_EMC_WALL_4, -1, -1
5921 Xdecor_4, TRUE, FALSE,
5922 EL_EMC_WALL_7, -1, -1
5925 Xdecor_5, TRUE, FALSE,
5926 EL_EMC_WALL_5, -1, -1
5929 Xdecor_6, TRUE, FALSE,
5930 EL_EMC_WALL_9, -1, -1
5933 Xdecor_7, TRUE, FALSE,
5934 EL_EMC_WALL_10, -1, -1
5937 Xdecor_8, TRUE, FALSE,
5938 EL_EMC_WALL_1, -1, -1
5941 Xdecor_9, TRUE, FALSE,
5942 EL_EMC_WALL_2, -1, -1
5945 Xdecor_10, TRUE, FALSE,
5946 EL_EMC_WALL_3, -1, -1
5949 Xdecor_11, TRUE, FALSE,
5950 EL_EMC_WALL_11, -1, -1
5953 Xdecor_12, TRUE, FALSE,
5954 EL_EMC_WALL_12, -1, -1
5957 Xalpha_0, TRUE, FALSE,
5958 EL_CHAR('0'), -1, -1
5961 Xalpha_1, TRUE, FALSE,
5962 EL_CHAR('1'), -1, -1
5965 Xalpha_2, TRUE, FALSE,
5966 EL_CHAR('2'), -1, -1
5969 Xalpha_3, TRUE, FALSE,
5970 EL_CHAR('3'), -1, -1
5973 Xalpha_4, TRUE, FALSE,
5974 EL_CHAR('4'), -1, -1
5977 Xalpha_5, TRUE, FALSE,
5978 EL_CHAR('5'), -1, -1
5981 Xalpha_6, TRUE, FALSE,
5982 EL_CHAR('6'), -1, -1
5985 Xalpha_7, TRUE, FALSE,
5986 EL_CHAR('7'), -1, -1
5989 Xalpha_8, TRUE, FALSE,
5990 EL_CHAR('8'), -1, -1
5993 Xalpha_9, TRUE, FALSE,
5994 EL_CHAR('9'), -1, -1
5997 Xalpha_excla, TRUE, FALSE,
5998 EL_CHAR('!'), -1, -1
6001 Xalpha_quote, TRUE, FALSE,
6002 EL_CHAR('"'), -1, -1
6005 Xalpha_comma, TRUE, FALSE,
6006 EL_CHAR(','), -1, -1
6009 Xalpha_minus, TRUE, FALSE,
6010 EL_CHAR('-'), -1, -1
6013 Xalpha_perio, TRUE, FALSE,
6014 EL_CHAR('.'), -1, -1
6017 Xalpha_colon, TRUE, FALSE,
6018 EL_CHAR(':'), -1, -1
6021 Xalpha_quest, TRUE, FALSE,
6022 EL_CHAR('?'), -1, -1
6025 Xalpha_a, TRUE, FALSE,
6026 EL_CHAR('A'), -1, -1
6029 Xalpha_b, TRUE, FALSE,
6030 EL_CHAR('B'), -1, -1
6033 Xalpha_c, TRUE, FALSE,
6034 EL_CHAR('C'), -1, -1
6037 Xalpha_d, TRUE, FALSE,
6038 EL_CHAR('D'), -1, -1
6041 Xalpha_e, TRUE, FALSE,
6042 EL_CHAR('E'), -1, -1
6045 Xalpha_f, TRUE, FALSE,
6046 EL_CHAR('F'), -1, -1
6049 Xalpha_g, TRUE, FALSE,
6050 EL_CHAR('G'), -1, -1
6053 Xalpha_h, TRUE, FALSE,
6054 EL_CHAR('H'), -1, -1
6057 Xalpha_i, TRUE, FALSE,
6058 EL_CHAR('I'), -1, -1
6061 Xalpha_j, TRUE, FALSE,
6062 EL_CHAR('J'), -1, -1
6065 Xalpha_k, TRUE, FALSE,
6066 EL_CHAR('K'), -1, -1
6069 Xalpha_l, TRUE, FALSE,
6070 EL_CHAR('L'), -1, -1
6073 Xalpha_m, TRUE, FALSE,
6074 EL_CHAR('M'), -1, -1
6077 Xalpha_n, TRUE, FALSE,
6078 EL_CHAR('N'), -1, -1
6081 Xalpha_o, TRUE, FALSE,
6082 EL_CHAR('O'), -1, -1
6085 Xalpha_p, TRUE, FALSE,
6086 EL_CHAR('P'), -1, -1
6089 Xalpha_q, TRUE, FALSE,
6090 EL_CHAR('Q'), -1, -1
6093 Xalpha_r, TRUE, FALSE,
6094 EL_CHAR('R'), -1, -1
6097 Xalpha_s, TRUE, FALSE,
6098 EL_CHAR('S'), -1, -1
6101 Xalpha_t, TRUE, FALSE,
6102 EL_CHAR('T'), -1, -1
6105 Xalpha_u, TRUE, FALSE,
6106 EL_CHAR('U'), -1, -1
6109 Xalpha_v, TRUE, FALSE,
6110 EL_CHAR('V'), -1, -1
6113 Xalpha_w, TRUE, FALSE,
6114 EL_CHAR('W'), -1, -1
6117 Xalpha_x, TRUE, FALSE,
6118 EL_CHAR('X'), -1, -1
6121 Xalpha_y, TRUE, FALSE,
6122 EL_CHAR('Y'), -1, -1
6125 Xalpha_z, TRUE, FALSE,
6126 EL_CHAR('Z'), -1, -1
6129 Xalpha_arrow_e, TRUE, FALSE,
6130 EL_CHAR('>'), -1, -1
6133 Xalpha_arrow_w, TRUE, FALSE,
6134 EL_CHAR('<'), -1, -1
6137 Xalpha_copyr, TRUE, FALSE,
6138 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6142 Xboom_bug, FALSE, FALSE,
6143 EL_BUG, ACTION_EXPLODING, -1
6146 Xboom_bomb, FALSE, FALSE,
6147 EL_BOMB, ACTION_EXPLODING, -1
6150 Xboom_android, FALSE, FALSE,
6151 EL_EMC_ANDROID, ACTION_OTHER, -1
6154 Xboom_1, FALSE, FALSE,
6155 EL_DEFAULT, ACTION_EXPLODING, -1
6158 Xboom_2, FALSE, FALSE,
6159 EL_DEFAULT, ACTION_EXPLODING, -1
6162 Znormal, FALSE, FALSE,
6166 Zdynamite, FALSE, FALSE,
6170 Zplayer, FALSE, FALSE,
6174 ZBORDER, FALSE, FALSE,
6184 static struct Mapping_EM_to_RND_player
6193 em_player_mapping_list[] =
6197 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6201 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6205 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6209 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6213 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6217 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6221 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6225 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6229 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6233 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6237 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6241 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6245 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6249 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6253 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6257 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6261 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6265 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6269 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6273 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6277 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6281 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6285 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6289 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6293 EL_PLAYER_1, ACTION_DEFAULT, -1,
6297 EL_PLAYER_2, ACTION_DEFAULT, -1,
6301 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6305 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6309 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6313 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6317 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6321 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6325 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6329 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6333 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6337 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6341 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6345 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6349 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6353 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6357 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6361 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6365 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6369 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6373 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6377 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6381 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6385 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6389 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6393 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6397 EL_PLAYER_3, ACTION_DEFAULT, -1,
6401 EL_PLAYER_4, ACTION_DEFAULT, -1,
6410 int map_element_RND_to_EM(int element_rnd)
6412 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6413 static boolean mapping_initialized = FALSE;
6415 if (!mapping_initialized)
6419 /* return "Xalpha_quest" for all undefined elements in mapping array */
6420 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6421 mapping_RND_to_EM[i] = Xalpha_quest;
6423 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6424 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6425 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6426 em_object_mapping_list[i].element_em;
6428 mapping_initialized = TRUE;
6431 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6432 return mapping_RND_to_EM[element_rnd];
6434 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6439 int map_element_EM_to_RND(int element_em)
6441 static unsigned short mapping_EM_to_RND[TILE_MAX];
6442 static boolean mapping_initialized = FALSE;
6444 if (!mapping_initialized)
6448 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6449 for (i = 0; i < TILE_MAX; i++)
6450 mapping_EM_to_RND[i] = EL_UNKNOWN;
6452 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6453 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6454 em_object_mapping_list[i].element_rnd;
6456 mapping_initialized = TRUE;
6459 if (element_em >= 0 && element_em < TILE_MAX)
6460 return mapping_EM_to_RND[element_em];
6462 Error(ERR_WARN, "invalid EM level element %d", element_em);
6467 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6469 struct LevelInfo_EM *level_em = level->native_em_level;
6470 struct LEVEL *lev = level_em->lev;
6473 for (i = 0; i < TILE_MAX; i++)
6474 lev->android_array[i] = Xblank;
6476 for (i = 0; i < level->num_android_clone_elements; i++)
6478 int element_rnd = level->android_clone_element[i];
6479 int element_em = map_element_RND_to_EM(element_rnd);
6481 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6482 if (em_object_mapping_list[j].element_rnd == element_rnd)
6483 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6487 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6489 struct LevelInfo_EM *level_em = level->native_em_level;
6490 struct LEVEL *lev = level_em->lev;
6493 level->num_android_clone_elements = 0;
6495 for (i = 0; i < TILE_MAX; i++)
6497 int element_em = lev->android_array[i];
6499 boolean element_found = FALSE;
6501 if (element_em == Xblank)
6504 element_rnd = map_element_EM_to_RND(element_em);
6506 for (j = 0; j < level->num_android_clone_elements; j++)
6507 if (level->android_clone_element[j] == element_rnd)
6508 element_found = TRUE;
6512 level->android_clone_element[level->num_android_clone_elements++] =
6515 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6520 if (level->num_android_clone_elements == 0)
6522 level->num_android_clone_elements = 1;
6523 level->android_clone_element[0] = EL_EMPTY;
6527 int map_direction_RND_to_EM(int direction)
6529 return (direction == MV_UP ? 0 :
6530 direction == MV_RIGHT ? 1 :
6531 direction == MV_DOWN ? 2 :
6532 direction == MV_LEFT ? 3 :
6536 int map_direction_EM_to_RND(int direction)
6538 return (direction == 0 ? MV_UP :
6539 direction == 1 ? MV_RIGHT :
6540 direction == 2 ? MV_DOWN :
6541 direction == 3 ? MV_LEFT :
6545 int map_element_RND_to_SP(int element_rnd)
6547 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6549 if (element_rnd >= EL_SP_START &&
6550 element_rnd <= EL_SP_END)
6551 element_sp = element_rnd - EL_SP_START;
6552 else if (element_rnd == EL_EMPTY_SPACE)
6554 else if (element_rnd == EL_INVISIBLE_WALL)
6560 int map_element_SP_to_RND(int element_sp)
6562 int element_rnd = EL_UNKNOWN;
6564 if (element_sp >= 0x00 &&
6566 element_rnd = EL_SP_START + element_sp;
6567 else if (element_sp == 0x28)
6568 element_rnd = EL_INVISIBLE_WALL;
6573 int map_action_SP_to_RND(int action_sp)
6577 case actActive: return ACTION_ACTIVE;
6578 case actImpact: return ACTION_IMPACT;
6579 case actExploding: return ACTION_EXPLODING;
6580 case actDigging: return ACTION_DIGGING;
6581 case actSnapping: return ACTION_SNAPPING;
6582 case actCollecting: return ACTION_COLLECTING;
6583 case actPassing: return ACTION_PASSING;
6584 case actPushing: return ACTION_PUSHING;
6585 case actDropping: return ACTION_DROPPING;
6587 default: return ACTION_DEFAULT;
6591 int get_next_element(int element)
6595 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6596 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6597 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6598 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6599 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6600 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6601 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6602 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6603 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6604 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6605 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6607 default: return element;
6611 int el_act_dir2img(int element, int action, int direction)
6613 element = GFX_ELEMENT(element);
6614 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6616 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6617 return element_info[element].direction_graphic[action][direction];
6620 static int el_act_dir2crm(int element, int action, int direction)
6622 element = GFX_ELEMENT(element);
6623 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6625 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6626 return element_info[element].direction_crumbled[action][direction];
6629 int el_act2img(int element, int action)
6631 element = GFX_ELEMENT(element);
6633 return element_info[element].graphic[action];
6636 int el_act2crm(int element, int action)
6638 element = GFX_ELEMENT(element);
6640 return element_info[element].crumbled[action];
6643 int el_dir2img(int element, int direction)
6645 element = GFX_ELEMENT(element);
6647 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6650 int el2baseimg(int element)
6652 return element_info[element].graphic[ACTION_DEFAULT];
6655 int el2img(int element)
6657 element = GFX_ELEMENT(element);
6659 return element_info[element].graphic[ACTION_DEFAULT];
6662 int el2edimg(int element)
6664 element = GFX_ELEMENT(element);
6666 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6669 int el2preimg(int element)
6671 element = GFX_ELEMENT(element);
6673 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6676 int el2panelimg(int element)
6678 element = GFX_ELEMENT(element);
6680 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6683 int font2baseimg(int font_nr)
6685 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6688 int getBeltNrFromBeltElement(int element)
6690 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6691 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6692 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6695 int getBeltNrFromBeltActiveElement(int element)
6697 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6698 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6699 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6702 int getBeltNrFromBeltSwitchElement(int element)
6704 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6705 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6706 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6709 int getBeltDirNrFromBeltElement(int element)
6711 static int belt_base_element[4] =
6713 EL_CONVEYOR_BELT_1_LEFT,
6714 EL_CONVEYOR_BELT_2_LEFT,
6715 EL_CONVEYOR_BELT_3_LEFT,
6716 EL_CONVEYOR_BELT_4_LEFT
6719 int belt_nr = getBeltNrFromBeltElement(element);
6720 int belt_dir_nr = element - belt_base_element[belt_nr];
6722 return (belt_dir_nr % 3);
6725 int getBeltDirNrFromBeltSwitchElement(int element)
6727 static int belt_base_element[4] =
6729 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6730 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6731 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6732 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6735 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6736 int belt_dir_nr = element - belt_base_element[belt_nr];
6738 return (belt_dir_nr % 3);
6741 int getBeltDirFromBeltElement(int element)
6743 static int belt_move_dir[3] =
6750 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6752 return belt_move_dir[belt_dir_nr];
6755 int getBeltDirFromBeltSwitchElement(int element)
6757 static int belt_move_dir[3] =
6764 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6766 return belt_move_dir[belt_dir_nr];
6769 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6771 static int belt_base_element[4] =
6773 EL_CONVEYOR_BELT_1_LEFT,
6774 EL_CONVEYOR_BELT_2_LEFT,
6775 EL_CONVEYOR_BELT_3_LEFT,
6776 EL_CONVEYOR_BELT_4_LEFT
6779 return belt_base_element[belt_nr] + belt_dir_nr;
6782 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6784 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6786 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6789 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6791 static int belt_base_element[4] =
6793 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6794 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6795 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6796 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6799 return belt_base_element[belt_nr] + belt_dir_nr;
6802 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6804 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6806 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6809 boolean getTeamMode_EM()
6811 return game.team_mode;
6814 int getGameFrameDelay_EM(int native_em_game_frame_delay)
6816 int game_frame_delay_value;
6818 game_frame_delay_value =
6819 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
6820 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
6823 if (tape.playing && tape.warp_forward && !tape.pausing)
6824 game_frame_delay_value = 0;
6826 return game_frame_delay_value;
6829 unsigned int InitRND(int seed)
6831 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
6832 return InitEngineRandom_EM(seed);
6833 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
6834 return InitEngineRandom_SP(seed);
6836 return InitEngineRandom_RND(seed);
6839 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
6840 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
6842 inline static int get_effective_element_EM(int tile, int frame_em)
6844 int element = object_mapping[tile].element_rnd;
6845 int action = object_mapping[tile].action;
6846 boolean is_backside = object_mapping[tile].is_backside;
6847 boolean action_removing = (action == ACTION_DIGGING ||
6848 action == ACTION_SNAPPING ||
6849 action == ACTION_COLLECTING);
6855 case Yacid_splash_eB:
6856 case Yacid_splash_wB:
6857 return (frame_em > 5 ? EL_EMPTY : element);
6863 else /* frame_em == 7 */
6867 case Yacid_splash_eB:
6868 case Yacid_splash_wB:
6871 case Yemerald_stone:
6874 case Ydiamond_stone:
6878 case Xdrip_stretchB:
6897 case Xsand_stonein_1:
6898 case Xsand_stonein_2:
6899 case Xsand_stonein_3:
6900 case Xsand_stonein_4:
6904 return (is_backside || action_removing ? EL_EMPTY : element);
6909 inline static boolean check_linear_animation_EM(int tile)
6913 case Xsand_stonesand_1:
6914 case Xsand_stonesand_quickout_1:
6915 case Xsand_sandstone_1:
6916 case Xsand_stonein_1:
6917 case Xsand_stoneout_1:
6936 case Yacid_splash_eB:
6937 case Yacid_splash_wB:
6938 case Yemerald_stone:
6945 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
6946 boolean has_crumbled_graphics,
6947 int crumbled, int sync_frame)
6949 /* if element can be crumbled, but certain action graphics are just empty
6950 space (like instantly snapping sand to empty space in 1 frame), do not
6951 treat these empty space graphics as crumbled graphics in EMC engine */
6952 if (crumbled == IMG_EMPTY_SPACE)
6953 has_crumbled_graphics = FALSE;
6955 if (has_crumbled_graphics)
6957 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
6958 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
6959 g_crumbled->anim_delay,
6960 g_crumbled->anim_mode,
6961 g_crumbled->anim_start_frame,
6964 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
6965 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
6967 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
6969 g_em->has_crumbled_graphics = TRUE;
6973 g_em->crumbled_bitmap = NULL;
6974 g_em->crumbled_src_x = 0;
6975 g_em->crumbled_src_y = 0;
6976 g_em->crumbled_border_size = 0;
6978 g_em->has_crumbled_graphics = FALSE;
6982 void ResetGfxAnimation_EM(int x, int y, int tile)
6987 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
6988 int tile, int frame_em, int x, int y)
6990 int action = object_mapping[tile].action;
6991 int direction = object_mapping[tile].direction;
6992 int effective_element = get_effective_element_EM(tile, frame_em);
6993 int graphic = (direction == MV_NONE ?
6994 el_act2img(effective_element, action) :
6995 el_act_dir2img(effective_element, action, direction));
6996 struct GraphicInfo *g = &graphic_info[graphic];
6998 boolean action_removing = (action == ACTION_DIGGING ||
6999 action == ACTION_SNAPPING ||
7000 action == ACTION_COLLECTING);
7001 boolean action_moving = (action == ACTION_FALLING ||
7002 action == ACTION_MOVING ||
7003 action == ACTION_PUSHING ||
7004 action == ACTION_EATING ||
7005 action == ACTION_FILLING ||
7006 action == ACTION_EMPTYING);
7007 boolean action_falling = (action == ACTION_FALLING ||
7008 action == ACTION_FILLING ||
7009 action == ACTION_EMPTYING);
7011 /* special case: graphic uses "2nd movement tile" and has defined
7012 7 frames for movement animation (or less) => use default graphic
7013 for last (8th) frame which ends the movement animation */
7014 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7016 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7017 graphic = (direction == MV_NONE ?
7018 el_act2img(effective_element, action) :
7019 el_act_dir2img(effective_element, action, direction));
7021 g = &graphic_info[graphic];
7024 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7028 else if (action_moving)
7030 boolean is_backside = object_mapping[tile].is_backside;
7034 int direction = object_mapping[tile].direction;
7035 int move_dir = (action_falling ? MV_DOWN : direction);
7040 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7041 if (g->double_movement && frame_em == 0)
7045 if (move_dir == MV_LEFT)
7046 GfxFrame[x - 1][y] = GfxFrame[x][y];
7047 else if (move_dir == MV_RIGHT)
7048 GfxFrame[x + 1][y] = GfxFrame[x][y];
7049 else if (move_dir == MV_UP)
7050 GfxFrame[x][y - 1] = GfxFrame[x][y];
7051 else if (move_dir == MV_DOWN)
7052 GfxFrame[x][y + 1] = GfxFrame[x][y];
7059 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7060 if (tile == Xsand_stonesand_quickout_1 ||
7061 tile == Xsand_stonesand_quickout_2)
7065 if (graphic_info[graphic].anim_global_sync)
7066 sync_frame = FrameCounter;
7067 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7068 sync_frame = GfxFrame[x][y];
7070 sync_frame = 0; /* playfield border (pseudo steel) */
7072 SetRandomAnimationValue(x, y);
7074 int frame = getAnimationFrame(g->anim_frames,
7077 g->anim_start_frame,
7080 g_em->unique_identifier =
7081 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7084 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7085 int tile, int frame_em, int x, int y)
7087 int action = object_mapping[tile].action;
7088 int direction = object_mapping[tile].direction;
7089 boolean is_backside = object_mapping[tile].is_backside;
7090 int effective_element = get_effective_element_EM(tile, frame_em);
7091 int effective_action = action;
7092 int graphic = (direction == MV_NONE ?
7093 el_act2img(effective_element, effective_action) :
7094 el_act_dir2img(effective_element, effective_action,
7096 int crumbled = (direction == MV_NONE ?
7097 el_act2crm(effective_element, effective_action) :
7098 el_act_dir2crm(effective_element, effective_action,
7100 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7101 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7102 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7103 struct GraphicInfo *g = &graphic_info[graphic];
7106 /* special case: graphic uses "2nd movement tile" and has defined
7107 7 frames for movement animation (or less) => use default graphic
7108 for last (8th) frame which ends the movement animation */
7109 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7111 effective_action = ACTION_DEFAULT;
7112 graphic = (direction == MV_NONE ?
7113 el_act2img(effective_element, effective_action) :
7114 el_act_dir2img(effective_element, effective_action,
7116 crumbled = (direction == MV_NONE ?
7117 el_act2crm(effective_element, effective_action) :
7118 el_act_dir2crm(effective_element, effective_action,
7121 g = &graphic_info[graphic];
7124 if (graphic_info[graphic].anim_global_sync)
7125 sync_frame = FrameCounter;
7126 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7127 sync_frame = GfxFrame[x][y];
7129 sync_frame = 0; /* playfield border (pseudo steel) */
7131 SetRandomAnimationValue(x, y);
7133 int frame = getAnimationFrame(g->anim_frames,
7136 g->anim_start_frame,
7139 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7140 g->double_movement && is_backside);
7142 /* (updating the "crumbled" graphic definitions is probably not really needed,
7143 as animations for crumbled graphics can't be longer than one EMC cycle) */
7144 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7148 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7149 int player_nr, int anim, int frame_em)
7151 int element = player_mapping[player_nr][anim].element_rnd;
7152 int action = player_mapping[player_nr][anim].action;
7153 int direction = player_mapping[player_nr][anim].direction;
7154 int graphic = (direction == MV_NONE ?
7155 el_act2img(element, action) :
7156 el_act_dir2img(element, action, direction));
7157 struct GraphicInfo *g = &graphic_info[graphic];
7160 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7162 stored_player[player_nr].StepFrame = frame_em;
7164 sync_frame = stored_player[player_nr].Frame;
7166 int frame = getAnimationFrame(g->anim_frames,
7169 g->anim_start_frame,
7172 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7173 &g_em->src_x, &g_em->src_y, FALSE);
7176 void InitGraphicInfo_EM(void)
7181 int num_em_gfx_errors = 0;
7183 if (graphic_info_em_object[0][0].bitmap == NULL)
7185 /* EM graphics not yet initialized in em_open_all() */
7190 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7193 /* always start with reliable default values */
7194 for (i = 0; i < TILE_MAX; i++)
7196 object_mapping[i].element_rnd = EL_UNKNOWN;
7197 object_mapping[i].is_backside = FALSE;
7198 object_mapping[i].action = ACTION_DEFAULT;
7199 object_mapping[i].direction = MV_NONE;
7202 /* always start with reliable default values */
7203 for (p = 0; p < MAX_PLAYERS; p++)
7205 for (i = 0; i < SPR_MAX; i++)
7207 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7208 player_mapping[p][i].action = ACTION_DEFAULT;
7209 player_mapping[p][i].direction = MV_NONE;
7213 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7215 int e = em_object_mapping_list[i].element_em;
7217 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7218 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7220 if (em_object_mapping_list[i].action != -1)
7221 object_mapping[e].action = em_object_mapping_list[i].action;
7223 if (em_object_mapping_list[i].direction != -1)
7224 object_mapping[e].direction =
7225 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7228 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7230 int a = em_player_mapping_list[i].action_em;
7231 int p = em_player_mapping_list[i].player_nr;
7233 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7235 if (em_player_mapping_list[i].action != -1)
7236 player_mapping[p][a].action = em_player_mapping_list[i].action;
7238 if (em_player_mapping_list[i].direction != -1)
7239 player_mapping[p][a].direction =
7240 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7243 for (i = 0; i < TILE_MAX; i++)
7245 int element = object_mapping[i].element_rnd;
7246 int action = object_mapping[i].action;
7247 int direction = object_mapping[i].direction;
7248 boolean is_backside = object_mapping[i].is_backside;
7249 boolean action_exploding = ((action == ACTION_EXPLODING ||
7250 action == ACTION_SMASHED_BY_ROCK ||
7251 action == ACTION_SMASHED_BY_SPRING) &&
7252 element != EL_DIAMOND);
7253 boolean action_active = (action == ACTION_ACTIVE);
7254 boolean action_other = (action == ACTION_OTHER);
7256 for (j = 0; j < 8; j++)
7258 int effective_element = get_effective_element_EM(i, j);
7259 int effective_action = (j < 7 ? action :
7260 i == Xdrip_stretch ? action :
7261 i == Xdrip_stretchB ? action :
7262 i == Ydrip_s1 ? action :
7263 i == Ydrip_s1B ? action :
7264 i == Xball_1B ? action :
7265 i == Xball_2 ? action :
7266 i == Xball_2B ? action :
7267 i == Yball_eat ? action :
7268 i == Ykey_1_eat ? action :
7269 i == Ykey_2_eat ? action :
7270 i == Ykey_3_eat ? action :
7271 i == Ykey_4_eat ? action :
7272 i == Ykey_5_eat ? action :
7273 i == Ykey_6_eat ? action :
7274 i == Ykey_7_eat ? action :
7275 i == Ykey_8_eat ? action :
7276 i == Ylenses_eat ? action :
7277 i == Ymagnify_eat ? action :
7278 i == Ygrass_eat ? action :
7279 i == Ydirt_eat ? action :
7280 i == Xsand_stonein_1 ? action :
7281 i == Xsand_stonein_2 ? action :
7282 i == Xsand_stonein_3 ? action :
7283 i == Xsand_stonein_4 ? action :
7284 i == Xsand_stoneout_1 ? action :
7285 i == Xsand_stoneout_2 ? action :
7286 i == Xboom_android ? ACTION_EXPLODING :
7287 action_exploding ? ACTION_EXPLODING :
7288 action_active ? action :
7289 action_other ? action :
7291 int graphic = (el_act_dir2img(effective_element, effective_action,
7293 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7295 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7296 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7297 boolean has_action_graphics = (graphic != base_graphic);
7298 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7299 struct GraphicInfo *g = &graphic_info[graphic];
7300 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7303 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7304 boolean special_animation = (action != ACTION_DEFAULT &&
7305 g->anim_frames == 3 &&
7306 g->anim_delay == 2 &&
7307 g->anim_mode & ANIM_LINEAR);
7308 int sync_frame = (i == Xdrip_stretch ? 7 :
7309 i == Xdrip_stretchB ? 7 :
7310 i == Ydrip_s2 ? j + 8 :
7311 i == Ydrip_s2B ? j + 8 :
7320 i == Xfake_acid_1 ? 0 :
7321 i == Xfake_acid_2 ? 10 :
7322 i == Xfake_acid_3 ? 20 :
7323 i == Xfake_acid_4 ? 30 :
7324 i == Xfake_acid_5 ? 40 :
7325 i == Xfake_acid_6 ? 50 :
7326 i == Xfake_acid_7 ? 60 :
7327 i == Xfake_acid_8 ? 70 :
7329 i == Xball_2B ? j + 8 :
7330 i == Yball_eat ? j + 1 :
7331 i == Ykey_1_eat ? j + 1 :
7332 i == Ykey_2_eat ? j + 1 :
7333 i == Ykey_3_eat ? j + 1 :
7334 i == Ykey_4_eat ? j + 1 :
7335 i == Ykey_5_eat ? j + 1 :
7336 i == Ykey_6_eat ? j + 1 :
7337 i == Ykey_7_eat ? j + 1 :
7338 i == Ykey_8_eat ? j + 1 :
7339 i == Ylenses_eat ? j + 1 :
7340 i == Ymagnify_eat ? j + 1 :
7341 i == Ygrass_eat ? j + 1 :
7342 i == Ydirt_eat ? j + 1 :
7343 i == Xamoeba_1 ? 0 :
7344 i == Xamoeba_2 ? 1 :
7345 i == Xamoeba_3 ? 2 :
7346 i == Xamoeba_4 ? 3 :
7347 i == Xamoeba_5 ? 0 :
7348 i == Xamoeba_6 ? 1 :
7349 i == Xamoeba_7 ? 2 :
7350 i == Xamoeba_8 ? 3 :
7351 i == Xexit_2 ? j + 8 :
7352 i == Xexit_3 ? j + 16 :
7353 i == Xdynamite_1 ? 0 :
7354 i == Xdynamite_2 ? 8 :
7355 i == Xdynamite_3 ? 16 :
7356 i == Xdynamite_4 ? 24 :
7357 i == Xsand_stonein_1 ? j + 1 :
7358 i == Xsand_stonein_2 ? j + 9 :
7359 i == Xsand_stonein_3 ? j + 17 :
7360 i == Xsand_stonein_4 ? j + 25 :
7361 i == Xsand_stoneout_1 && j == 0 ? 0 :
7362 i == Xsand_stoneout_1 && j == 1 ? 0 :
7363 i == Xsand_stoneout_1 && j == 2 ? 1 :
7364 i == Xsand_stoneout_1 && j == 3 ? 2 :
7365 i == Xsand_stoneout_1 && j == 4 ? 2 :
7366 i == Xsand_stoneout_1 && j == 5 ? 3 :
7367 i == Xsand_stoneout_1 && j == 6 ? 4 :
7368 i == Xsand_stoneout_1 && j == 7 ? 4 :
7369 i == Xsand_stoneout_2 && j == 0 ? 5 :
7370 i == Xsand_stoneout_2 && j == 1 ? 6 :
7371 i == Xsand_stoneout_2 && j == 2 ? 7 :
7372 i == Xsand_stoneout_2 && j == 3 ? 8 :
7373 i == Xsand_stoneout_2 && j == 4 ? 9 :
7374 i == Xsand_stoneout_2 && j == 5 ? 11 :
7375 i == Xsand_stoneout_2 && j == 6 ? 13 :
7376 i == Xsand_stoneout_2 && j == 7 ? 15 :
7377 i == Xboom_bug && j == 1 ? 2 :
7378 i == Xboom_bug && j == 2 ? 2 :
7379 i == Xboom_bug && j == 3 ? 4 :
7380 i == Xboom_bug && j == 4 ? 4 :
7381 i == Xboom_bug && j == 5 ? 2 :
7382 i == Xboom_bug && j == 6 ? 2 :
7383 i == Xboom_bug && j == 7 ? 0 :
7384 i == Xboom_bomb && j == 1 ? 2 :
7385 i == Xboom_bomb && j == 2 ? 2 :
7386 i == Xboom_bomb && j == 3 ? 4 :
7387 i == Xboom_bomb && j == 4 ? 4 :
7388 i == Xboom_bomb && j == 5 ? 2 :
7389 i == Xboom_bomb && j == 6 ? 2 :
7390 i == Xboom_bomb && j == 7 ? 0 :
7391 i == Xboom_android && j == 7 ? 6 :
7392 i == Xboom_1 && j == 1 ? 2 :
7393 i == Xboom_1 && j == 2 ? 2 :
7394 i == Xboom_1 && j == 3 ? 4 :
7395 i == Xboom_1 && j == 4 ? 4 :
7396 i == Xboom_1 && j == 5 ? 6 :
7397 i == Xboom_1 && j == 6 ? 6 :
7398 i == Xboom_1 && j == 7 ? 8 :
7399 i == Xboom_2 && j == 0 ? 8 :
7400 i == Xboom_2 && j == 1 ? 8 :
7401 i == Xboom_2 && j == 2 ? 10 :
7402 i == Xboom_2 && j == 3 ? 10 :
7403 i == Xboom_2 && j == 4 ? 10 :
7404 i == Xboom_2 && j == 5 ? 12 :
7405 i == Xboom_2 && j == 6 ? 12 :
7406 i == Xboom_2 && j == 7 ? 12 :
7407 special_animation && j == 4 ? 3 :
7408 effective_action != action ? 0 :
7412 Bitmap *debug_bitmap = g_em->bitmap;
7413 int debug_src_x = g_em->src_x;
7414 int debug_src_y = g_em->src_y;
7417 int frame = getAnimationFrame(g->anim_frames,
7420 g->anim_start_frame,
7423 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7424 g->double_movement && is_backside);
7426 g_em->bitmap = src_bitmap;
7427 g_em->src_x = src_x;
7428 g_em->src_y = src_y;
7429 g_em->src_offset_x = 0;
7430 g_em->src_offset_y = 0;
7431 g_em->dst_offset_x = 0;
7432 g_em->dst_offset_y = 0;
7433 g_em->width = TILEX;
7434 g_em->height = TILEY;
7436 g_em->preserve_background = FALSE;
7438 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7441 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7442 effective_action == ACTION_MOVING ||
7443 effective_action == ACTION_PUSHING ||
7444 effective_action == ACTION_EATING)) ||
7445 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7446 effective_action == ACTION_EMPTYING)))
7449 (effective_action == ACTION_FALLING ||
7450 effective_action == ACTION_FILLING ||
7451 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7452 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7453 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7454 int num_steps = (i == Ydrip_s1 ? 16 :
7455 i == Ydrip_s1B ? 16 :
7456 i == Ydrip_s2 ? 16 :
7457 i == Ydrip_s2B ? 16 :
7458 i == Xsand_stonein_1 ? 32 :
7459 i == Xsand_stonein_2 ? 32 :
7460 i == Xsand_stonein_3 ? 32 :
7461 i == Xsand_stonein_4 ? 32 :
7462 i == Xsand_stoneout_1 ? 16 :
7463 i == Xsand_stoneout_2 ? 16 : 8);
7464 int cx = ABS(dx) * (TILEX / num_steps);
7465 int cy = ABS(dy) * (TILEY / num_steps);
7466 int step_frame = (i == Ydrip_s2 ? j + 8 :
7467 i == Ydrip_s2B ? j + 8 :
7468 i == Xsand_stonein_2 ? j + 8 :
7469 i == Xsand_stonein_3 ? j + 16 :
7470 i == Xsand_stonein_4 ? j + 24 :
7471 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7472 int step = (is_backside ? step_frame : num_steps - step_frame);
7474 if (is_backside) /* tile where movement starts */
7476 if (dx < 0 || dy < 0)
7478 g_em->src_offset_x = cx * step;
7479 g_em->src_offset_y = cy * step;
7483 g_em->dst_offset_x = cx * step;
7484 g_em->dst_offset_y = cy * step;
7487 else /* tile where movement ends */
7489 if (dx < 0 || dy < 0)
7491 g_em->dst_offset_x = cx * step;
7492 g_em->dst_offset_y = cy * step;
7496 g_em->src_offset_x = cx * step;
7497 g_em->src_offset_y = cy * step;
7501 g_em->width = TILEX - cx * step;
7502 g_em->height = TILEY - cy * step;
7505 /* create unique graphic identifier to decide if tile must be redrawn */
7506 /* bit 31 - 16 (16 bit): EM style graphic
7507 bit 15 - 12 ( 4 bit): EM style frame
7508 bit 11 - 6 ( 6 bit): graphic width
7509 bit 5 - 0 ( 6 bit): graphic height */
7510 g_em->unique_identifier =
7511 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7515 /* skip check for EMC elements not contained in original EMC artwork */
7516 if (element == EL_EMC_FAKE_ACID)
7519 if (g_em->bitmap != debug_bitmap ||
7520 g_em->src_x != debug_src_x ||
7521 g_em->src_y != debug_src_y ||
7522 g_em->src_offset_x != 0 ||
7523 g_em->src_offset_y != 0 ||
7524 g_em->dst_offset_x != 0 ||
7525 g_em->dst_offset_y != 0 ||
7526 g_em->width != TILEX ||
7527 g_em->height != TILEY)
7529 static int last_i = -1;
7537 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7538 i, element, element_info[element].token_name,
7539 element_action_info[effective_action].suffix, direction);
7541 if (element != effective_element)
7542 printf(" [%d ('%s')]",
7544 element_info[effective_element].token_name);
7548 if (g_em->bitmap != debug_bitmap)
7549 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7550 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7552 if (g_em->src_x != debug_src_x ||
7553 g_em->src_y != debug_src_y)
7554 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7555 j, (is_backside ? 'B' : 'F'),
7556 g_em->src_x, g_em->src_y,
7557 g_em->src_x / 32, g_em->src_y / 32,
7558 debug_src_x, debug_src_y,
7559 debug_src_x / 32, debug_src_y / 32);
7561 if (g_em->src_offset_x != 0 ||
7562 g_em->src_offset_y != 0 ||
7563 g_em->dst_offset_x != 0 ||
7564 g_em->dst_offset_y != 0)
7565 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7567 g_em->src_offset_x, g_em->src_offset_y,
7568 g_em->dst_offset_x, g_em->dst_offset_y);
7570 if (g_em->width != TILEX ||
7571 g_em->height != TILEY)
7572 printf(" %d (%d): size %d,%d should be %d,%d\n",
7574 g_em->width, g_em->height, TILEX, TILEY);
7576 num_em_gfx_errors++;
7583 for (i = 0; i < TILE_MAX; i++)
7585 for (j = 0; j < 8; j++)
7587 int element = object_mapping[i].element_rnd;
7588 int action = object_mapping[i].action;
7589 int direction = object_mapping[i].direction;
7590 boolean is_backside = object_mapping[i].is_backside;
7591 int graphic_action = el_act_dir2img(element, action, direction);
7592 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7594 if ((action == ACTION_SMASHED_BY_ROCK ||
7595 action == ACTION_SMASHED_BY_SPRING ||
7596 action == ACTION_EATING) &&
7597 graphic_action == graphic_default)
7599 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7600 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7601 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7602 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7605 /* no separate animation for "smashed by rock" -- use rock instead */
7606 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7607 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7609 g_em->bitmap = g_xx->bitmap;
7610 g_em->src_x = g_xx->src_x;
7611 g_em->src_y = g_xx->src_y;
7612 g_em->src_offset_x = g_xx->src_offset_x;
7613 g_em->src_offset_y = g_xx->src_offset_y;
7614 g_em->dst_offset_x = g_xx->dst_offset_x;
7615 g_em->dst_offset_y = g_xx->dst_offset_y;
7616 g_em->width = g_xx->width;
7617 g_em->height = g_xx->height;
7618 g_em->unique_identifier = g_xx->unique_identifier;
7621 g_em->preserve_background = TRUE;
7626 for (p = 0; p < MAX_PLAYERS; p++)
7628 for (i = 0; i < SPR_MAX; i++)
7630 int element = player_mapping[p][i].element_rnd;
7631 int action = player_mapping[p][i].action;
7632 int direction = player_mapping[p][i].direction;
7634 for (j = 0; j < 8; j++)
7636 int effective_element = element;
7637 int effective_action = action;
7638 int graphic = (direction == MV_NONE ?
7639 el_act2img(effective_element, effective_action) :
7640 el_act_dir2img(effective_element, effective_action,
7642 struct GraphicInfo *g = &graphic_info[graphic];
7643 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7649 Bitmap *debug_bitmap = g_em->bitmap;
7650 int debug_src_x = g_em->src_x;
7651 int debug_src_y = g_em->src_y;
7654 int frame = getAnimationFrame(g->anim_frames,
7657 g->anim_start_frame,
7660 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7662 g_em->bitmap = src_bitmap;
7663 g_em->src_x = src_x;
7664 g_em->src_y = src_y;
7665 g_em->src_offset_x = 0;
7666 g_em->src_offset_y = 0;
7667 g_em->dst_offset_x = 0;
7668 g_em->dst_offset_y = 0;
7669 g_em->width = TILEX;
7670 g_em->height = TILEY;
7674 /* skip check for EMC elements not contained in original EMC artwork */
7675 if (element == EL_PLAYER_3 ||
7676 element == EL_PLAYER_4)
7679 if (g_em->bitmap != debug_bitmap ||
7680 g_em->src_x != debug_src_x ||
7681 g_em->src_y != debug_src_y)
7683 static int last_i = -1;
7691 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7692 p, i, element, element_info[element].token_name,
7693 element_action_info[effective_action].suffix, direction);
7695 if (element != effective_element)
7696 printf(" [%d ('%s')]",
7698 element_info[effective_element].token_name);
7702 if (g_em->bitmap != debug_bitmap)
7703 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7704 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7706 if (g_em->src_x != debug_src_x ||
7707 g_em->src_y != debug_src_y)
7708 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7710 g_em->src_x, g_em->src_y,
7711 g_em->src_x / 32, g_em->src_y / 32,
7712 debug_src_x, debug_src_y,
7713 debug_src_x / 32, debug_src_y / 32);
7715 num_em_gfx_errors++;
7725 printf("::: [%d errors found]\n", num_em_gfx_errors);
7731 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7732 boolean any_player_moving,
7733 boolean any_player_snapping,
7734 boolean any_player_dropping)
7736 static boolean player_was_waiting = TRUE;
7738 if (frame == 0 && !any_player_dropping)
7740 if (!player_was_waiting)
7742 if (!SaveEngineSnapshotToList())
7745 player_was_waiting = TRUE;
7748 else if (any_player_moving || any_player_snapping || any_player_dropping)
7750 player_was_waiting = FALSE;
7754 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7755 boolean murphy_is_dropping)
7757 static boolean player_was_waiting = TRUE;
7759 if (murphy_is_waiting)
7761 if (!player_was_waiting)
7763 if (!SaveEngineSnapshotToList())
7766 player_was_waiting = TRUE;
7771 player_was_waiting = FALSE;
7775 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7776 boolean any_player_moving,
7777 boolean any_player_snapping,
7778 boolean any_player_dropping)
7780 if (tape.single_step && tape.recording && !tape.pausing)
7781 if (frame == 0 && !any_player_dropping)
7782 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7784 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7785 any_player_snapping, any_player_dropping);
7788 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7789 boolean murphy_is_dropping)
7791 if (tape.single_step && tape.recording && !tape.pausing)
7792 if (murphy_is_waiting)
7793 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7795 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7798 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7799 int graphic, int sync_frame, int x, int y)
7801 int frame = getGraphicAnimationFrame(graphic, sync_frame);
7803 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
7806 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
7808 return (IS_NEXT_FRAME(sync_frame, graphic));
7811 int getGraphicInfo_Delay(int graphic)
7813 return graphic_info[graphic].anim_delay;
7816 void PlayMenuSoundExt(int sound)
7818 if (sound == SND_UNDEFINED)
7821 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7822 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7825 if (IS_LOOP_SOUND(sound))
7826 PlaySoundLoop(sound);
7831 void PlayMenuSound()
7833 PlayMenuSoundExt(menu.sound[game_status]);
7836 void PlayMenuSoundStereo(int sound, int stereo_position)
7838 if (sound == SND_UNDEFINED)
7841 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7842 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7845 if (IS_LOOP_SOUND(sound))
7846 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
7848 PlaySoundStereo(sound, stereo_position);
7851 void PlayMenuSoundIfLoopExt(int sound)
7853 if (sound == SND_UNDEFINED)
7856 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
7857 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
7860 if (IS_LOOP_SOUND(sound))
7861 PlaySoundLoop(sound);
7864 void PlayMenuSoundIfLoop()
7866 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
7869 void PlayMenuMusicExt(int music)
7871 if (music == MUS_UNDEFINED)
7874 if (!setup.sound_music)
7880 void PlayMenuMusic()
7882 PlayMenuMusicExt(menu.music[game_status]);
7885 void PlaySoundActivating()
7888 PlaySound(SND_MENU_ITEM_ACTIVATING);
7892 void PlaySoundSelecting()
7895 PlaySound(SND_MENU_ITEM_SELECTING);
7899 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
7901 boolean change_fullscreen = (setup.fullscreen !=
7902 video.fullscreen_enabled);
7903 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
7904 !strEqual(setup.fullscreen_mode,
7905 video.fullscreen_mode_current));
7906 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
7907 setup.window_scaling_percent !=
7908 video.window_scaling_percent);
7910 if (change_window_scaling_percent && video.fullscreen_enabled)
7913 if (!change_window_scaling_percent && !video.fullscreen_available)
7916 #if defined(TARGET_SDL2)
7917 if (change_window_scaling_percent)
7919 SDLSetWindowScaling(setup.window_scaling_percent);
7923 else if (change_fullscreen)
7925 SDLSetWindowFullscreen(setup.fullscreen);
7927 /* set setup value according to successfully changed fullscreen mode */
7928 setup.fullscreen = video.fullscreen_enabled;
7934 if (change_fullscreen ||
7935 change_fullscreen_mode ||
7936 change_window_scaling_percent)
7938 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
7940 /* save backbuffer content which gets lost when toggling fullscreen mode */
7941 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7943 if (change_fullscreen_mode)
7945 /* keep fullscreen, but change fullscreen mode (screen resolution) */
7946 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
7949 if (change_window_scaling_percent)
7951 /* keep window mode, but change window scaling */
7952 video.fullscreen_enabled = TRUE; /* force new window scaling */
7955 /* toggle fullscreen */
7956 ChangeVideoModeIfNeeded(setup.fullscreen);
7958 /* set setup value according to successfully changed fullscreen mode */
7959 setup.fullscreen = video.fullscreen_enabled;
7961 /* restore backbuffer content from temporary backbuffer backup bitmap */
7962 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7964 FreeBitmap(tmp_backbuffer);
7966 /* update visible window/screen */
7967 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
7971 void ChangeViewportPropertiesIfNeeded()
7973 int gfx_game_mode = game_status;
7974 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
7976 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
7977 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
7978 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
7979 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
7980 int border_size = vp_playfield->border_size;
7981 int new_sx = vp_playfield->x + border_size;
7982 int new_sy = vp_playfield->y + border_size;
7983 int new_sxsize = vp_playfield->width - 2 * border_size;
7984 int new_sysize = vp_playfield->height - 2 * border_size;
7985 int new_real_sx = vp_playfield->x;
7986 int new_real_sy = vp_playfield->y;
7987 int new_full_sxsize = vp_playfield->width;
7988 int new_full_sysize = vp_playfield->height;
7989 int new_dx = vp_door_1->x;
7990 int new_dy = vp_door_1->y;
7991 int new_dxsize = vp_door_1->width;
7992 int new_dysize = vp_door_1->height;
7993 int new_vx = vp_door_2->x;
7994 int new_vy = vp_door_2->y;
7995 int new_vxsize = vp_door_2->width;
7996 int new_vysize = vp_door_2->height;
7997 int new_ex = vp_door_3->x;
7998 int new_ey = vp_door_3->y;
7999 int new_exsize = vp_door_3->width;
8000 int new_eysize = vp_door_3->height;
8001 int new_tilesize_var =
8002 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8004 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8005 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8006 int new_scr_fieldx = new_sxsize / tilesize;
8007 int new_scr_fieldy = new_sysize / tilesize;
8008 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8009 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8010 boolean init_gfx_buffers = FALSE;
8011 boolean init_video_buffer = FALSE;
8012 boolean init_gadgets_and_toons = FALSE;
8013 boolean init_em_graphics = FALSE;
8014 boolean drawing_area_changed = FALSE;
8016 if (viewport.window.width != WIN_XSIZE ||
8017 viewport.window.height != WIN_YSIZE)
8019 WIN_XSIZE = viewport.window.width;
8020 WIN_YSIZE = viewport.window.height;
8022 init_video_buffer = TRUE;
8023 init_gfx_buffers = TRUE;
8025 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8028 if (new_scr_fieldx != SCR_FIELDX ||
8029 new_scr_fieldy != SCR_FIELDY)
8031 /* this always toggles between MAIN and GAME when using small tile size */
8033 SCR_FIELDX = new_scr_fieldx;
8034 SCR_FIELDY = new_scr_fieldy;
8036 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8047 new_sxsize != SXSIZE ||
8048 new_sysize != SYSIZE ||
8049 new_dxsize != DXSIZE ||
8050 new_dysize != DYSIZE ||
8051 new_vxsize != VXSIZE ||
8052 new_vysize != VYSIZE ||
8053 new_exsize != EXSIZE ||
8054 new_eysize != EYSIZE ||
8055 new_real_sx != REAL_SX ||
8056 new_real_sy != REAL_SY ||
8057 new_full_sxsize != FULL_SXSIZE ||
8058 new_full_sysize != FULL_SYSIZE ||
8059 new_tilesize_var != TILESIZE_VAR
8062 if (new_tilesize_var != TILESIZE_VAR)
8064 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8066 // changing tile size invalidates scroll values of engine snapshots
8067 FreeEngineSnapshotSingle();
8069 // changing tile size requires update of graphic mapping for EM engine
8070 init_em_graphics = TRUE;
8075 new_sxsize != SXSIZE ||
8076 new_sysize != SYSIZE ||
8077 new_real_sx != REAL_SX ||
8078 new_real_sy != REAL_SY ||
8079 new_full_sxsize != FULL_SXSIZE ||
8080 new_full_sysize != FULL_SYSIZE)
8082 if (!init_video_buffer)
8083 drawing_area_changed = TRUE;
8094 SXSIZE = new_sxsize;
8095 SYSIZE = new_sysize;
8096 DXSIZE = new_dxsize;
8097 DYSIZE = new_dysize;
8098 VXSIZE = new_vxsize;
8099 VYSIZE = new_vysize;
8100 EXSIZE = new_exsize;
8101 EYSIZE = new_eysize;
8102 REAL_SX = new_real_sx;
8103 REAL_SY = new_real_sy;
8104 FULL_SXSIZE = new_full_sxsize;
8105 FULL_SYSIZE = new_full_sysize;
8106 TILESIZE_VAR = new_tilesize_var;
8108 init_gfx_buffers = TRUE;
8109 init_gadgets_and_toons = TRUE;
8111 // printf("::: viewports: init_gfx_buffers\n");
8112 // printf("::: viewports: init_gadgets_and_toons\n");
8115 if (init_gfx_buffers)
8117 // printf("::: init_gfx_buffers\n");
8119 SCR_FIELDX = new_scr_fieldx_buffers;
8120 SCR_FIELDY = new_scr_fieldy_buffers;
8124 SCR_FIELDX = new_scr_fieldx;
8125 SCR_FIELDY = new_scr_fieldy;
8127 gfx.drawing_area_changed = drawing_area_changed;
8129 SetDrawDeactivationMask(REDRAW_NONE);
8130 SetDrawBackgroundMask(REDRAW_FIELD);
8133 if (init_video_buffer)
8135 // printf("::: init_video_buffer\n");
8137 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8140 if (init_gadgets_and_toons)
8142 // printf("::: init_gadgets_and_toons\n");
8148 if (init_em_graphics)
8150 InitGraphicInfo_EM();