1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 static int getFieldbufferOffsetX_RND()
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND()
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 static int getLevelFromScreenX_MM(int sx)
363 int level_xsize = level.native_mm_level->fieldx;
364 int full_xsize = level_xsize * TILESIZE_VAR;
366 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
369 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
374 static int getLevelFromScreenY_MM(int sy)
376 int level_ysize = level.native_mm_level->fieldy;
377 int full_ysize = level_ysize * TILESIZE_VAR;
379 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
382 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
387 int getLevelFromScreenX(int x)
389 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
390 return getLevelFromScreenX_EM(x);
391 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
392 return getLevelFromScreenX_SP(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
394 return getLevelFromScreenX_MM(x);
396 return getLevelFromScreenX_RND(x);
399 int getLevelFromScreenY(int y)
401 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
402 return getLevelFromScreenY_EM(y);
403 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
404 return getLevelFromScreenY_SP(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
406 return getLevelFromScreenY_MM(y);
408 return getLevelFromScreenY_RND(y);
411 void DumpTile(int x, int y)
417 printf_line("-", 79);
418 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
419 printf_line("-", 79);
421 if (!IN_LEV_FIELD(x, y))
423 printf("(not in level field)\n");
429 token_name = element_info[Feld[x][y]].token_name;
431 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
432 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
433 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
434 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
435 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
436 printf(" MovPos: %d\n", MovPos[x][y]);
437 printf(" MovDir: %d\n", MovDir[x][y]);
438 printf(" MovDelay: %d\n", MovDelay[x][y]);
439 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
440 printf(" CustomValue: %d\n", CustomValue[x][y]);
441 printf(" GfxElement: %d\n", GfxElement[x][y]);
442 printf(" GfxAction: %d\n", GfxAction[x][y]);
443 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
444 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
448 void DumpTileFromScreen(int sx, int sy)
450 int lx = getLevelFromScreenX(sx);
451 int ly = getLevelFromScreenY(sy);
456 void SetDrawtoField(int mode)
458 if (mode == DRAW_TO_FIELDBUFFER)
464 BX2 = SCR_FIELDX + 1;
465 BY2 = SCR_FIELDY + 1;
467 drawto_field = fieldbuffer;
469 else /* DRAW_TO_BACKBUFFER */
475 BX2 = SCR_FIELDX - 1;
476 BY2 = SCR_FIELDY - 1;
478 drawto_field = backbuffer;
482 static void RedrawPlayfield_RND()
484 if (game.envelope_active)
487 DrawLevel(REDRAW_ALL);
491 void RedrawPlayfield()
493 if (game_status != GAME_MODE_PLAYING)
496 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
497 RedrawPlayfield_EM(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
499 RedrawPlayfield_SP(TRUE);
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
501 RedrawPlayfield_MM();
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
503 RedrawPlayfield_RND();
505 BlitScreenToBitmap(backbuffer);
507 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
511 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
514 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
515 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
517 if (x == -1 && y == -1)
520 if (draw_target == DRAW_TO_SCREEN)
521 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
523 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
526 static void DrawMaskedBorderExt_FIELD(int draw_target)
528 if (global.border_status >= GAME_MODE_MAIN &&
529 global.border_status <= GAME_MODE_PLAYING &&
530 border.draw_masked[global.border_status])
531 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
535 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
537 // when drawing to backbuffer, never draw border over open doors
538 if (draw_target == DRAW_TO_BACKBUFFER &&
539 (GetDoorState() & DOOR_OPEN_1))
542 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
543 (global.border_status != GAME_MODE_EDITOR ||
544 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
545 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
548 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
550 // when drawing to backbuffer, never draw border over open doors
551 if (draw_target == DRAW_TO_BACKBUFFER &&
552 (GetDoorState() & DOOR_OPEN_2))
555 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
556 global.border_status != GAME_MODE_EDITOR)
557 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
560 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
562 /* currently not available */
565 static void DrawMaskedBorderExt_ALL(int draw_target)
567 DrawMaskedBorderExt_FIELD(draw_target);
568 DrawMaskedBorderExt_DOOR_1(draw_target);
569 DrawMaskedBorderExt_DOOR_2(draw_target);
570 DrawMaskedBorderExt_DOOR_3(draw_target);
573 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
575 /* never draw masked screen borders on borderless screens */
576 if (global.border_status == GAME_MODE_LOADING ||
577 global.border_status == GAME_MODE_TITLE)
580 if (redraw_mask & REDRAW_ALL)
581 DrawMaskedBorderExt_ALL(draw_target);
584 if (redraw_mask & REDRAW_FIELD)
585 DrawMaskedBorderExt_FIELD(draw_target);
586 if (redraw_mask & REDRAW_DOOR_1)
587 DrawMaskedBorderExt_DOOR_1(draw_target);
588 if (redraw_mask & REDRAW_DOOR_2)
589 DrawMaskedBorderExt_DOOR_2(draw_target);
590 if (redraw_mask & REDRAW_DOOR_3)
591 DrawMaskedBorderExt_DOOR_3(draw_target);
595 void DrawMaskedBorder_FIELD()
597 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
600 void DrawMaskedBorder(int redraw_mask)
602 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
605 void DrawMaskedBorderToTarget(int draw_target)
607 if (draw_target == DRAW_TO_BACKBUFFER ||
608 draw_target == DRAW_TO_SCREEN)
610 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
614 int last_border_status = global.border_status;
616 if (draw_target == DRAW_TO_FADE_SOURCE)
618 global.border_status = gfx.fade_border_source_status;
619 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
621 else if (draw_target == DRAW_TO_FADE_TARGET)
623 global.border_status = gfx.fade_border_target_status;
624 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
627 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
629 global.border_status = last_border_status;
630 gfx.masked_border_bitmap_ptr = backbuffer;
634 void DrawTileCursor(int draw_target)
640 int graphic = IMG_GLOBAL_TILE_CURSOR;
642 int tilesize = TILESIZE_VAR;
643 int width = tilesize;
644 int height = tilesize;
646 if (game_status != GAME_MODE_PLAYING)
649 if (!tile_cursor.enabled ||
653 if (tile_cursor.moving)
655 int step = TILESIZE_VAR / 4;
656 int dx = tile_cursor.target_x - tile_cursor.x;
657 int dy = tile_cursor.target_y - tile_cursor.y;
660 tile_cursor.x = tile_cursor.target_x;
662 tile_cursor.x += SIGN(dx) * step;
665 tile_cursor.y = tile_cursor.target_y;
667 tile_cursor.y += SIGN(dy) * step;
669 if (tile_cursor.x == tile_cursor.target_x &&
670 tile_cursor.y == tile_cursor.target_y)
671 tile_cursor.moving = FALSE;
674 dst_x = tile_cursor.x;
675 dst_y = tile_cursor.y;
677 frame = getGraphicAnimationFrame(graphic, -1);
679 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
682 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
683 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
685 if (draw_target == DRAW_TO_SCREEN)
686 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
688 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
692 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
694 int fx = getFieldbufferOffsetX_RND();
695 int fy = getFieldbufferOffsetY_RND();
697 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
700 void BlitScreenToBitmap(Bitmap *target_bitmap)
702 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
703 BlitScreenToBitmap_EM(target_bitmap);
704 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
705 BlitScreenToBitmap_SP(target_bitmap);
706 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
707 BlitScreenToBitmap_MM(target_bitmap);
708 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
709 BlitScreenToBitmap_RND(target_bitmap);
711 redraw_mask |= REDRAW_FIELD;
714 void DrawFramesPerSecond()
717 int font_nr = FONT_TEXT_2;
718 int font_width = getFontWidth(font_nr);
719 int draw_deactivation_mask = GetDrawDeactivationMask();
720 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
722 /* draw FPS with leading space (needed if field buffer deactivated) */
723 sprintf(text, " %04.1f fps", global.frames_per_second);
725 /* override draw deactivation mask (required for invisible warp mode) */
726 SetDrawDeactivationMask(REDRAW_NONE);
728 /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
729 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
730 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
732 /* set draw deactivation mask to previous value */
733 SetDrawDeactivationMask(draw_deactivation_mask);
735 /* force full-screen redraw in this frame */
736 redraw_mask = REDRAW_ALL;
740 static void PrintFrameTimeDebugging()
742 static unsigned int last_counter = 0;
743 unsigned int counter = Counter();
744 int diff_1 = counter - last_counter;
745 int diff_2 = diff_1 - GAME_FRAME_DELAY;
747 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
748 char diff_bar[2 * diff_2_max + 5];
752 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
754 for (i = 0; i < diff_2_max; i++)
755 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
756 i >= diff_2_max - diff_2_cut ? '-' : ' ');
758 diff_bar[pos++] = '|';
760 for (i = 0; i < diff_2_max; i++)
761 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
763 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
765 diff_bar[pos++] = '\0';
767 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
770 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
773 last_counter = counter;
777 static int unifiedRedrawMask(int mask)
779 if (mask & REDRAW_ALL)
782 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
788 static boolean equalRedrawMasks(int mask_1, int mask_2)
790 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
795 static int last_redraw_mask = REDRAW_NONE;
797 // force screen redraw in every frame to continue drawing global animations
798 // (but always use the last redraw mask to prevent unwanted side effects)
799 if (redraw_mask == REDRAW_NONE)
800 redraw_mask = last_redraw_mask;
802 last_redraw_mask = redraw_mask;
805 // masked border now drawn immediately when blitting backbuffer to window
807 // draw masked border to all viewports, if defined
808 DrawMaskedBorder(redraw_mask);
811 // draw frames per second (only if debug mode is enabled)
812 if (redraw_mask & REDRAW_FPS)
813 DrawFramesPerSecond();
815 // remove playfield redraw before potentially merging with doors redraw
816 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
817 redraw_mask &= ~REDRAW_FIELD;
819 // redraw complete window if both playfield and (some) doors need redraw
820 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
821 redraw_mask = REDRAW_ALL;
823 /* although redrawing the whole window would be fine for normal gameplay,
824 being able to only redraw the playfield is required for deactivating
825 certain drawing areas (mainly playfield) to work, which is needed for
826 warp-forward to be fast enough (by skipping redraw of most frames) */
828 if (redraw_mask & REDRAW_ALL)
830 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
832 else if (redraw_mask & REDRAW_FIELD)
834 BlitBitmap(backbuffer, window,
835 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
837 else if (redraw_mask & REDRAW_DOORS)
839 // merge door areas to prevent calling screen redraw more than once
845 if (redraw_mask & REDRAW_DOOR_1)
849 x2 = MAX(x2, DX + DXSIZE);
850 y2 = MAX(y2, DY + DYSIZE);
853 if (redraw_mask & REDRAW_DOOR_2)
857 x2 = MAX(x2, VX + VXSIZE);
858 y2 = MAX(y2, VY + VYSIZE);
861 if (redraw_mask & REDRAW_DOOR_3)
865 x2 = MAX(x2, EX + EXSIZE);
866 y2 = MAX(y2, EY + EYSIZE);
869 // make sure that at least one pixel is blitted, and inside the screen
870 // (else nothing is blitted, causing the animations not to be updated)
871 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
872 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
873 x2 = MIN(MAX(1, x2), WIN_XSIZE);
874 y2 = MIN(MAX(1, y2), WIN_YSIZE);
876 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
879 redraw_mask = REDRAW_NONE;
882 PrintFrameTimeDebugging();
886 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
888 unsigned int frame_delay_value_old = GetVideoFrameDelay();
890 SetVideoFrameDelay(frame_delay_value);
894 SetVideoFrameDelay(frame_delay_value_old);
897 static int fade_type_skip = FADE_TYPE_NONE;
899 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
901 void (*draw_border_function)(void) = NULL;
902 int x, y, width, height;
903 int fade_delay, post_delay;
905 if (fade_type == FADE_TYPE_FADE_OUT)
907 if (fade_type_skip != FADE_TYPE_NONE)
909 /* skip all fade operations until specified fade operation */
910 if (fade_type & fade_type_skip)
911 fade_type_skip = FADE_TYPE_NONE;
916 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
920 redraw_mask |= fade_mask;
922 if (fade_type == FADE_TYPE_SKIP)
924 fade_type_skip = fade_mode;
929 fade_delay = fading.fade_delay;
930 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
932 if (fade_type_skip != FADE_TYPE_NONE)
934 /* skip all fade operations until specified fade operation */
935 if (fade_type & fade_type_skip)
936 fade_type_skip = FADE_TYPE_NONE;
941 if (global.autoplay_leveldir)
946 if (fade_mask == REDRAW_FIELD)
951 height = FADE_SYSIZE;
953 if (border.draw_masked_when_fading)
954 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
956 DrawMaskedBorder_FIELD(); /* draw once */
958 else /* REDRAW_ALL */
966 if (!setup.fade_screens ||
968 fading.fade_mode == FADE_MODE_NONE)
970 if (fade_mode == FADE_MODE_FADE_OUT)
973 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
975 redraw_mask &= ~fade_mask;
980 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
981 draw_border_function);
983 redraw_mask &= ~fade_mask;
986 static void SetScreenStates_BeforeFadingIn()
988 // temporarily set screen mode for animations to screen after fading in
989 global.anim_status = global.anim_status_next;
991 // store backbuffer with all animations that will be started after fading in
992 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
993 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
995 // set screen mode for animations back to fading
996 global.anim_status = GAME_MODE_PSEUDO_FADING;
999 static void SetScreenStates_AfterFadingIn()
1001 // store new source screen (to use correct masked border for fading)
1002 gfx.fade_border_source_status = global.border_status;
1004 global.anim_status = global.anim_status_next;
1007 static void SetScreenStates_BeforeFadingOut()
1009 // store new target screen (to use correct masked border for fading)
1010 gfx.fade_border_target_status = game_status;
1012 // set screen mode for animations to fading
1013 global.anim_status = GAME_MODE_PSEUDO_FADING;
1015 // store backbuffer with all animations that will be stopped for fading out
1016 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1017 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1020 static void SetScreenStates_AfterFadingOut()
1022 global.border_status = game_status;
1025 void FadeIn(int fade_mask)
1027 SetScreenStates_BeforeFadingIn();
1030 DrawMaskedBorder(REDRAW_ALL);
1033 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1034 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1036 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1040 FADE_SXSIZE = FULL_SXSIZE;
1041 FADE_SYSIZE = FULL_SYSIZE;
1043 if (game_status == GAME_MODE_PLAYING &&
1044 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1045 SetOverlayActive(TRUE);
1047 SetScreenStates_AfterFadingIn();
1049 // force update of global animation status in case of rapid screen changes
1050 redraw_mask = REDRAW_ALL;
1054 void FadeOut(int fade_mask)
1056 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1057 if (!equalRedrawMasks(fade_mask, redraw_mask))
1060 SetScreenStates_BeforeFadingOut();
1062 SetTileCursorActive(FALSE);
1063 SetOverlayActive(FALSE);
1066 DrawMaskedBorder(REDRAW_ALL);
1069 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1070 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1072 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1074 SetScreenStates_AfterFadingOut();
1077 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1079 static struct TitleFadingInfo fading_leave_stored;
1082 fading_leave_stored = fading_leave;
1084 fading = fading_leave_stored;
1087 void FadeSetEnterMenu()
1089 fading = menu.enter_menu;
1091 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1094 void FadeSetLeaveMenu()
1096 fading = menu.leave_menu;
1098 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1101 void FadeSetEnterScreen()
1103 fading = menu.enter_screen[game_status];
1105 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1108 void FadeSetNextScreen()
1110 fading = menu.next_screen[game_status];
1112 // (do not overwrite fade mode set by FadeSetEnterScreen)
1113 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1116 void FadeSetLeaveScreen()
1118 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1121 void FadeSetFromType(int type)
1123 if (type & TYPE_ENTER_SCREEN)
1124 FadeSetEnterScreen();
1125 else if (type & TYPE_ENTER)
1127 else if (type & TYPE_LEAVE)
1131 void FadeSetDisabled()
1133 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1135 fading = fading_none;
1138 void FadeSkipNextFadeIn()
1140 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1143 void FadeSkipNextFadeOut()
1145 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1148 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1150 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1152 return (graphic == IMG_UNDEFINED ? NULL :
1153 graphic_info[graphic].bitmap != NULL || redefined ?
1154 graphic_info[graphic].bitmap :
1155 graphic_info[default_graphic].bitmap);
1158 Bitmap *getBackgroundBitmap(int graphic)
1160 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1163 Bitmap *getGlobalBorderBitmap(int graphic)
1165 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1168 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1171 (status == GAME_MODE_MAIN ||
1172 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1173 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1174 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1175 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1178 return getGlobalBorderBitmap(graphic);
1181 void SetWindowBackgroundImageIfDefined(int graphic)
1183 if (graphic_info[graphic].bitmap)
1184 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1187 void SetMainBackgroundImageIfDefined(int graphic)
1189 if (graphic_info[graphic].bitmap)
1190 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1193 void SetDoorBackgroundImageIfDefined(int graphic)
1195 if (graphic_info[graphic].bitmap)
1196 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1199 void SetWindowBackgroundImage(int graphic)
1201 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1204 void SetMainBackgroundImage(int graphic)
1206 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1209 void SetDoorBackgroundImage(int graphic)
1211 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1214 void SetPanelBackground()
1216 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1218 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1219 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1221 SetDoorBackgroundBitmap(bitmap_db_panel);
1224 void DrawBackground(int x, int y, int width, int height)
1226 /* "drawto" might still point to playfield buffer here (hall of fame) */
1227 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1229 if (IN_GFX_FIELD_FULL(x, y))
1230 redraw_mask |= REDRAW_FIELD;
1231 else if (IN_GFX_DOOR_1(x, y))
1232 redraw_mask |= REDRAW_DOOR_1;
1233 else if (IN_GFX_DOOR_2(x, y))
1234 redraw_mask |= REDRAW_DOOR_2;
1235 else if (IN_GFX_DOOR_3(x, y))
1236 redraw_mask |= REDRAW_DOOR_3;
1239 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1241 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1243 if (font->bitmap == NULL)
1246 DrawBackground(x, y, width, height);
1249 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1251 struct GraphicInfo *g = &graphic_info[graphic];
1253 if (g->bitmap == NULL)
1256 DrawBackground(x, y, width, height);
1259 static int game_status_last = -1;
1260 static Bitmap *global_border_bitmap_last = NULL;
1261 static Bitmap *global_border_bitmap = NULL;
1262 static int real_sx_last = -1, real_sy_last = -1;
1263 static int full_sxsize_last = -1, full_sysize_last = -1;
1264 static int dx_last = -1, dy_last = -1;
1265 static int dxsize_last = -1, dysize_last = -1;
1266 static int vx_last = -1, vy_last = -1;
1267 static int vxsize_last = -1, vysize_last = -1;
1268 static int ex_last = -1, ey_last = -1;
1269 static int exsize_last = -1, eysize_last = -1;
1271 boolean CheckIfGlobalBorderHasChanged()
1273 // if game status has not changed, global border has not changed either
1274 if (game_status == game_status_last)
1277 // determine and store new global border bitmap for current game status
1278 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1280 return (global_border_bitmap_last != global_border_bitmap);
1283 boolean CheckIfGlobalBorderRedrawIsNeeded()
1285 // if game status has not changed, nothing has to be redrawn
1286 if (game_status == game_status_last)
1289 // redraw if last screen was title screen
1290 if (game_status_last == GAME_MODE_TITLE)
1293 // redraw if global screen border has changed
1294 if (CheckIfGlobalBorderHasChanged())
1297 // redraw if position or size of playfield area has changed
1298 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1299 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1302 // redraw if position or size of door area has changed
1303 if (dx_last != DX || dy_last != DY ||
1304 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1307 // redraw if position or size of tape area has changed
1308 if (vx_last != VX || vy_last != VY ||
1309 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1312 // redraw if position or size of editor area has changed
1313 if (ex_last != EX || ey_last != EY ||
1314 exsize_last != EXSIZE || eysize_last != EYSIZE)
1320 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1323 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1325 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1328 void RedrawGlobalBorder()
1330 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1332 RedrawGlobalBorderFromBitmap(bitmap);
1334 redraw_mask = REDRAW_ALL;
1337 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1339 static void RedrawGlobalBorderIfNeeded()
1341 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1342 if (game_status == game_status_last)
1346 // copy current draw buffer to later copy back areas that have not changed
1347 if (game_status_last != GAME_MODE_TITLE)
1348 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1350 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1351 if (CheckIfGlobalBorderRedrawIsNeeded())
1354 // redraw global screen border (or clear, if defined to be empty)
1355 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1357 if (game_status == GAME_MODE_EDITOR)
1358 DrawSpecialEditorDoor();
1360 // copy previous playfield and door areas, if they are defined on both
1361 // previous and current screen and if they still have the same size
1363 if (real_sx_last != -1 && real_sy_last != -1 &&
1364 REAL_SX != -1 && REAL_SY != -1 &&
1365 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1366 BlitBitmap(bitmap_db_store_1, backbuffer,
1367 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1370 if (dx_last != -1 && dy_last != -1 &&
1371 DX != -1 && DY != -1 &&
1372 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1373 BlitBitmap(bitmap_db_store_1, backbuffer,
1374 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1376 if (game_status != GAME_MODE_EDITOR)
1378 if (vx_last != -1 && vy_last != -1 &&
1379 VX != -1 && VY != -1 &&
1380 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1381 BlitBitmap(bitmap_db_store_1, backbuffer,
1382 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1386 if (ex_last != -1 && ey_last != -1 &&
1387 EX != -1 && EY != -1 &&
1388 exsize_last == EXSIZE && eysize_last == EYSIZE)
1389 BlitBitmap(bitmap_db_store_1, backbuffer,
1390 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1393 redraw_mask = REDRAW_ALL;
1396 game_status_last = game_status;
1398 global_border_bitmap_last = global_border_bitmap;
1400 real_sx_last = REAL_SX;
1401 real_sy_last = REAL_SY;
1402 full_sxsize_last = FULL_SXSIZE;
1403 full_sysize_last = FULL_SYSIZE;
1406 dxsize_last = DXSIZE;
1407 dysize_last = DYSIZE;
1410 vxsize_last = VXSIZE;
1411 vysize_last = VYSIZE;
1414 exsize_last = EXSIZE;
1415 eysize_last = EYSIZE;
1420 RedrawGlobalBorderIfNeeded();
1422 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1423 /* (when entering hall of fame after playing) */
1424 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1426 /* !!! maybe this should be done before clearing the background !!! */
1427 if (game_status == GAME_MODE_PLAYING)
1429 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1430 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1434 SetDrawtoField(DRAW_TO_BACKBUFFER);
1438 void MarkTileDirty(int x, int y)
1440 redraw_mask |= REDRAW_FIELD;
1443 void SetBorderElement()
1447 BorderElement = EL_EMPTY;
1449 /* the MM game engine does not use a visible border element */
1450 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1453 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1455 for (x = 0; x < lev_fieldx; x++)
1457 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1458 BorderElement = EL_STEELWALL;
1460 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1466 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1467 int max_array_fieldx, int max_array_fieldy,
1468 short field[max_array_fieldx][max_array_fieldy],
1469 int max_fieldx, int max_fieldy)
1473 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1474 static int safety = 0;
1476 /* check if starting field still has the desired content */
1477 if (field[from_x][from_y] == fill_element)
1482 if (safety > max_fieldx * max_fieldy)
1483 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1485 old_element = field[from_x][from_y];
1486 field[from_x][from_y] = fill_element;
1488 for (i = 0; i < 4; i++)
1490 x = from_x + check[i][0];
1491 y = from_y + check[i][1];
1493 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1494 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1495 field, max_fieldx, max_fieldy);
1501 void FloodFillLevel(int from_x, int from_y, int fill_element,
1502 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1503 int max_fieldx, int max_fieldy)
1505 FloodFillLevelExt(from_x, from_y, fill_element,
1506 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1507 max_fieldx, max_fieldy);
1510 void SetRandomAnimationValue(int x, int y)
1512 gfx.anim_random_frame = GfxRandom[x][y];
1515 int getGraphicAnimationFrame(int graphic, int sync_frame)
1517 /* animation synchronized with global frame counter, not move position */
1518 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1519 sync_frame = FrameCounter;
1521 return getAnimationFrame(graphic_info[graphic].anim_frames,
1522 graphic_info[graphic].anim_delay,
1523 graphic_info[graphic].anim_mode,
1524 graphic_info[graphic].anim_start_frame,
1528 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1530 struct GraphicInfo *g = &graphic_info[graphic];
1531 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1533 if (tilesize == gfx.standard_tile_size)
1534 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1535 else if (tilesize == game.tile_size)
1536 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1538 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1541 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1542 boolean get_backside)
1544 struct GraphicInfo *g = &graphic_info[graphic];
1545 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1546 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1548 if (g->offset_y == 0) /* frames are ordered horizontally */
1550 int max_width = g->anim_frames_per_line * g->width;
1551 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1553 *x = pos % max_width;
1554 *y = src_y % g->height + pos / max_width * g->height;
1556 else if (g->offset_x == 0) /* frames are ordered vertically */
1558 int max_height = g->anim_frames_per_line * g->height;
1559 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1561 *x = src_x % g->width + pos / max_height * g->width;
1562 *y = pos % max_height;
1564 else /* frames are ordered diagonally */
1566 *x = src_x + frame * g->offset_x;
1567 *y = src_y + frame * g->offset_y;
1571 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1572 Bitmap **bitmap, int *x, int *y,
1573 boolean get_backside)
1575 struct GraphicInfo *g = &graphic_info[graphic];
1577 // if no in-game graphics defined, always use standard graphic size
1578 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1579 tilesize = TILESIZE;
1581 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1582 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1584 *x = *x * tilesize / g->tile_size;
1585 *y = *y * tilesize / g->tile_size;
1588 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1589 Bitmap **bitmap, int *x, int *y)
1591 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1594 void getFixedGraphicSource(int graphic, int frame,
1595 Bitmap **bitmap, int *x, int *y)
1597 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1600 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1602 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1605 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1606 int *x, int *y, boolean get_backside)
1608 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1612 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1614 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1617 void DrawGraphic(int x, int y, int graphic, int frame)
1620 if (!IN_SCR_FIELD(x, y))
1622 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1623 printf("DrawGraphic(): This should never happen!\n");
1628 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1631 MarkTileDirty(x, y);
1634 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1637 if (!IN_SCR_FIELD(x, y))
1639 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1640 printf("DrawGraphic(): This should never happen!\n");
1645 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1647 MarkTileDirty(x, y);
1650 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1656 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1658 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1661 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1667 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1668 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1671 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1674 if (!IN_SCR_FIELD(x, y))
1676 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1677 printf("DrawGraphicThruMask(): This should never happen!\n");
1682 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1685 MarkTileDirty(x, y);
1688 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1691 if (!IN_SCR_FIELD(x, y))
1693 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1694 printf("DrawGraphicThruMask(): This should never happen!\n");
1699 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1701 MarkTileDirty(x, y);
1704 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1710 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1712 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1716 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1717 int graphic, int frame)
1722 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1724 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1728 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1730 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1732 MarkTileDirty(x / tilesize, y / tilesize);
1735 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1738 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1739 graphic, frame, tilesize);
1740 MarkTileDirty(x / tilesize, y / tilesize);
1743 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1749 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1750 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1753 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1754 int frame, int tilesize)
1759 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1760 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1763 void DrawMiniGraphic(int x, int y, int graphic)
1765 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1766 MarkTileDirty(x / 2, y / 2);
1769 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1774 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1775 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1778 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1779 int graphic, int frame,
1780 int cut_mode, int mask_mode)
1785 int width = TILEX, height = TILEY;
1788 if (dx || dy) /* shifted graphic */
1790 if (x < BX1) /* object enters playfield from the left */
1797 else if (x > BX2) /* object enters playfield from the right */
1803 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1809 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1811 else if (dx) /* general horizontal movement */
1812 MarkTileDirty(x + SIGN(dx), y);
1814 if (y < BY1) /* object enters playfield from the top */
1816 if (cut_mode == CUT_BELOW) /* object completely above top border */
1824 else if (y > BY2) /* object enters playfield from the bottom */
1830 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1836 else if (dy > 0 && cut_mode == CUT_ABOVE)
1838 if (y == BY2) /* object completely above bottom border */
1844 MarkTileDirty(x, y + 1);
1845 } /* object leaves playfield to the bottom */
1846 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1848 else if (dy) /* general vertical movement */
1849 MarkTileDirty(x, y + SIGN(dy));
1853 if (!IN_SCR_FIELD(x, y))
1855 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1856 printf("DrawGraphicShifted(): This should never happen!\n");
1861 width = width * TILESIZE_VAR / TILESIZE;
1862 height = height * TILESIZE_VAR / TILESIZE;
1863 cx = cx * TILESIZE_VAR / TILESIZE;
1864 cy = cy * TILESIZE_VAR / TILESIZE;
1865 dx = dx * TILESIZE_VAR / TILESIZE;
1866 dy = dy * TILESIZE_VAR / TILESIZE;
1868 if (width > 0 && height > 0)
1870 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1875 dst_x = FX + x * TILEX_VAR + dx;
1876 dst_y = FY + y * TILEY_VAR + dy;
1878 if (mask_mode == USE_MASKING)
1879 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1882 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1885 MarkTileDirty(x, y);
1889 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1890 int graphic, int frame,
1891 int cut_mode, int mask_mode)
1896 int width = TILEX_VAR, height = TILEY_VAR;
1899 int x2 = x + SIGN(dx);
1900 int y2 = y + SIGN(dy);
1902 /* movement with two-tile animations must be sync'ed with movement position,
1903 not with current GfxFrame (which can be higher when using slow movement) */
1904 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1905 int anim_frames = graphic_info[graphic].anim_frames;
1907 /* (we also need anim_delay here for movement animations with less frames) */
1908 int anim_delay = graphic_info[graphic].anim_delay;
1909 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1911 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1912 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1914 /* re-calculate animation frame for two-tile movement animation */
1915 frame = getGraphicAnimationFrame(graphic, sync_frame);
1917 /* check if movement start graphic inside screen area and should be drawn */
1918 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1920 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1922 dst_x = FX + x1 * TILEX_VAR;
1923 dst_y = FY + y1 * TILEY_VAR;
1925 if (mask_mode == USE_MASKING)
1926 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1929 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1932 MarkTileDirty(x1, y1);
1935 /* check if movement end graphic inside screen area and should be drawn */
1936 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1938 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1940 dst_x = FX + x2 * TILEX_VAR;
1941 dst_y = FY + y2 * TILEY_VAR;
1943 if (mask_mode == USE_MASKING)
1944 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1947 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1950 MarkTileDirty(x2, y2);
1954 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1955 int graphic, int frame,
1956 int cut_mode, int mask_mode)
1960 DrawGraphic(x, y, graphic, frame);
1965 if (graphic_info[graphic].double_movement) /* EM style movement images */
1966 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1968 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1971 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1972 int frame, int cut_mode)
1974 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1977 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1978 int cut_mode, int mask_mode)
1980 int lx = LEVELX(x), ly = LEVELY(y);
1984 if (IN_LEV_FIELD(lx, ly))
1986 SetRandomAnimationValue(lx, ly);
1988 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1989 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1991 /* do not use double (EM style) movement graphic when not moving */
1992 if (graphic_info[graphic].double_movement && !dx && !dy)
1994 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1995 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1998 else /* border element */
2000 graphic = el2img(element);
2001 frame = getGraphicAnimationFrame(graphic, -1);
2004 if (element == EL_EXPANDABLE_WALL)
2006 boolean left_stopped = FALSE, right_stopped = FALSE;
2008 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2009 left_stopped = TRUE;
2010 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2011 right_stopped = TRUE;
2013 if (left_stopped && right_stopped)
2015 else if (left_stopped)
2017 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2018 frame = graphic_info[graphic].anim_frames - 1;
2020 else if (right_stopped)
2022 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2023 frame = graphic_info[graphic].anim_frames - 1;
2028 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2029 else if (mask_mode == USE_MASKING)
2030 DrawGraphicThruMask(x, y, graphic, frame);
2032 DrawGraphic(x, y, graphic, frame);
2035 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2036 int cut_mode, int mask_mode)
2038 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2039 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2040 cut_mode, mask_mode);
2043 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2046 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2049 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2052 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2055 void DrawLevelElementThruMask(int x, int y, int element)
2057 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2060 void DrawLevelFieldThruMask(int x, int y)
2062 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2065 /* !!! implementation of quicksand is totally broken !!! */
2066 #define IS_CRUMBLED_TILE(x, y, e) \
2067 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2068 !IS_MOVING(x, y) || \
2069 (e) == EL_QUICKSAND_EMPTYING || \
2070 (e) == EL_QUICKSAND_FAST_EMPTYING))
2072 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2077 int width, height, cx, cy;
2078 int sx = SCREENX(x), sy = SCREENY(y);
2079 int crumbled_border_size = graphic_info[graphic].border_size;
2080 int crumbled_tile_size = graphic_info[graphic].tile_size;
2081 int crumbled_border_size_var =
2082 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2085 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2087 for (i = 1; i < 4; i++)
2089 int dxx = (i & 1 ? dx : 0);
2090 int dyy = (i & 2 ? dy : 0);
2093 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2096 /* check if neighbour field is of same crumble type */
2097 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2098 graphic_info[graphic].class ==
2099 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2101 /* return if check prevents inner corner */
2102 if (same == (dxx == dx && dyy == dy))
2106 /* if we reach this point, we have an inner corner */
2108 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2110 width = crumbled_border_size_var;
2111 height = crumbled_border_size_var;
2112 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2113 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2115 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2116 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2119 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2124 int width, height, bx, by, cx, cy;
2125 int sx = SCREENX(x), sy = SCREENY(y);
2126 int crumbled_border_size = graphic_info[graphic].border_size;
2127 int crumbled_tile_size = graphic_info[graphic].tile_size;
2128 int crumbled_border_size_var =
2129 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2130 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2133 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2135 /* draw simple, sloppy, non-corner-accurate crumbled border */
2137 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2138 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2139 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2140 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2142 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2143 FX + sx * TILEX_VAR + cx,
2144 FY + sy * TILEY_VAR + cy);
2146 /* (remaining middle border part must be at least as big as corner part) */
2147 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2148 crumbled_border_size_var >= TILESIZE_VAR / 3)
2151 /* correct corners of crumbled border, if needed */
2153 for (i = -1; i <= 1; i += 2)
2155 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2156 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2157 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2160 /* check if neighbour field is of same crumble type */
2161 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2162 graphic_info[graphic].class ==
2163 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2165 /* no crumbled corner, but continued crumbled border */
2167 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2168 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2169 int b1 = (i == 1 ? crumbled_border_size_var :
2170 TILESIZE_VAR - 2 * crumbled_border_size_var);
2172 width = crumbled_border_size_var;
2173 height = crumbled_border_size_var;
2175 if (dir == 1 || dir == 2)
2190 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2192 FX + sx * TILEX_VAR + cx,
2193 FY + sy * TILEY_VAR + cy);
2198 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2200 int sx = SCREENX(x), sy = SCREENY(y);
2203 static int xy[4][2] =
2211 if (!IN_LEV_FIELD(x, y))
2214 element = TILE_GFX_ELEMENT(x, y);
2216 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2218 if (!IN_SCR_FIELD(sx, sy))
2221 /* crumble field borders towards direct neighbour fields */
2222 for (i = 0; i < 4; i++)
2224 int xx = x + xy[i][0];
2225 int yy = y + xy[i][1];
2227 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2230 /* check if neighbour field is of same crumble type */
2231 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2232 graphic_info[graphic].class ==
2233 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2236 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2239 /* crumble inner field corners towards corner neighbour fields */
2240 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2241 graphic_info[graphic].anim_frames == 2)
2243 for (i = 0; i < 4; i++)
2245 int dx = (i & 1 ? +1 : -1);
2246 int dy = (i & 2 ? +1 : -1);
2248 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2252 MarkTileDirty(sx, sy);
2254 else /* center field is not crumbled -- crumble neighbour fields */
2256 /* crumble field borders of direct neighbour fields */
2257 for (i = 0; i < 4; i++)
2259 int xx = x + xy[i][0];
2260 int yy = y + xy[i][1];
2261 int sxx = sx + xy[i][0];
2262 int syy = sy + xy[i][1];
2264 if (!IN_LEV_FIELD(xx, yy) ||
2265 !IN_SCR_FIELD(sxx, syy))
2268 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2271 element = TILE_GFX_ELEMENT(xx, yy);
2273 if (!IS_CRUMBLED_TILE(xx, yy, element))
2276 graphic = el_act2crm(element, ACTION_DEFAULT);
2278 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2280 MarkTileDirty(sxx, syy);
2283 /* crumble inner field corners of corner neighbour fields */
2284 for (i = 0; i < 4; i++)
2286 int dx = (i & 1 ? +1 : -1);
2287 int dy = (i & 2 ? +1 : -1);
2293 if (!IN_LEV_FIELD(xx, yy) ||
2294 !IN_SCR_FIELD(sxx, syy))
2297 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2300 element = TILE_GFX_ELEMENT(xx, yy);
2302 if (!IS_CRUMBLED_TILE(xx, yy, element))
2305 graphic = el_act2crm(element, ACTION_DEFAULT);
2307 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2308 graphic_info[graphic].anim_frames == 2)
2309 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2311 MarkTileDirty(sxx, syy);
2316 void DrawLevelFieldCrumbled(int x, int y)
2320 if (!IN_LEV_FIELD(x, y))
2323 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2324 GfxElement[x][y] != EL_UNDEFINED &&
2325 GFX_CRUMBLED(GfxElement[x][y]))
2327 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2332 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2334 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2337 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2340 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2341 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2342 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2343 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2344 int sx = SCREENX(x), sy = SCREENY(y);
2346 DrawGraphic(sx, sy, graphic1, frame1);
2347 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2350 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2352 int sx = SCREENX(x), sy = SCREENY(y);
2353 static int xy[4][2] =
2362 /* crumble direct neighbour fields (required for field borders) */
2363 for (i = 0; i < 4; i++)
2365 int xx = x + xy[i][0];
2366 int yy = y + xy[i][1];
2367 int sxx = sx + xy[i][0];
2368 int syy = sy + xy[i][1];
2370 if (!IN_LEV_FIELD(xx, yy) ||
2371 !IN_SCR_FIELD(sxx, syy) ||
2372 !GFX_CRUMBLED(Feld[xx][yy]) ||
2376 DrawLevelField(xx, yy);
2379 /* crumble corner neighbour fields (required for inner field corners) */
2380 for (i = 0; i < 4; i++)
2382 int dx = (i & 1 ? +1 : -1);
2383 int dy = (i & 2 ? +1 : -1);
2389 if (!IN_LEV_FIELD(xx, yy) ||
2390 !IN_SCR_FIELD(sxx, syy) ||
2391 !GFX_CRUMBLED(Feld[xx][yy]) ||
2395 int element = TILE_GFX_ELEMENT(xx, yy);
2396 int graphic = el_act2crm(element, ACTION_DEFAULT);
2398 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2399 graphic_info[graphic].anim_frames == 2)
2400 DrawLevelField(xx, yy);
2404 static int getBorderElement(int x, int y)
2408 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2409 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2410 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2411 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2412 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2413 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2414 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2416 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2417 int steel_position = (x == -1 && y == -1 ? 0 :
2418 x == lev_fieldx && y == -1 ? 1 :
2419 x == -1 && y == lev_fieldy ? 2 :
2420 x == lev_fieldx && y == lev_fieldy ? 3 :
2421 x == -1 || x == lev_fieldx ? 4 :
2422 y == -1 || y == lev_fieldy ? 5 : 6);
2424 return border[steel_position][steel_type];
2427 void DrawScreenElement(int x, int y, int element)
2429 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2430 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2433 void DrawLevelElement(int x, int y, int element)
2435 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2436 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2439 void DrawScreenField(int x, int y)
2441 int lx = LEVELX(x), ly = LEVELY(y);
2442 int element, content;
2444 if (!IN_LEV_FIELD(lx, ly))
2446 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2449 element = getBorderElement(lx, ly);
2451 DrawScreenElement(x, y, element);
2456 element = Feld[lx][ly];
2457 content = Store[lx][ly];
2459 if (IS_MOVING(lx, ly))
2461 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2462 boolean cut_mode = NO_CUTTING;
2464 if (element == EL_QUICKSAND_EMPTYING ||
2465 element == EL_QUICKSAND_FAST_EMPTYING ||
2466 element == EL_MAGIC_WALL_EMPTYING ||
2467 element == EL_BD_MAGIC_WALL_EMPTYING ||
2468 element == EL_DC_MAGIC_WALL_EMPTYING ||
2469 element == EL_AMOEBA_DROPPING)
2470 cut_mode = CUT_ABOVE;
2471 else if (element == EL_QUICKSAND_FILLING ||
2472 element == EL_QUICKSAND_FAST_FILLING ||
2473 element == EL_MAGIC_WALL_FILLING ||
2474 element == EL_BD_MAGIC_WALL_FILLING ||
2475 element == EL_DC_MAGIC_WALL_FILLING)
2476 cut_mode = CUT_BELOW;
2478 if (cut_mode == CUT_ABOVE)
2479 DrawScreenElement(x, y, element);
2481 DrawScreenElement(x, y, EL_EMPTY);
2484 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2485 else if (cut_mode == NO_CUTTING)
2486 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2489 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2491 if (cut_mode == CUT_BELOW &&
2492 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2493 DrawLevelElement(lx, ly + 1, element);
2496 if (content == EL_ACID)
2498 int dir = MovDir[lx][ly];
2499 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2500 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2502 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2504 // prevent target field from being drawn again (but without masking)
2505 // (this would happen if target field is scanned after moving element)
2506 Stop[newlx][newly] = TRUE;
2509 else if (IS_BLOCKED(lx, ly))
2514 boolean cut_mode = NO_CUTTING;
2515 int element_old, content_old;
2517 Blocked2Moving(lx, ly, &oldx, &oldy);
2520 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2521 MovDir[oldx][oldy] == MV_RIGHT);
2523 element_old = Feld[oldx][oldy];
2524 content_old = Store[oldx][oldy];
2526 if (element_old == EL_QUICKSAND_EMPTYING ||
2527 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2528 element_old == EL_MAGIC_WALL_EMPTYING ||
2529 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2530 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2531 element_old == EL_AMOEBA_DROPPING)
2532 cut_mode = CUT_ABOVE;
2534 DrawScreenElement(x, y, EL_EMPTY);
2537 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2539 else if (cut_mode == NO_CUTTING)
2540 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2543 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2546 else if (IS_DRAWABLE(element))
2547 DrawScreenElement(x, y, element);
2549 DrawScreenElement(x, y, EL_EMPTY);
2552 void DrawLevelField(int x, int y)
2554 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2555 DrawScreenField(SCREENX(x), SCREENY(y));
2556 else if (IS_MOVING(x, y))
2560 Moving2Blocked(x, y, &newx, &newy);
2561 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2562 DrawScreenField(SCREENX(newx), SCREENY(newy));
2564 else if (IS_BLOCKED(x, y))
2568 Blocked2Moving(x, y, &oldx, &oldy);
2569 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2570 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2574 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2575 int (*el2img_function)(int), boolean masked,
2576 int element_bits_draw)
2578 int element_base = map_mm_wall_element(element);
2579 int element_bits = (IS_DF_WALL(element) ?
2580 element - EL_DF_WALL_START :
2581 IS_MM_WALL(element) ?
2582 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2583 int graphic = el2img_function(element_base);
2584 int tilesize_draw = tilesize / 2;
2589 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2591 for (i = 0; i < 4; i++)
2593 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2594 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2596 if (!(element_bits_draw & (1 << i)))
2599 if (element_bits & (1 << i))
2602 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2603 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2605 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2606 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2611 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2612 tilesize_draw, tilesize_draw);
2617 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2618 boolean masked, int element_bits_draw)
2620 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2621 element, tilesize, el2edimg, masked, element_bits_draw);
2624 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2625 int (*el2img_function)(int))
2627 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2631 void DrawSizedElementExt(int x, int y, int element, int tilesize,
2634 if (IS_MM_WALL(element))
2636 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2637 element, tilesize, el2edimg, masked, 0x000f);
2641 int graphic = el2edimg(element);
2644 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2646 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2650 void DrawSizedElement(int x, int y, int element, int tilesize)
2652 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2655 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2657 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2660 void DrawMiniElement(int x, int y, int element)
2664 graphic = el2edimg(element);
2665 DrawMiniGraphic(x, y, graphic);
2668 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2671 int x = sx + scroll_x, y = sy + scroll_y;
2673 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2674 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2675 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2676 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2678 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2681 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2683 int x = sx + scroll_x, y = sy + scroll_y;
2685 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2686 DrawMiniElement(sx, sy, EL_EMPTY);
2687 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2688 DrawMiniElement(sx, sy, Feld[x][y]);
2690 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2693 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2694 int x, int y, int xsize, int ysize,
2695 int tile_width, int tile_height)
2699 int dst_x = startx + x * tile_width;
2700 int dst_y = starty + y * tile_height;
2701 int width = graphic_info[graphic].width;
2702 int height = graphic_info[graphic].height;
2703 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2704 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2705 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2706 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2707 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2708 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2709 boolean draw_masked = graphic_info[graphic].draw_masked;
2711 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2713 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2715 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2719 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2720 inner_sx + (x - 1) * tile_width % inner_width);
2721 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2722 inner_sy + (y - 1) * tile_height % inner_height);
2725 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2728 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2732 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2733 int x, int y, int xsize, int ysize, int font_nr)
2735 int font_width = getFontWidth(font_nr);
2736 int font_height = getFontHeight(font_nr);
2738 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2739 font_width, font_height);
2742 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2744 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2745 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2746 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2747 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2748 boolean no_delay = (tape.warp_forward);
2749 unsigned int anim_delay = 0;
2750 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2751 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2752 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2753 int font_width = getFontWidth(font_nr);
2754 int font_height = getFontHeight(font_nr);
2755 int max_xsize = level.envelope[envelope_nr].xsize;
2756 int max_ysize = level.envelope[envelope_nr].ysize;
2757 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2758 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2759 int xend = max_xsize;
2760 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2761 int xstep = (xstart < xend ? 1 : 0);
2762 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2764 int end = MAX(xend - xstart, yend - ystart);
2767 for (i = start; i <= end; i++)
2769 int last_frame = end; // last frame of this "for" loop
2770 int x = xstart + i * xstep;
2771 int y = ystart + i * ystep;
2772 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2773 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2774 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2775 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2778 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2780 BlitScreenToBitmap(backbuffer);
2782 SetDrawtoField(DRAW_TO_BACKBUFFER);
2784 for (yy = 0; yy < ysize; yy++)
2785 for (xx = 0; xx < xsize; xx++)
2786 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2788 DrawTextBuffer(sx + font_width, sy + font_height,
2789 level.envelope[envelope_nr].text, font_nr, max_xsize,
2790 xsize - 2, ysize - 2, 0, mask_mode,
2791 level.envelope[envelope_nr].autowrap,
2792 level.envelope[envelope_nr].centered, FALSE);
2794 redraw_mask |= REDRAW_FIELD;
2797 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2801 void ShowEnvelope(int envelope_nr)
2803 int element = EL_ENVELOPE_1 + envelope_nr;
2804 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2805 int sound_opening = element_info[element].sound[ACTION_OPENING];
2806 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2807 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2808 boolean no_delay = (tape.warp_forward);
2809 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2810 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2811 int anim_mode = graphic_info[graphic].anim_mode;
2812 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2813 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2815 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2817 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2819 if (anim_mode == ANIM_DEFAULT)
2820 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2822 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2825 Delay(wait_delay_value);
2827 WaitForEventToContinue();
2829 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2831 if (anim_mode != ANIM_NONE)
2832 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2834 if (anim_mode == ANIM_DEFAULT)
2835 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2837 game.envelope_active = FALSE;
2839 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2841 redraw_mask |= REDRAW_FIELD;
2845 static void setRequestBasePosition(int *x, int *y)
2847 int sx_base, sy_base;
2849 if (request.x != -1)
2850 sx_base = request.x;
2851 else if (request.align == ALIGN_LEFT)
2853 else if (request.align == ALIGN_RIGHT)
2854 sx_base = SX + SXSIZE;
2856 sx_base = SX + SXSIZE / 2;
2858 if (request.y != -1)
2859 sy_base = request.y;
2860 else if (request.valign == VALIGN_TOP)
2862 else if (request.valign == VALIGN_BOTTOM)
2863 sy_base = SY + SYSIZE;
2865 sy_base = SY + SYSIZE / 2;
2871 static void setRequestPositionExt(int *x, int *y, int width, int height,
2872 boolean add_border_size)
2874 int border_size = request.border_size;
2875 int sx_base, sy_base;
2878 setRequestBasePosition(&sx_base, &sy_base);
2880 if (request.align == ALIGN_LEFT)
2882 else if (request.align == ALIGN_RIGHT)
2883 sx = sx_base - width;
2885 sx = sx_base - width / 2;
2887 if (request.valign == VALIGN_TOP)
2889 else if (request.valign == VALIGN_BOTTOM)
2890 sy = sy_base - height;
2892 sy = sy_base - height / 2;
2894 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2895 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2897 if (add_border_size)
2907 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2909 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2912 void DrawEnvelopeRequest(char *text)
2914 char *text_final = text;
2915 char *text_door_style = NULL;
2916 int graphic = IMG_BACKGROUND_REQUEST;
2917 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2918 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2919 int font_nr = FONT_REQUEST;
2920 int font_width = getFontWidth(font_nr);
2921 int font_height = getFontHeight(font_nr);
2922 int border_size = request.border_size;
2923 int line_spacing = request.line_spacing;
2924 int line_height = font_height + line_spacing;
2925 int max_text_width = request.width - 2 * border_size;
2926 int max_text_height = request.height - 2 * border_size;
2927 int line_length = max_text_width / font_width;
2928 int max_lines = max_text_height / line_height;
2929 int text_width = line_length * font_width;
2930 int width = request.width;
2931 int height = request.height;
2932 int tile_size = MAX(request.step_offset, 1);
2933 int x_steps = width / tile_size;
2934 int y_steps = height / tile_size;
2935 int sx_offset = border_size;
2936 int sy_offset = border_size;
2940 if (request.centered)
2941 sx_offset = (request.width - text_width) / 2;
2943 if (request.wrap_single_words && !request.autowrap)
2945 char *src_text_ptr, *dst_text_ptr;
2947 text_door_style = checked_malloc(2 * strlen(text) + 1);
2949 src_text_ptr = text;
2950 dst_text_ptr = text_door_style;
2952 while (*src_text_ptr)
2954 if (*src_text_ptr == ' ' ||
2955 *src_text_ptr == '?' ||
2956 *src_text_ptr == '!')
2957 *dst_text_ptr++ = '\n';
2959 if (*src_text_ptr != ' ')
2960 *dst_text_ptr++ = *src_text_ptr;
2965 *dst_text_ptr = '\0';
2967 text_final = text_door_style;
2970 setRequestPosition(&sx, &sy, FALSE);
2972 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2974 for (y = 0; y < y_steps; y++)
2975 for (x = 0; x < x_steps; x++)
2976 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2977 x, y, x_steps, y_steps,
2978 tile_size, tile_size);
2980 /* force DOOR font inside door area */
2981 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2983 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2984 line_length, -1, max_lines, line_spacing, mask_mode,
2985 request.autowrap, request.centered, FALSE);
2989 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2990 RedrawGadget(tool_gadget[i]);
2992 // store readily prepared envelope request for later use when animating
2993 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2995 if (text_door_style)
2996 free(text_door_style);
2999 void AnimateEnvelopeRequest(int anim_mode, int action)
3001 int graphic = IMG_BACKGROUND_REQUEST;
3002 boolean draw_masked = graphic_info[graphic].draw_masked;
3003 int delay_value_normal = request.step_delay;
3004 int delay_value_fast = delay_value_normal / 2;
3005 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3006 boolean no_delay = (tape.warp_forward);
3007 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3008 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3009 unsigned int anim_delay = 0;
3011 int tile_size = MAX(request.step_offset, 1);
3012 int max_xsize = request.width / tile_size;
3013 int max_ysize = request.height / tile_size;
3014 int max_xsize_inner = max_xsize - 2;
3015 int max_ysize_inner = max_ysize - 2;
3017 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3018 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3019 int xend = max_xsize_inner;
3020 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3021 int xstep = (xstart < xend ? 1 : 0);
3022 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3024 int end = MAX(xend - xstart, yend - ystart);
3027 if (setup.quick_doors)
3034 for (i = start; i <= end; i++)
3036 int last_frame = end; // last frame of this "for" loop
3037 int x = xstart + i * xstep;
3038 int y = ystart + i * ystep;
3039 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3040 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3041 int xsize_size_left = (xsize - 1) * tile_size;
3042 int ysize_size_top = (ysize - 1) * tile_size;
3043 int max_xsize_pos = (max_xsize - 1) * tile_size;
3044 int max_ysize_pos = (max_ysize - 1) * tile_size;
3045 int width = xsize * tile_size;
3046 int height = ysize * tile_size;
3051 setRequestPosition(&src_x, &src_y, FALSE);
3052 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3054 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3056 for (yy = 0; yy < 2; yy++)
3058 for (xx = 0; xx < 2; xx++)
3060 int src_xx = src_x + xx * max_xsize_pos;
3061 int src_yy = src_y + yy * max_ysize_pos;
3062 int dst_xx = dst_x + xx * xsize_size_left;
3063 int dst_yy = dst_y + yy * ysize_size_top;
3064 int xx_size = (xx ? tile_size : xsize_size_left);
3065 int yy_size = (yy ? tile_size : ysize_size_top);
3068 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3069 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3071 BlitBitmap(bitmap_db_store_2, backbuffer,
3072 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3076 redraw_mask |= REDRAW_FIELD;
3080 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3084 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3086 int graphic = IMG_BACKGROUND_REQUEST;
3087 int sound_opening = SND_REQUEST_OPENING;
3088 int sound_closing = SND_REQUEST_CLOSING;
3089 int anim_mode_1 = request.anim_mode; /* (higher priority) */
3090 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
3091 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3092 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3093 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3095 if (game_status == GAME_MODE_PLAYING)
3096 BlitScreenToBitmap(backbuffer);
3098 SetDrawtoField(DRAW_TO_BACKBUFFER);
3100 // SetDrawBackgroundMask(REDRAW_NONE);
3102 if (action == ACTION_OPENING)
3104 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3106 if (req_state & REQ_ASK)
3108 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3109 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3111 else if (req_state & REQ_CONFIRM)
3113 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3115 else if (req_state & REQ_PLAYER)
3117 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3118 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3119 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3120 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3123 DrawEnvelopeRequest(text);
3126 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
3128 if (action == ACTION_OPENING)
3130 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3132 if (anim_mode == ANIM_DEFAULT)
3133 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3135 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3139 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3141 if (anim_mode != ANIM_NONE)
3142 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3144 if (anim_mode == ANIM_DEFAULT)
3145 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3148 game.envelope_active = FALSE;
3150 if (action == ACTION_CLOSING)
3151 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3153 // SetDrawBackgroundMask(last_draw_background_mask);
3155 redraw_mask |= REDRAW_FIELD;
3159 if (action == ACTION_CLOSING &&
3160 game_status == GAME_MODE_PLAYING &&
3161 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3162 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3165 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3167 if (IS_MM_WALL(element))
3169 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3175 int graphic = el2preimg(element);
3177 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3178 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3183 void DrawLevel(int draw_background_mask)
3187 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3188 SetDrawBackgroundMask(draw_background_mask);
3192 for (x = BX1; x <= BX2; x++)
3193 for (y = BY1; y <= BY2; y++)
3194 DrawScreenField(x, y);
3196 redraw_mask |= REDRAW_FIELD;
3199 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3204 for (x = 0; x < size_x; x++)
3205 for (y = 0; y < size_y; y++)
3206 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3208 redraw_mask |= REDRAW_FIELD;
3211 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3215 for (x = 0; x < size_x; x++)
3216 for (y = 0; y < size_y; y++)
3217 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3219 redraw_mask |= REDRAW_FIELD;
3222 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3224 boolean show_level_border = (BorderElement != EL_EMPTY);
3225 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3226 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3227 int tile_size = preview.tile_size;
3228 int preview_width = preview.xsize * tile_size;
3229 int preview_height = preview.ysize * tile_size;
3230 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3231 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3232 int real_preview_width = real_preview_xsize * tile_size;
3233 int real_preview_height = real_preview_ysize * tile_size;
3234 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3235 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3238 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3241 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3243 dst_x += (preview_width - real_preview_width) / 2;
3244 dst_y += (preview_height - real_preview_height) / 2;
3246 for (x = 0; x < real_preview_xsize; x++)
3248 for (y = 0; y < real_preview_ysize; y++)
3250 int lx = from_x + x + (show_level_border ? -1 : 0);
3251 int ly = from_y + y + (show_level_border ? -1 : 0);
3252 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3253 getBorderElement(lx, ly));
3255 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3256 element, tile_size);
3260 redraw_mask |= REDRAW_FIELD;
3263 #define MICROLABEL_EMPTY 0
3264 #define MICROLABEL_LEVEL_NAME 1
3265 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3266 #define MICROLABEL_LEVEL_AUTHOR 3
3267 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3268 #define MICROLABEL_IMPORTED_FROM 5
3269 #define MICROLABEL_IMPORTED_BY_HEAD 6
3270 #define MICROLABEL_IMPORTED_BY 7
3272 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3274 int max_text_width = SXSIZE;
3275 int font_width = getFontWidth(font_nr);
3277 if (pos->align == ALIGN_CENTER)
3278 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3279 else if (pos->align == ALIGN_RIGHT)
3280 max_text_width = pos->x;
3282 max_text_width = SXSIZE - pos->x;
3284 return max_text_width / font_width;
3287 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3289 char label_text[MAX_OUTPUT_LINESIZE + 1];
3290 int max_len_label_text;
3291 int font_nr = pos->font;
3294 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3297 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3298 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3299 mode == MICROLABEL_IMPORTED_BY_HEAD)
3300 font_nr = pos->font_alt;
3302 max_len_label_text = getMaxTextLength(pos, font_nr);
3304 if (pos->size != -1)
3305 max_len_label_text = pos->size;
3307 for (i = 0; i < max_len_label_text; i++)
3308 label_text[i] = ' ';
3309 label_text[max_len_label_text] = '\0';
3311 if (strlen(label_text) > 0)
3312 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3315 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3316 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3317 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3318 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3319 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3320 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3321 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3322 max_len_label_text);
3323 label_text[max_len_label_text] = '\0';
3325 if (strlen(label_text) > 0)
3326 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3328 redraw_mask |= REDRAW_FIELD;
3331 static void DrawPreviewLevelLabel(int mode)
3333 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3336 static void DrawPreviewLevelInfo(int mode)
3338 if (mode == MICROLABEL_LEVEL_NAME)
3339 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3340 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3341 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3344 static void DrawPreviewLevelExt(boolean restart)
3346 static unsigned int scroll_delay = 0;
3347 static unsigned int label_delay = 0;
3348 static int from_x, from_y, scroll_direction;
3349 static int label_state, label_counter;
3350 unsigned int scroll_delay_value = preview.step_delay;
3351 boolean show_level_border = (BorderElement != EL_EMPTY);
3352 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3353 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3360 if (preview.anim_mode == ANIM_CENTERED)
3362 if (level_xsize > preview.xsize)
3363 from_x = (level_xsize - preview.xsize) / 2;
3364 if (level_ysize > preview.ysize)
3365 from_y = (level_ysize - preview.ysize) / 2;
3368 from_x += preview.xoffset;
3369 from_y += preview.yoffset;
3371 scroll_direction = MV_RIGHT;
3375 DrawPreviewLevelPlayfield(from_x, from_y);
3376 DrawPreviewLevelLabel(label_state);
3378 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3379 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3381 /* initialize delay counters */
3382 DelayReached(&scroll_delay, 0);
3383 DelayReached(&label_delay, 0);
3385 if (leveldir_current->name)
3387 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3388 char label_text[MAX_OUTPUT_LINESIZE + 1];
3389 int font_nr = pos->font;
3390 int max_len_label_text = getMaxTextLength(pos, font_nr);
3392 if (pos->size != -1)
3393 max_len_label_text = pos->size;
3395 strncpy(label_text, leveldir_current->name, max_len_label_text);
3396 label_text[max_len_label_text] = '\0';
3398 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3399 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3405 /* scroll preview level, if needed */
3406 if (preview.anim_mode != ANIM_NONE &&
3407 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3408 DelayReached(&scroll_delay, scroll_delay_value))
3410 switch (scroll_direction)
3415 from_x -= preview.step_offset;
3416 from_x = (from_x < 0 ? 0 : from_x);
3419 scroll_direction = MV_UP;
3423 if (from_x < level_xsize - preview.xsize)
3425 from_x += preview.step_offset;
3426 from_x = (from_x > level_xsize - preview.xsize ?
3427 level_xsize - preview.xsize : from_x);
3430 scroll_direction = MV_DOWN;
3436 from_y -= preview.step_offset;
3437 from_y = (from_y < 0 ? 0 : from_y);
3440 scroll_direction = MV_RIGHT;
3444 if (from_y < level_ysize - preview.ysize)
3446 from_y += preview.step_offset;
3447 from_y = (from_y > level_ysize - preview.ysize ?
3448 level_ysize - preview.ysize : from_y);
3451 scroll_direction = MV_LEFT;
3458 DrawPreviewLevelPlayfield(from_x, from_y);
3461 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3462 /* redraw micro level label, if needed */
3463 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3464 !strEqual(level.author, ANONYMOUS_NAME) &&
3465 !strEqual(level.author, leveldir_current->name) &&
3466 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3468 int max_label_counter = 23;
3470 if (leveldir_current->imported_from != NULL &&
3471 strlen(leveldir_current->imported_from) > 0)
3472 max_label_counter += 14;
3473 if (leveldir_current->imported_by != NULL &&
3474 strlen(leveldir_current->imported_by) > 0)
3475 max_label_counter += 14;
3477 label_counter = (label_counter + 1) % max_label_counter;
3478 label_state = (label_counter >= 0 && label_counter <= 7 ?
3479 MICROLABEL_LEVEL_NAME :
3480 label_counter >= 9 && label_counter <= 12 ?
3481 MICROLABEL_LEVEL_AUTHOR_HEAD :
3482 label_counter >= 14 && label_counter <= 21 ?
3483 MICROLABEL_LEVEL_AUTHOR :
3484 label_counter >= 23 && label_counter <= 26 ?
3485 MICROLABEL_IMPORTED_FROM_HEAD :
3486 label_counter >= 28 && label_counter <= 35 ?
3487 MICROLABEL_IMPORTED_FROM :
3488 label_counter >= 37 && label_counter <= 40 ?
3489 MICROLABEL_IMPORTED_BY_HEAD :
3490 label_counter >= 42 && label_counter <= 49 ?
3491 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3493 if (leveldir_current->imported_from == NULL &&
3494 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3495 label_state == MICROLABEL_IMPORTED_FROM))
3496 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3497 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3499 DrawPreviewLevelLabel(label_state);
3503 void DrawPreviewLevelInitial()
3505 DrawPreviewLevelExt(TRUE);
3508 void DrawPreviewLevelAnimation()
3510 DrawPreviewLevelExt(FALSE);
3513 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3514 int graphic, int sync_frame,
3517 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3519 if (mask_mode == USE_MASKING)
3520 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3522 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3525 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3526 int graphic, int sync_frame, int mask_mode)
3528 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3530 if (mask_mode == USE_MASKING)
3531 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3533 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3536 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3538 int lx = LEVELX(x), ly = LEVELY(y);
3540 if (!IN_SCR_FIELD(x, y))
3543 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3544 graphic, GfxFrame[lx][ly], NO_MASKING);
3546 MarkTileDirty(x, y);
3549 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3551 int lx = LEVELX(x), ly = LEVELY(y);
3553 if (!IN_SCR_FIELD(x, y))
3556 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3557 graphic, GfxFrame[lx][ly], NO_MASKING);
3558 MarkTileDirty(x, y);
3561 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3563 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3566 void DrawLevelElementAnimation(int x, int y, int element)
3568 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3570 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3573 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3575 int sx = SCREENX(x), sy = SCREENY(y);
3577 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3580 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3583 DrawGraphicAnimation(sx, sy, graphic);
3586 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3587 DrawLevelFieldCrumbled(x, y);
3589 if (GFX_CRUMBLED(Feld[x][y]))
3590 DrawLevelFieldCrumbled(x, y);
3594 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3596 int sx = SCREENX(x), sy = SCREENY(y);
3599 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3602 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3604 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3607 DrawGraphicAnimation(sx, sy, graphic);
3609 if (GFX_CRUMBLED(element))
3610 DrawLevelFieldCrumbled(x, y);
3613 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3615 if (player->use_murphy)
3617 /* this works only because currently only one player can be "murphy" ... */
3618 static int last_horizontal_dir = MV_LEFT;
3619 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3621 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3622 last_horizontal_dir = move_dir;
3624 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3626 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3628 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3634 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3637 static boolean equalGraphics(int graphic1, int graphic2)
3639 struct GraphicInfo *g1 = &graphic_info[graphic1];
3640 struct GraphicInfo *g2 = &graphic_info[graphic2];
3642 return (g1->bitmap == g2->bitmap &&
3643 g1->src_x == g2->src_x &&
3644 g1->src_y == g2->src_y &&
3645 g1->anim_frames == g2->anim_frames &&
3646 g1->anim_delay == g2->anim_delay &&
3647 g1->anim_mode == g2->anim_mode);
3650 void DrawAllPlayers()
3654 for (i = 0; i < MAX_PLAYERS; i++)
3655 if (stored_player[i].active)
3656 DrawPlayer(&stored_player[i]);
3659 void DrawPlayerField(int x, int y)
3661 if (!IS_PLAYER(x, y))
3664 DrawPlayer(PLAYERINFO(x, y));
3667 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3669 void DrawPlayer(struct PlayerInfo *player)
3671 int jx = player->jx;
3672 int jy = player->jy;
3673 int move_dir = player->MovDir;
3674 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3675 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3676 int last_jx = (player->is_moving ? jx - dx : jx);
3677 int last_jy = (player->is_moving ? jy - dy : jy);
3678 int next_jx = jx + dx;
3679 int next_jy = jy + dy;
3680 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3681 boolean player_is_opaque = FALSE;
3682 int sx = SCREENX(jx), sy = SCREENY(jy);
3683 int sxx = 0, syy = 0;
3684 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3686 int action = ACTION_DEFAULT;
3687 int last_player_graphic = getPlayerGraphic(player, move_dir);
3688 int last_player_frame = player->Frame;
3691 /* GfxElement[][] is set to the element the player is digging or collecting;
3692 remove also for off-screen player if the player is not moving anymore */
3693 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3694 GfxElement[jx][jy] = EL_UNDEFINED;
3696 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3700 if (!IN_LEV_FIELD(jx, jy))
3702 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3703 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3704 printf("DrawPlayerField(): This should never happen!\n");
3709 if (element == EL_EXPLOSION)
3712 action = (player->is_pushing ? ACTION_PUSHING :
3713 player->is_digging ? ACTION_DIGGING :
3714 player->is_collecting ? ACTION_COLLECTING :
3715 player->is_moving ? ACTION_MOVING :
3716 player->is_snapping ? ACTION_SNAPPING :
3717 player->is_dropping ? ACTION_DROPPING :
3718 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3720 if (player->is_waiting)
3721 move_dir = player->dir_waiting;
3723 InitPlayerGfxAnimation(player, action, move_dir);
3725 /* ----------------------------------------------------------------------- */
3726 /* draw things in the field the player is leaving, if needed */
3727 /* ----------------------------------------------------------------------- */
3729 if (player->is_moving)
3731 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3733 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3735 if (last_element == EL_DYNAMITE_ACTIVE ||
3736 last_element == EL_EM_DYNAMITE_ACTIVE ||
3737 last_element == EL_SP_DISK_RED_ACTIVE)
3738 DrawDynamite(last_jx, last_jy);
3740 DrawLevelFieldThruMask(last_jx, last_jy);
3742 else if (last_element == EL_DYNAMITE_ACTIVE ||
3743 last_element == EL_EM_DYNAMITE_ACTIVE ||
3744 last_element == EL_SP_DISK_RED_ACTIVE)
3745 DrawDynamite(last_jx, last_jy);
3747 /* !!! this is not enough to prevent flickering of players which are
3748 moving next to each others without a free tile between them -- this
3749 can only be solved by drawing all players layer by layer (first the
3750 background, then the foreground etc.) !!! => TODO */
3751 else if (!IS_PLAYER(last_jx, last_jy))
3752 DrawLevelField(last_jx, last_jy);
3755 DrawLevelField(last_jx, last_jy);
3758 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3759 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3762 if (!IN_SCR_FIELD(sx, sy))
3765 /* ----------------------------------------------------------------------- */
3766 /* draw things behind the player, if needed */
3767 /* ----------------------------------------------------------------------- */
3770 DrawLevelElement(jx, jy, Back[jx][jy]);
3771 else if (IS_ACTIVE_BOMB(element))
3772 DrawLevelElement(jx, jy, EL_EMPTY);
3775 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3777 int old_element = GfxElement[jx][jy];
3778 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3779 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3781 if (GFX_CRUMBLED(old_element))
3782 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3784 DrawGraphic(sx, sy, old_graphic, frame);
3786 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3787 player_is_opaque = TRUE;
3791 GfxElement[jx][jy] = EL_UNDEFINED;
3793 /* make sure that pushed elements are drawn with correct frame rate */
3794 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3796 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3797 GfxFrame[jx][jy] = player->StepFrame;
3799 DrawLevelField(jx, jy);
3803 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3804 /* ----------------------------------------------------------------------- */
3805 /* draw player himself */
3806 /* ----------------------------------------------------------------------- */
3808 graphic = getPlayerGraphic(player, move_dir);
3810 /* in the case of changed player action or direction, prevent the current
3811 animation frame from being restarted for identical animations */
3812 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3813 player->Frame = last_player_frame;
3815 frame = getGraphicAnimationFrame(graphic, player->Frame);
3819 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3820 sxx = player->GfxPos;
3822 syy = player->GfxPos;
3825 if (player_is_opaque)
3826 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3828 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3830 if (SHIELD_ON(player))
3832 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3833 IMG_SHIELD_NORMAL_ACTIVE);
3834 int frame = getGraphicAnimationFrame(graphic, -1);
3836 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3840 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3843 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3844 sxx = player->GfxPos;
3846 syy = player->GfxPos;
3850 /* ----------------------------------------------------------------------- */
3851 /* draw things the player is pushing, if needed */
3852 /* ----------------------------------------------------------------------- */
3854 if (player->is_pushing && player->is_moving)
3856 int px = SCREENX(jx), py = SCREENY(jy);
3857 int pxx = (TILEX - ABS(sxx)) * dx;
3858 int pyy = (TILEY - ABS(syy)) * dy;
3859 int gfx_frame = GfxFrame[jx][jy];
3865 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3867 element = Feld[next_jx][next_jy];
3868 gfx_frame = GfxFrame[next_jx][next_jy];
3871 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3873 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3874 frame = getGraphicAnimationFrame(graphic, sync_frame);
3876 /* draw background element under pushed element (like the Sokoban field) */
3877 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3879 /* this allows transparent pushing animation over non-black background */
3882 DrawLevelElement(jx, jy, Back[jx][jy]);
3884 DrawLevelElement(jx, jy, EL_EMPTY);
3886 if (Back[next_jx][next_jy])
3887 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3889 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3891 else if (Back[next_jx][next_jy])
3892 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3895 /* do not draw (EM style) pushing animation when pushing is finished */
3896 /* (two-tile animations usually do not contain start and end frame) */
3897 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3898 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3900 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3902 /* masked drawing is needed for EMC style (double) movement graphics */
3903 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3904 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3908 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3909 /* ----------------------------------------------------------------------- */
3910 /* draw player himself */
3911 /* ----------------------------------------------------------------------- */
3913 graphic = getPlayerGraphic(player, move_dir);
3915 /* in the case of changed player action or direction, prevent the current
3916 animation frame from being restarted for identical animations */
3917 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3918 player->Frame = last_player_frame;
3920 frame = getGraphicAnimationFrame(graphic, player->Frame);
3924 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3925 sxx = player->GfxPos;
3927 syy = player->GfxPos;
3930 if (player_is_opaque)
3931 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3933 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3935 if (SHIELD_ON(player))
3937 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3938 IMG_SHIELD_NORMAL_ACTIVE);
3939 int frame = getGraphicAnimationFrame(graphic, -1);
3941 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3945 /* ----------------------------------------------------------------------- */
3946 /* draw things in front of player (active dynamite or dynabombs) */
3947 /* ----------------------------------------------------------------------- */
3949 if (IS_ACTIVE_BOMB(element))
3951 graphic = el2img(element);
3952 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3954 if (game.emulation == EMU_SUPAPLEX)
3955 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3957 DrawGraphicThruMask(sx, sy, graphic, frame);
3960 if (player_is_moving && last_element == EL_EXPLOSION)
3962 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3963 GfxElement[last_jx][last_jy] : EL_EMPTY);
3964 int graphic = el_act2img(element, ACTION_EXPLODING);
3965 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3966 int phase = ExplodePhase[last_jx][last_jy] - 1;
3967 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3970 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3973 /* ----------------------------------------------------------------------- */
3974 /* draw elements the player is just walking/passing through/under */
3975 /* ----------------------------------------------------------------------- */
3977 if (player_is_moving)
3979 /* handle the field the player is leaving ... */
3980 if (IS_ACCESSIBLE_INSIDE(last_element))
3981 DrawLevelField(last_jx, last_jy);
3982 else if (IS_ACCESSIBLE_UNDER(last_element))
3983 DrawLevelFieldThruMask(last_jx, last_jy);
3986 /* do not redraw accessible elements if the player is just pushing them */
3987 if (!player_is_moving || !player->is_pushing)
3989 /* ... and the field the player is entering */
3990 if (IS_ACCESSIBLE_INSIDE(element))
3991 DrawLevelField(jx, jy);
3992 else if (IS_ACCESSIBLE_UNDER(element))
3993 DrawLevelFieldThruMask(jx, jy);
3996 MarkTileDirty(sx, sy);
3999 /* ------------------------------------------------------------------------- */
4001 void WaitForEventToContinue()
4003 boolean still_wait = TRUE;
4005 if (program.headless)
4008 /* simulate releasing mouse button over last gadget, if still pressed */
4010 HandleGadgets(-1, -1, 0);
4012 button_status = MB_RELEASED;
4020 if (NextValidEvent(&event))
4024 case EVENT_BUTTONPRESS:
4025 case EVENT_KEYPRESS:
4026 #if defined(TARGET_SDL2)
4027 case SDL_CONTROLLERBUTTONDOWN:
4029 case SDL_JOYBUTTONDOWN:
4033 case EVENT_KEYRELEASE:
4034 ClearPlayerAction();
4038 HandleOtherEvents(&event);
4042 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4051 #define MAX_REQUEST_LINES 13
4052 #define MAX_REQUEST_LINE_FONT1_LEN 7
4053 #define MAX_REQUEST_LINE_FONT2_LEN 10
4055 static int RequestHandleEvents(unsigned int req_state)
4057 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
4058 local_player->LevelSolved_GameEnd);
4059 int width = request.width;
4060 int height = request.height;
4064 setRequestPosition(&sx, &sy, FALSE);
4066 button_status = MB_RELEASED;
4068 request_gadget_id = -1;
4075 /* the MM game engine does not use a special (scrollable) field buffer */
4076 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4077 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4079 HandleGameActions();
4081 SetDrawtoField(DRAW_TO_BACKBUFFER);
4083 if (global.use_envelope_request)
4085 /* copy current state of request area to middle of playfield area */
4086 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4094 while (NextValidEvent(&event))
4098 case EVENT_BUTTONPRESS:
4099 case EVENT_BUTTONRELEASE:
4100 case EVENT_MOTIONNOTIFY:
4104 if (event.type == EVENT_MOTIONNOTIFY)
4109 motion_status = TRUE;
4110 mx = ((MotionEvent *) &event)->x;
4111 my = ((MotionEvent *) &event)->y;
4115 motion_status = FALSE;
4116 mx = ((ButtonEvent *) &event)->x;
4117 my = ((ButtonEvent *) &event)->y;
4118 if (event.type == EVENT_BUTTONPRESS)
4119 button_status = ((ButtonEvent *) &event)->button;
4121 button_status = MB_RELEASED;
4124 /* this sets 'request_gadget_id' */
4125 HandleGadgets(mx, my, button_status);
4127 switch (request_gadget_id)
4129 case TOOL_CTRL_ID_YES:
4132 case TOOL_CTRL_ID_NO:
4135 case TOOL_CTRL_ID_CONFIRM:
4136 result = TRUE | FALSE;
4139 case TOOL_CTRL_ID_PLAYER_1:
4142 case TOOL_CTRL_ID_PLAYER_2:
4145 case TOOL_CTRL_ID_PLAYER_3:
4148 case TOOL_CTRL_ID_PLAYER_4:
4159 #if defined(TARGET_SDL2)
4160 case SDL_WINDOWEVENT:
4161 HandleWindowEvent((WindowEvent *) &event);
4164 case SDL_APP_WILLENTERBACKGROUND:
4165 case SDL_APP_DIDENTERBACKGROUND:
4166 case SDL_APP_WILLENTERFOREGROUND:
4167 case SDL_APP_DIDENTERFOREGROUND:
4168 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4172 case EVENT_KEYPRESS:
4174 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4179 if (req_state & REQ_CONFIRM)
4186 #if defined(TARGET_SDL2)
4189 #if defined(KSYM_Rewind)
4190 case KSYM_Rewind: /* for Amazon Fire TV remote */
4199 #if defined(TARGET_SDL2)
4201 #if defined(KSYM_FastForward)
4202 case KSYM_FastForward: /* for Amazon Fire TV remote */
4209 HandleKeysDebug(key);
4213 if (req_state & REQ_PLAYER)
4219 case EVENT_KEYRELEASE:
4220 ClearPlayerAction();
4223 #if defined(TARGET_SDL2)
4224 case SDL_CONTROLLERBUTTONDOWN:
4225 switch (event.cbutton.button)
4227 case SDL_CONTROLLER_BUTTON_A:
4228 case SDL_CONTROLLER_BUTTON_X:
4229 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4233 case SDL_CONTROLLER_BUTTON_B:
4234 case SDL_CONTROLLER_BUTTON_Y:
4235 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4236 case SDL_CONTROLLER_BUTTON_BACK:
4241 if (req_state & REQ_PLAYER)
4246 case SDL_CONTROLLERBUTTONUP:
4247 HandleJoystickEvent(&event);
4248 ClearPlayerAction();
4253 HandleOtherEvents(&event);
4258 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4260 int joy = AnyJoystick();
4262 if (joy & JOY_BUTTON_1)
4264 else if (joy & JOY_BUTTON_2)
4270 if (global.use_envelope_request)
4272 /* copy back current state of pressed buttons inside request area */
4273 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4283 static boolean RequestDoor(char *text, unsigned int req_state)
4285 unsigned int old_door_state;
4286 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4287 int font_nr = FONT_TEXT_2;
4292 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4294 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4295 font_nr = FONT_TEXT_1;
4298 if (game_status == GAME_MODE_PLAYING)
4299 BlitScreenToBitmap(backbuffer);
4301 /* disable deactivated drawing when quick-loading level tape recording */
4302 if (tape.playing && tape.deactivate_display)
4303 TapeDeactivateDisplayOff(TRUE);
4305 SetMouseCursor(CURSOR_DEFAULT);
4307 #if defined(NETWORK_AVALIABLE)
4308 /* pause network game while waiting for request to answer */
4309 if (options.network &&
4310 game_status == GAME_MODE_PLAYING &&
4311 req_state & REQUEST_WAIT_FOR_INPUT)
4312 SendToServer_PausePlaying();
4315 old_door_state = GetDoorState();
4317 /* simulate releasing mouse button over last gadget, if still pressed */
4319 HandleGadgets(-1, -1, 0);
4323 /* draw released gadget before proceeding */
4326 if (old_door_state & DOOR_OPEN_1)
4328 CloseDoor(DOOR_CLOSE_1);
4330 /* save old door content */
4331 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4332 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4335 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4336 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4338 /* clear door drawing field */
4339 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4341 /* force DOOR font inside door area */
4342 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4344 /* write text for request */
4345 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4347 char text_line[max_request_line_len + 1];
4353 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4355 tc = *(text_ptr + tx);
4356 // if (!tc || tc == ' ')
4357 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4361 if ((tc == '?' || tc == '!') && tl == 0)
4371 strncpy(text_line, text_ptr, tl);
4374 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4375 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4376 text_line, font_nr);
4378 text_ptr += tl + (tc == ' ' ? 1 : 0);
4379 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4384 if (req_state & REQ_ASK)
4386 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4387 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4389 else if (req_state & REQ_CONFIRM)
4391 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4393 else if (req_state & REQ_PLAYER)
4395 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4396 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4397 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4398 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4401 /* copy request gadgets to door backbuffer */
4402 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4404 OpenDoor(DOOR_OPEN_1);
4406 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4408 if (game_status == GAME_MODE_PLAYING)
4410 SetPanelBackground();
4411 SetDrawBackgroundMask(REDRAW_DOOR_1);
4415 SetDrawBackgroundMask(REDRAW_FIELD);
4421 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4423 // ---------- handle request buttons ----------
4424 result = RequestHandleEvents(req_state);
4428 if (!(req_state & REQ_STAY_OPEN))
4430 CloseDoor(DOOR_CLOSE_1);
4432 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4433 (req_state & REQ_REOPEN))
4434 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4439 if (game_status == GAME_MODE_PLAYING)
4441 SetPanelBackground();
4442 SetDrawBackgroundMask(REDRAW_DOOR_1);
4446 SetDrawBackgroundMask(REDRAW_FIELD);
4449 #if defined(NETWORK_AVALIABLE)
4450 /* continue network game after request */
4451 if (options.network &&
4452 game_status == GAME_MODE_PLAYING &&
4453 req_state & REQUEST_WAIT_FOR_INPUT)
4454 SendToServer_ContinuePlaying();
4457 /* restore deactivated drawing when quick-loading level tape recording */
4458 if (tape.playing && tape.deactivate_display)
4459 TapeDeactivateDisplayOn();
4464 static boolean RequestEnvelope(char *text, unsigned int req_state)
4468 if (game_status == GAME_MODE_PLAYING)
4469 BlitScreenToBitmap(backbuffer);
4471 /* disable deactivated drawing when quick-loading level tape recording */
4472 if (tape.playing && tape.deactivate_display)
4473 TapeDeactivateDisplayOff(TRUE);
4475 SetMouseCursor(CURSOR_DEFAULT);
4477 #if defined(NETWORK_AVALIABLE)
4478 /* pause network game while waiting for request to answer */
4479 if (options.network &&
4480 game_status == GAME_MODE_PLAYING &&
4481 req_state & REQUEST_WAIT_FOR_INPUT)
4482 SendToServer_PausePlaying();
4485 /* simulate releasing mouse button over last gadget, if still pressed */
4487 HandleGadgets(-1, -1, 0);
4491 // (replace with setting corresponding request background)
4492 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4493 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4495 /* clear door drawing field */
4496 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4498 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4500 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4502 if (game_status == GAME_MODE_PLAYING)
4504 SetPanelBackground();
4505 SetDrawBackgroundMask(REDRAW_DOOR_1);
4509 SetDrawBackgroundMask(REDRAW_FIELD);
4515 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4517 // ---------- handle request buttons ----------
4518 result = RequestHandleEvents(req_state);
4522 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4526 if (game_status == GAME_MODE_PLAYING)
4528 SetPanelBackground();
4529 SetDrawBackgroundMask(REDRAW_DOOR_1);
4533 SetDrawBackgroundMask(REDRAW_FIELD);
4536 #if defined(NETWORK_AVALIABLE)
4537 /* continue network game after request */
4538 if (options.network &&
4539 game_status == GAME_MODE_PLAYING &&
4540 req_state & REQUEST_WAIT_FOR_INPUT)
4541 SendToServer_ContinuePlaying();
4544 /* restore deactivated drawing when quick-loading level tape recording */
4545 if (tape.playing && tape.deactivate_display)
4546 TapeDeactivateDisplayOn();
4551 boolean Request(char *text, unsigned int req_state)
4553 boolean overlay_active = GetOverlayActive();
4556 SetOverlayActive(FALSE);
4558 if (global.use_envelope_request)
4559 result = RequestEnvelope(text, req_state);
4561 result = RequestDoor(text, req_state);
4563 SetOverlayActive(overlay_active);
4568 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4570 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4571 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4574 if (dpo1->sort_priority != dpo2->sort_priority)
4575 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4577 compare_result = dpo1->nr - dpo2->nr;
4579 return compare_result;
4582 void InitGraphicCompatibilityInfo_Doors()
4588 struct DoorInfo *door;
4592 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4593 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4595 { -1, -1, -1, NULL }
4597 struct Rect door_rect_list[] =
4599 { DX, DY, DXSIZE, DYSIZE },
4600 { VX, VY, VXSIZE, VYSIZE }
4604 for (i = 0; doors[i].door_token != -1; i++)
4606 int door_token = doors[i].door_token;
4607 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4608 int part_1 = doors[i].part_1;
4609 int part_8 = doors[i].part_8;
4610 int part_2 = part_1 + 1;
4611 int part_3 = part_1 + 2;
4612 struct DoorInfo *door = doors[i].door;
4613 struct Rect *door_rect = &door_rect_list[door_index];
4614 boolean door_gfx_redefined = FALSE;
4616 /* check if any door part graphic definitions have been redefined */
4618 for (j = 0; door_part_controls[j].door_token != -1; j++)
4620 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4621 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4623 if (dpc->door_token == door_token && fi->redefined)
4624 door_gfx_redefined = TRUE;
4627 /* check for old-style door graphic/animation modifications */
4629 if (!door_gfx_redefined)
4631 if (door->anim_mode & ANIM_STATIC_PANEL)
4633 door->panel.step_xoffset = 0;
4634 door->panel.step_yoffset = 0;
4637 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4639 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4640 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4641 int num_door_steps, num_panel_steps;
4643 /* remove door part graphics other than the two default wings */
4645 for (j = 0; door_part_controls[j].door_token != -1; j++)
4647 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4648 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4650 if (dpc->graphic >= part_3 &&
4651 dpc->graphic <= part_8)
4655 /* set graphics and screen positions of the default wings */
4657 g_part_1->width = door_rect->width;
4658 g_part_1->height = door_rect->height;
4659 g_part_2->width = door_rect->width;
4660 g_part_2->height = door_rect->height;
4661 g_part_2->src_x = door_rect->width;
4662 g_part_2->src_y = g_part_1->src_y;
4664 door->part_2.x = door->part_1.x;
4665 door->part_2.y = door->part_1.y;
4667 if (door->width != -1)
4669 g_part_1->width = door->width;
4670 g_part_2->width = door->width;
4672 // special treatment for graphics and screen position of right wing
4673 g_part_2->src_x += door_rect->width - door->width;
4674 door->part_2.x += door_rect->width - door->width;
4677 if (door->height != -1)
4679 g_part_1->height = door->height;
4680 g_part_2->height = door->height;
4682 // special treatment for graphics and screen position of bottom wing
4683 g_part_2->src_y += door_rect->height - door->height;
4684 door->part_2.y += door_rect->height - door->height;
4687 /* set animation delays for the default wings and panels */
4689 door->part_1.step_delay = door->step_delay;
4690 door->part_2.step_delay = door->step_delay;
4691 door->panel.step_delay = door->step_delay;
4693 /* set animation draw order for the default wings */
4695 door->part_1.sort_priority = 2; /* draw left wing over ... */
4696 door->part_2.sort_priority = 1; /* ... right wing */
4698 /* set animation draw offset for the default wings */
4700 if (door->anim_mode & ANIM_HORIZONTAL)
4702 door->part_1.step_xoffset = door->step_offset;
4703 door->part_1.step_yoffset = 0;
4704 door->part_2.step_xoffset = door->step_offset * -1;
4705 door->part_2.step_yoffset = 0;
4707 num_door_steps = g_part_1->width / door->step_offset;
4709 else // ANIM_VERTICAL
4711 door->part_1.step_xoffset = 0;
4712 door->part_1.step_yoffset = door->step_offset;
4713 door->part_2.step_xoffset = 0;
4714 door->part_2.step_yoffset = door->step_offset * -1;
4716 num_door_steps = g_part_1->height / door->step_offset;
4719 /* set animation draw offset for the default panels */
4721 if (door->step_offset > 1)
4723 num_panel_steps = 2 * door_rect->height / door->step_offset;
4724 door->panel.start_step = num_panel_steps - num_door_steps;
4725 door->panel.start_step_closing = door->panel.start_step;
4729 num_panel_steps = door_rect->height / door->step_offset;
4730 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4731 door->panel.start_step_closing = door->panel.start_step;
4732 door->panel.step_delay *= 2;
4743 for (i = 0; door_part_controls[i].door_token != -1; i++)
4745 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4746 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4748 /* initialize "start_step_opening" and "start_step_closing", if needed */
4749 if (dpc->pos->start_step_opening == 0 &&
4750 dpc->pos->start_step_closing == 0)
4752 // dpc->pos->start_step_opening = dpc->pos->start_step;
4753 dpc->pos->start_step_closing = dpc->pos->start_step;
4756 /* fill structure for door part draw order (sorted below) */
4758 dpo->sort_priority = dpc->pos->sort_priority;
4761 /* sort door part controls according to sort_priority and graphic number */
4762 qsort(door_part_order, MAX_DOOR_PARTS,
4763 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4766 unsigned int OpenDoor(unsigned int door_state)
4768 if (door_state & DOOR_COPY_BACK)
4770 if (door_state & DOOR_OPEN_1)
4771 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4772 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4774 if (door_state & DOOR_OPEN_2)
4775 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4776 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4778 door_state &= ~DOOR_COPY_BACK;
4781 return MoveDoor(door_state);
4784 unsigned int CloseDoor(unsigned int door_state)
4786 unsigned int old_door_state = GetDoorState();
4788 if (!(door_state & DOOR_NO_COPY_BACK))
4790 if (old_door_state & DOOR_OPEN_1)
4791 BlitBitmap(backbuffer, bitmap_db_door_1,
4792 DX, DY, DXSIZE, DYSIZE, 0, 0);
4794 if (old_door_state & DOOR_OPEN_2)
4795 BlitBitmap(backbuffer, bitmap_db_door_2,
4796 VX, VY, VXSIZE, VYSIZE, 0, 0);
4798 door_state &= ~DOOR_NO_COPY_BACK;
4801 return MoveDoor(door_state);
4804 unsigned int GetDoorState()
4806 return MoveDoor(DOOR_GET_STATE);
4809 unsigned int SetDoorState(unsigned int door_state)
4811 return MoveDoor(door_state | DOOR_SET_STATE);
4814 int euclid(int a, int b)
4816 return (b ? euclid(b, a % b) : a);
4819 unsigned int MoveDoor(unsigned int door_state)
4821 struct Rect door_rect_list[] =
4823 { DX, DY, DXSIZE, DYSIZE },
4824 { VX, VY, VXSIZE, VYSIZE }
4826 static int door1 = DOOR_CLOSE_1;
4827 static int door2 = DOOR_CLOSE_2;
4828 unsigned int door_delay = 0;
4829 unsigned int door_delay_value;
4832 if (door_state == DOOR_GET_STATE)
4833 return (door1 | door2);
4835 if (door_state & DOOR_SET_STATE)
4837 if (door_state & DOOR_ACTION_1)
4838 door1 = door_state & DOOR_ACTION_1;
4839 if (door_state & DOOR_ACTION_2)
4840 door2 = door_state & DOOR_ACTION_2;
4842 return (door1 | door2);
4845 if (!(door_state & DOOR_FORCE_REDRAW))
4847 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4848 door_state &= ~DOOR_OPEN_1;
4849 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4850 door_state &= ~DOOR_CLOSE_1;
4851 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4852 door_state &= ~DOOR_OPEN_2;
4853 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4854 door_state &= ~DOOR_CLOSE_2;
4857 if (global.autoplay_leveldir)
4859 door_state |= DOOR_NO_DELAY;
4860 door_state &= ~DOOR_CLOSE_ALL;
4863 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
4864 door_state |= DOOR_NO_DELAY;
4866 if (door_state & DOOR_ACTION)
4868 boolean door_panel_drawn[NUM_DOORS];
4869 boolean panel_has_doors[NUM_DOORS];
4870 boolean door_part_skip[MAX_DOOR_PARTS];
4871 boolean door_part_done[MAX_DOOR_PARTS];
4872 boolean door_part_done_all;
4873 int num_steps[MAX_DOOR_PARTS];
4874 int max_move_delay = 0; // delay for complete animations of all doors
4875 int max_step_delay = 0; // delay (ms) between two animation frames
4876 int num_move_steps = 0; // number of animation steps for all doors
4877 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4878 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4879 int current_move_delay = 0;
4883 for (i = 0; i < NUM_DOORS; i++)
4884 panel_has_doors[i] = FALSE;
4886 for (i = 0; i < MAX_DOOR_PARTS; i++)
4888 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4889 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4890 int door_token = dpc->door_token;
4892 door_part_done[i] = FALSE;
4893 door_part_skip[i] = (!(door_state & door_token) ||
4897 for (i = 0; i < MAX_DOOR_PARTS; i++)
4899 int nr = door_part_order[i].nr;
4900 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4901 struct DoorPartPosInfo *pos = dpc->pos;
4902 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4903 int door_token = dpc->door_token;
4904 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4905 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4906 int step_xoffset = ABS(pos->step_xoffset);
4907 int step_yoffset = ABS(pos->step_yoffset);
4908 int step_delay = pos->step_delay;
4909 int current_door_state = door_state & door_token;
4910 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4911 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4912 boolean part_opening = (is_panel ? door_closing : door_opening);
4913 int start_step = (part_opening ? pos->start_step_opening :
4914 pos->start_step_closing);
4915 float move_xsize = (step_xoffset ? g->width : 0);
4916 float move_ysize = (step_yoffset ? g->height : 0);
4917 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4918 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4919 int move_steps = (move_xsteps && move_ysteps ?
4920 MIN(move_xsteps, move_ysteps) :
4921 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4922 int move_delay = move_steps * step_delay;
4924 if (door_part_skip[nr])
4927 max_move_delay = MAX(max_move_delay, move_delay);
4928 max_step_delay = (max_step_delay == 0 ? step_delay :
4929 euclid(max_step_delay, step_delay));
4930 num_steps[nr] = move_steps;
4934 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4936 panel_has_doors[door_index] = TRUE;
4940 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4942 num_move_steps = max_move_delay / max_step_delay;
4943 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4945 door_delay_value = max_step_delay;
4947 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4949 start = num_move_steps - 1;
4953 /* opening door sound has priority over simultaneously closing door */
4954 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4956 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4958 if (door_state & DOOR_OPEN_1)
4959 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
4960 if (door_state & DOOR_OPEN_2)
4961 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
4963 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4965 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4967 if (door_state & DOOR_CLOSE_1)
4968 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
4969 if (door_state & DOOR_CLOSE_2)
4970 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
4974 for (k = start; k < num_move_steps; k++)
4976 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4978 door_part_done_all = TRUE;
4980 for (i = 0; i < NUM_DOORS; i++)
4981 door_panel_drawn[i] = FALSE;
4983 for (i = 0; i < MAX_DOOR_PARTS; i++)
4985 int nr = door_part_order[i].nr;
4986 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4987 struct DoorPartPosInfo *pos = dpc->pos;
4988 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4989 int door_token = dpc->door_token;
4990 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4991 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4992 boolean is_panel_and_door_has_closed = FALSE;
4993 struct Rect *door_rect = &door_rect_list[door_index];
4994 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4996 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4997 int current_door_state = door_state & door_token;
4998 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4999 boolean door_closing = !door_opening;
5000 boolean part_opening = (is_panel ? door_closing : door_opening);
5001 boolean part_closing = !part_opening;
5002 int start_step = (part_opening ? pos->start_step_opening :
5003 pos->start_step_closing);
5004 int step_delay = pos->step_delay;
5005 int step_factor = step_delay / max_step_delay;
5006 int k1 = (step_factor ? k / step_factor + 1 : k);
5007 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5008 int kk = MAX(0, k2);
5011 int src_x, src_y, src_xx, src_yy;
5012 int dst_x, dst_y, dst_xx, dst_yy;
5015 if (door_part_skip[nr])
5018 if (!(door_state & door_token))
5026 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5027 int kk_door = MAX(0, k2_door);
5028 int sync_frame = kk_door * door_delay_value;
5029 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5031 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5032 &g_src_x, &g_src_y);
5037 if (!door_panel_drawn[door_index])
5039 ClearRectangle(drawto, door_rect->x, door_rect->y,
5040 door_rect->width, door_rect->height);
5042 door_panel_drawn[door_index] = TRUE;
5045 // draw opening or closing door parts
5047 if (pos->step_xoffset < 0) // door part on right side
5050 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5053 if (dst_xx + width > door_rect->width)
5054 width = door_rect->width - dst_xx;
5056 else // door part on left side
5059 dst_xx = pos->x - kk * pos->step_xoffset;
5063 src_xx = ABS(dst_xx);
5067 width = g->width - src_xx;
5069 if (width > door_rect->width)
5070 width = door_rect->width;
5072 // printf("::: k == %d [%d] \n", k, start_step);
5075 if (pos->step_yoffset < 0) // door part on bottom side
5078 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5081 if (dst_yy + height > door_rect->height)
5082 height = door_rect->height - dst_yy;
5084 else // door part on top side
5087 dst_yy = pos->y - kk * pos->step_yoffset;
5091 src_yy = ABS(dst_yy);
5095 height = g->height - src_yy;
5098 src_x = g_src_x + src_xx;
5099 src_y = g_src_y + src_yy;
5101 dst_x = door_rect->x + dst_xx;
5102 dst_y = door_rect->y + dst_yy;
5104 is_panel_and_door_has_closed =
5107 panel_has_doors[door_index] &&
5108 k >= num_move_steps_doors_only - 1);
5110 if (width >= 0 && width <= g->width &&
5111 height >= 0 && height <= g->height &&
5112 !is_panel_and_door_has_closed)
5114 if (is_panel || !pos->draw_masked)
5115 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5118 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5122 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5124 if ((part_opening && (width < 0 || height < 0)) ||
5125 (part_closing && (width >= g->width && height >= g->height)))
5126 door_part_done[nr] = TRUE;
5128 // continue door part animations, but not panel after door has closed
5129 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5130 door_part_done_all = FALSE;
5133 if (!(door_state & DOOR_NO_DELAY))
5137 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5139 current_move_delay += max_step_delay;
5141 /* prevent OS (Windows) from complaining about program not responding */
5145 if (door_part_done_all)
5149 if (!(door_state & DOOR_NO_DELAY))
5151 /* wait for specified door action post delay */
5152 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5153 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5154 else if (door_state & DOOR_ACTION_1)
5155 door_delay_value = door_1.post_delay;
5156 else if (door_state & DOOR_ACTION_2)
5157 door_delay_value = door_2.post_delay;
5159 while (!DelayReached(&door_delay, door_delay_value))
5164 if (door_state & DOOR_ACTION_1)
5165 door1 = door_state & DOOR_ACTION_1;
5166 if (door_state & DOOR_ACTION_2)
5167 door2 = door_state & DOOR_ACTION_2;
5169 // draw masked border over door area
5170 DrawMaskedBorder(REDRAW_DOOR_1);
5171 DrawMaskedBorder(REDRAW_DOOR_2);
5173 return (door1 | door2);
5176 static boolean useSpecialEditorDoor()
5178 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5179 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5181 // do not draw special editor door if editor border defined or redefined
5182 if (graphic_info[graphic].bitmap != NULL || redefined)
5185 // do not draw special editor door if global border defined to be empty
5186 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5189 // do not draw special editor door if viewport definitions do not match
5193 EY + EYSIZE != VY + VYSIZE)
5199 void DrawSpecialEditorDoor()
5201 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5202 int top_border_width = gfx1->width;
5203 int top_border_height = gfx1->height;
5204 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5205 int ex = EX - outer_border;
5206 int ey = EY - outer_border;
5207 int vy = VY - outer_border;
5208 int exsize = EXSIZE + 2 * outer_border;
5210 if (!useSpecialEditorDoor())
5213 /* draw bigger level editor toolbox window */
5214 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5215 top_border_width, top_border_height, ex, ey - top_border_height);
5216 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5217 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5219 redraw_mask |= REDRAW_ALL;
5222 void UndrawSpecialEditorDoor()
5224 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5225 int top_border_width = gfx1->width;
5226 int top_border_height = gfx1->height;
5227 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5228 int ex = EX - outer_border;
5229 int ey = EY - outer_border;
5230 int ey_top = ey - top_border_height;
5231 int exsize = EXSIZE + 2 * outer_border;
5232 int eysize = EYSIZE + 2 * outer_border;
5234 if (!useSpecialEditorDoor())
5237 /* draw normal tape recorder window */
5238 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5240 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5241 ex, ey_top, top_border_width, top_border_height,
5243 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5244 ex, ey, exsize, eysize, ex, ey);
5248 // if screen background is set to "[NONE]", clear editor toolbox window
5249 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5250 ClearRectangle(drawto, ex, ey, exsize, eysize);
5253 redraw_mask |= REDRAW_ALL;
5257 /* ---------- new tool button stuff ---------------------------------------- */
5262 struct TextPosInfo *pos;
5265 } toolbutton_info[NUM_TOOL_BUTTONS] =
5268 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5269 TOOL_CTRL_ID_YES, "yes"
5272 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5273 TOOL_CTRL_ID_NO, "no"
5276 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5277 TOOL_CTRL_ID_CONFIRM, "confirm"
5280 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5281 TOOL_CTRL_ID_PLAYER_1, "player 1"
5284 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5285 TOOL_CTRL_ID_PLAYER_2, "player 2"
5288 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5289 TOOL_CTRL_ID_PLAYER_3, "player 3"
5292 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5293 TOOL_CTRL_ID_PLAYER_4, "player 4"
5297 void CreateToolButtons()
5301 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5303 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5304 struct TextPosInfo *pos = toolbutton_info[i].pos;
5305 struct GadgetInfo *gi;
5306 Bitmap *deco_bitmap = None;
5307 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5308 unsigned int event_mask = GD_EVENT_RELEASED;
5311 int gd_x = gfx->src_x;
5312 int gd_y = gfx->src_y;
5313 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5314 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5319 if (global.use_envelope_request)
5321 setRequestPosition(&dx, &dy, TRUE);
5323 // check if request buttons are outside of envelope and fix, if needed
5324 if (x < 0 || x + gfx->width > request.width ||
5325 y < 0 || y + gfx->height > request.height)
5327 // use left padding of "yes" button as default border padding
5328 int padding = toolbutton_info[TOOL_CTRL_ID_YES].pos->x;
5330 if (id == TOOL_CTRL_ID_YES)
5333 y = request.height - 2 * request.border_size - gfx->height - padding;
5335 else if (id == TOOL_CTRL_ID_NO)
5337 x = request.width - 2 * request.border_size - gfx->width - padding;
5338 y = request.height - 2 * request.border_size - gfx->height - padding;
5340 else if (id == TOOL_CTRL_ID_CONFIRM)
5342 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5343 y = request.height - 2 * request.border_size - gfx->height - padding;
5345 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5347 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5349 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5350 y = request.height - 2 * request.border_size - gfx->height - padding;
5352 x += (player_nr % 2 ? +1 : -1) * gfx->width / 2;
5353 y += (player_nr / 2 ? 0 : -1) * gfx->height;
5358 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5360 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5362 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5363 pos->size, &deco_bitmap, &deco_x, &deco_y);
5364 deco_xpos = (gfx->width - pos->size) / 2;
5365 deco_ypos = (gfx->height - pos->size) / 2;
5368 gi = CreateGadget(GDI_CUSTOM_ID, id,
5369 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5372 GDI_WIDTH, gfx->width,
5373 GDI_HEIGHT, gfx->height,
5374 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5375 GDI_STATE, GD_BUTTON_UNPRESSED,
5376 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5377 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5378 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5379 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5380 GDI_DECORATION_SIZE, pos->size, pos->size,
5381 GDI_DECORATION_SHIFTING, 1, 1,
5382 GDI_DIRECT_DRAW, FALSE,
5383 GDI_EVENT_MASK, event_mask,
5384 GDI_CALLBACK_ACTION, HandleToolButtons,
5388 Error(ERR_EXIT, "cannot create gadget");
5390 tool_gadget[id] = gi;
5394 void FreeToolButtons()
5398 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5399 FreeGadget(tool_gadget[i]);
5402 static void UnmapToolButtons()
5406 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5407 UnmapGadget(tool_gadget[i]);
5410 static void HandleToolButtons(struct GadgetInfo *gi)
5412 request_gadget_id = gi->custom_id;
5415 static struct Mapping_EM_to_RND_object
5418 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5419 boolean is_backside; /* backside of moving element */
5425 em_object_mapping_list[] =
5428 Xblank, TRUE, FALSE,
5432 Yacid_splash_eB, FALSE, FALSE,
5433 EL_ACID_SPLASH_RIGHT, -1, -1
5436 Yacid_splash_wB, FALSE, FALSE,
5437 EL_ACID_SPLASH_LEFT, -1, -1
5440 #ifdef EM_ENGINE_BAD_ROLL
5442 Xstone_force_e, FALSE, FALSE,
5443 EL_ROCK, -1, MV_BIT_RIGHT
5446 Xstone_force_w, FALSE, FALSE,
5447 EL_ROCK, -1, MV_BIT_LEFT
5450 Xnut_force_e, FALSE, FALSE,
5451 EL_NUT, -1, MV_BIT_RIGHT
5454 Xnut_force_w, FALSE, FALSE,
5455 EL_NUT, -1, MV_BIT_LEFT
5458 Xspring_force_e, FALSE, FALSE,
5459 EL_SPRING, -1, MV_BIT_RIGHT
5462 Xspring_force_w, FALSE, FALSE,
5463 EL_SPRING, -1, MV_BIT_LEFT
5466 Xemerald_force_e, FALSE, FALSE,
5467 EL_EMERALD, -1, MV_BIT_RIGHT
5470 Xemerald_force_w, FALSE, FALSE,
5471 EL_EMERALD, -1, MV_BIT_LEFT
5474 Xdiamond_force_e, FALSE, FALSE,
5475 EL_DIAMOND, -1, MV_BIT_RIGHT
5478 Xdiamond_force_w, FALSE, FALSE,
5479 EL_DIAMOND, -1, MV_BIT_LEFT
5482 Xbomb_force_e, FALSE, FALSE,
5483 EL_BOMB, -1, MV_BIT_RIGHT
5486 Xbomb_force_w, FALSE, FALSE,
5487 EL_BOMB, -1, MV_BIT_LEFT
5489 #endif /* EM_ENGINE_BAD_ROLL */
5492 Xstone, TRUE, FALSE,
5496 Xstone_pause, FALSE, FALSE,
5500 Xstone_fall, FALSE, FALSE,
5504 Ystone_s, FALSE, FALSE,
5505 EL_ROCK, ACTION_FALLING, -1
5508 Ystone_sB, FALSE, TRUE,
5509 EL_ROCK, ACTION_FALLING, -1
5512 Ystone_e, FALSE, FALSE,
5513 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5516 Ystone_eB, FALSE, TRUE,
5517 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5520 Ystone_w, FALSE, FALSE,
5521 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5524 Ystone_wB, FALSE, TRUE,
5525 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5532 Xnut_pause, FALSE, FALSE,
5536 Xnut_fall, FALSE, FALSE,
5540 Ynut_s, FALSE, FALSE,
5541 EL_NUT, ACTION_FALLING, -1
5544 Ynut_sB, FALSE, TRUE,
5545 EL_NUT, ACTION_FALLING, -1
5548 Ynut_e, FALSE, FALSE,
5549 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5552 Ynut_eB, FALSE, TRUE,
5553 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5556 Ynut_w, FALSE, FALSE,
5557 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5560 Ynut_wB, FALSE, TRUE,
5561 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5564 Xbug_n, TRUE, FALSE,
5568 Xbug_e, TRUE, FALSE,
5569 EL_BUG_RIGHT, -1, -1
5572 Xbug_s, TRUE, FALSE,
5576 Xbug_w, TRUE, FALSE,
5580 Xbug_gon, FALSE, FALSE,
5584 Xbug_goe, FALSE, FALSE,
5585 EL_BUG_RIGHT, -1, -1
5588 Xbug_gos, FALSE, FALSE,
5592 Xbug_gow, FALSE, FALSE,
5596 Ybug_n, FALSE, FALSE,
5597 EL_BUG, ACTION_MOVING, MV_BIT_UP
5600 Ybug_nB, FALSE, TRUE,
5601 EL_BUG, ACTION_MOVING, MV_BIT_UP
5604 Ybug_e, FALSE, FALSE,
5605 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5608 Ybug_eB, FALSE, TRUE,
5609 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5612 Ybug_s, FALSE, FALSE,
5613 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5616 Ybug_sB, FALSE, TRUE,
5617 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5620 Ybug_w, FALSE, FALSE,
5621 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5624 Ybug_wB, FALSE, TRUE,
5625 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5628 Ybug_w_n, FALSE, FALSE,
5629 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5632 Ybug_n_e, FALSE, FALSE,
5633 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5636 Ybug_e_s, FALSE, FALSE,
5637 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5640 Ybug_s_w, FALSE, FALSE,
5641 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5644 Ybug_e_n, FALSE, FALSE,
5645 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5648 Ybug_s_e, FALSE, FALSE,
5649 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5652 Ybug_w_s, FALSE, FALSE,
5653 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5656 Ybug_n_w, FALSE, FALSE,
5657 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5660 Ybug_stone, FALSE, FALSE,
5661 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5664 Ybug_spring, FALSE, FALSE,
5665 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5668 Xtank_n, TRUE, FALSE,
5669 EL_SPACESHIP_UP, -1, -1
5672 Xtank_e, TRUE, FALSE,
5673 EL_SPACESHIP_RIGHT, -1, -1
5676 Xtank_s, TRUE, FALSE,
5677 EL_SPACESHIP_DOWN, -1, -1
5680 Xtank_w, TRUE, FALSE,
5681 EL_SPACESHIP_LEFT, -1, -1
5684 Xtank_gon, FALSE, FALSE,
5685 EL_SPACESHIP_UP, -1, -1
5688 Xtank_goe, FALSE, FALSE,
5689 EL_SPACESHIP_RIGHT, -1, -1
5692 Xtank_gos, FALSE, FALSE,
5693 EL_SPACESHIP_DOWN, -1, -1
5696 Xtank_gow, FALSE, FALSE,
5697 EL_SPACESHIP_LEFT, -1, -1
5700 Ytank_n, FALSE, FALSE,
5701 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5704 Ytank_nB, FALSE, TRUE,
5705 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5708 Ytank_e, FALSE, FALSE,
5709 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5712 Ytank_eB, FALSE, TRUE,
5713 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5716 Ytank_s, FALSE, FALSE,
5717 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5720 Ytank_sB, FALSE, TRUE,
5721 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5724 Ytank_w, FALSE, FALSE,
5725 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5728 Ytank_wB, FALSE, TRUE,
5729 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5732 Ytank_w_n, FALSE, FALSE,
5733 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5736 Ytank_n_e, FALSE, FALSE,
5737 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5740 Ytank_e_s, FALSE, FALSE,
5741 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5744 Ytank_s_w, FALSE, FALSE,
5745 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5748 Ytank_e_n, FALSE, FALSE,
5749 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5752 Ytank_s_e, FALSE, FALSE,
5753 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5756 Ytank_w_s, FALSE, FALSE,
5757 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5760 Ytank_n_w, FALSE, FALSE,
5761 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5764 Ytank_stone, FALSE, FALSE,
5765 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5768 Ytank_spring, FALSE, FALSE,
5769 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5772 Xandroid, TRUE, FALSE,
5773 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5776 Xandroid_1_n, FALSE, FALSE,
5777 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5780 Xandroid_2_n, FALSE, FALSE,
5781 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5784 Xandroid_1_e, FALSE, FALSE,
5785 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5788 Xandroid_2_e, FALSE, FALSE,
5789 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5792 Xandroid_1_w, FALSE, FALSE,
5793 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5796 Xandroid_2_w, FALSE, FALSE,
5797 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5800 Xandroid_1_s, FALSE, FALSE,
5801 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5804 Xandroid_2_s, FALSE, FALSE,
5805 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5808 Yandroid_n, FALSE, FALSE,
5809 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5812 Yandroid_nB, FALSE, TRUE,
5813 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5816 Yandroid_ne, FALSE, FALSE,
5817 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5820 Yandroid_neB, FALSE, TRUE,
5821 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5824 Yandroid_e, FALSE, FALSE,
5825 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5828 Yandroid_eB, FALSE, TRUE,
5829 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5832 Yandroid_se, FALSE, FALSE,
5833 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5836 Yandroid_seB, FALSE, TRUE,
5837 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5840 Yandroid_s, FALSE, FALSE,
5841 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5844 Yandroid_sB, FALSE, TRUE,
5845 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5848 Yandroid_sw, FALSE, FALSE,
5849 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5852 Yandroid_swB, FALSE, TRUE,
5853 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5856 Yandroid_w, FALSE, FALSE,
5857 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5860 Yandroid_wB, FALSE, TRUE,
5861 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5864 Yandroid_nw, FALSE, FALSE,
5865 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5868 Yandroid_nwB, FALSE, TRUE,
5869 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5872 Xspring, TRUE, FALSE,
5876 Xspring_pause, FALSE, FALSE,
5880 Xspring_e, FALSE, FALSE,
5884 Xspring_w, FALSE, FALSE,
5888 Xspring_fall, FALSE, FALSE,
5892 Yspring_s, FALSE, FALSE,
5893 EL_SPRING, ACTION_FALLING, -1
5896 Yspring_sB, FALSE, TRUE,
5897 EL_SPRING, ACTION_FALLING, -1
5900 Yspring_e, FALSE, FALSE,
5901 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5904 Yspring_eB, FALSE, TRUE,
5905 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5908 Yspring_w, FALSE, FALSE,
5909 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5912 Yspring_wB, FALSE, TRUE,
5913 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5916 Yspring_kill_e, FALSE, FALSE,
5917 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5920 Yspring_kill_eB, FALSE, TRUE,
5921 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5924 Yspring_kill_w, FALSE, FALSE,
5925 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5928 Yspring_kill_wB, FALSE, TRUE,
5929 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5932 Xeater_n, TRUE, FALSE,
5933 EL_YAMYAM_UP, -1, -1
5936 Xeater_e, TRUE, FALSE,
5937 EL_YAMYAM_RIGHT, -1, -1
5940 Xeater_w, TRUE, FALSE,
5941 EL_YAMYAM_LEFT, -1, -1
5944 Xeater_s, TRUE, FALSE,
5945 EL_YAMYAM_DOWN, -1, -1
5948 Yeater_n, FALSE, FALSE,
5949 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5952 Yeater_nB, FALSE, TRUE,
5953 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5956 Yeater_e, FALSE, FALSE,
5957 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5960 Yeater_eB, FALSE, TRUE,
5961 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5964 Yeater_s, FALSE, FALSE,
5965 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5968 Yeater_sB, FALSE, TRUE,
5969 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5972 Yeater_w, FALSE, FALSE,
5973 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5976 Yeater_wB, FALSE, TRUE,
5977 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5980 Yeater_stone, FALSE, FALSE,
5981 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5984 Yeater_spring, FALSE, FALSE,
5985 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5988 Xalien, TRUE, FALSE,
5992 Xalien_pause, FALSE, FALSE,
5996 Yalien_n, FALSE, FALSE,
5997 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6000 Yalien_nB, FALSE, TRUE,
6001 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6004 Yalien_e, FALSE, FALSE,
6005 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6008 Yalien_eB, FALSE, TRUE,
6009 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6012 Yalien_s, FALSE, FALSE,
6013 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6016 Yalien_sB, FALSE, TRUE,
6017 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6020 Yalien_w, FALSE, FALSE,
6021 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6024 Yalien_wB, FALSE, TRUE,
6025 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6028 Yalien_stone, FALSE, FALSE,
6029 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6032 Yalien_spring, FALSE, FALSE,
6033 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6036 Xemerald, TRUE, FALSE,
6040 Xemerald_pause, FALSE, FALSE,
6044 Xemerald_fall, FALSE, FALSE,
6048 Xemerald_shine, FALSE, FALSE,
6049 EL_EMERALD, ACTION_TWINKLING, -1
6052 Yemerald_s, FALSE, FALSE,
6053 EL_EMERALD, ACTION_FALLING, -1
6056 Yemerald_sB, FALSE, TRUE,
6057 EL_EMERALD, ACTION_FALLING, -1
6060 Yemerald_e, FALSE, FALSE,
6061 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6064 Yemerald_eB, FALSE, TRUE,
6065 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6068 Yemerald_w, FALSE, FALSE,
6069 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6072 Yemerald_wB, FALSE, TRUE,
6073 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6076 Yemerald_eat, FALSE, FALSE,
6077 EL_EMERALD, ACTION_COLLECTING, -1
6080 Yemerald_stone, FALSE, FALSE,
6081 EL_NUT, ACTION_BREAKING, -1
6084 Xdiamond, TRUE, FALSE,
6088 Xdiamond_pause, FALSE, FALSE,
6092 Xdiamond_fall, FALSE, FALSE,
6096 Xdiamond_shine, FALSE, FALSE,
6097 EL_DIAMOND, ACTION_TWINKLING, -1
6100 Ydiamond_s, FALSE, FALSE,
6101 EL_DIAMOND, ACTION_FALLING, -1
6104 Ydiamond_sB, FALSE, TRUE,
6105 EL_DIAMOND, ACTION_FALLING, -1
6108 Ydiamond_e, FALSE, FALSE,
6109 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6112 Ydiamond_eB, FALSE, TRUE,
6113 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6116 Ydiamond_w, FALSE, FALSE,
6117 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6120 Ydiamond_wB, FALSE, TRUE,
6121 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6124 Ydiamond_eat, FALSE, FALSE,
6125 EL_DIAMOND, ACTION_COLLECTING, -1
6128 Ydiamond_stone, FALSE, FALSE,
6129 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6132 Xdrip_fall, TRUE, FALSE,
6133 EL_AMOEBA_DROP, -1, -1
6136 Xdrip_stretch, FALSE, FALSE,
6137 EL_AMOEBA_DROP, ACTION_FALLING, -1
6140 Xdrip_stretchB, FALSE, TRUE,
6141 EL_AMOEBA_DROP, ACTION_FALLING, -1
6144 Xdrip_eat, FALSE, FALSE,
6145 EL_AMOEBA_DROP, ACTION_GROWING, -1
6148 Ydrip_s1, FALSE, FALSE,
6149 EL_AMOEBA_DROP, ACTION_FALLING, -1
6152 Ydrip_s1B, FALSE, TRUE,
6153 EL_AMOEBA_DROP, ACTION_FALLING, -1
6156 Ydrip_s2, FALSE, FALSE,
6157 EL_AMOEBA_DROP, ACTION_FALLING, -1
6160 Ydrip_s2B, FALSE, TRUE,
6161 EL_AMOEBA_DROP, ACTION_FALLING, -1
6168 Xbomb_pause, FALSE, FALSE,
6172 Xbomb_fall, FALSE, FALSE,
6176 Ybomb_s, FALSE, FALSE,
6177 EL_BOMB, ACTION_FALLING, -1
6180 Ybomb_sB, FALSE, TRUE,
6181 EL_BOMB, ACTION_FALLING, -1
6184 Ybomb_e, FALSE, FALSE,
6185 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6188 Ybomb_eB, FALSE, TRUE,
6189 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6192 Ybomb_w, FALSE, FALSE,
6193 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6196 Ybomb_wB, FALSE, TRUE,
6197 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6200 Ybomb_eat, FALSE, FALSE,
6201 EL_BOMB, ACTION_ACTIVATING, -1
6204 Xballoon, TRUE, FALSE,
6208 Yballoon_n, FALSE, FALSE,
6209 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6212 Yballoon_nB, FALSE, TRUE,
6213 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6216 Yballoon_e, FALSE, FALSE,
6217 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6220 Yballoon_eB, FALSE, TRUE,
6221 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6224 Yballoon_s, FALSE, FALSE,
6225 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6228 Yballoon_sB, FALSE, TRUE,
6229 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6232 Yballoon_w, FALSE, FALSE,
6233 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6236 Yballoon_wB, FALSE, TRUE,
6237 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6240 Xgrass, TRUE, FALSE,
6241 EL_EMC_GRASS, -1, -1
6244 Ygrass_nB, FALSE, FALSE,
6245 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6248 Ygrass_eB, FALSE, FALSE,
6249 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6252 Ygrass_sB, FALSE, FALSE,
6253 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6256 Ygrass_wB, FALSE, FALSE,
6257 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6264 Ydirt_nB, FALSE, FALSE,
6265 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6268 Ydirt_eB, FALSE, FALSE,
6269 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6272 Ydirt_sB, FALSE, FALSE,
6273 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6276 Ydirt_wB, FALSE, FALSE,
6277 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6280 Xacid_ne, TRUE, FALSE,
6281 EL_ACID_POOL_TOPRIGHT, -1, -1
6284 Xacid_se, TRUE, FALSE,
6285 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6288 Xacid_s, TRUE, FALSE,
6289 EL_ACID_POOL_BOTTOM, -1, -1
6292 Xacid_sw, TRUE, FALSE,
6293 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6296 Xacid_nw, TRUE, FALSE,
6297 EL_ACID_POOL_TOPLEFT, -1, -1
6300 Xacid_1, TRUE, FALSE,
6304 Xacid_2, FALSE, FALSE,
6308 Xacid_3, FALSE, FALSE,
6312 Xacid_4, FALSE, FALSE,
6316 Xacid_5, FALSE, FALSE,
6320 Xacid_6, FALSE, FALSE,
6324 Xacid_7, FALSE, FALSE,
6328 Xacid_8, FALSE, FALSE,
6332 Xball_1, TRUE, FALSE,
6333 EL_EMC_MAGIC_BALL, -1, -1
6336 Xball_1B, FALSE, FALSE,
6337 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6340 Xball_2, FALSE, FALSE,
6341 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6344 Xball_2B, FALSE, FALSE,
6345 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6348 Yball_eat, FALSE, FALSE,
6349 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6352 Ykey_1_eat, FALSE, FALSE,
6353 EL_EM_KEY_1, ACTION_COLLECTING, -1
6356 Ykey_2_eat, FALSE, FALSE,
6357 EL_EM_KEY_2, ACTION_COLLECTING, -1
6360 Ykey_3_eat, FALSE, FALSE,
6361 EL_EM_KEY_3, ACTION_COLLECTING, -1
6364 Ykey_4_eat, FALSE, FALSE,
6365 EL_EM_KEY_4, ACTION_COLLECTING, -1
6368 Ykey_5_eat, FALSE, FALSE,
6369 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6372 Ykey_6_eat, FALSE, FALSE,
6373 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6376 Ykey_7_eat, FALSE, FALSE,
6377 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6380 Ykey_8_eat, FALSE, FALSE,
6381 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6384 Ylenses_eat, FALSE, FALSE,
6385 EL_EMC_LENSES, ACTION_COLLECTING, -1
6388 Ymagnify_eat, FALSE, FALSE,
6389 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6392 Ygrass_eat, FALSE, FALSE,
6393 EL_EMC_GRASS, ACTION_SNAPPING, -1
6396 Ydirt_eat, FALSE, FALSE,
6397 EL_SAND, ACTION_SNAPPING, -1
6400 Xgrow_ns, TRUE, FALSE,
6401 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6404 Ygrow_ns_eat, FALSE, FALSE,
6405 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6408 Xgrow_ew, TRUE, FALSE,
6409 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6412 Ygrow_ew_eat, FALSE, FALSE,
6413 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6416 Xwonderwall, TRUE, FALSE,
6417 EL_MAGIC_WALL, -1, -1
6420 XwonderwallB, FALSE, FALSE,
6421 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6424 Xamoeba_1, TRUE, FALSE,
6425 EL_AMOEBA_DRY, ACTION_OTHER, -1
6428 Xamoeba_2, FALSE, FALSE,
6429 EL_AMOEBA_DRY, ACTION_OTHER, -1
6432 Xamoeba_3, FALSE, FALSE,
6433 EL_AMOEBA_DRY, ACTION_OTHER, -1
6436 Xamoeba_4, FALSE, FALSE,
6437 EL_AMOEBA_DRY, ACTION_OTHER, -1
6440 Xamoeba_5, TRUE, FALSE,
6441 EL_AMOEBA_WET, ACTION_OTHER, -1
6444 Xamoeba_6, FALSE, FALSE,
6445 EL_AMOEBA_WET, ACTION_OTHER, -1
6448 Xamoeba_7, FALSE, FALSE,
6449 EL_AMOEBA_WET, ACTION_OTHER, -1
6452 Xamoeba_8, FALSE, FALSE,
6453 EL_AMOEBA_WET, ACTION_OTHER, -1
6456 Xdoor_1, TRUE, FALSE,
6457 EL_EM_GATE_1, -1, -1
6460 Xdoor_2, TRUE, FALSE,
6461 EL_EM_GATE_2, -1, -1
6464 Xdoor_3, TRUE, FALSE,
6465 EL_EM_GATE_3, -1, -1
6468 Xdoor_4, TRUE, FALSE,
6469 EL_EM_GATE_4, -1, -1
6472 Xdoor_5, TRUE, FALSE,
6473 EL_EMC_GATE_5, -1, -1
6476 Xdoor_6, TRUE, FALSE,
6477 EL_EMC_GATE_6, -1, -1
6480 Xdoor_7, TRUE, FALSE,
6481 EL_EMC_GATE_7, -1, -1
6484 Xdoor_8, TRUE, FALSE,
6485 EL_EMC_GATE_8, -1, -1
6488 Xkey_1, TRUE, FALSE,
6492 Xkey_2, TRUE, FALSE,
6496 Xkey_3, TRUE, FALSE,
6500 Xkey_4, TRUE, FALSE,
6504 Xkey_5, TRUE, FALSE,
6505 EL_EMC_KEY_5, -1, -1
6508 Xkey_6, TRUE, FALSE,
6509 EL_EMC_KEY_6, -1, -1
6512 Xkey_7, TRUE, FALSE,
6513 EL_EMC_KEY_7, -1, -1
6516 Xkey_8, TRUE, FALSE,
6517 EL_EMC_KEY_8, -1, -1
6520 Xwind_n, TRUE, FALSE,
6521 EL_BALLOON_SWITCH_UP, -1, -1
6524 Xwind_e, TRUE, FALSE,
6525 EL_BALLOON_SWITCH_RIGHT, -1, -1
6528 Xwind_s, TRUE, FALSE,
6529 EL_BALLOON_SWITCH_DOWN, -1, -1
6532 Xwind_w, TRUE, FALSE,
6533 EL_BALLOON_SWITCH_LEFT, -1, -1
6536 Xwind_nesw, TRUE, FALSE,
6537 EL_BALLOON_SWITCH_ANY, -1, -1
6540 Xwind_stop, TRUE, FALSE,
6541 EL_BALLOON_SWITCH_NONE, -1, -1
6545 EL_EM_EXIT_CLOSED, -1, -1
6548 Xexit_1, TRUE, FALSE,
6549 EL_EM_EXIT_OPEN, -1, -1
6552 Xexit_2, FALSE, FALSE,
6553 EL_EM_EXIT_OPEN, -1, -1
6556 Xexit_3, FALSE, FALSE,
6557 EL_EM_EXIT_OPEN, -1, -1
6560 Xdynamite, TRUE, FALSE,
6561 EL_EM_DYNAMITE, -1, -1
6564 Ydynamite_eat, FALSE, FALSE,
6565 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6568 Xdynamite_1, TRUE, FALSE,
6569 EL_EM_DYNAMITE_ACTIVE, -1, -1
6572 Xdynamite_2, FALSE, FALSE,
6573 EL_EM_DYNAMITE_ACTIVE, -1, -1
6576 Xdynamite_3, FALSE, FALSE,
6577 EL_EM_DYNAMITE_ACTIVE, -1, -1
6580 Xdynamite_4, FALSE, FALSE,
6581 EL_EM_DYNAMITE_ACTIVE, -1, -1
6584 Xbumper, TRUE, FALSE,
6585 EL_EMC_SPRING_BUMPER, -1, -1
6588 XbumperB, FALSE, FALSE,
6589 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6592 Xwheel, TRUE, FALSE,
6593 EL_ROBOT_WHEEL, -1, -1
6596 XwheelB, FALSE, FALSE,
6597 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6600 Xswitch, TRUE, FALSE,
6601 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6604 XswitchB, FALSE, FALSE,
6605 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6609 EL_QUICKSAND_EMPTY, -1, -1
6612 Xsand_stone, TRUE, FALSE,
6613 EL_QUICKSAND_FULL, -1, -1
6616 Xsand_stonein_1, FALSE, TRUE,
6617 EL_ROCK, ACTION_FILLING, -1
6620 Xsand_stonein_2, FALSE, TRUE,
6621 EL_ROCK, ACTION_FILLING, -1
6624 Xsand_stonein_3, FALSE, TRUE,
6625 EL_ROCK, ACTION_FILLING, -1
6628 Xsand_stonein_4, FALSE, TRUE,
6629 EL_ROCK, ACTION_FILLING, -1
6632 Xsand_stonesand_1, FALSE, FALSE,
6633 EL_QUICKSAND_EMPTYING, -1, -1
6636 Xsand_stonesand_2, FALSE, FALSE,
6637 EL_QUICKSAND_EMPTYING, -1, -1
6640 Xsand_stonesand_3, FALSE, FALSE,
6641 EL_QUICKSAND_EMPTYING, -1, -1
6644 Xsand_stonesand_4, FALSE, FALSE,
6645 EL_QUICKSAND_EMPTYING, -1, -1
6648 Xsand_stonesand_quickout_1, FALSE, FALSE,
6649 EL_QUICKSAND_EMPTYING, -1, -1
6652 Xsand_stonesand_quickout_2, FALSE, FALSE,
6653 EL_QUICKSAND_EMPTYING, -1, -1
6656 Xsand_stoneout_1, FALSE, FALSE,
6657 EL_ROCK, ACTION_EMPTYING, -1
6660 Xsand_stoneout_2, FALSE, FALSE,
6661 EL_ROCK, ACTION_EMPTYING, -1
6664 Xsand_sandstone_1, FALSE, FALSE,
6665 EL_QUICKSAND_FILLING, -1, -1
6668 Xsand_sandstone_2, FALSE, FALSE,
6669 EL_QUICKSAND_FILLING, -1, -1
6672 Xsand_sandstone_3, FALSE, FALSE,
6673 EL_QUICKSAND_FILLING, -1, -1
6676 Xsand_sandstone_4, FALSE, FALSE,
6677 EL_QUICKSAND_FILLING, -1, -1
6680 Xplant, TRUE, FALSE,
6681 EL_EMC_PLANT, -1, -1
6684 Yplant, FALSE, FALSE,
6685 EL_EMC_PLANT, -1, -1
6688 Xlenses, TRUE, FALSE,
6689 EL_EMC_LENSES, -1, -1
6692 Xmagnify, TRUE, FALSE,
6693 EL_EMC_MAGNIFIER, -1, -1
6696 Xdripper, TRUE, FALSE,
6697 EL_EMC_DRIPPER, -1, -1
6700 XdripperB, FALSE, FALSE,
6701 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6704 Xfake_blank, TRUE, FALSE,
6705 EL_INVISIBLE_WALL, -1, -1
6708 Xfake_blankB, FALSE, FALSE,
6709 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6712 Xfake_grass, TRUE, FALSE,
6713 EL_EMC_FAKE_GRASS, -1, -1
6716 Xfake_grassB, FALSE, FALSE,
6717 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6720 Xfake_door_1, TRUE, FALSE,
6721 EL_EM_GATE_1_GRAY, -1, -1
6724 Xfake_door_2, TRUE, FALSE,
6725 EL_EM_GATE_2_GRAY, -1, -1
6728 Xfake_door_3, TRUE, FALSE,
6729 EL_EM_GATE_3_GRAY, -1, -1
6732 Xfake_door_4, TRUE, FALSE,
6733 EL_EM_GATE_4_GRAY, -1, -1
6736 Xfake_door_5, TRUE, FALSE,
6737 EL_EMC_GATE_5_GRAY, -1, -1
6740 Xfake_door_6, TRUE, FALSE,
6741 EL_EMC_GATE_6_GRAY, -1, -1
6744 Xfake_door_7, TRUE, FALSE,
6745 EL_EMC_GATE_7_GRAY, -1, -1
6748 Xfake_door_8, TRUE, FALSE,
6749 EL_EMC_GATE_8_GRAY, -1, -1
6752 Xfake_acid_1, TRUE, FALSE,
6753 EL_EMC_FAKE_ACID, -1, -1
6756 Xfake_acid_2, FALSE, FALSE,
6757 EL_EMC_FAKE_ACID, -1, -1
6760 Xfake_acid_3, FALSE, FALSE,
6761 EL_EMC_FAKE_ACID, -1, -1
6764 Xfake_acid_4, FALSE, FALSE,
6765 EL_EMC_FAKE_ACID, -1, -1
6768 Xfake_acid_5, FALSE, FALSE,
6769 EL_EMC_FAKE_ACID, -1, -1
6772 Xfake_acid_6, FALSE, FALSE,
6773 EL_EMC_FAKE_ACID, -1, -1
6776 Xfake_acid_7, FALSE, FALSE,
6777 EL_EMC_FAKE_ACID, -1, -1
6780 Xfake_acid_8, FALSE, FALSE,
6781 EL_EMC_FAKE_ACID, -1, -1
6784 Xsteel_1, TRUE, FALSE,
6785 EL_STEELWALL, -1, -1
6788 Xsteel_2, TRUE, FALSE,
6789 EL_EMC_STEELWALL_2, -1, -1
6792 Xsteel_3, TRUE, FALSE,
6793 EL_EMC_STEELWALL_3, -1, -1
6796 Xsteel_4, TRUE, FALSE,
6797 EL_EMC_STEELWALL_4, -1, -1
6800 Xwall_1, TRUE, FALSE,
6804 Xwall_2, TRUE, FALSE,
6805 EL_EMC_WALL_14, -1, -1
6808 Xwall_3, TRUE, FALSE,
6809 EL_EMC_WALL_15, -1, -1
6812 Xwall_4, TRUE, FALSE,
6813 EL_EMC_WALL_16, -1, -1
6816 Xround_wall_1, TRUE, FALSE,
6817 EL_WALL_SLIPPERY, -1, -1
6820 Xround_wall_2, TRUE, FALSE,
6821 EL_EMC_WALL_SLIPPERY_2, -1, -1
6824 Xround_wall_3, TRUE, FALSE,
6825 EL_EMC_WALL_SLIPPERY_3, -1, -1
6828 Xround_wall_4, TRUE, FALSE,
6829 EL_EMC_WALL_SLIPPERY_4, -1, -1
6832 Xdecor_1, TRUE, FALSE,
6833 EL_EMC_WALL_8, -1, -1
6836 Xdecor_2, TRUE, FALSE,
6837 EL_EMC_WALL_6, -1, -1
6840 Xdecor_3, TRUE, FALSE,
6841 EL_EMC_WALL_4, -1, -1
6844 Xdecor_4, TRUE, FALSE,
6845 EL_EMC_WALL_7, -1, -1
6848 Xdecor_5, TRUE, FALSE,
6849 EL_EMC_WALL_5, -1, -1
6852 Xdecor_6, TRUE, FALSE,
6853 EL_EMC_WALL_9, -1, -1
6856 Xdecor_7, TRUE, FALSE,
6857 EL_EMC_WALL_10, -1, -1
6860 Xdecor_8, TRUE, FALSE,
6861 EL_EMC_WALL_1, -1, -1
6864 Xdecor_9, TRUE, FALSE,
6865 EL_EMC_WALL_2, -1, -1
6868 Xdecor_10, TRUE, FALSE,
6869 EL_EMC_WALL_3, -1, -1
6872 Xdecor_11, TRUE, FALSE,
6873 EL_EMC_WALL_11, -1, -1
6876 Xdecor_12, TRUE, FALSE,
6877 EL_EMC_WALL_12, -1, -1
6880 Xalpha_0, TRUE, FALSE,
6881 EL_CHAR('0'), -1, -1
6884 Xalpha_1, TRUE, FALSE,
6885 EL_CHAR('1'), -1, -1
6888 Xalpha_2, TRUE, FALSE,
6889 EL_CHAR('2'), -1, -1
6892 Xalpha_3, TRUE, FALSE,
6893 EL_CHAR('3'), -1, -1
6896 Xalpha_4, TRUE, FALSE,
6897 EL_CHAR('4'), -1, -1
6900 Xalpha_5, TRUE, FALSE,
6901 EL_CHAR('5'), -1, -1
6904 Xalpha_6, TRUE, FALSE,
6905 EL_CHAR('6'), -1, -1
6908 Xalpha_7, TRUE, FALSE,
6909 EL_CHAR('7'), -1, -1
6912 Xalpha_8, TRUE, FALSE,
6913 EL_CHAR('8'), -1, -1
6916 Xalpha_9, TRUE, FALSE,
6917 EL_CHAR('9'), -1, -1
6920 Xalpha_excla, TRUE, FALSE,
6921 EL_CHAR('!'), -1, -1
6924 Xalpha_quote, TRUE, FALSE,
6925 EL_CHAR('"'), -1, -1
6928 Xalpha_comma, TRUE, FALSE,
6929 EL_CHAR(','), -1, -1
6932 Xalpha_minus, TRUE, FALSE,
6933 EL_CHAR('-'), -1, -1
6936 Xalpha_perio, TRUE, FALSE,
6937 EL_CHAR('.'), -1, -1
6940 Xalpha_colon, TRUE, FALSE,
6941 EL_CHAR(':'), -1, -1
6944 Xalpha_quest, TRUE, FALSE,
6945 EL_CHAR('?'), -1, -1
6948 Xalpha_a, TRUE, FALSE,
6949 EL_CHAR('A'), -1, -1
6952 Xalpha_b, TRUE, FALSE,
6953 EL_CHAR('B'), -1, -1
6956 Xalpha_c, TRUE, FALSE,
6957 EL_CHAR('C'), -1, -1
6960 Xalpha_d, TRUE, FALSE,
6961 EL_CHAR('D'), -1, -1
6964 Xalpha_e, TRUE, FALSE,
6965 EL_CHAR('E'), -1, -1
6968 Xalpha_f, TRUE, FALSE,
6969 EL_CHAR('F'), -1, -1
6972 Xalpha_g, TRUE, FALSE,
6973 EL_CHAR('G'), -1, -1
6976 Xalpha_h, TRUE, FALSE,
6977 EL_CHAR('H'), -1, -1
6980 Xalpha_i, TRUE, FALSE,
6981 EL_CHAR('I'), -1, -1
6984 Xalpha_j, TRUE, FALSE,
6985 EL_CHAR('J'), -1, -1
6988 Xalpha_k, TRUE, FALSE,
6989 EL_CHAR('K'), -1, -1
6992 Xalpha_l, TRUE, FALSE,
6993 EL_CHAR('L'), -1, -1
6996 Xalpha_m, TRUE, FALSE,
6997 EL_CHAR('M'), -1, -1
7000 Xalpha_n, TRUE, FALSE,
7001 EL_CHAR('N'), -1, -1
7004 Xalpha_o, TRUE, FALSE,
7005 EL_CHAR('O'), -1, -1
7008 Xalpha_p, TRUE, FALSE,
7009 EL_CHAR('P'), -1, -1
7012 Xalpha_q, TRUE, FALSE,
7013 EL_CHAR('Q'), -1, -1
7016 Xalpha_r, TRUE, FALSE,
7017 EL_CHAR('R'), -1, -1
7020 Xalpha_s, TRUE, FALSE,
7021 EL_CHAR('S'), -1, -1
7024 Xalpha_t, TRUE, FALSE,
7025 EL_CHAR('T'), -1, -1
7028 Xalpha_u, TRUE, FALSE,
7029 EL_CHAR('U'), -1, -1
7032 Xalpha_v, TRUE, FALSE,
7033 EL_CHAR('V'), -1, -1
7036 Xalpha_w, TRUE, FALSE,
7037 EL_CHAR('W'), -1, -1
7040 Xalpha_x, TRUE, FALSE,
7041 EL_CHAR('X'), -1, -1
7044 Xalpha_y, TRUE, FALSE,
7045 EL_CHAR('Y'), -1, -1
7048 Xalpha_z, TRUE, FALSE,
7049 EL_CHAR('Z'), -1, -1
7052 Xalpha_arrow_e, TRUE, FALSE,
7053 EL_CHAR('>'), -1, -1
7056 Xalpha_arrow_w, TRUE, FALSE,
7057 EL_CHAR('<'), -1, -1
7060 Xalpha_copyr, TRUE, FALSE,
7061 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7065 Xboom_bug, FALSE, FALSE,
7066 EL_BUG, ACTION_EXPLODING, -1
7069 Xboom_bomb, FALSE, FALSE,
7070 EL_BOMB, ACTION_EXPLODING, -1
7073 Xboom_android, FALSE, FALSE,
7074 EL_EMC_ANDROID, ACTION_OTHER, -1
7077 Xboom_1, FALSE, FALSE,
7078 EL_DEFAULT, ACTION_EXPLODING, -1
7081 Xboom_2, FALSE, FALSE,
7082 EL_DEFAULT, ACTION_EXPLODING, -1
7085 Znormal, FALSE, FALSE,
7089 Zdynamite, FALSE, FALSE,
7093 Zplayer, FALSE, FALSE,
7097 ZBORDER, FALSE, FALSE,
7107 static struct Mapping_EM_to_RND_player
7116 em_player_mapping_list[] =
7120 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7124 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7128 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7132 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7136 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7140 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7144 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7148 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7152 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7156 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7160 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7164 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7168 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7172 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7176 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7180 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7184 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7188 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7192 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7196 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7200 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7204 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7208 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7212 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7216 EL_PLAYER_1, ACTION_DEFAULT, -1,
7220 EL_PLAYER_2, ACTION_DEFAULT, -1,
7224 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7228 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7232 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7236 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7240 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7244 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7248 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7252 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7256 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7260 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7264 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7268 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7272 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7276 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7280 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7284 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7288 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7292 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7296 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7300 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7304 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7308 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7312 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7316 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7320 EL_PLAYER_3, ACTION_DEFAULT, -1,
7324 EL_PLAYER_4, ACTION_DEFAULT, -1,
7333 int map_element_RND_to_EM(int element_rnd)
7335 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7336 static boolean mapping_initialized = FALSE;
7338 if (!mapping_initialized)
7342 /* return "Xalpha_quest" for all undefined elements in mapping array */
7343 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7344 mapping_RND_to_EM[i] = Xalpha_quest;
7346 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7347 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7348 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7349 em_object_mapping_list[i].element_em;
7351 mapping_initialized = TRUE;
7354 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7355 return mapping_RND_to_EM[element_rnd];
7357 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7362 int map_element_EM_to_RND(int element_em)
7364 static unsigned short mapping_EM_to_RND[TILE_MAX];
7365 static boolean mapping_initialized = FALSE;
7367 if (!mapping_initialized)
7371 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7372 for (i = 0; i < TILE_MAX; i++)
7373 mapping_EM_to_RND[i] = EL_UNKNOWN;
7375 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7376 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7377 em_object_mapping_list[i].element_rnd;
7379 mapping_initialized = TRUE;
7382 if (element_em >= 0 && element_em < TILE_MAX)
7383 return mapping_EM_to_RND[element_em];
7385 Error(ERR_WARN, "invalid EM level element %d", element_em);
7390 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7392 struct LevelInfo_EM *level_em = level->native_em_level;
7393 struct LEVEL *lev = level_em->lev;
7396 for (i = 0; i < TILE_MAX; i++)
7397 lev->android_array[i] = Xblank;
7399 for (i = 0; i < level->num_android_clone_elements; i++)
7401 int element_rnd = level->android_clone_element[i];
7402 int element_em = map_element_RND_to_EM(element_rnd);
7404 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7405 if (em_object_mapping_list[j].element_rnd == element_rnd)
7406 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7410 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7412 struct LevelInfo_EM *level_em = level->native_em_level;
7413 struct LEVEL *lev = level_em->lev;
7416 level->num_android_clone_elements = 0;
7418 for (i = 0; i < TILE_MAX; i++)
7420 int element_em = lev->android_array[i];
7422 boolean element_found = FALSE;
7424 if (element_em == Xblank)
7427 element_rnd = map_element_EM_to_RND(element_em);
7429 for (j = 0; j < level->num_android_clone_elements; j++)
7430 if (level->android_clone_element[j] == element_rnd)
7431 element_found = TRUE;
7435 level->android_clone_element[level->num_android_clone_elements++] =
7438 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7443 if (level->num_android_clone_elements == 0)
7445 level->num_android_clone_elements = 1;
7446 level->android_clone_element[0] = EL_EMPTY;
7450 int map_direction_RND_to_EM(int direction)
7452 return (direction == MV_UP ? 0 :
7453 direction == MV_RIGHT ? 1 :
7454 direction == MV_DOWN ? 2 :
7455 direction == MV_LEFT ? 3 :
7459 int map_direction_EM_to_RND(int direction)
7461 return (direction == 0 ? MV_UP :
7462 direction == 1 ? MV_RIGHT :
7463 direction == 2 ? MV_DOWN :
7464 direction == 3 ? MV_LEFT :
7468 int map_element_RND_to_SP(int element_rnd)
7470 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7472 if (element_rnd >= EL_SP_START &&
7473 element_rnd <= EL_SP_END)
7474 element_sp = element_rnd - EL_SP_START;
7475 else if (element_rnd == EL_EMPTY_SPACE)
7477 else if (element_rnd == EL_INVISIBLE_WALL)
7483 int map_element_SP_to_RND(int element_sp)
7485 int element_rnd = EL_UNKNOWN;
7487 if (element_sp >= 0x00 &&
7489 element_rnd = EL_SP_START + element_sp;
7490 else if (element_sp == 0x28)
7491 element_rnd = EL_INVISIBLE_WALL;
7496 int map_action_SP_to_RND(int action_sp)
7500 case actActive: return ACTION_ACTIVE;
7501 case actImpact: return ACTION_IMPACT;
7502 case actExploding: return ACTION_EXPLODING;
7503 case actDigging: return ACTION_DIGGING;
7504 case actSnapping: return ACTION_SNAPPING;
7505 case actCollecting: return ACTION_COLLECTING;
7506 case actPassing: return ACTION_PASSING;
7507 case actPushing: return ACTION_PUSHING;
7508 case actDropping: return ACTION_DROPPING;
7510 default: return ACTION_DEFAULT;
7514 int map_element_RND_to_MM(int element_rnd)
7516 return (element_rnd >= EL_MM_START_1 &&
7517 element_rnd <= EL_MM_END_1 ?
7518 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7520 element_rnd >= EL_MM_START_2 &&
7521 element_rnd <= EL_MM_END_2 ?
7522 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7524 element_rnd >= EL_CHAR_START &&
7525 element_rnd <= EL_CHAR_END ?
7526 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7528 element_rnd >= EL_MM_RUNTIME_START &&
7529 element_rnd <= EL_MM_RUNTIME_END ?
7530 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7532 element_rnd >= EL_MM_DUMMY_START &&
7533 element_rnd <= EL_MM_DUMMY_END ?
7534 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7536 EL_MM_EMPTY_NATIVE);
7539 int map_element_MM_to_RND(int element_mm)
7541 return (element_mm == EL_MM_EMPTY_NATIVE ||
7542 element_mm == EL_DF_EMPTY_NATIVE ?
7545 element_mm >= EL_MM_START_1_NATIVE &&
7546 element_mm <= EL_MM_END_1_NATIVE ?
7547 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7549 element_mm >= EL_MM_START_2_NATIVE &&
7550 element_mm <= EL_MM_END_2_NATIVE ?
7551 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7553 element_mm >= EL_MM_CHAR_START_NATIVE &&
7554 element_mm <= EL_MM_CHAR_END_NATIVE ?
7555 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7557 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7558 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7559 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7561 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7562 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7563 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7568 int map_action_MM_to_RND(int action_mm)
7570 /* all MM actions are defined to exactly match their RND counterparts */
7574 int map_sound_MM_to_RND(int sound_mm)
7578 case SND_MM_GAME_LEVELTIME_CHARGING:
7579 return SND_GAME_LEVELTIME_CHARGING;
7581 case SND_MM_GAME_HEALTH_CHARGING:
7582 return SND_GAME_HEALTH_CHARGING;
7585 return SND_UNDEFINED;
7589 int map_mm_wall_element(int element)
7591 return (element >= EL_MM_STEEL_WALL_START &&
7592 element <= EL_MM_STEEL_WALL_END ?
7595 element >= EL_MM_WOODEN_WALL_START &&
7596 element <= EL_MM_WOODEN_WALL_END ?
7599 element >= EL_MM_ICE_WALL_START &&
7600 element <= EL_MM_ICE_WALL_END ?
7603 element >= EL_MM_AMOEBA_WALL_START &&
7604 element <= EL_MM_AMOEBA_WALL_END ?
7607 element >= EL_DF_STEEL_WALL_START &&
7608 element <= EL_DF_STEEL_WALL_END ?
7611 element >= EL_DF_WOODEN_WALL_START &&
7612 element <= EL_DF_WOODEN_WALL_END ?
7618 int map_mm_wall_element_editor(int element)
7622 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7623 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7624 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7625 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7626 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7627 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7629 default: return element;
7633 int get_next_element(int element)
7637 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7638 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7639 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7640 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7641 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7642 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7643 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7644 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7645 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7646 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7647 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7649 default: return element;
7653 int el2img_mm(int element_mm)
7655 return el2img(map_element_MM_to_RND(element_mm));
7658 int el_act_dir2img(int element, int action, int direction)
7660 element = GFX_ELEMENT(element);
7661 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7663 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7664 return element_info[element].direction_graphic[action][direction];
7667 static int el_act_dir2crm(int element, int action, int direction)
7669 element = GFX_ELEMENT(element);
7670 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7672 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7673 return element_info[element].direction_crumbled[action][direction];
7676 int el_act2img(int element, int action)
7678 element = GFX_ELEMENT(element);
7680 return element_info[element].graphic[action];
7683 int el_act2crm(int element, int action)
7685 element = GFX_ELEMENT(element);
7687 return element_info[element].crumbled[action];
7690 int el_dir2img(int element, int direction)
7692 element = GFX_ELEMENT(element);
7694 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7697 int el2baseimg(int element)
7699 return element_info[element].graphic[ACTION_DEFAULT];
7702 int el2img(int element)
7704 element = GFX_ELEMENT(element);
7706 return element_info[element].graphic[ACTION_DEFAULT];
7709 int el2edimg(int element)
7711 element = GFX_ELEMENT(element);
7713 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7716 int el2preimg(int element)
7718 element = GFX_ELEMENT(element);
7720 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7723 int el2panelimg(int element)
7725 element = GFX_ELEMENT(element);
7727 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7730 int font2baseimg(int font_nr)
7732 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7735 int getBeltNrFromBeltElement(int element)
7737 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7738 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7739 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7742 int getBeltNrFromBeltActiveElement(int element)
7744 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7745 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7746 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7749 int getBeltNrFromBeltSwitchElement(int element)
7751 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7752 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7753 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7756 int getBeltDirNrFromBeltElement(int element)
7758 static int belt_base_element[4] =
7760 EL_CONVEYOR_BELT_1_LEFT,
7761 EL_CONVEYOR_BELT_2_LEFT,
7762 EL_CONVEYOR_BELT_3_LEFT,
7763 EL_CONVEYOR_BELT_4_LEFT
7766 int belt_nr = getBeltNrFromBeltElement(element);
7767 int belt_dir_nr = element - belt_base_element[belt_nr];
7769 return (belt_dir_nr % 3);
7772 int getBeltDirNrFromBeltSwitchElement(int element)
7774 static int belt_base_element[4] =
7776 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7777 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7778 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7779 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7782 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7783 int belt_dir_nr = element - belt_base_element[belt_nr];
7785 return (belt_dir_nr % 3);
7788 int getBeltDirFromBeltElement(int element)
7790 static int belt_move_dir[3] =
7797 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7799 return belt_move_dir[belt_dir_nr];
7802 int getBeltDirFromBeltSwitchElement(int element)
7804 static int belt_move_dir[3] =
7811 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7813 return belt_move_dir[belt_dir_nr];
7816 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7818 static int belt_base_element[4] =
7820 EL_CONVEYOR_BELT_1_LEFT,
7821 EL_CONVEYOR_BELT_2_LEFT,
7822 EL_CONVEYOR_BELT_3_LEFT,
7823 EL_CONVEYOR_BELT_4_LEFT
7826 return belt_base_element[belt_nr] + belt_dir_nr;
7829 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7831 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7833 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7836 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7838 static int belt_base_element[4] =
7840 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7841 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7842 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7843 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7846 return belt_base_element[belt_nr] + belt_dir_nr;
7849 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7851 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7853 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7856 boolean getTeamMode_EM()
7858 return game.team_mode;
7861 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7863 int game_frame_delay_value;
7865 game_frame_delay_value =
7866 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7867 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7870 if (tape.playing && tape.warp_forward && !tape.pausing)
7871 game_frame_delay_value = 0;
7873 return game_frame_delay_value;
7876 unsigned int InitRND(int seed)
7878 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7879 return InitEngineRandom_EM(seed);
7880 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7881 return InitEngineRandom_SP(seed);
7882 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7883 return InitEngineRandom_MM(seed);
7885 return InitEngineRandom_RND(seed);
7888 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7889 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7891 inline static int get_effective_element_EM(int tile, int frame_em)
7893 int element = object_mapping[tile].element_rnd;
7894 int action = object_mapping[tile].action;
7895 boolean is_backside = object_mapping[tile].is_backside;
7896 boolean action_removing = (action == ACTION_DIGGING ||
7897 action == ACTION_SNAPPING ||
7898 action == ACTION_COLLECTING);
7904 case Yacid_splash_eB:
7905 case Yacid_splash_wB:
7906 return (frame_em > 5 ? EL_EMPTY : element);
7912 else /* frame_em == 7 */
7916 case Yacid_splash_eB:
7917 case Yacid_splash_wB:
7920 case Yemerald_stone:
7923 case Ydiamond_stone:
7927 case Xdrip_stretchB:
7946 case Xsand_stonein_1:
7947 case Xsand_stonein_2:
7948 case Xsand_stonein_3:
7949 case Xsand_stonein_4:
7953 return (is_backside || action_removing ? EL_EMPTY : element);
7958 inline static boolean check_linear_animation_EM(int tile)
7962 case Xsand_stonesand_1:
7963 case Xsand_stonesand_quickout_1:
7964 case Xsand_sandstone_1:
7965 case Xsand_stonein_1:
7966 case Xsand_stoneout_1:
7985 case Yacid_splash_eB:
7986 case Yacid_splash_wB:
7987 case Yemerald_stone:
7994 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7995 boolean has_crumbled_graphics,
7996 int crumbled, int sync_frame)
7998 /* if element can be crumbled, but certain action graphics are just empty
7999 space (like instantly snapping sand to empty space in 1 frame), do not
8000 treat these empty space graphics as crumbled graphics in EMC engine */
8001 if (crumbled == IMG_EMPTY_SPACE)
8002 has_crumbled_graphics = FALSE;
8004 if (has_crumbled_graphics)
8006 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8007 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8008 g_crumbled->anim_delay,
8009 g_crumbled->anim_mode,
8010 g_crumbled->anim_start_frame,
8013 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8014 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8016 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8017 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8019 g_em->has_crumbled_graphics = TRUE;
8023 g_em->crumbled_bitmap = NULL;
8024 g_em->crumbled_src_x = 0;
8025 g_em->crumbled_src_y = 0;
8026 g_em->crumbled_border_size = 0;
8027 g_em->crumbled_tile_size = 0;
8029 g_em->has_crumbled_graphics = FALSE;
8033 void ResetGfxAnimation_EM(int x, int y, int tile)
8038 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8039 int tile, int frame_em, int x, int y)
8041 int action = object_mapping[tile].action;
8042 int direction = object_mapping[tile].direction;
8043 int effective_element = get_effective_element_EM(tile, frame_em);
8044 int graphic = (direction == MV_NONE ?
8045 el_act2img(effective_element, action) :
8046 el_act_dir2img(effective_element, action, direction));
8047 struct GraphicInfo *g = &graphic_info[graphic];
8049 boolean action_removing = (action == ACTION_DIGGING ||
8050 action == ACTION_SNAPPING ||
8051 action == ACTION_COLLECTING);
8052 boolean action_moving = (action == ACTION_FALLING ||
8053 action == ACTION_MOVING ||
8054 action == ACTION_PUSHING ||
8055 action == ACTION_EATING ||
8056 action == ACTION_FILLING ||
8057 action == ACTION_EMPTYING);
8058 boolean action_falling = (action == ACTION_FALLING ||
8059 action == ACTION_FILLING ||
8060 action == ACTION_EMPTYING);
8062 /* special case: graphic uses "2nd movement tile" and has defined
8063 7 frames for movement animation (or less) => use default graphic
8064 for last (8th) frame which ends the movement animation */
8065 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8067 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
8068 graphic = (direction == MV_NONE ?
8069 el_act2img(effective_element, action) :
8070 el_act_dir2img(effective_element, action, direction));
8072 g = &graphic_info[graphic];
8075 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8079 else if (action_moving)
8081 boolean is_backside = object_mapping[tile].is_backside;
8085 int direction = object_mapping[tile].direction;
8086 int move_dir = (action_falling ? MV_DOWN : direction);
8091 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
8092 if (g->double_movement && frame_em == 0)
8096 if (move_dir == MV_LEFT)
8097 GfxFrame[x - 1][y] = GfxFrame[x][y];
8098 else if (move_dir == MV_RIGHT)
8099 GfxFrame[x + 1][y] = GfxFrame[x][y];
8100 else if (move_dir == MV_UP)
8101 GfxFrame[x][y - 1] = GfxFrame[x][y];
8102 else if (move_dir == MV_DOWN)
8103 GfxFrame[x][y + 1] = GfxFrame[x][y];
8110 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
8111 if (tile == Xsand_stonesand_quickout_1 ||
8112 tile == Xsand_stonesand_quickout_2)
8116 if (graphic_info[graphic].anim_global_sync)
8117 sync_frame = FrameCounter;
8118 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8119 sync_frame = GfxFrame[x][y];
8121 sync_frame = 0; /* playfield border (pseudo steel) */
8123 SetRandomAnimationValue(x, y);
8125 int frame = getAnimationFrame(g->anim_frames,
8128 g->anim_start_frame,
8131 g_em->unique_identifier =
8132 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8135 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8136 int tile, int frame_em, int x, int y)
8138 int action = object_mapping[tile].action;
8139 int direction = object_mapping[tile].direction;
8140 boolean is_backside = object_mapping[tile].is_backside;
8141 int effective_element = get_effective_element_EM(tile, frame_em);
8142 int effective_action = action;
8143 int graphic = (direction == MV_NONE ?
8144 el_act2img(effective_element, effective_action) :
8145 el_act_dir2img(effective_element, effective_action,
8147 int crumbled = (direction == MV_NONE ?
8148 el_act2crm(effective_element, effective_action) :
8149 el_act_dir2crm(effective_element, effective_action,
8151 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8152 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8153 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8154 struct GraphicInfo *g = &graphic_info[graphic];
8157 /* special case: graphic uses "2nd movement tile" and has defined
8158 7 frames for movement animation (or less) => use default graphic
8159 for last (8th) frame which ends the movement animation */
8160 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8162 effective_action = ACTION_DEFAULT;
8163 graphic = (direction == MV_NONE ?
8164 el_act2img(effective_element, effective_action) :
8165 el_act_dir2img(effective_element, effective_action,
8167 crumbled = (direction == MV_NONE ?
8168 el_act2crm(effective_element, effective_action) :
8169 el_act_dir2crm(effective_element, effective_action,
8172 g = &graphic_info[graphic];
8175 if (graphic_info[graphic].anim_global_sync)
8176 sync_frame = FrameCounter;
8177 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8178 sync_frame = GfxFrame[x][y];
8180 sync_frame = 0; /* playfield border (pseudo steel) */
8182 SetRandomAnimationValue(x, y);
8184 int frame = getAnimationFrame(g->anim_frames,
8187 g->anim_start_frame,
8190 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8191 g->double_movement && is_backside);
8193 /* (updating the "crumbled" graphic definitions is probably not really needed,
8194 as animations for crumbled graphics can't be longer than one EMC cycle) */
8195 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8199 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8200 int player_nr, int anim, int frame_em)
8202 int element = player_mapping[player_nr][anim].element_rnd;
8203 int action = player_mapping[player_nr][anim].action;
8204 int direction = player_mapping[player_nr][anim].direction;
8205 int graphic = (direction == MV_NONE ?
8206 el_act2img(element, action) :
8207 el_act_dir2img(element, action, direction));
8208 struct GraphicInfo *g = &graphic_info[graphic];
8211 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8213 stored_player[player_nr].StepFrame = frame_em;
8215 sync_frame = stored_player[player_nr].Frame;
8217 int frame = getAnimationFrame(g->anim_frames,
8220 g->anim_start_frame,
8223 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8224 &g_em->src_x, &g_em->src_y, FALSE);
8227 void InitGraphicInfo_EM(void)
8232 int num_em_gfx_errors = 0;
8234 if (graphic_info_em_object[0][0].bitmap == NULL)
8236 /* EM graphics not yet initialized in em_open_all() */
8241 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8244 /* always start with reliable default values */
8245 for (i = 0; i < TILE_MAX; i++)
8247 object_mapping[i].element_rnd = EL_UNKNOWN;
8248 object_mapping[i].is_backside = FALSE;
8249 object_mapping[i].action = ACTION_DEFAULT;
8250 object_mapping[i].direction = MV_NONE;
8253 /* always start with reliable default values */
8254 for (p = 0; p < MAX_PLAYERS; p++)
8256 for (i = 0; i < SPR_MAX; i++)
8258 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8259 player_mapping[p][i].action = ACTION_DEFAULT;
8260 player_mapping[p][i].direction = MV_NONE;
8264 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8266 int e = em_object_mapping_list[i].element_em;
8268 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8269 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8271 if (em_object_mapping_list[i].action != -1)
8272 object_mapping[e].action = em_object_mapping_list[i].action;
8274 if (em_object_mapping_list[i].direction != -1)
8275 object_mapping[e].direction =
8276 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8279 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8281 int a = em_player_mapping_list[i].action_em;
8282 int p = em_player_mapping_list[i].player_nr;
8284 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8286 if (em_player_mapping_list[i].action != -1)
8287 player_mapping[p][a].action = em_player_mapping_list[i].action;
8289 if (em_player_mapping_list[i].direction != -1)
8290 player_mapping[p][a].direction =
8291 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8294 for (i = 0; i < TILE_MAX; i++)
8296 int element = object_mapping[i].element_rnd;
8297 int action = object_mapping[i].action;
8298 int direction = object_mapping[i].direction;
8299 boolean is_backside = object_mapping[i].is_backside;
8300 boolean action_exploding = ((action == ACTION_EXPLODING ||
8301 action == ACTION_SMASHED_BY_ROCK ||
8302 action == ACTION_SMASHED_BY_SPRING) &&
8303 element != EL_DIAMOND);
8304 boolean action_active = (action == ACTION_ACTIVE);
8305 boolean action_other = (action == ACTION_OTHER);
8307 for (j = 0; j < 8; j++)
8309 int effective_element = get_effective_element_EM(i, j);
8310 int effective_action = (j < 7 ? action :
8311 i == Xdrip_stretch ? action :
8312 i == Xdrip_stretchB ? action :
8313 i == Ydrip_s1 ? action :
8314 i == Ydrip_s1B ? action :
8315 i == Xball_1B ? action :
8316 i == Xball_2 ? action :
8317 i == Xball_2B ? action :
8318 i == Yball_eat ? action :
8319 i == Ykey_1_eat ? action :
8320 i == Ykey_2_eat ? action :
8321 i == Ykey_3_eat ? action :
8322 i == Ykey_4_eat ? action :
8323 i == Ykey_5_eat ? action :
8324 i == Ykey_6_eat ? action :
8325 i == Ykey_7_eat ? action :
8326 i == Ykey_8_eat ? action :
8327 i == Ylenses_eat ? action :
8328 i == Ymagnify_eat ? action :
8329 i == Ygrass_eat ? action :
8330 i == Ydirt_eat ? action :
8331 i == Xsand_stonein_1 ? action :
8332 i == Xsand_stonein_2 ? action :
8333 i == Xsand_stonein_3 ? action :
8334 i == Xsand_stonein_4 ? action :
8335 i == Xsand_stoneout_1 ? action :
8336 i == Xsand_stoneout_2 ? action :
8337 i == Xboom_android ? ACTION_EXPLODING :
8338 action_exploding ? ACTION_EXPLODING :
8339 action_active ? action :
8340 action_other ? action :
8342 int graphic = (el_act_dir2img(effective_element, effective_action,
8344 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8346 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8347 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8348 boolean has_action_graphics = (graphic != base_graphic);
8349 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8350 struct GraphicInfo *g = &graphic_info[graphic];
8351 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8354 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8355 boolean special_animation = (action != ACTION_DEFAULT &&
8356 g->anim_frames == 3 &&
8357 g->anim_delay == 2 &&
8358 g->anim_mode & ANIM_LINEAR);
8359 int sync_frame = (i == Xdrip_stretch ? 7 :
8360 i == Xdrip_stretchB ? 7 :
8361 i == Ydrip_s2 ? j + 8 :
8362 i == Ydrip_s2B ? j + 8 :
8371 i == Xfake_acid_1 ? 0 :
8372 i == Xfake_acid_2 ? 10 :
8373 i == Xfake_acid_3 ? 20 :
8374 i == Xfake_acid_4 ? 30 :
8375 i == Xfake_acid_5 ? 40 :
8376 i == Xfake_acid_6 ? 50 :
8377 i == Xfake_acid_7 ? 60 :
8378 i == Xfake_acid_8 ? 70 :
8380 i == Xball_2B ? j + 8 :
8381 i == Yball_eat ? j + 1 :
8382 i == Ykey_1_eat ? j + 1 :
8383 i == Ykey_2_eat ? j + 1 :
8384 i == Ykey_3_eat ? j + 1 :
8385 i == Ykey_4_eat ? j + 1 :
8386 i == Ykey_5_eat ? j + 1 :
8387 i == Ykey_6_eat ? j + 1 :
8388 i == Ykey_7_eat ? j + 1 :
8389 i == Ykey_8_eat ? j + 1 :
8390 i == Ylenses_eat ? j + 1 :
8391 i == Ymagnify_eat ? j + 1 :
8392 i == Ygrass_eat ? j + 1 :
8393 i == Ydirt_eat ? j + 1 :
8394 i == Xamoeba_1 ? 0 :
8395 i == Xamoeba_2 ? 1 :
8396 i == Xamoeba_3 ? 2 :
8397 i == Xamoeba_4 ? 3 :
8398 i == Xamoeba_5 ? 0 :
8399 i == Xamoeba_6 ? 1 :
8400 i == Xamoeba_7 ? 2 :
8401 i == Xamoeba_8 ? 3 :
8402 i == Xexit_2 ? j + 8 :
8403 i == Xexit_3 ? j + 16 :
8404 i == Xdynamite_1 ? 0 :
8405 i == Xdynamite_2 ? 8 :
8406 i == Xdynamite_3 ? 16 :
8407 i == Xdynamite_4 ? 24 :
8408 i == Xsand_stonein_1 ? j + 1 :
8409 i == Xsand_stonein_2 ? j + 9 :
8410 i == Xsand_stonein_3 ? j + 17 :
8411 i == Xsand_stonein_4 ? j + 25 :
8412 i == Xsand_stoneout_1 && j == 0 ? 0 :
8413 i == Xsand_stoneout_1 && j == 1 ? 0 :
8414 i == Xsand_stoneout_1 && j == 2 ? 1 :
8415 i == Xsand_stoneout_1 && j == 3 ? 2 :
8416 i == Xsand_stoneout_1 && j == 4 ? 2 :
8417 i == Xsand_stoneout_1 && j == 5 ? 3 :
8418 i == Xsand_stoneout_1 && j == 6 ? 4 :
8419 i == Xsand_stoneout_1 && j == 7 ? 4 :
8420 i == Xsand_stoneout_2 && j == 0 ? 5 :
8421 i == Xsand_stoneout_2 && j == 1 ? 6 :
8422 i == Xsand_stoneout_2 && j == 2 ? 7 :
8423 i == Xsand_stoneout_2 && j == 3 ? 8 :
8424 i == Xsand_stoneout_2 && j == 4 ? 9 :
8425 i == Xsand_stoneout_2 && j == 5 ? 11 :
8426 i == Xsand_stoneout_2 && j == 6 ? 13 :
8427 i == Xsand_stoneout_2 && j == 7 ? 15 :
8428 i == Xboom_bug && j == 1 ? 2 :
8429 i == Xboom_bug && j == 2 ? 2 :
8430 i == Xboom_bug && j == 3 ? 4 :
8431 i == Xboom_bug && j == 4 ? 4 :
8432 i == Xboom_bug && j == 5 ? 2 :
8433 i == Xboom_bug && j == 6 ? 2 :
8434 i == Xboom_bug && j == 7 ? 0 :
8435 i == Xboom_bomb && j == 1 ? 2 :
8436 i == Xboom_bomb && j == 2 ? 2 :
8437 i == Xboom_bomb && j == 3 ? 4 :
8438 i == Xboom_bomb && j == 4 ? 4 :
8439 i == Xboom_bomb && j == 5 ? 2 :
8440 i == Xboom_bomb && j == 6 ? 2 :
8441 i == Xboom_bomb && j == 7 ? 0 :
8442 i == Xboom_android && j == 7 ? 6 :
8443 i == Xboom_1 && j == 1 ? 2 :
8444 i == Xboom_1 && j == 2 ? 2 :
8445 i == Xboom_1 && j == 3 ? 4 :
8446 i == Xboom_1 && j == 4 ? 4 :
8447 i == Xboom_1 && j == 5 ? 6 :
8448 i == Xboom_1 && j == 6 ? 6 :
8449 i == Xboom_1 && j == 7 ? 8 :
8450 i == Xboom_2 && j == 0 ? 8 :
8451 i == Xboom_2 && j == 1 ? 8 :
8452 i == Xboom_2 && j == 2 ? 10 :
8453 i == Xboom_2 && j == 3 ? 10 :
8454 i == Xboom_2 && j == 4 ? 10 :
8455 i == Xboom_2 && j == 5 ? 12 :
8456 i == Xboom_2 && j == 6 ? 12 :
8457 i == Xboom_2 && j == 7 ? 12 :
8458 special_animation && j == 4 ? 3 :
8459 effective_action != action ? 0 :
8463 Bitmap *debug_bitmap = g_em->bitmap;
8464 int debug_src_x = g_em->src_x;
8465 int debug_src_y = g_em->src_y;
8468 int frame = getAnimationFrame(g->anim_frames,
8471 g->anim_start_frame,
8474 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8475 g->double_movement && is_backside);
8477 g_em->bitmap = src_bitmap;
8478 g_em->src_x = src_x;
8479 g_em->src_y = src_y;
8480 g_em->src_offset_x = 0;
8481 g_em->src_offset_y = 0;
8482 g_em->dst_offset_x = 0;
8483 g_em->dst_offset_y = 0;
8484 g_em->width = TILEX;
8485 g_em->height = TILEY;
8487 g_em->preserve_background = FALSE;
8489 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8492 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8493 effective_action == ACTION_MOVING ||
8494 effective_action == ACTION_PUSHING ||
8495 effective_action == ACTION_EATING)) ||
8496 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8497 effective_action == ACTION_EMPTYING)))
8500 (effective_action == ACTION_FALLING ||
8501 effective_action == ACTION_FILLING ||
8502 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8503 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8504 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8505 int num_steps = (i == Ydrip_s1 ? 16 :
8506 i == Ydrip_s1B ? 16 :
8507 i == Ydrip_s2 ? 16 :
8508 i == Ydrip_s2B ? 16 :
8509 i == Xsand_stonein_1 ? 32 :
8510 i == Xsand_stonein_2 ? 32 :
8511 i == Xsand_stonein_3 ? 32 :
8512 i == Xsand_stonein_4 ? 32 :
8513 i == Xsand_stoneout_1 ? 16 :
8514 i == Xsand_stoneout_2 ? 16 : 8);
8515 int cx = ABS(dx) * (TILEX / num_steps);
8516 int cy = ABS(dy) * (TILEY / num_steps);
8517 int step_frame = (i == Ydrip_s2 ? j + 8 :
8518 i == Ydrip_s2B ? j + 8 :
8519 i == Xsand_stonein_2 ? j + 8 :
8520 i == Xsand_stonein_3 ? j + 16 :
8521 i == Xsand_stonein_4 ? j + 24 :
8522 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8523 int step = (is_backside ? step_frame : num_steps - step_frame);
8525 if (is_backside) /* tile where movement starts */
8527 if (dx < 0 || dy < 0)
8529 g_em->src_offset_x = cx * step;
8530 g_em->src_offset_y = cy * step;
8534 g_em->dst_offset_x = cx * step;
8535 g_em->dst_offset_y = cy * step;
8538 else /* tile where movement ends */
8540 if (dx < 0 || dy < 0)
8542 g_em->dst_offset_x = cx * step;
8543 g_em->dst_offset_y = cy * step;
8547 g_em->src_offset_x = cx * step;
8548 g_em->src_offset_y = cy * step;
8552 g_em->width = TILEX - cx * step;
8553 g_em->height = TILEY - cy * step;
8556 /* create unique graphic identifier to decide if tile must be redrawn */
8557 /* bit 31 - 16 (16 bit): EM style graphic
8558 bit 15 - 12 ( 4 bit): EM style frame
8559 bit 11 - 6 ( 6 bit): graphic width
8560 bit 5 - 0 ( 6 bit): graphic height */
8561 g_em->unique_identifier =
8562 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8566 /* skip check for EMC elements not contained in original EMC artwork */
8567 if (element == EL_EMC_FAKE_ACID)
8570 if (g_em->bitmap != debug_bitmap ||
8571 g_em->src_x != debug_src_x ||
8572 g_em->src_y != debug_src_y ||
8573 g_em->src_offset_x != 0 ||
8574 g_em->src_offset_y != 0 ||
8575 g_em->dst_offset_x != 0 ||
8576 g_em->dst_offset_y != 0 ||
8577 g_em->width != TILEX ||
8578 g_em->height != TILEY)
8580 static int last_i = -1;
8588 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8589 i, element, element_info[element].token_name,
8590 element_action_info[effective_action].suffix, direction);
8592 if (element != effective_element)
8593 printf(" [%d ('%s')]",
8595 element_info[effective_element].token_name);
8599 if (g_em->bitmap != debug_bitmap)
8600 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8601 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8603 if (g_em->src_x != debug_src_x ||
8604 g_em->src_y != debug_src_y)
8605 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8606 j, (is_backside ? 'B' : 'F'),
8607 g_em->src_x, g_em->src_y,
8608 g_em->src_x / 32, g_em->src_y / 32,
8609 debug_src_x, debug_src_y,
8610 debug_src_x / 32, debug_src_y / 32);
8612 if (g_em->src_offset_x != 0 ||
8613 g_em->src_offset_y != 0 ||
8614 g_em->dst_offset_x != 0 ||
8615 g_em->dst_offset_y != 0)
8616 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8618 g_em->src_offset_x, g_em->src_offset_y,
8619 g_em->dst_offset_x, g_em->dst_offset_y);
8621 if (g_em->width != TILEX ||
8622 g_em->height != TILEY)
8623 printf(" %d (%d): size %d,%d should be %d,%d\n",
8625 g_em->width, g_em->height, TILEX, TILEY);
8627 num_em_gfx_errors++;
8634 for (i = 0; i < TILE_MAX; i++)
8636 for (j = 0; j < 8; j++)
8638 int element = object_mapping[i].element_rnd;
8639 int action = object_mapping[i].action;
8640 int direction = object_mapping[i].direction;
8641 boolean is_backside = object_mapping[i].is_backside;
8642 int graphic_action = el_act_dir2img(element, action, direction);
8643 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8645 if ((action == ACTION_SMASHED_BY_ROCK ||
8646 action == ACTION_SMASHED_BY_SPRING ||
8647 action == ACTION_EATING) &&
8648 graphic_action == graphic_default)
8650 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8651 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8652 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8653 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8656 /* no separate animation for "smashed by rock" -- use rock instead */
8657 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8658 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8660 g_em->bitmap = g_xx->bitmap;
8661 g_em->src_x = g_xx->src_x;
8662 g_em->src_y = g_xx->src_y;
8663 g_em->src_offset_x = g_xx->src_offset_x;
8664 g_em->src_offset_y = g_xx->src_offset_y;
8665 g_em->dst_offset_x = g_xx->dst_offset_x;
8666 g_em->dst_offset_y = g_xx->dst_offset_y;
8667 g_em->width = g_xx->width;
8668 g_em->height = g_xx->height;
8669 g_em->unique_identifier = g_xx->unique_identifier;
8672 g_em->preserve_background = TRUE;
8677 for (p = 0; p < MAX_PLAYERS; p++)
8679 for (i = 0; i < SPR_MAX; i++)
8681 int element = player_mapping[p][i].element_rnd;
8682 int action = player_mapping[p][i].action;
8683 int direction = player_mapping[p][i].direction;
8685 for (j = 0; j < 8; j++)
8687 int effective_element = element;
8688 int effective_action = action;
8689 int graphic = (direction == MV_NONE ?
8690 el_act2img(effective_element, effective_action) :
8691 el_act_dir2img(effective_element, effective_action,
8693 struct GraphicInfo *g = &graphic_info[graphic];
8694 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8700 Bitmap *debug_bitmap = g_em->bitmap;
8701 int debug_src_x = g_em->src_x;
8702 int debug_src_y = g_em->src_y;
8705 int frame = getAnimationFrame(g->anim_frames,
8708 g->anim_start_frame,
8711 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8713 g_em->bitmap = src_bitmap;
8714 g_em->src_x = src_x;
8715 g_em->src_y = src_y;
8716 g_em->src_offset_x = 0;
8717 g_em->src_offset_y = 0;
8718 g_em->dst_offset_x = 0;
8719 g_em->dst_offset_y = 0;
8720 g_em->width = TILEX;
8721 g_em->height = TILEY;
8725 /* skip check for EMC elements not contained in original EMC artwork */
8726 if (element == EL_PLAYER_3 ||
8727 element == EL_PLAYER_4)
8730 if (g_em->bitmap != debug_bitmap ||
8731 g_em->src_x != debug_src_x ||
8732 g_em->src_y != debug_src_y)
8734 static int last_i = -1;
8742 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8743 p, i, element, element_info[element].token_name,
8744 element_action_info[effective_action].suffix, direction);
8746 if (element != effective_element)
8747 printf(" [%d ('%s')]",
8749 element_info[effective_element].token_name);
8753 if (g_em->bitmap != debug_bitmap)
8754 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8755 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8757 if (g_em->src_x != debug_src_x ||
8758 g_em->src_y != debug_src_y)
8759 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8761 g_em->src_x, g_em->src_y,
8762 g_em->src_x / 32, g_em->src_y / 32,
8763 debug_src_x, debug_src_y,
8764 debug_src_x / 32, debug_src_y / 32);
8766 num_em_gfx_errors++;
8776 printf("::: [%d errors found]\n", num_em_gfx_errors);
8782 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8783 boolean any_player_moving,
8784 boolean any_player_snapping,
8785 boolean any_player_dropping)
8787 if (frame == 0 && !any_player_dropping)
8789 if (!local_player->was_waiting)
8791 if (!CheckSaveEngineSnapshotToList())
8794 local_player->was_waiting = TRUE;
8797 else if (any_player_moving || any_player_snapping || any_player_dropping)
8799 local_player->was_waiting = FALSE;
8803 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8804 boolean murphy_is_dropping)
8806 if (murphy_is_waiting)
8808 if (!local_player->was_waiting)
8810 if (!CheckSaveEngineSnapshotToList())
8813 local_player->was_waiting = TRUE;
8818 local_player->was_waiting = FALSE;
8822 void CheckSaveEngineSnapshot_MM(boolean element_clicked,
8823 boolean button_released)
8825 if (button_released)
8827 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
8828 CheckSaveEngineSnapshotToList();
8830 else if (element_clicked)
8832 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
8833 CheckSaveEngineSnapshotToList();
8835 game.snapshot.changed_action = TRUE;
8839 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8840 boolean any_player_moving,
8841 boolean any_player_snapping,
8842 boolean any_player_dropping)
8844 if (tape.single_step && tape.recording && !tape.pausing)
8845 if (frame == 0 && !any_player_dropping)
8846 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8848 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8849 any_player_snapping, any_player_dropping);
8852 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8853 boolean murphy_is_dropping)
8855 boolean murphy_starts_dropping = FALSE;
8858 for (i = 0; i < MAX_PLAYERS; i++)
8859 if (stored_player[i].force_dropping)
8860 murphy_starts_dropping = TRUE;
8862 if (tape.single_step && tape.recording && !tape.pausing)
8863 if (murphy_is_waiting && !murphy_starts_dropping)
8864 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8866 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8869 void CheckSingleStepMode_MM(boolean element_clicked,
8870 boolean button_released)
8872 if (tape.single_step && tape.recording && !tape.pausing)
8873 if (button_released)
8874 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8876 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
8879 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8880 int graphic, int sync_frame, int x, int y)
8882 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8884 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8887 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8889 return (IS_NEXT_FRAME(sync_frame, graphic));
8892 int getGraphicInfo_Delay(int graphic)
8894 return graphic_info[graphic].anim_delay;
8897 void PlayMenuSoundExt(int sound)
8899 if (sound == SND_UNDEFINED)
8902 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8903 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8906 if (IS_LOOP_SOUND(sound))
8907 PlaySoundLoop(sound);
8912 void PlayMenuSound()
8914 PlayMenuSoundExt(menu.sound[game_status]);
8917 void PlayMenuSoundStereo(int sound, int stereo_position)
8919 if (sound == SND_UNDEFINED)
8922 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8923 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8926 if (IS_LOOP_SOUND(sound))
8927 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8929 PlaySoundStereo(sound, stereo_position);
8932 void PlayMenuSoundIfLoopExt(int sound)
8934 if (sound == SND_UNDEFINED)
8937 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8938 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8941 if (IS_LOOP_SOUND(sound))
8942 PlaySoundLoop(sound);
8945 void PlayMenuSoundIfLoop()
8947 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8950 void PlayMenuMusicExt(int music)
8952 if (music == MUS_UNDEFINED)
8955 if (!setup.sound_music)
8961 void PlayMenuMusic()
8963 char *curr_music = getCurrentlyPlayingMusicFilename();
8964 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8966 if (!strEqual(curr_music, next_music))
8967 PlayMenuMusicExt(menu.music[game_status]);
8970 void PlayMenuSoundsAndMusic()
8976 static void FadeMenuSounds()
8981 static void FadeMenuMusic()
8983 char *curr_music = getCurrentlyPlayingMusicFilename();
8984 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8986 if (!strEqual(curr_music, next_music))
8990 void FadeMenuSoundsAndMusic()
8996 void PlaySoundActivating()
8999 PlaySound(SND_MENU_ITEM_ACTIVATING);
9003 void PlaySoundSelecting()
9006 PlaySound(SND_MENU_ITEM_SELECTING);
9010 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
9012 boolean change_fullscreen = (setup.fullscreen !=
9013 video.fullscreen_enabled);
9014 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9015 setup.window_scaling_percent !=
9016 video.window_scaling_percent);
9018 if (change_window_scaling_percent && video.fullscreen_enabled)
9021 if (!change_window_scaling_percent && !video.fullscreen_available)
9024 #if defined(TARGET_SDL2)
9025 if (change_window_scaling_percent)
9027 SDLSetWindowScaling(setup.window_scaling_percent);
9031 else if (change_fullscreen)
9033 SDLSetWindowFullscreen(setup.fullscreen);
9035 /* set setup value according to successfully changed fullscreen mode */
9036 setup.fullscreen = video.fullscreen_enabled;
9042 if (change_fullscreen ||
9043 change_window_scaling_percent)
9045 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9047 /* save backbuffer content which gets lost when toggling fullscreen mode */
9048 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9050 if (change_window_scaling_percent)
9052 /* keep window mode, but change window scaling */
9053 video.fullscreen_enabled = TRUE; /* force new window scaling */
9056 /* toggle fullscreen */
9057 ChangeVideoModeIfNeeded(setup.fullscreen);
9059 /* set setup value according to successfully changed fullscreen mode */
9060 setup.fullscreen = video.fullscreen_enabled;
9062 /* restore backbuffer content from temporary backbuffer backup bitmap */
9063 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9065 FreeBitmap(tmp_backbuffer);
9067 /* update visible window/screen */
9068 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9072 void JoinRectangles(int *x, int *y, int *width, int *height,
9073 int x2, int y2, int width2, int height2)
9075 // do not join with "off-screen" rectangle
9076 if (x2 == -1 || y2 == -1)
9081 *width = MAX(*width, width2);
9082 *height = MAX(*height, height2);
9085 void SetAnimStatus(int anim_status_new)
9087 if (anim_status_new == GAME_MODE_MAIN)
9088 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9089 else if (anim_status_new == GAME_MODE_SCORES)
9090 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9092 global.anim_status_next = anim_status_new;
9094 // directly set screen modes that are entered without fading
9095 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9096 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9097 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9098 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9099 global.anim_status = global.anim_status_next;
9102 void SetGameStatus(int game_status_new)
9104 if (game_status_new != game_status)
9105 game_status_last_screen = game_status;
9107 game_status = game_status_new;
9109 SetAnimStatus(game_status_new);
9112 void SetFontStatus(int game_status_new)
9114 static int last_game_status = -1;
9116 if (game_status_new != -1)
9118 // set game status for font use after storing last game status
9119 last_game_status = game_status;
9120 game_status = game_status_new;
9124 // reset game status after font use from last stored game status
9125 game_status = last_game_status;
9129 void ResetFontStatus()
9134 boolean CheckIfPlayfieldViewportHasChanged()
9136 // if game status has not changed, playfield viewport has not changed either
9137 if (game_status == game_status_last)
9140 // check if playfield viewport has changed with current game status
9141 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9142 int new_real_sx = vp_playfield->x;
9143 int new_real_sy = vp_playfield->y;
9144 int new_full_sxsize = vp_playfield->width;
9145 int new_full_sysize = vp_playfield->height;
9147 return (new_real_sx != REAL_SX ||
9148 new_real_sy != REAL_SY ||
9149 new_full_sxsize != FULL_SXSIZE ||
9150 new_full_sysize != FULL_SYSIZE);
9153 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
9155 return (CheckIfGlobalBorderHasChanged() ||
9156 CheckIfPlayfieldViewportHasChanged());
9159 void ChangeViewportPropertiesIfNeeded()
9161 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9162 FALSE : setup.small_game_graphics);
9163 int gfx_game_mode = game_status;
9164 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9166 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9167 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9168 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9169 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9170 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9171 int new_win_xsize = vp_window->width;
9172 int new_win_ysize = vp_window->height;
9173 int border_size = vp_playfield->border_size;
9174 int new_sx = vp_playfield->x + border_size;
9175 int new_sy = vp_playfield->y + border_size;
9176 int new_sxsize = vp_playfield->width - 2 * border_size;
9177 int new_sysize = vp_playfield->height - 2 * border_size;
9178 int new_real_sx = vp_playfield->x;
9179 int new_real_sy = vp_playfield->y;
9180 int new_full_sxsize = vp_playfield->width;
9181 int new_full_sysize = vp_playfield->height;
9182 int new_dx = vp_door_1->x;
9183 int new_dy = vp_door_1->y;
9184 int new_dxsize = vp_door_1->width;
9185 int new_dysize = vp_door_1->height;
9186 int new_vx = vp_door_2->x;
9187 int new_vy = vp_door_2->y;
9188 int new_vxsize = vp_door_2->width;
9189 int new_vysize = vp_door_2->height;
9190 int new_ex = vp_door_3->x;
9191 int new_ey = vp_door_3->y;
9192 int new_exsize = vp_door_3->width;
9193 int new_eysize = vp_door_3->height;
9194 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9195 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9196 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9197 int new_scr_fieldx = new_sxsize / tilesize;
9198 int new_scr_fieldy = new_sysize / tilesize;
9199 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9200 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9201 boolean init_gfx_buffers = FALSE;
9202 boolean init_video_buffer = FALSE;
9203 boolean init_gadgets_and_anims = FALSE;
9204 boolean init_em_graphics = FALSE;
9206 if (new_win_xsize != WIN_XSIZE ||
9207 new_win_ysize != WIN_YSIZE)
9209 WIN_XSIZE = new_win_xsize;
9210 WIN_YSIZE = new_win_ysize;
9212 init_video_buffer = TRUE;
9213 init_gfx_buffers = TRUE;
9214 init_gadgets_and_anims = TRUE;
9216 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9219 if (new_scr_fieldx != SCR_FIELDX ||
9220 new_scr_fieldy != SCR_FIELDY)
9222 /* this always toggles between MAIN and GAME when using small tile size */
9224 SCR_FIELDX = new_scr_fieldx;
9225 SCR_FIELDY = new_scr_fieldy;
9227 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9238 new_sxsize != SXSIZE ||
9239 new_sysize != SYSIZE ||
9240 new_dxsize != DXSIZE ||
9241 new_dysize != DYSIZE ||
9242 new_vxsize != VXSIZE ||
9243 new_vysize != VYSIZE ||
9244 new_exsize != EXSIZE ||
9245 new_eysize != EYSIZE ||
9246 new_real_sx != REAL_SX ||
9247 new_real_sy != REAL_SY ||
9248 new_full_sxsize != FULL_SXSIZE ||
9249 new_full_sysize != FULL_SYSIZE ||
9250 new_tilesize_var != TILESIZE_VAR
9253 // ------------------------------------------------------------------------
9254 // determine next fading area for changed viewport definitions
9255 // ------------------------------------------------------------------------
9257 // start with current playfield area (default fading area)
9260 FADE_SXSIZE = FULL_SXSIZE;
9261 FADE_SYSIZE = FULL_SYSIZE;
9263 // add new playfield area if position or size has changed
9264 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9265 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9267 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9268 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9271 // add current and new door 1 area if position or size has changed
9272 if (new_dx != DX || new_dy != DY ||
9273 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9275 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9276 DX, DY, DXSIZE, DYSIZE);
9277 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9278 new_dx, new_dy, new_dxsize, new_dysize);
9281 // add current and new door 2 area if position or size has changed
9282 if (new_dx != VX || new_dy != VY ||
9283 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9285 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9286 VX, VY, VXSIZE, VYSIZE);
9287 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9288 new_vx, new_vy, new_vxsize, new_vysize);
9291 // ------------------------------------------------------------------------
9292 // handle changed tile size
9293 // ------------------------------------------------------------------------
9295 if (new_tilesize_var != TILESIZE_VAR)
9297 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9299 // changing tile size invalidates scroll values of engine snapshots
9300 FreeEngineSnapshotSingle();
9302 // changing tile size requires update of graphic mapping for EM engine
9303 init_em_graphics = TRUE;
9314 SXSIZE = new_sxsize;
9315 SYSIZE = new_sysize;
9316 DXSIZE = new_dxsize;
9317 DYSIZE = new_dysize;
9318 VXSIZE = new_vxsize;
9319 VYSIZE = new_vysize;
9320 EXSIZE = new_exsize;
9321 EYSIZE = new_eysize;
9322 REAL_SX = new_real_sx;
9323 REAL_SY = new_real_sy;
9324 FULL_SXSIZE = new_full_sxsize;
9325 FULL_SYSIZE = new_full_sysize;
9326 TILESIZE_VAR = new_tilesize_var;
9328 init_gfx_buffers = TRUE;
9329 init_gadgets_and_anims = TRUE;
9331 // printf("::: viewports: init_gfx_buffers\n");
9332 // printf("::: viewports: init_gadgets_and_anims\n");
9335 if (init_gfx_buffers)
9337 // printf("::: init_gfx_buffers\n");
9339 SCR_FIELDX = new_scr_fieldx_buffers;
9340 SCR_FIELDY = new_scr_fieldy_buffers;
9344 SCR_FIELDX = new_scr_fieldx;
9345 SCR_FIELDY = new_scr_fieldy;
9347 SetDrawDeactivationMask(REDRAW_NONE);
9348 SetDrawBackgroundMask(REDRAW_FIELD);
9351 if (init_video_buffer)
9353 // printf("::: init_video_buffer\n");
9355 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9356 InitImageTextures();
9359 if (init_gadgets_and_anims)
9361 // printf("::: init_gadgets_and_anims\n");
9364 InitGlobalAnimations();
9367 if (init_em_graphics)
9369 InitGraphicInfo_EM();