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 Delay(MAX(door_1.post_delay, door_2.post_delay));
5154 else if (door_state & DOOR_ACTION_1)
5155 Delay(door_1.post_delay);
5156 else if (door_state & DOOR_ACTION_2)
5157 Delay(door_2.post_delay);
5161 if (door_state & DOOR_ACTION_1)
5162 door1 = door_state & DOOR_ACTION_1;
5163 if (door_state & DOOR_ACTION_2)
5164 door2 = door_state & DOOR_ACTION_2;
5166 // draw masked border over door area
5167 DrawMaskedBorder(REDRAW_DOOR_1);
5168 DrawMaskedBorder(REDRAW_DOOR_2);
5170 return (door1 | door2);
5173 static boolean useSpecialEditorDoor()
5175 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5176 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5178 // do not draw special editor door if editor border defined or redefined
5179 if (graphic_info[graphic].bitmap != NULL || redefined)
5182 // do not draw special editor door if global border defined to be empty
5183 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5186 // do not draw special editor door if viewport definitions do not match
5190 EY + EYSIZE != VY + VYSIZE)
5196 void DrawSpecialEditorDoor()
5198 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5199 int top_border_width = gfx1->width;
5200 int top_border_height = gfx1->height;
5201 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5202 int ex = EX - outer_border;
5203 int ey = EY - outer_border;
5204 int vy = VY - outer_border;
5205 int exsize = EXSIZE + 2 * outer_border;
5207 if (!useSpecialEditorDoor())
5210 /* draw bigger level editor toolbox window */
5211 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5212 top_border_width, top_border_height, ex, ey - top_border_height);
5213 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5214 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5216 redraw_mask |= REDRAW_ALL;
5219 void UndrawSpecialEditorDoor()
5221 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5222 int top_border_width = gfx1->width;
5223 int top_border_height = gfx1->height;
5224 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5225 int ex = EX - outer_border;
5226 int ey = EY - outer_border;
5227 int ey_top = ey - top_border_height;
5228 int exsize = EXSIZE + 2 * outer_border;
5229 int eysize = EYSIZE + 2 * outer_border;
5231 if (!useSpecialEditorDoor())
5234 /* draw normal tape recorder window */
5235 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5237 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5238 ex, ey_top, top_border_width, top_border_height,
5240 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5241 ex, ey, exsize, eysize, ex, ey);
5245 // if screen background is set to "[NONE]", clear editor toolbox window
5246 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5247 ClearRectangle(drawto, ex, ey, exsize, eysize);
5250 redraw_mask |= REDRAW_ALL;
5254 /* ---------- new tool button stuff ---------------------------------------- */
5259 struct TextPosInfo *pos;
5262 } toolbutton_info[NUM_TOOL_BUTTONS] =
5265 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5266 TOOL_CTRL_ID_YES, "yes"
5269 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5270 TOOL_CTRL_ID_NO, "no"
5273 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5274 TOOL_CTRL_ID_CONFIRM, "confirm"
5277 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5278 TOOL_CTRL_ID_PLAYER_1, "player 1"
5281 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5282 TOOL_CTRL_ID_PLAYER_2, "player 2"
5285 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5286 TOOL_CTRL_ID_PLAYER_3, "player 3"
5289 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5290 TOOL_CTRL_ID_PLAYER_4, "player 4"
5294 void CreateToolButtons()
5298 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5300 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5301 struct TextPosInfo *pos = toolbutton_info[i].pos;
5302 struct GadgetInfo *gi;
5303 Bitmap *deco_bitmap = None;
5304 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5305 unsigned int event_mask = GD_EVENT_RELEASED;
5308 int gd_x = gfx->src_x;
5309 int gd_y = gfx->src_y;
5310 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5311 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5316 if (global.use_envelope_request)
5318 setRequestPosition(&dx, &dy, TRUE);
5320 // check if request buttons are outside of envelope and fix, if needed
5321 if (x < 0 || x + gfx->width > request.width ||
5322 y < 0 || y + gfx->height > request.height)
5324 // use left padding of "yes" button as default border padding
5325 int padding = toolbutton_info[TOOL_CTRL_ID_YES].pos->x;
5327 if (id == TOOL_CTRL_ID_YES)
5330 y = request.height - 2 * request.border_size - gfx->height - padding;
5332 else if (id == TOOL_CTRL_ID_NO)
5334 x = request.width - 2 * request.border_size - gfx->width - padding;
5335 y = request.height - 2 * request.border_size - gfx->height - padding;
5337 else if (id == TOOL_CTRL_ID_CONFIRM)
5339 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5340 y = request.height - 2 * request.border_size - gfx->height - padding;
5342 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5344 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5346 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5347 y = request.height - 2 * request.border_size - gfx->height - padding;
5349 x += (player_nr % 2 ? +1 : -1) * gfx->width / 2;
5350 y += (player_nr / 2 ? 0 : -1) * gfx->height;
5355 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5357 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5359 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5360 pos->size, &deco_bitmap, &deco_x, &deco_y);
5361 deco_xpos = (gfx->width - pos->size) / 2;
5362 deco_ypos = (gfx->height - pos->size) / 2;
5365 gi = CreateGadget(GDI_CUSTOM_ID, id,
5366 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5369 GDI_WIDTH, gfx->width,
5370 GDI_HEIGHT, gfx->height,
5371 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5372 GDI_STATE, GD_BUTTON_UNPRESSED,
5373 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5374 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5375 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5376 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5377 GDI_DECORATION_SIZE, pos->size, pos->size,
5378 GDI_DECORATION_SHIFTING, 1, 1,
5379 GDI_DIRECT_DRAW, FALSE,
5380 GDI_EVENT_MASK, event_mask,
5381 GDI_CALLBACK_ACTION, HandleToolButtons,
5385 Error(ERR_EXIT, "cannot create gadget");
5387 tool_gadget[id] = gi;
5391 void FreeToolButtons()
5395 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5396 FreeGadget(tool_gadget[i]);
5399 static void UnmapToolButtons()
5403 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5404 UnmapGadget(tool_gadget[i]);
5407 static void HandleToolButtons(struct GadgetInfo *gi)
5409 request_gadget_id = gi->custom_id;
5412 static struct Mapping_EM_to_RND_object
5415 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5416 boolean is_backside; /* backside of moving element */
5422 em_object_mapping_list[] =
5425 Xblank, TRUE, FALSE,
5429 Yacid_splash_eB, FALSE, FALSE,
5430 EL_ACID_SPLASH_RIGHT, -1, -1
5433 Yacid_splash_wB, FALSE, FALSE,
5434 EL_ACID_SPLASH_LEFT, -1, -1
5437 #ifdef EM_ENGINE_BAD_ROLL
5439 Xstone_force_e, FALSE, FALSE,
5440 EL_ROCK, -1, MV_BIT_RIGHT
5443 Xstone_force_w, FALSE, FALSE,
5444 EL_ROCK, -1, MV_BIT_LEFT
5447 Xnut_force_e, FALSE, FALSE,
5448 EL_NUT, -1, MV_BIT_RIGHT
5451 Xnut_force_w, FALSE, FALSE,
5452 EL_NUT, -1, MV_BIT_LEFT
5455 Xspring_force_e, FALSE, FALSE,
5456 EL_SPRING, -1, MV_BIT_RIGHT
5459 Xspring_force_w, FALSE, FALSE,
5460 EL_SPRING, -1, MV_BIT_LEFT
5463 Xemerald_force_e, FALSE, FALSE,
5464 EL_EMERALD, -1, MV_BIT_RIGHT
5467 Xemerald_force_w, FALSE, FALSE,
5468 EL_EMERALD, -1, MV_BIT_LEFT
5471 Xdiamond_force_e, FALSE, FALSE,
5472 EL_DIAMOND, -1, MV_BIT_RIGHT
5475 Xdiamond_force_w, FALSE, FALSE,
5476 EL_DIAMOND, -1, MV_BIT_LEFT
5479 Xbomb_force_e, FALSE, FALSE,
5480 EL_BOMB, -1, MV_BIT_RIGHT
5483 Xbomb_force_w, FALSE, FALSE,
5484 EL_BOMB, -1, MV_BIT_LEFT
5486 #endif /* EM_ENGINE_BAD_ROLL */
5489 Xstone, TRUE, FALSE,
5493 Xstone_pause, FALSE, FALSE,
5497 Xstone_fall, FALSE, FALSE,
5501 Ystone_s, FALSE, FALSE,
5502 EL_ROCK, ACTION_FALLING, -1
5505 Ystone_sB, FALSE, TRUE,
5506 EL_ROCK, ACTION_FALLING, -1
5509 Ystone_e, FALSE, FALSE,
5510 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5513 Ystone_eB, FALSE, TRUE,
5514 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5517 Ystone_w, FALSE, FALSE,
5518 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5521 Ystone_wB, FALSE, TRUE,
5522 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5529 Xnut_pause, FALSE, FALSE,
5533 Xnut_fall, FALSE, FALSE,
5537 Ynut_s, FALSE, FALSE,
5538 EL_NUT, ACTION_FALLING, -1
5541 Ynut_sB, FALSE, TRUE,
5542 EL_NUT, ACTION_FALLING, -1
5545 Ynut_e, FALSE, FALSE,
5546 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5549 Ynut_eB, FALSE, TRUE,
5550 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5553 Ynut_w, FALSE, FALSE,
5554 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5557 Ynut_wB, FALSE, TRUE,
5558 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5561 Xbug_n, TRUE, FALSE,
5565 Xbug_e, TRUE, FALSE,
5566 EL_BUG_RIGHT, -1, -1
5569 Xbug_s, TRUE, FALSE,
5573 Xbug_w, TRUE, FALSE,
5577 Xbug_gon, FALSE, FALSE,
5581 Xbug_goe, FALSE, FALSE,
5582 EL_BUG_RIGHT, -1, -1
5585 Xbug_gos, FALSE, FALSE,
5589 Xbug_gow, FALSE, FALSE,
5593 Ybug_n, FALSE, FALSE,
5594 EL_BUG, ACTION_MOVING, MV_BIT_UP
5597 Ybug_nB, FALSE, TRUE,
5598 EL_BUG, ACTION_MOVING, MV_BIT_UP
5601 Ybug_e, FALSE, FALSE,
5602 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5605 Ybug_eB, FALSE, TRUE,
5606 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5609 Ybug_s, FALSE, FALSE,
5610 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5613 Ybug_sB, FALSE, TRUE,
5614 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5617 Ybug_w, FALSE, FALSE,
5618 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5621 Ybug_wB, FALSE, TRUE,
5622 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5625 Ybug_w_n, FALSE, FALSE,
5626 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5629 Ybug_n_e, FALSE, FALSE,
5630 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5633 Ybug_e_s, FALSE, FALSE,
5634 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5637 Ybug_s_w, FALSE, FALSE,
5638 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5641 Ybug_e_n, FALSE, FALSE,
5642 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5645 Ybug_s_e, FALSE, FALSE,
5646 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5649 Ybug_w_s, FALSE, FALSE,
5650 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5653 Ybug_n_w, FALSE, FALSE,
5654 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5657 Ybug_stone, FALSE, FALSE,
5658 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5661 Ybug_spring, FALSE, FALSE,
5662 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5665 Xtank_n, TRUE, FALSE,
5666 EL_SPACESHIP_UP, -1, -1
5669 Xtank_e, TRUE, FALSE,
5670 EL_SPACESHIP_RIGHT, -1, -1
5673 Xtank_s, TRUE, FALSE,
5674 EL_SPACESHIP_DOWN, -1, -1
5677 Xtank_w, TRUE, FALSE,
5678 EL_SPACESHIP_LEFT, -1, -1
5681 Xtank_gon, FALSE, FALSE,
5682 EL_SPACESHIP_UP, -1, -1
5685 Xtank_goe, FALSE, FALSE,
5686 EL_SPACESHIP_RIGHT, -1, -1
5689 Xtank_gos, FALSE, FALSE,
5690 EL_SPACESHIP_DOWN, -1, -1
5693 Xtank_gow, FALSE, FALSE,
5694 EL_SPACESHIP_LEFT, -1, -1
5697 Ytank_n, FALSE, FALSE,
5698 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5701 Ytank_nB, FALSE, TRUE,
5702 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5705 Ytank_e, FALSE, FALSE,
5706 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5709 Ytank_eB, FALSE, TRUE,
5710 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5713 Ytank_s, FALSE, FALSE,
5714 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5717 Ytank_sB, FALSE, TRUE,
5718 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5721 Ytank_w, FALSE, FALSE,
5722 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5725 Ytank_wB, FALSE, TRUE,
5726 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5729 Ytank_w_n, FALSE, FALSE,
5730 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5733 Ytank_n_e, FALSE, FALSE,
5734 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5737 Ytank_e_s, FALSE, FALSE,
5738 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5741 Ytank_s_w, FALSE, FALSE,
5742 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5745 Ytank_e_n, FALSE, FALSE,
5746 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5749 Ytank_s_e, FALSE, FALSE,
5750 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5753 Ytank_w_s, FALSE, FALSE,
5754 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5757 Ytank_n_w, FALSE, FALSE,
5758 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5761 Ytank_stone, FALSE, FALSE,
5762 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5765 Ytank_spring, FALSE, FALSE,
5766 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5769 Xandroid, TRUE, FALSE,
5770 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5773 Xandroid_1_n, FALSE, FALSE,
5774 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5777 Xandroid_2_n, FALSE, FALSE,
5778 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5781 Xandroid_1_e, FALSE, FALSE,
5782 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5785 Xandroid_2_e, FALSE, FALSE,
5786 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5789 Xandroid_1_w, FALSE, FALSE,
5790 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5793 Xandroid_2_w, FALSE, FALSE,
5794 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5797 Xandroid_1_s, FALSE, FALSE,
5798 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5801 Xandroid_2_s, FALSE, FALSE,
5802 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5805 Yandroid_n, FALSE, FALSE,
5806 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5809 Yandroid_nB, FALSE, TRUE,
5810 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5813 Yandroid_ne, FALSE, FALSE,
5814 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5817 Yandroid_neB, FALSE, TRUE,
5818 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5821 Yandroid_e, FALSE, FALSE,
5822 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5825 Yandroid_eB, FALSE, TRUE,
5826 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5829 Yandroid_se, FALSE, FALSE,
5830 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5833 Yandroid_seB, FALSE, TRUE,
5834 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5837 Yandroid_s, FALSE, FALSE,
5838 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5841 Yandroid_sB, FALSE, TRUE,
5842 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5845 Yandroid_sw, FALSE, FALSE,
5846 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5849 Yandroid_swB, FALSE, TRUE,
5850 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5853 Yandroid_w, FALSE, FALSE,
5854 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5857 Yandroid_wB, FALSE, TRUE,
5858 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5861 Yandroid_nw, FALSE, FALSE,
5862 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5865 Yandroid_nwB, FALSE, TRUE,
5866 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5869 Xspring, TRUE, FALSE,
5873 Xspring_pause, FALSE, FALSE,
5877 Xspring_e, FALSE, FALSE,
5881 Xspring_w, FALSE, FALSE,
5885 Xspring_fall, FALSE, FALSE,
5889 Yspring_s, FALSE, FALSE,
5890 EL_SPRING, ACTION_FALLING, -1
5893 Yspring_sB, FALSE, TRUE,
5894 EL_SPRING, ACTION_FALLING, -1
5897 Yspring_e, FALSE, FALSE,
5898 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5901 Yspring_eB, FALSE, TRUE,
5902 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5905 Yspring_w, FALSE, FALSE,
5906 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5909 Yspring_wB, FALSE, TRUE,
5910 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5913 Yspring_kill_e, FALSE, FALSE,
5914 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5917 Yspring_kill_eB, FALSE, TRUE,
5918 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5921 Yspring_kill_w, FALSE, FALSE,
5922 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5925 Yspring_kill_wB, FALSE, TRUE,
5926 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5929 Xeater_n, TRUE, FALSE,
5930 EL_YAMYAM_UP, -1, -1
5933 Xeater_e, TRUE, FALSE,
5934 EL_YAMYAM_RIGHT, -1, -1
5937 Xeater_w, TRUE, FALSE,
5938 EL_YAMYAM_LEFT, -1, -1
5941 Xeater_s, TRUE, FALSE,
5942 EL_YAMYAM_DOWN, -1, -1
5945 Yeater_n, FALSE, FALSE,
5946 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5949 Yeater_nB, FALSE, TRUE,
5950 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5953 Yeater_e, FALSE, FALSE,
5954 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5957 Yeater_eB, FALSE, TRUE,
5958 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5961 Yeater_s, FALSE, FALSE,
5962 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5965 Yeater_sB, FALSE, TRUE,
5966 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5969 Yeater_w, FALSE, FALSE,
5970 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5973 Yeater_wB, FALSE, TRUE,
5974 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5977 Yeater_stone, FALSE, FALSE,
5978 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5981 Yeater_spring, FALSE, FALSE,
5982 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5985 Xalien, TRUE, FALSE,
5989 Xalien_pause, FALSE, FALSE,
5993 Yalien_n, FALSE, FALSE,
5994 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5997 Yalien_nB, FALSE, TRUE,
5998 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6001 Yalien_e, FALSE, FALSE,
6002 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6005 Yalien_eB, FALSE, TRUE,
6006 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6009 Yalien_s, FALSE, FALSE,
6010 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6013 Yalien_sB, FALSE, TRUE,
6014 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6017 Yalien_w, FALSE, FALSE,
6018 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6021 Yalien_wB, FALSE, TRUE,
6022 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6025 Yalien_stone, FALSE, FALSE,
6026 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6029 Yalien_spring, FALSE, FALSE,
6030 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6033 Xemerald, TRUE, FALSE,
6037 Xemerald_pause, FALSE, FALSE,
6041 Xemerald_fall, FALSE, FALSE,
6045 Xemerald_shine, FALSE, FALSE,
6046 EL_EMERALD, ACTION_TWINKLING, -1
6049 Yemerald_s, FALSE, FALSE,
6050 EL_EMERALD, ACTION_FALLING, -1
6053 Yemerald_sB, FALSE, TRUE,
6054 EL_EMERALD, ACTION_FALLING, -1
6057 Yemerald_e, FALSE, FALSE,
6058 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6061 Yemerald_eB, FALSE, TRUE,
6062 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6065 Yemerald_w, FALSE, FALSE,
6066 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6069 Yemerald_wB, FALSE, TRUE,
6070 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6073 Yemerald_eat, FALSE, FALSE,
6074 EL_EMERALD, ACTION_COLLECTING, -1
6077 Yemerald_stone, FALSE, FALSE,
6078 EL_NUT, ACTION_BREAKING, -1
6081 Xdiamond, TRUE, FALSE,
6085 Xdiamond_pause, FALSE, FALSE,
6089 Xdiamond_fall, FALSE, FALSE,
6093 Xdiamond_shine, FALSE, FALSE,
6094 EL_DIAMOND, ACTION_TWINKLING, -1
6097 Ydiamond_s, FALSE, FALSE,
6098 EL_DIAMOND, ACTION_FALLING, -1
6101 Ydiamond_sB, FALSE, TRUE,
6102 EL_DIAMOND, ACTION_FALLING, -1
6105 Ydiamond_e, FALSE, FALSE,
6106 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6109 Ydiamond_eB, FALSE, TRUE,
6110 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6113 Ydiamond_w, FALSE, FALSE,
6114 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6117 Ydiamond_wB, FALSE, TRUE,
6118 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6121 Ydiamond_eat, FALSE, FALSE,
6122 EL_DIAMOND, ACTION_COLLECTING, -1
6125 Ydiamond_stone, FALSE, FALSE,
6126 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6129 Xdrip_fall, TRUE, FALSE,
6130 EL_AMOEBA_DROP, -1, -1
6133 Xdrip_stretch, FALSE, FALSE,
6134 EL_AMOEBA_DROP, ACTION_FALLING, -1
6137 Xdrip_stretchB, FALSE, TRUE,
6138 EL_AMOEBA_DROP, ACTION_FALLING, -1
6141 Xdrip_eat, FALSE, FALSE,
6142 EL_AMOEBA_DROP, ACTION_GROWING, -1
6145 Ydrip_s1, FALSE, FALSE,
6146 EL_AMOEBA_DROP, ACTION_FALLING, -1
6149 Ydrip_s1B, FALSE, TRUE,
6150 EL_AMOEBA_DROP, ACTION_FALLING, -1
6153 Ydrip_s2, FALSE, FALSE,
6154 EL_AMOEBA_DROP, ACTION_FALLING, -1
6157 Ydrip_s2B, FALSE, TRUE,
6158 EL_AMOEBA_DROP, ACTION_FALLING, -1
6165 Xbomb_pause, FALSE, FALSE,
6169 Xbomb_fall, FALSE, FALSE,
6173 Ybomb_s, FALSE, FALSE,
6174 EL_BOMB, ACTION_FALLING, -1
6177 Ybomb_sB, FALSE, TRUE,
6178 EL_BOMB, ACTION_FALLING, -1
6181 Ybomb_e, FALSE, FALSE,
6182 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6185 Ybomb_eB, FALSE, TRUE,
6186 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6189 Ybomb_w, FALSE, FALSE,
6190 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6193 Ybomb_wB, FALSE, TRUE,
6194 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6197 Ybomb_eat, FALSE, FALSE,
6198 EL_BOMB, ACTION_ACTIVATING, -1
6201 Xballoon, TRUE, FALSE,
6205 Yballoon_n, FALSE, FALSE,
6206 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6209 Yballoon_nB, FALSE, TRUE,
6210 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6213 Yballoon_e, FALSE, FALSE,
6214 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6217 Yballoon_eB, FALSE, TRUE,
6218 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6221 Yballoon_s, FALSE, FALSE,
6222 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6225 Yballoon_sB, FALSE, TRUE,
6226 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6229 Yballoon_w, FALSE, FALSE,
6230 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6233 Yballoon_wB, FALSE, TRUE,
6234 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6237 Xgrass, TRUE, FALSE,
6238 EL_EMC_GRASS, -1, -1
6241 Ygrass_nB, FALSE, FALSE,
6242 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6245 Ygrass_eB, FALSE, FALSE,
6246 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6249 Ygrass_sB, FALSE, FALSE,
6250 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6253 Ygrass_wB, FALSE, FALSE,
6254 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6261 Ydirt_nB, FALSE, FALSE,
6262 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6265 Ydirt_eB, FALSE, FALSE,
6266 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6269 Ydirt_sB, FALSE, FALSE,
6270 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6273 Ydirt_wB, FALSE, FALSE,
6274 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6277 Xacid_ne, TRUE, FALSE,
6278 EL_ACID_POOL_TOPRIGHT, -1, -1
6281 Xacid_se, TRUE, FALSE,
6282 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6285 Xacid_s, TRUE, FALSE,
6286 EL_ACID_POOL_BOTTOM, -1, -1
6289 Xacid_sw, TRUE, FALSE,
6290 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6293 Xacid_nw, TRUE, FALSE,
6294 EL_ACID_POOL_TOPLEFT, -1, -1
6297 Xacid_1, TRUE, FALSE,
6301 Xacid_2, FALSE, FALSE,
6305 Xacid_3, FALSE, FALSE,
6309 Xacid_4, FALSE, FALSE,
6313 Xacid_5, FALSE, FALSE,
6317 Xacid_6, FALSE, FALSE,
6321 Xacid_7, FALSE, FALSE,
6325 Xacid_8, FALSE, FALSE,
6329 Xball_1, TRUE, FALSE,
6330 EL_EMC_MAGIC_BALL, -1, -1
6333 Xball_1B, FALSE, FALSE,
6334 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6337 Xball_2, FALSE, FALSE,
6338 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6341 Xball_2B, FALSE, FALSE,
6342 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6345 Yball_eat, FALSE, FALSE,
6346 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6349 Ykey_1_eat, FALSE, FALSE,
6350 EL_EM_KEY_1, ACTION_COLLECTING, -1
6353 Ykey_2_eat, FALSE, FALSE,
6354 EL_EM_KEY_2, ACTION_COLLECTING, -1
6357 Ykey_3_eat, FALSE, FALSE,
6358 EL_EM_KEY_3, ACTION_COLLECTING, -1
6361 Ykey_4_eat, FALSE, FALSE,
6362 EL_EM_KEY_4, ACTION_COLLECTING, -1
6365 Ykey_5_eat, FALSE, FALSE,
6366 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6369 Ykey_6_eat, FALSE, FALSE,
6370 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6373 Ykey_7_eat, FALSE, FALSE,
6374 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6377 Ykey_8_eat, FALSE, FALSE,
6378 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6381 Ylenses_eat, FALSE, FALSE,
6382 EL_EMC_LENSES, ACTION_COLLECTING, -1
6385 Ymagnify_eat, FALSE, FALSE,
6386 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6389 Ygrass_eat, FALSE, FALSE,
6390 EL_EMC_GRASS, ACTION_SNAPPING, -1
6393 Ydirt_eat, FALSE, FALSE,
6394 EL_SAND, ACTION_SNAPPING, -1
6397 Xgrow_ns, TRUE, FALSE,
6398 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6401 Ygrow_ns_eat, FALSE, FALSE,
6402 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6405 Xgrow_ew, TRUE, FALSE,
6406 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6409 Ygrow_ew_eat, FALSE, FALSE,
6410 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6413 Xwonderwall, TRUE, FALSE,
6414 EL_MAGIC_WALL, -1, -1
6417 XwonderwallB, FALSE, FALSE,
6418 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6421 Xamoeba_1, TRUE, FALSE,
6422 EL_AMOEBA_DRY, ACTION_OTHER, -1
6425 Xamoeba_2, FALSE, FALSE,
6426 EL_AMOEBA_DRY, ACTION_OTHER, -1
6429 Xamoeba_3, FALSE, FALSE,
6430 EL_AMOEBA_DRY, ACTION_OTHER, -1
6433 Xamoeba_4, FALSE, FALSE,
6434 EL_AMOEBA_DRY, ACTION_OTHER, -1
6437 Xamoeba_5, TRUE, FALSE,
6438 EL_AMOEBA_WET, ACTION_OTHER, -1
6441 Xamoeba_6, FALSE, FALSE,
6442 EL_AMOEBA_WET, ACTION_OTHER, -1
6445 Xamoeba_7, FALSE, FALSE,
6446 EL_AMOEBA_WET, ACTION_OTHER, -1
6449 Xamoeba_8, FALSE, FALSE,
6450 EL_AMOEBA_WET, ACTION_OTHER, -1
6453 Xdoor_1, TRUE, FALSE,
6454 EL_EM_GATE_1, -1, -1
6457 Xdoor_2, TRUE, FALSE,
6458 EL_EM_GATE_2, -1, -1
6461 Xdoor_3, TRUE, FALSE,
6462 EL_EM_GATE_3, -1, -1
6465 Xdoor_4, TRUE, FALSE,
6466 EL_EM_GATE_4, -1, -1
6469 Xdoor_5, TRUE, FALSE,
6470 EL_EMC_GATE_5, -1, -1
6473 Xdoor_6, TRUE, FALSE,
6474 EL_EMC_GATE_6, -1, -1
6477 Xdoor_7, TRUE, FALSE,
6478 EL_EMC_GATE_7, -1, -1
6481 Xdoor_8, TRUE, FALSE,
6482 EL_EMC_GATE_8, -1, -1
6485 Xkey_1, TRUE, FALSE,
6489 Xkey_2, TRUE, FALSE,
6493 Xkey_3, TRUE, FALSE,
6497 Xkey_4, TRUE, FALSE,
6501 Xkey_5, TRUE, FALSE,
6502 EL_EMC_KEY_5, -1, -1
6505 Xkey_6, TRUE, FALSE,
6506 EL_EMC_KEY_6, -1, -1
6509 Xkey_7, TRUE, FALSE,
6510 EL_EMC_KEY_7, -1, -1
6513 Xkey_8, TRUE, FALSE,
6514 EL_EMC_KEY_8, -1, -1
6517 Xwind_n, TRUE, FALSE,
6518 EL_BALLOON_SWITCH_UP, -1, -1
6521 Xwind_e, TRUE, FALSE,
6522 EL_BALLOON_SWITCH_RIGHT, -1, -1
6525 Xwind_s, TRUE, FALSE,
6526 EL_BALLOON_SWITCH_DOWN, -1, -1
6529 Xwind_w, TRUE, FALSE,
6530 EL_BALLOON_SWITCH_LEFT, -1, -1
6533 Xwind_nesw, TRUE, FALSE,
6534 EL_BALLOON_SWITCH_ANY, -1, -1
6537 Xwind_stop, TRUE, FALSE,
6538 EL_BALLOON_SWITCH_NONE, -1, -1
6542 EL_EM_EXIT_CLOSED, -1, -1
6545 Xexit_1, TRUE, FALSE,
6546 EL_EM_EXIT_OPEN, -1, -1
6549 Xexit_2, FALSE, FALSE,
6550 EL_EM_EXIT_OPEN, -1, -1
6553 Xexit_3, FALSE, FALSE,
6554 EL_EM_EXIT_OPEN, -1, -1
6557 Xdynamite, TRUE, FALSE,
6558 EL_EM_DYNAMITE, -1, -1
6561 Ydynamite_eat, FALSE, FALSE,
6562 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6565 Xdynamite_1, TRUE, FALSE,
6566 EL_EM_DYNAMITE_ACTIVE, -1, -1
6569 Xdynamite_2, FALSE, FALSE,
6570 EL_EM_DYNAMITE_ACTIVE, -1, -1
6573 Xdynamite_3, FALSE, FALSE,
6574 EL_EM_DYNAMITE_ACTIVE, -1, -1
6577 Xdynamite_4, FALSE, FALSE,
6578 EL_EM_DYNAMITE_ACTIVE, -1, -1
6581 Xbumper, TRUE, FALSE,
6582 EL_EMC_SPRING_BUMPER, -1, -1
6585 XbumperB, FALSE, FALSE,
6586 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6589 Xwheel, TRUE, FALSE,
6590 EL_ROBOT_WHEEL, -1, -1
6593 XwheelB, FALSE, FALSE,
6594 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6597 Xswitch, TRUE, FALSE,
6598 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6601 XswitchB, FALSE, FALSE,
6602 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6606 EL_QUICKSAND_EMPTY, -1, -1
6609 Xsand_stone, TRUE, FALSE,
6610 EL_QUICKSAND_FULL, -1, -1
6613 Xsand_stonein_1, FALSE, TRUE,
6614 EL_ROCK, ACTION_FILLING, -1
6617 Xsand_stonein_2, FALSE, TRUE,
6618 EL_ROCK, ACTION_FILLING, -1
6621 Xsand_stonein_3, FALSE, TRUE,
6622 EL_ROCK, ACTION_FILLING, -1
6625 Xsand_stonein_4, FALSE, TRUE,
6626 EL_ROCK, ACTION_FILLING, -1
6629 Xsand_stonesand_1, FALSE, FALSE,
6630 EL_QUICKSAND_EMPTYING, -1, -1
6633 Xsand_stonesand_2, FALSE, FALSE,
6634 EL_QUICKSAND_EMPTYING, -1, -1
6637 Xsand_stonesand_3, FALSE, FALSE,
6638 EL_QUICKSAND_EMPTYING, -1, -1
6641 Xsand_stonesand_4, FALSE, FALSE,
6642 EL_QUICKSAND_EMPTYING, -1, -1
6645 Xsand_stonesand_quickout_1, FALSE, FALSE,
6646 EL_QUICKSAND_EMPTYING, -1, -1
6649 Xsand_stonesand_quickout_2, FALSE, FALSE,
6650 EL_QUICKSAND_EMPTYING, -1, -1
6653 Xsand_stoneout_1, FALSE, FALSE,
6654 EL_ROCK, ACTION_EMPTYING, -1
6657 Xsand_stoneout_2, FALSE, FALSE,
6658 EL_ROCK, ACTION_EMPTYING, -1
6661 Xsand_sandstone_1, FALSE, FALSE,
6662 EL_QUICKSAND_FILLING, -1, -1
6665 Xsand_sandstone_2, FALSE, FALSE,
6666 EL_QUICKSAND_FILLING, -1, -1
6669 Xsand_sandstone_3, FALSE, FALSE,
6670 EL_QUICKSAND_FILLING, -1, -1
6673 Xsand_sandstone_4, FALSE, FALSE,
6674 EL_QUICKSAND_FILLING, -1, -1
6677 Xplant, TRUE, FALSE,
6678 EL_EMC_PLANT, -1, -1
6681 Yplant, FALSE, FALSE,
6682 EL_EMC_PLANT, -1, -1
6685 Xlenses, TRUE, FALSE,
6686 EL_EMC_LENSES, -1, -1
6689 Xmagnify, TRUE, FALSE,
6690 EL_EMC_MAGNIFIER, -1, -1
6693 Xdripper, TRUE, FALSE,
6694 EL_EMC_DRIPPER, -1, -1
6697 XdripperB, FALSE, FALSE,
6698 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6701 Xfake_blank, TRUE, FALSE,
6702 EL_INVISIBLE_WALL, -1, -1
6705 Xfake_blankB, FALSE, FALSE,
6706 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6709 Xfake_grass, TRUE, FALSE,
6710 EL_EMC_FAKE_GRASS, -1, -1
6713 Xfake_grassB, FALSE, FALSE,
6714 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6717 Xfake_door_1, TRUE, FALSE,
6718 EL_EM_GATE_1_GRAY, -1, -1
6721 Xfake_door_2, TRUE, FALSE,
6722 EL_EM_GATE_2_GRAY, -1, -1
6725 Xfake_door_3, TRUE, FALSE,
6726 EL_EM_GATE_3_GRAY, -1, -1
6729 Xfake_door_4, TRUE, FALSE,
6730 EL_EM_GATE_4_GRAY, -1, -1
6733 Xfake_door_5, TRUE, FALSE,
6734 EL_EMC_GATE_5_GRAY, -1, -1
6737 Xfake_door_6, TRUE, FALSE,
6738 EL_EMC_GATE_6_GRAY, -1, -1
6741 Xfake_door_7, TRUE, FALSE,
6742 EL_EMC_GATE_7_GRAY, -1, -1
6745 Xfake_door_8, TRUE, FALSE,
6746 EL_EMC_GATE_8_GRAY, -1, -1
6749 Xfake_acid_1, TRUE, FALSE,
6750 EL_EMC_FAKE_ACID, -1, -1
6753 Xfake_acid_2, FALSE, FALSE,
6754 EL_EMC_FAKE_ACID, -1, -1
6757 Xfake_acid_3, FALSE, FALSE,
6758 EL_EMC_FAKE_ACID, -1, -1
6761 Xfake_acid_4, FALSE, FALSE,
6762 EL_EMC_FAKE_ACID, -1, -1
6765 Xfake_acid_5, FALSE, FALSE,
6766 EL_EMC_FAKE_ACID, -1, -1
6769 Xfake_acid_6, FALSE, FALSE,
6770 EL_EMC_FAKE_ACID, -1, -1
6773 Xfake_acid_7, FALSE, FALSE,
6774 EL_EMC_FAKE_ACID, -1, -1
6777 Xfake_acid_8, FALSE, FALSE,
6778 EL_EMC_FAKE_ACID, -1, -1
6781 Xsteel_1, TRUE, FALSE,
6782 EL_STEELWALL, -1, -1
6785 Xsteel_2, TRUE, FALSE,
6786 EL_EMC_STEELWALL_2, -1, -1
6789 Xsteel_3, TRUE, FALSE,
6790 EL_EMC_STEELWALL_3, -1, -1
6793 Xsteel_4, TRUE, FALSE,
6794 EL_EMC_STEELWALL_4, -1, -1
6797 Xwall_1, TRUE, FALSE,
6801 Xwall_2, TRUE, FALSE,
6802 EL_EMC_WALL_14, -1, -1
6805 Xwall_3, TRUE, FALSE,
6806 EL_EMC_WALL_15, -1, -1
6809 Xwall_4, TRUE, FALSE,
6810 EL_EMC_WALL_16, -1, -1
6813 Xround_wall_1, TRUE, FALSE,
6814 EL_WALL_SLIPPERY, -1, -1
6817 Xround_wall_2, TRUE, FALSE,
6818 EL_EMC_WALL_SLIPPERY_2, -1, -1
6821 Xround_wall_3, TRUE, FALSE,
6822 EL_EMC_WALL_SLIPPERY_3, -1, -1
6825 Xround_wall_4, TRUE, FALSE,
6826 EL_EMC_WALL_SLIPPERY_4, -1, -1
6829 Xdecor_1, TRUE, FALSE,
6830 EL_EMC_WALL_8, -1, -1
6833 Xdecor_2, TRUE, FALSE,
6834 EL_EMC_WALL_6, -1, -1
6837 Xdecor_3, TRUE, FALSE,
6838 EL_EMC_WALL_4, -1, -1
6841 Xdecor_4, TRUE, FALSE,
6842 EL_EMC_WALL_7, -1, -1
6845 Xdecor_5, TRUE, FALSE,
6846 EL_EMC_WALL_5, -1, -1
6849 Xdecor_6, TRUE, FALSE,
6850 EL_EMC_WALL_9, -1, -1
6853 Xdecor_7, TRUE, FALSE,
6854 EL_EMC_WALL_10, -1, -1
6857 Xdecor_8, TRUE, FALSE,
6858 EL_EMC_WALL_1, -1, -1
6861 Xdecor_9, TRUE, FALSE,
6862 EL_EMC_WALL_2, -1, -1
6865 Xdecor_10, TRUE, FALSE,
6866 EL_EMC_WALL_3, -1, -1
6869 Xdecor_11, TRUE, FALSE,
6870 EL_EMC_WALL_11, -1, -1
6873 Xdecor_12, TRUE, FALSE,
6874 EL_EMC_WALL_12, -1, -1
6877 Xalpha_0, TRUE, FALSE,
6878 EL_CHAR('0'), -1, -1
6881 Xalpha_1, TRUE, FALSE,
6882 EL_CHAR('1'), -1, -1
6885 Xalpha_2, TRUE, FALSE,
6886 EL_CHAR('2'), -1, -1
6889 Xalpha_3, TRUE, FALSE,
6890 EL_CHAR('3'), -1, -1
6893 Xalpha_4, TRUE, FALSE,
6894 EL_CHAR('4'), -1, -1
6897 Xalpha_5, TRUE, FALSE,
6898 EL_CHAR('5'), -1, -1
6901 Xalpha_6, TRUE, FALSE,
6902 EL_CHAR('6'), -1, -1
6905 Xalpha_7, TRUE, FALSE,
6906 EL_CHAR('7'), -1, -1
6909 Xalpha_8, TRUE, FALSE,
6910 EL_CHAR('8'), -1, -1
6913 Xalpha_9, TRUE, FALSE,
6914 EL_CHAR('9'), -1, -1
6917 Xalpha_excla, TRUE, FALSE,
6918 EL_CHAR('!'), -1, -1
6921 Xalpha_quote, TRUE, FALSE,
6922 EL_CHAR('"'), -1, -1
6925 Xalpha_comma, TRUE, FALSE,
6926 EL_CHAR(','), -1, -1
6929 Xalpha_minus, TRUE, FALSE,
6930 EL_CHAR('-'), -1, -1
6933 Xalpha_perio, TRUE, FALSE,
6934 EL_CHAR('.'), -1, -1
6937 Xalpha_colon, TRUE, FALSE,
6938 EL_CHAR(':'), -1, -1
6941 Xalpha_quest, TRUE, FALSE,
6942 EL_CHAR('?'), -1, -1
6945 Xalpha_a, TRUE, FALSE,
6946 EL_CHAR('A'), -1, -1
6949 Xalpha_b, TRUE, FALSE,
6950 EL_CHAR('B'), -1, -1
6953 Xalpha_c, TRUE, FALSE,
6954 EL_CHAR('C'), -1, -1
6957 Xalpha_d, TRUE, FALSE,
6958 EL_CHAR('D'), -1, -1
6961 Xalpha_e, TRUE, FALSE,
6962 EL_CHAR('E'), -1, -1
6965 Xalpha_f, TRUE, FALSE,
6966 EL_CHAR('F'), -1, -1
6969 Xalpha_g, TRUE, FALSE,
6970 EL_CHAR('G'), -1, -1
6973 Xalpha_h, TRUE, FALSE,
6974 EL_CHAR('H'), -1, -1
6977 Xalpha_i, TRUE, FALSE,
6978 EL_CHAR('I'), -1, -1
6981 Xalpha_j, TRUE, FALSE,
6982 EL_CHAR('J'), -1, -1
6985 Xalpha_k, TRUE, FALSE,
6986 EL_CHAR('K'), -1, -1
6989 Xalpha_l, TRUE, FALSE,
6990 EL_CHAR('L'), -1, -1
6993 Xalpha_m, TRUE, FALSE,
6994 EL_CHAR('M'), -1, -1
6997 Xalpha_n, TRUE, FALSE,
6998 EL_CHAR('N'), -1, -1
7001 Xalpha_o, TRUE, FALSE,
7002 EL_CHAR('O'), -1, -1
7005 Xalpha_p, TRUE, FALSE,
7006 EL_CHAR('P'), -1, -1
7009 Xalpha_q, TRUE, FALSE,
7010 EL_CHAR('Q'), -1, -1
7013 Xalpha_r, TRUE, FALSE,
7014 EL_CHAR('R'), -1, -1
7017 Xalpha_s, TRUE, FALSE,
7018 EL_CHAR('S'), -1, -1
7021 Xalpha_t, TRUE, FALSE,
7022 EL_CHAR('T'), -1, -1
7025 Xalpha_u, TRUE, FALSE,
7026 EL_CHAR('U'), -1, -1
7029 Xalpha_v, TRUE, FALSE,
7030 EL_CHAR('V'), -1, -1
7033 Xalpha_w, TRUE, FALSE,
7034 EL_CHAR('W'), -1, -1
7037 Xalpha_x, TRUE, FALSE,
7038 EL_CHAR('X'), -1, -1
7041 Xalpha_y, TRUE, FALSE,
7042 EL_CHAR('Y'), -1, -1
7045 Xalpha_z, TRUE, FALSE,
7046 EL_CHAR('Z'), -1, -1
7049 Xalpha_arrow_e, TRUE, FALSE,
7050 EL_CHAR('>'), -1, -1
7053 Xalpha_arrow_w, TRUE, FALSE,
7054 EL_CHAR('<'), -1, -1
7057 Xalpha_copyr, TRUE, FALSE,
7058 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7062 Xboom_bug, FALSE, FALSE,
7063 EL_BUG, ACTION_EXPLODING, -1
7066 Xboom_bomb, FALSE, FALSE,
7067 EL_BOMB, ACTION_EXPLODING, -1
7070 Xboom_android, FALSE, FALSE,
7071 EL_EMC_ANDROID, ACTION_OTHER, -1
7074 Xboom_1, FALSE, FALSE,
7075 EL_DEFAULT, ACTION_EXPLODING, -1
7078 Xboom_2, FALSE, FALSE,
7079 EL_DEFAULT, ACTION_EXPLODING, -1
7082 Znormal, FALSE, FALSE,
7086 Zdynamite, FALSE, FALSE,
7090 Zplayer, FALSE, FALSE,
7094 ZBORDER, FALSE, FALSE,
7104 static struct Mapping_EM_to_RND_player
7113 em_player_mapping_list[] =
7117 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7121 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7125 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7129 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7133 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7137 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7141 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7145 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7149 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7153 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7157 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7161 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7165 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7169 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7173 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7177 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7181 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7185 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7189 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7193 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7197 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7201 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7205 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7209 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7213 EL_PLAYER_1, ACTION_DEFAULT, -1,
7217 EL_PLAYER_2, ACTION_DEFAULT, -1,
7221 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7225 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7229 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7233 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7237 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7241 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7245 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7249 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7253 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7257 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7261 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7265 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7269 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7273 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7277 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7281 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7285 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7289 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7293 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7297 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7301 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7305 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7309 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7313 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7317 EL_PLAYER_3, ACTION_DEFAULT, -1,
7321 EL_PLAYER_4, ACTION_DEFAULT, -1,
7330 int map_element_RND_to_EM(int element_rnd)
7332 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7333 static boolean mapping_initialized = FALSE;
7335 if (!mapping_initialized)
7339 /* return "Xalpha_quest" for all undefined elements in mapping array */
7340 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7341 mapping_RND_to_EM[i] = Xalpha_quest;
7343 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7344 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7345 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7346 em_object_mapping_list[i].element_em;
7348 mapping_initialized = TRUE;
7351 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7352 return mapping_RND_to_EM[element_rnd];
7354 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7359 int map_element_EM_to_RND(int element_em)
7361 static unsigned short mapping_EM_to_RND[TILE_MAX];
7362 static boolean mapping_initialized = FALSE;
7364 if (!mapping_initialized)
7368 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7369 for (i = 0; i < TILE_MAX; i++)
7370 mapping_EM_to_RND[i] = EL_UNKNOWN;
7372 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7373 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7374 em_object_mapping_list[i].element_rnd;
7376 mapping_initialized = TRUE;
7379 if (element_em >= 0 && element_em < TILE_MAX)
7380 return mapping_EM_to_RND[element_em];
7382 Error(ERR_WARN, "invalid EM level element %d", element_em);
7387 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7389 struct LevelInfo_EM *level_em = level->native_em_level;
7390 struct LEVEL *lev = level_em->lev;
7393 for (i = 0; i < TILE_MAX; i++)
7394 lev->android_array[i] = Xblank;
7396 for (i = 0; i < level->num_android_clone_elements; i++)
7398 int element_rnd = level->android_clone_element[i];
7399 int element_em = map_element_RND_to_EM(element_rnd);
7401 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7402 if (em_object_mapping_list[j].element_rnd == element_rnd)
7403 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7407 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7409 struct LevelInfo_EM *level_em = level->native_em_level;
7410 struct LEVEL *lev = level_em->lev;
7413 level->num_android_clone_elements = 0;
7415 for (i = 0; i < TILE_MAX; i++)
7417 int element_em = lev->android_array[i];
7419 boolean element_found = FALSE;
7421 if (element_em == Xblank)
7424 element_rnd = map_element_EM_to_RND(element_em);
7426 for (j = 0; j < level->num_android_clone_elements; j++)
7427 if (level->android_clone_element[j] == element_rnd)
7428 element_found = TRUE;
7432 level->android_clone_element[level->num_android_clone_elements++] =
7435 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7440 if (level->num_android_clone_elements == 0)
7442 level->num_android_clone_elements = 1;
7443 level->android_clone_element[0] = EL_EMPTY;
7447 int map_direction_RND_to_EM(int direction)
7449 return (direction == MV_UP ? 0 :
7450 direction == MV_RIGHT ? 1 :
7451 direction == MV_DOWN ? 2 :
7452 direction == MV_LEFT ? 3 :
7456 int map_direction_EM_to_RND(int direction)
7458 return (direction == 0 ? MV_UP :
7459 direction == 1 ? MV_RIGHT :
7460 direction == 2 ? MV_DOWN :
7461 direction == 3 ? MV_LEFT :
7465 int map_element_RND_to_SP(int element_rnd)
7467 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7469 if (element_rnd >= EL_SP_START &&
7470 element_rnd <= EL_SP_END)
7471 element_sp = element_rnd - EL_SP_START;
7472 else if (element_rnd == EL_EMPTY_SPACE)
7474 else if (element_rnd == EL_INVISIBLE_WALL)
7480 int map_element_SP_to_RND(int element_sp)
7482 int element_rnd = EL_UNKNOWN;
7484 if (element_sp >= 0x00 &&
7486 element_rnd = EL_SP_START + element_sp;
7487 else if (element_sp == 0x28)
7488 element_rnd = EL_INVISIBLE_WALL;
7493 int map_action_SP_to_RND(int action_sp)
7497 case actActive: return ACTION_ACTIVE;
7498 case actImpact: return ACTION_IMPACT;
7499 case actExploding: return ACTION_EXPLODING;
7500 case actDigging: return ACTION_DIGGING;
7501 case actSnapping: return ACTION_SNAPPING;
7502 case actCollecting: return ACTION_COLLECTING;
7503 case actPassing: return ACTION_PASSING;
7504 case actPushing: return ACTION_PUSHING;
7505 case actDropping: return ACTION_DROPPING;
7507 default: return ACTION_DEFAULT;
7511 int map_element_RND_to_MM(int element_rnd)
7513 return (element_rnd >= EL_MM_START_1 &&
7514 element_rnd <= EL_MM_END_1 ?
7515 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7517 element_rnd >= EL_MM_START_2 &&
7518 element_rnd <= EL_MM_END_2 ?
7519 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7521 element_rnd >= EL_CHAR_START &&
7522 element_rnd <= EL_CHAR_END ?
7523 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7525 element_rnd >= EL_MM_RUNTIME_START &&
7526 element_rnd <= EL_MM_RUNTIME_END ?
7527 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7529 element_rnd >= EL_MM_DUMMY_START &&
7530 element_rnd <= EL_MM_DUMMY_END ?
7531 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7533 EL_MM_EMPTY_NATIVE);
7536 int map_element_MM_to_RND(int element_mm)
7538 return (element_mm == EL_MM_EMPTY_NATIVE ||
7539 element_mm == EL_DF_EMPTY_NATIVE ?
7542 element_mm >= EL_MM_START_1_NATIVE &&
7543 element_mm <= EL_MM_END_1_NATIVE ?
7544 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7546 element_mm >= EL_MM_START_2_NATIVE &&
7547 element_mm <= EL_MM_END_2_NATIVE ?
7548 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7550 element_mm >= EL_MM_CHAR_START_NATIVE &&
7551 element_mm <= EL_MM_CHAR_END_NATIVE ?
7552 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7554 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7555 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7556 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7558 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7559 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7560 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7565 int map_action_MM_to_RND(int action_mm)
7567 /* all MM actions are defined to exactly match their RND counterparts */
7571 int map_sound_MM_to_RND(int sound_mm)
7575 case SND_MM_GAME_LEVELTIME_CHARGING:
7576 return SND_GAME_LEVELTIME_CHARGING;
7578 case SND_MM_GAME_HEALTH_CHARGING:
7579 return SND_GAME_HEALTH_CHARGING;
7582 return SND_UNDEFINED;
7586 int map_mm_wall_element(int element)
7588 return (element >= EL_MM_STEEL_WALL_START &&
7589 element <= EL_MM_STEEL_WALL_END ?
7592 element >= EL_MM_WOODEN_WALL_START &&
7593 element <= EL_MM_WOODEN_WALL_END ?
7596 element >= EL_MM_ICE_WALL_START &&
7597 element <= EL_MM_ICE_WALL_END ?
7600 element >= EL_MM_AMOEBA_WALL_START &&
7601 element <= EL_MM_AMOEBA_WALL_END ?
7604 element >= EL_DF_STEEL_WALL_START &&
7605 element <= EL_DF_STEEL_WALL_END ?
7608 element >= EL_DF_WOODEN_WALL_START &&
7609 element <= EL_DF_WOODEN_WALL_END ?
7615 int map_mm_wall_element_editor(int element)
7619 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7620 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7621 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7622 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7623 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7624 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7626 default: return element;
7630 int get_next_element(int element)
7634 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7635 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7636 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7637 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7638 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7639 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7640 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7641 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7642 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7643 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7644 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7646 default: return element;
7650 int el2img_mm(int element_mm)
7652 return el2img(map_element_MM_to_RND(element_mm));
7655 int el_act_dir2img(int element, int action, int direction)
7657 element = GFX_ELEMENT(element);
7658 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7660 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7661 return element_info[element].direction_graphic[action][direction];
7664 static int el_act_dir2crm(int element, int action, int direction)
7666 element = GFX_ELEMENT(element);
7667 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7669 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7670 return element_info[element].direction_crumbled[action][direction];
7673 int el_act2img(int element, int action)
7675 element = GFX_ELEMENT(element);
7677 return element_info[element].graphic[action];
7680 int el_act2crm(int element, int action)
7682 element = GFX_ELEMENT(element);
7684 return element_info[element].crumbled[action];
7687 int el_dir2img(int element, int direction)
7689 element = GFX_ELEMENT(element);
7691 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7694 int el2baseimg(int element)
7696 return element_info[element].graphic[ACTION_DEFAULT];
7699 int el2img(int element)
7701 element = GFX_ELEMENT(element);
7703 return element_info[element].graphic[ACTION_DEFAULT];
7706 int el2edimg(int element)
7708 element = GFX_ELEMENT(element);
7710 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7713 int el2preimg(int element)
7715 element = GFX_ELEMENT(element);
7717 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7720 int el2panelimg(int element)
7722 element = GFX_ELEMENT(element);
7724 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7727 int font2baseimg(int font_nr)
7729 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7732 int getBeltNrFromBeltElement(int element)
7734 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7735 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7736 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7739 int getBeltNrFromBeltActiveElement(int element)
7741 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7742 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7743 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7746 int getBeltNrFromBeltSwitchElement(int element)
7748 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7749 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7750 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7753 int getBeltDirNrFromBeltElement(int element)
7755 static int belt_base_element[4] =
7757 EL_CONVEYOR_BELT_1_LEFT,
7758 EL_CONVEYOR_BELT_2_LEFT,
7759 EL_CONVEYOR_BELT_3_LEFT,
7760 EL_CONVEYOR_BELT_4_LEFT
7763 int belt_nr = getBeltNrFromBeltElement(element);
7764 int belt_dir_nr = element - belt_base_element[belt_nr];
7766 return (belt_dir_nr % 3);
7769 int getBeltDirNrFromBeltSwitchElement(int element)
7771 static int belt_base_element[4] =
7773 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7774 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7775 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7776 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7779 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7780 int belt_dir_nr = element - belt_base_element[belt_nr];
7782 return (belt_dir_nr % 3);
7785 int getBeltDirFromBeltElement(int element)
7787 static int belt_move_dir[3] =
7794 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7796 return belt_move_dir[belt_dir_nr];
7799 int getBeltDirFromBeltSwitchElement(int element)
7801 static int belt_move_dir[3] =
7808 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7810 return belt_move_dir[belt_dir_nr];
7813 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7815 static int belt_base_element[4] =
7817 EL_CONVEYOR_BELT_1_LEFT,
7818 EL_CONVEYOR_BELT_2_LEFT,
7819 EL_CONVEYOR_BELT_3_LEFT,
7820 EL_CONVEYOR_BELT_4_LEFT
7823 return belt_base_element[belt_nr] + belt_dir_nr;
7826 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7828 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7830 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7833 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7835 static int belt_base_element[4] =
7837 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7838 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7839 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7840 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7843 return belt_base_element[belt_nr] + belt_dir_nr;
7846 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7848 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7850 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7853 boolean getTeamMode_EM()
7855 return game.team_mode;
7858 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7860 int game_frame_delay_value;
7862 game_frame_delay_value =
7863 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7864 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7867 if (tape.playing && tape.warp_forward && !tape.pausing)
7868 game_frame_delay_value = 0;
7870 return game_frame_delay_value;
7873 unsigned int InitRND(int seed)
7875 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7876 return InitEngineRandom_EM(seed);
7877 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7878 return InitEngineRandom_SP(seed);
7879 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7880 return InitEngineRandom_MM(seed);
7882 return InitEngineRandom_RND(seed);
7885 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7886 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7888 inline static int get_effective_element_EM(int tile, int frame_em)
7890 int element = object_mapping[tile].element_rnd;
7891 int action = object_mapping[tile].action;
7892 boolean is_backside = object_mapping[tile].is_backside;
7893 boolean action_removing = (action == ACTION_DIGGING ||
7894 action == ACTION_SNAPPING ||
7895 action == ACTION_COLLECTING);
7901 case Yacid_splash_eB:
7902 case Yacid_splash_wB:
7903 return (frame_em > 5 ? EL_EMPTY : element);
7909 else /* frame_em == 7 */
7913 case Yacid_splash_eB:
7914 case Yacid_splash_wB:
7917 case Yemerald_stone:
7920 case Ydiamond_stone:
7924 case Xdrip_stretchB:
7943 case Xsand_stonein_1:
7944 case Xsand_stonein_2:
7945 case Xsand_stonein_3:
7946 case Xsand_stonein_4:
7950 return (is_backside || action_removing ? EL_EMPTY : element);
7955 inline static boolean check_linear_animation_EM(int tile)
7959 case Xsand_stonesand_1:
7960 case Xsand_stonesand_quickout_1:
7961 case Xsand_sandstone_1:
7962 case Xsand_stonein_1:
7963 case Xsand_stoneout_1:
7982 case Yacid_splash_eB:
7983 case Yacid_splash_wB:
7984 case Yemerald_stone:
7991 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7992 boolean has_crumbled_graphics,
7993 int crumbled, int sync_frame)
7995 /* if element can be crumbled, but certain action graphics are just empty
7996 space (like instantly snapping sand to empty space in 1 frame), do not
7997 treat these empty space graphics as crumbled graphics in EMC engine */
7998 if (crumbled == IMG_EMPTY_SPACE)
7999 has_crumbled_graphics = FALSE;
8001 if (has_crumbled_graphics)
8003 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8004 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8005 g_crumbled->anim_delay,
8006 g_crumbled->anim_mode,
8007 g_crumbled->anim_start_frame,
8010 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8011 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8013 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8014 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8016 g_em->has_crumbled_graphics = TRUE;
8020 g_em->crumbled_bitmap = NULL;
8021 g_em->crumbled_src_x = 0;
8022 g_em->crumbled_src_y = 0;
8023 g_em->crumbled_border_size = 0;
8024 g_em->crumbled_tile_size = 0;
8026 g_em->has_crumbled_graphics = FALSE;
8030 void ResetGfxAnimation_EM(int x, int y, int tile)
8035 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8036 int tile, int frame_em, int x, int y)
8038 int action = object_mapping[tile].action;
8039 int direction = object_mapping[tile].direction;
8040 int effective_element = get_effective_element_EM(tile, frame_em);
8041 int graphic = (direction == MV_NONE ?
8042 el_act2img(effective_element, action) :
8043 el_act_dir2img(effective_element, action, direction));
8044 struct GraphicInfo *g = &graphic_info[graphic];
8046 boolean action_removing = (action == ACTION_DIGGING ||
8047 action == ACTION_SNAPPING ||
8048 action == ACTION_COLLECTING);
8049 boolean action_moving = (action == ACTION_FALLING ||
8050 action == ACTION_MOVING ||
8051 action == ACTION_PUSHING ||
8052 action == ACTION_EATING ||
8053 action == ACTION_FILLING ||
8054 action == ACTION_EMPTYING);
8055 boolean action_falling = (action == ACTION_FALLING ||
8056 action == ACTION_FILLING ||
8057 action == ACTION_EMPTYING);
8059 /* special case: graphic uses "2nd movement tile" and has defined
8060 7 frames for movement animation (or less) => use default graphic
8061 for last (8th) frame which ends the movement animation */
8062 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8064 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
8065 graphic = (direction == MV_NONE ?
8066 el_act2img(effective_element, action) :
8067 el_act_dir2img(effective_element, action, direction));
8069 g = &graphic_info[graphic];
8072 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8076 else if (action_moving)
8078 boolean is_backside = object_mapping[tile].is_backside;
8082 int direction = object_mapping[tile].direction;
8083 int move_dir = (action_falling ? MV_DOWN : direction);
8088 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
8089 if (g->double_movement && frame_em == 0)
8093 if (move_dir == MV_LEFT)
8094 GfxFrame[x - 1][y] = GfxFrame[x][y];
8095 else if (move_dir == MV_RIGHT)
8096 GfxFrame[x + 1][y] = GfxFrame[x][y];
8097 else if (move_dir == MV_UP)
8098 GfxFrame[x][y - 1] = GfxFrame[x][y];
8099 else if (move_dir == MV_DOWN)
8100 GfxFrame[x][y + 1] = GfxFrame[x][y];
8107 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
8108 if (tile == Xsand_stonesand_quickout_1 ||
8109 tile == Xsand_stonesand_quickout_2)
8113 if (graphic_info[graphic].anim_global_sync)
8114 sync_frame = FrameCounter;
8115 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8116 sync_frame = GfxFrame[x][y];
8118 sync_frame = 0; /* playfield border (pseudo steel) */
8120 SetRandomAnimationValue(x, y);
8122 int frame = getAnimationFrame(g->anim_frames,
8125 g->anim_start_frame,
8128 g_em->unique_identifier =
8129 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8132 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8133 int tile, int frame_em, int x, int y)
8135 int action = object_mapping[tile].action;
8136 int direction = object_mapping[tile].direction;
8137 boolean is_backside = object_mapping[tile].is_backside;
8138 int effective_element = get_effective_element_EM(tile, frame_em);
8139 int effective_action = action;
8140 int graphic = (direction == MV_NONE ?
8141 el_act2img(effective_element, effective_action) :
8142 el_act_dir2img(effective_element, effective_action,
8144 int crumbled = (direction == MV_NONE ?
8145 el_act2crm(effective_element, effective_action) :
8146 el_act_dir2crm(effective_element, effective_action,
8148 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8149 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8150 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8151 struct GraphicInfo *g = &graphic_info[graphic];
8154 /* special case: graphic uses "2nd movement tile" and has defined
8155 7 frames for movement animation (or less) => use default graphic
8156 for last (8th) frame which ends the movement animation */
8157 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8159 effective_action = ACTION_DEFAULT;
8160 graphic = (direction == MV_NONE ?
8161 el_act2img(effective_element, effective_action) :
8162 el_act_dir2img(effective_element, effective_action,
8164 crumbled = (direction == MV_NONE ?
8165 el_act2crm(effective_element, effective_action) :
8166 el_act_dir2crm(effective_element, effective_action,
8169 g = &graphic_info[graphic];
8172 if (graphic_info[graphic].anim_global_sync)
8173 sync_frame = FrameCounter;
8174 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8175 sync_frame = GfxFrame[x][y];
8177 sync_frame = 0; /* playfield border (pseudo steel) */
8179 SetRandomAnimationValue(x, y);
8181 int frame = getAnimationFrame(g->anim_frames,
8184 g->anim_start_frame,
8187 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8188 g->double_movement && is_backside);
8190 /* (updating the "crumbled" graphic definitions is probably not really needed,
8191 as animations for crumbled graphics can't be longer than one EMC cycle) */
8192 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8196 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8197 int player_nr, int anim, int frame_em)
8199 int element = player_mapping[player_nr][anim].element_rnd;
8200 int action = player_mapping[player_nr][anim].action;
8201 int direction = player_mapping[player_nr][anim].direction;
8202 int graphic = (direction == MV_NONE ?
8203 el_act2img(element, action) :
8204 el_act_dir2img(element, action, direction));
8205 struct GraphicInfo *g = &graphic_info[graphic];
8208 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8210 stored_player[player_nr].StepFrame = frame_em;
8212 sync_frame = stored_player[player_nr].Frame;
8214 int frame = getAnimationFrame(g->anim_frames,
8217 g->anim_start_frame,
8220 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8221 &g_em->src_x, &g_em->src_y, FALSE);
8224 void InitGraphicInfo_EM(void)
8229 int num_em_gfx_errors = 0;
8231 if (graphic_info_em_object[0][0].bitmap == NULL)
8233 /* EM graphics not yet initialized in em_open_all() */
8238 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8241 /* always start with reliable default values */
8242 for (i = 0; i < TILE_MAX; i++)
8244 object_mapping[i].element_rnd = EL_UNKNOWN;
8245 object_mapping[i].is_backside = FALSE;
8246 object_mapping[i].action = ACTION_DEFAULT;
8247 object_mapping[i].direction = MV_NONE;
8250 /* always start with reliable default values */
8251 for (p = 0; p < MAX_PLAYERS; p++)
8253 for (i = 0; i < SPR_MAX; i++)
8255 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8256 player_mapping[p][i].action = ACTION_DEFAULT;
8257 player_mapping[p][i].direction = MV_NONE;
8261 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8263 int e = em_object_mapping_list[i].element_em;
8265 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8266 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8268 if (em_object_mapping_list[i].action != -1)
8269 object_mapping[e].action = em_object_mapping_list[i].action;
8271 if (em_object_mapping_list[i].direction != -1)
8272 object_mapping[e].direction =
8273 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8276 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8278 int a = em_player_mapping_list[i].action_em;
8279 int p = em_player_mapping_list[i].player_nr;
8281 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8283 if (em_player_mapping_list[i].action != -1)
8284 player_mapping[p][a].action = em_player_mapping_list[i].action;
8286 if (em_player_mapping_list[i].direction != -1)
8287 player_mapping[p][a].direction =
8288 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8291 for (i = 0; i < TILE_MAX; i++)
8293 int element = object_mapping[i].element_rnd;
8294 int action = object_mapping[i].action;
8295 int direction = object_mapping[i].direction;
8296 boolean is_backside = object_mapping[i].is_backside;
8297 boolean action_exploding = ((action == ACTION_EXPLODING ||
8298 action == ACTION_SMASHED_BY_ROCK ||
8299 action == ACTION_SMASHED_BY_SPRING) &&
8300 element != EL_DIAMOND);
8301 boolean action_active = (action == ACTION_ACTIVE);
8302 boolean action_other = (action == ACTION_OTHER);
8304 for (j = 0; j < 8; j++)
8306 int effective_element = get_effective_element_EM(i, j);
8307 int effective_action = (j < 7 ? action :
8308 i == Xdrip_stretch ? action :
8309 i == Xdrip_stretchB ? action :
8310 i == Ydrip_s1 ? action :
8311 i == Ydrip_s1B ? action :
8312 i == Xball_1B ? action :
8313 i == Xball_2 ? action :
8314 i == Xball_2B ? action :
8315 i == Yball_eat ? action :
8316 i == Ykey_1_eat ? action :
8317 i == Ykey_2_eat ? action :
8318 i == Ykey_3_eat ? action :
8319 i == Ykey_4_eat ? action :
8320 i == Ykey_5_eat ? action :
8321 i == Ykey_6_eat ? action :
8322 i == Ykey_7_eat ? action :
8323 i == Ykey_8_eat ? action :
8324 i == Ylenses_eat ? action :
8325 i == Ymagnify_eat ? action :
8326 i == Ygrass_eat ? action :
8327 i == Ydirt_eat ? action :
8328 i == Xsand_stonein_1 ? action :
8329 i == Xsand_stonein_2 ? action :
8330 i == Xsand_stonein_3 ? action :
8331 i == Xsand_stonein_4 ? action :
8332 i == Xsand_stoneout_1 ? action :
8333 i == Xsand_stoneout_2 ? action :
8334 i == Xboom_android ? ACTION_EXPLODING :
8335 action_exploding ? ACTION_EXPLODING :
8336 action_active ? action :
8337 action_other ? action :
8339 int graphic = (el_act_dir2img(effective_element, effective_action,
8341 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8343 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8344 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8345 boolean has_action_graphics = (graphic != base_graphic);
8346 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8347 struct GraphicInfo *g = &graphic_info[graphic];
8348 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8351 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8352 boolean special_animation = (action != ACTION_DEFAULT &&
8353 g->anim_frames == 3 &&
8354 g->anim_delay == 2 &&
8355 g->anim_mode & ANIM_LINEAR);
8356 int sync_frame = (i == Xdrip_stretch ? 7 :
8357 i == Xdrip_stretchB ? 7 :
8358 i == Ydrip_s2 ? j + 8 :
8359 i == Ydrip_s2B ? j + 8 :
8368 i == Xfake_acid_1 ? 0 :
8369 i == Xfake_acid_2 ? 10 :
8370 i == Xfake_acid_3 ? 20 :
8371 i == Xfake_acid_4 ? 30 :
8372 i == Xfake_acid_5 ? 40 :
8373 i == Xfake_acid_6 ? 50 :
8374 i == Xfake_acid_7 ? 60 :
8375 i == Xfake_acid_8 ? 70 :
8377 i == Xball_2B ? j + 8 :
8378 i == Yball_eat ? j + 1 :
8379 i == Ykey_1_eat ? j + 1 :
8380 i == Ykey_2_eat ? j + 1 :
8381 i == Ykey_3_eat ? j + 1 :
8382 i == Ykey_4_eat ? j + 1 :
8383 i == Ykey_5_eat ? j + 1 :
8384 i == Ykey_6_eat ? j + 1 :
8385 i == Ykey_7_eat ? j + 1 :
8386 i == Ykey_8_eat ? j + 1 :
8387 i == Ylenses_eat ? j + 1 :
8388 i == Ymagnify_eat ? j + 1 :
8389 i == Ygrass_eat ? j + 1 :
8390 i == Ydirt_eat ? j + 1 :
8391 i == Xamoeba_1 ? 0 :
8392 i == Xamoeba_2 ? 1 :
8393 i == Xamoeba_3 ? 2 :
8394 i == Xamoeba_4 ? 3 :
8395 i == Xamoeba_5 ? 0 :
8396 i == Xamoeba_6 ? 1 :
8397 i == Xamoeba_7 ? 2 :
8398 i == Xamoeba_8 ? 3 :
8399 i == Xexit_2 ? j + 8 :
8400 i == Xexit_3 ? j + 16 :
8401 i == Xdynamite_1 ? 0 :
8402 i == Xdynamite_2 ? 8 :
8403 i == Xdynamite_3 ? 16 :
8404 i == Xdynamite_4 ? 24 :
8405 i == Xsand_stonein_1 ? j + 1 :
8406 i == Xsand_stonein_2 ? j + 9 :
8407 i == Xsand_stonein_3 ? j + 17 :
8408 i == Xsand_stonein_4 ? j + 25 :
8409 i == Xsand_stoneout_1 && j == 0 ? 0 :
8410 i == Xsand_stoneout_1 && j == 1 ? 0 :
8411 i == Xsand_stoneout_1 && j == 2 ? 1 :
8412 i == Xsand_stoneout_1 && j == 3 ? 2 :
8413 i == Xsand_stoneout_1 && j == 4 ? 2 :
8414 i == Xsand_stoneout_1 && j == 5 ? 3 :
8415 i == Xsand_stoneout_1 && j == 6 ? 4 :
8416 i == Xsand_stoneout_1 && j == 7 ? 4 :
8417 i == Xsand_stoneout_2 && j == 0 ? 5 :
8418 i == Xsand_stoneout_2 && j == 1 ? 6 :
8419 i == Xsand_stoneout_2 && j == 2 ? 7 :
8420 i == Xsand_stoneout_2 && j == 3 ? 8 :
8421 i == Xsand_stoneout_2 && j == 4 ? 9 :
8422 i == Xsand_stoneout_2 && j == 5 ? 11 :
8423 i == Xsand_stoneout_2 && j == 6 ? 13 :
8424 i == Xsand_stoneout_2 && j == 7 ? 15 :
8425 i == Xboom_bug && j == 1 ? 2 :
8426 i == Xboom_bug && j == 2 ? 2 :
8427 i == Xboom_bug && j == 3 ? 4 :
8428 i == Xboom_bug && j == 4 ? 4 :
8429 i == Xboom_bug && j == 5 ? 2 :
8430 i == Xboom_bug && j == 6 ? 2 :
8431 i == Xboom_bug && j == 7 ? 0 :
8432 i == Xboom_bomb && j == 1 ? 2 :
8433 i == Xboom_bomb && j == 2 ? 2 :
8434 i == Xboom_bomb && j == 3 ? 4 :
8435 i == Xboom_bomb && j == 4 ? 4 :
8436 i == Xboom_bomb && j == 5 ? 2 :
8437 i == Xboom_bomb && j == 6 ? 2 :
8438 i == Xboom_bomb && j == 7 ? 0 :
8439 i == Xboom_android && j == 7 ? 6 :
8440 i == Xboom_1 && j == 1 ? 2 :
8441 i == Xboom_1 && j == 2 ? 2 :
8442 i == Xboom_1 && j == 3 ? 4 :
8443 i == Xboom_1 && j == 4 ? 4 :
8444 i == Xboom_1 && j == 5 ? 6 :
8445 i == Xboom_1 && j == 6 ? 6 :
8446 i == Xboom_1 && j == 7 ? 8 :
8447 i == Xboom_2 && j == 0 ? 8 :
8448 i == Xboom_2 && j == 1 ? 8 :
8449 i == Xboom_2 && j == 2 ? 10 :
8450 i == Xboom_2 && j == 3 ? 10 :
8451 i == Xboom_2 && j == 4 ? 10 :
8452 i == Xboom_2 && j == 5 ? 12 :
8453 i == Xboom_2 && j == 6 ? 12 :
8454 i == Xboom_2 && j == 7 ? 12 :
8455 special_animation && j == 4 ? 3 :
8456 effective_action != action ? 0 :
8460 Bitmap *debug_bitmap = g_em->bitmap;
8461 int debug_src_x = g_em->src_x;
8462 int debug_src_y = g_em->src_y;
8465 int frame = getAnimationFrame(g->anim_frames,
8468 g->anim_start_frame,
8471 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8472 g->double_movement && is_backside);
8474 g_em->bitmap = src_bitmap;
8475 g_em->src_x = src_x;
8476 g_em->src_y = src_y;
8477 g_em->src_offset_x = 0;
8478 g_em->src_offset_y = 0;
8479 g_em->dst_offset_x = 0;
8480 g_em->dst_offset_y = 0;
8481 g_em->width = TILEX;
8482 g_em->height = TILEY;
8484 g_em->preserve_background = FALSE;
8486 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8489 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8490 effective_action == ACTION_MOVING ||
8491 effective_action == ACTION_PUSHING ||
8492 effective_action == ACTION_EATING)) ||
8493 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8494 effective_action == ACTION_EMPTYING)))
8497 (effective_action == ACTION_FALLING ||
8498 effective_action == ACTION_FILLING ||
8499 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8500 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8501 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8502 int num_steps = (i == Ydrip_s1 ? 16 :
8503 i == Ydrip_s1B ? 16 :
8504 i == Ydrip_s2 ? 16 :
8505 i == Ydrip_s2B ? 16 :
8506 i == Xsand_stonein_1 ? 32 :
8507 i == Xsand_stonein_2 ? 32 :
8508 i == Xsand_stonein_3 ? 32 :
8509 i == Xsand_stonein_4 ? 32 :
8510 i == Xsand_stoneout_1 ? 16 :
8511 i == Xsand_stoneout_2 ? 16 : 8);
8512 int cx = ABS(dx) * (TILEX / num_steps);
8513 int cy = ABS(dy) * (TILEY / num_steps);
8514 int step_frame = (i == Ydrip_s2 ? j + 8 :
8515 i == Ydrip_s2B ? j + 8 :
8516 i == Xsand_stonein_2 ? j + 8 :
8517 i == Xsand_stonein_3 ? j + 16 :
8518 i == Xsand_stonein_4 ? j + 24 :
8519 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8520 int step = (is_backside ? step_frame : num_steps - step_frame);
8522 if (is_backside) /* tile where movement starts */
8524 if (dx < 0 || dy < 0)
8526 g_em->src_offset_x = cx * step;
8527 g_em->src_offset_y = cy * step;
8531 g_em->dst_offset_x = cx * step;
8532 g_em->dst_offset_y = cy * step;
8535 else /* tile where movement ends */
8537 if (dx < 0 || dy < 0)
8539 g_em->dst_offset_x = cx * step;
8540 g_em->dst_offset_y = cy * step;
8544 g_em->src_offset_x = cx * step;
8545 g_em->src_offset_y = cy * step;
8549 g_em->width = TILEX - cx * step;
8550 g_em->height = TILEY - cy * step;
8553 /* create unique graphic identifier to decide if tile must be redrawn */
8554 /* bit 31 - 16 (16 bit): EM style graphic
8555 bit 15 - 12 ( 4 bit): EM style frame
8556 bit 11 - 6 ( 6 bit): graphic width
8557 bit 5 - 0 ( 6 bit): graphic height */
8558 g_em->unique_identifier =
8559 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8563 /* skip check for EMC elements not contained in original EMC artwork */
8564 if (element == EL_EMC_FAKE_ACID)
8567 if (g_em->bitmap != debug_bitmap ||
8568 g_em->src_x != debug_src_x ||
8569 g_em->src_y != debug_src_y ||
8570 g_em->src_offset_x != 0 ||
8571 g_em->src_offset_y != 0 ||
8572 g_em->dst_offset_x != 0 ||
8573 g_em->dst_offset_y != 0 ||
8574 g_em->width != TILEX ||
8575 g_em->height != TILEY)
8577 static int last_i = -1;
8585 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8586 i, element, element_info[element].token_name,
8587 element_action_info[effective_action].suffix, direction);
8589 if (element != effective_element)
8590 printf(" [%d ('%s')]",
8592 element_info[effective_element].token_name);
8596 if (g_em->bitmap != debug_bitmap)
8597 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8598 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8600 if (g_em->src_x != debug_src_x ||
8601 g_em->src_y != debug_src_y)
8602 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8603 j, (is_backside ? 'B' : 'F'),
8604 g_em->src_x, g_em->src_y,
8605 g_em->src_x / 32, g_em->src_y / 32,
8606 debug_src_x, debug_src_y,
8607 debug_src_x / 32, debug_src_y / 32);
8609 if (g_em->src_offset_x != 0 ||
8610 g_em->src_offset_y != 0 ||
8611 g_em->dst_offset_x != 0 ||
8612 g_em->dst_offset_y != 0)
8613 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8615 g_em->src_offset_x, g_em->src_offset_y,
8616 g_em->dst_offset_x, g_em->dst_offset_y);
8618 if (g_em->width != TILEX ||
8619 g_em->height != TILEY)
8620 printf(" %d (%d): size %d,%d should be %d,%d\n",
8622 g_em->width, g_em->height, TILEX, TILEY);
8624 num_em_gfx_errors++;
8631 for (i = 0; i < TILE_MAX; i++)
8633 for (j = 0; j < 8; j++)
8635 int element = object_mapping[i].element_rnd;
8636 int action = object_mapping[i].action;
8637 int direction = object_mapping[i].direction;
8638 boolean is_backside = object_mapping[i].is_backside;
8639 int graphic_action = el_act_dir2img(element, action, direction);
8640 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8642 if ((action == ACTION_SMASHED_BY_ROCK ||
8643 action == ACTION_SMASHED_BY_SPRING ||
8644 action == ACTION_EATING) &&
8645 graphic_action == graphic_default)
8647 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8648 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8649 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8650 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8653 /* no separate animation for "smashed by rock" -- use rock instead */
8654 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8655 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8657 g_em->bitmap = g_xx->bitmap;
8658 g_em->src_x = g_xx->src_x;
8659 g_em->src_y = g_xx->src_y;
8660 g_em->src_offset_x = g_xx->src_offset_x;
8661 g_em->src_offset_y = g_xx->src_offset_y;
8662 g_em->dst_offset_x = g_xx->dst_offset_x;
8663 g_em->dst_offset_y = g_xx->dst_offset_y;
8664 g_em->width = g_xx->width;
8665 g_em->height = g_xx->height;
8666 g_em->unique_identifier = g_xx->unique_identifier;
8669 g_em->preserve_background = TRUE;
8674 for (p = 0; p < MAX_PLAYERS; p++)
8676 for (i = 0; i < SPR_MAX; i++)
8678 int element = player_mapping[p][i].element_rnd;
8679 int action = player_mapping[p][i].action;
8680 int direction = player_mapping[p][i].direction;
8682 for (j = 0; j < 8; j++)
8684 int effective_element = element;
8685 int effective_action = action;
8686 int graphic = (direction == MV_NONE ?
8687 el_act2img(effective_element, effective_action) :
8688 el_act_dir2img(effective_element, effective_action,
8690 struct GraphicInfo *g = &graphic_info[graphic];
8691 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8697 Bitmap *debug_bitmap = g_em->bitmap;
8698 int debug_src_x = g_em->src_x;
8699 int debug_src_y = g_em->src_y;
8702 int frame = getAnimationFrame(g->anim_frames,
8705 g->anim_start_frame,
8708 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8710 g_em->bitmap = src_bitmap;
8711 g_em->src_x = src_x;
8712 g_em->src_y = src_y;
8713 g_em->src_offset_x = 0;
8714 g_em->src_offset_y = 0;
8715 g_em->dst_offset_x = 0;
8716 g_em->dst_offset_y = 0;
8717 g_em->width = TILEX;
8718 g_em->height = TILEY;
8722 /* skip check for EMC elements not contained in original EMC artwork */
8723 if (element == EL_PLAYER_3 ||
8724 element == EL_PLAYER_4)
8727 if (g_em->bitmap != debug_bitmap ||
8728 g_em->src_x != debug_src_x ||
8729 g_em->src_y != debug_src_y)
8731 static int last_i = -1;
8739 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8740 p, i, element, element_info[element].token_name,
8741 element_action_info[effective_action].suffix, direction);
8743 if (element != effective_element)
8744 printf(" [%d ('%s')]",
8746 element_info[effective_element].token_name);
8750 if (g_em->bitmap != debug_bitmap)
8751 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8752 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8754 if (g_em->src_x != debug_src_x ||
8755 g_em->src_y != debug_src_y)
8756 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8758 g_em->src_x, g_em->src_y,
8759 g_em->src_x / 32, g_em->src_y / 32,
8760 debug_src_x, debug_src_y,
8761 debug_src_x / 32, debug_src_y / 32);
8763 num_em_gfx_errors++;
8773 printf("::: [%d errors found]\n", num_em_gfx_errors);
8779 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8780 boolean any_player_moving,
8781 boolean any_player_snapping,
8782 boolean any_player_dropping)
8784 if (frame == 0 && !any_player_dropping)
8786 if (!local_player->was_waiting)
8788 if (!CheckSaveEngineSnapshotToList())
8791 local_player->was_waiting = TRUE;
8794 else if (any_player_moving || any_player_snapping || any_player_dropping)
8796 local_player->was_waiting = FALSE;
8800 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8801 boolean murphy_is_dropping)
8803 if (murphy_is_waiting)
8805 if (!local_player->was_waiting)
8807 if (!CheckSaveEngineSnapshotToList())
8810 local_player->was_waiting = TRUE;
8815 local_player->was_waiting = FALSE;
8819 void CheckSaveEngineSnapshot_MM(boolean element_clicked,
8820 boolean button_released)
8822 if (button_released)
8824 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
8825 CheckSaveEngineSnapshotToList();
8827 else if (element_clicked)
8829 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
8830 CheckSaveEngineSnapshotToList();
8832 game.snapshot.changed_action = TRUE;
8836 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8837 boolean any_player_moving,
8838 boolean any_player_snapping,
8839 boolean any_player_dropping)
8841 if (tape.single_step && tape.recording && !tape.pausing)
8842 if (frame == 0 && !any_player_dropping)
8843 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8845 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8846 any_player_snapping, any_player_dropping);
8849 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8850 boolean murphy_is_dropping)
8852 boolean murphy_starts_dropping = FALSE;
8855 for (i = 0; i < MAX_PLAYERS; i++)
8856 if (stored_player[i].force_dropping)
8857 murphy_starts_dropping = TRUE;
8859 if (tape.single_step && tape.recording && !tape.pausing)
8860 if (murphy_is_waiting && !murphy_starts_dropping)
8861 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8863 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8866 void CheckSingleStepMode_MM(boolean element_clicked,
8867 boolean button_released)
8869 if (tape.single_step && tape.recording && !tape.pausing)
8870 if (button_released)
8871 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8873 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
8876 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8877 int graphic, int sync_frame, int x, int y)
8879 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8881 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8884 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8886 return (IS_NEXT_FRAME(sync_frame, graphic));
8889 int getGraphicInfo_Delay(int graphic)
8891 return graphic_info[graphic].anim_delay;
8894 void PlayMenuSoundExt(int sound)
8896 if (sound == SND_UNDEFINED)
8899 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8900 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8903 if (IS_LOOP_SOUND(sound))
8904 PlaySoundLoop(sound);
8909 void PlayMenuSound()
8911 PlayMenuSoundExt(menu.sound[game_status]);
8914 void PlayMenuSoundStereo(int sound, int stereo_position)
8916 if (sound == SND_UNDEFINED)
8919 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8920 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8923 if (IS_LOOP_SOUND(sound))
8924 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8926 PlaySoundStereo(sound, stereo_position);
8929 void PlayMenuSoundIfLoopExt(int sound)
8931 if (sound == SND_UNDEFINED)
8934 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8935 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8938 if (IS_LOOP_SOUND(sound))
8939 PlaySoundLoop(sound);
8942 void PlayMenuSoundIfLoop()
8944 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8947 void PlayMenuMusicExt(int music)
8949 if (music == MUS_UNDEFINED)
8952 if (!setup.sound_music)
8958 void PlayMenuMusic()
8960 char *curr_music = getCurrentlyPlayingMusicFilename();
8961 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8963 if (!strEqual(curr_music, next_music))
8964 PlayMenuMusicExt(menu.music[game_status]);
8967 void PlayMenuSoundsAndMusic()
8973 static void FadeMenuSounds()
8978 static void FadeMenuMusic()
8980 char *curr_music = getCurrentlyPlayingMusicFilename();
8981 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8983 if (!strEqual(curr_music, next_music))
8987 void FadeMenuSoundsAndMusic()
8993 void PlaySoundActivating()
8996 PlaySound(SND_MENU_ITEM_ACTIVATING);
9000 void PlaySoundSelecting()
9003 PlaySound(SND_MENU_ITEM_SELECTING);
9007 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
9009 boolean change_fullscreen = (setup.fullscreen !=
9010 video.fullscreen_enabled);
9011 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9012 setup.window_scaling_percent !=
9013 video.window_scaling_percent);
9015 if (change_window_scaling_percent && video.fullscreen_enabled)
9018 if (!change_window_scaling_percent && !video.fullscreen_available)
9021 #if defined(TARGET_SDL2)
9022 if (change_window_scaling_percent)
9024 SDLSetWindowScaling(setup.window_scaling_percent);
9028 else if (change_fullscreen)
9030 SDLSetWindowFullscreen(setup.fullscreen);
9032 /* set setup value according to successfully changed fullscreen mode */
9033 setup.fullscreen = video.fullscreen_enabled;
9039 if (change_fullscreen ||
9040 change_window_scaling_percent)
9042 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9044 /* save backbuffer content which gets lost when toggling fullscreen mode */
9045 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9047 if (change_window_scaling_percent)
9049 /* keep window mode, but change window scaling */
9050 video.fullscreen_enabled = TRUE; /* force new window scaling */
9053 /* toggle fullscreen */
9054 ChangeVideoModeIfNeeded(setup.fullscreen);
9056 /* set setup value according to successfully changed fullscreen mode */
9057 setup.fullscreen = video.fullscreen_enabled;
9059 /* restore backbuffer content from temporary backbuffer backup bitmap */
9060 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9062 FreeBitmap(tmp_backbuffer);
9064 /* update visible window/screen */
9065 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9069 void JoinRectangles(int *x, int *y, int *width, int *height,
9070 int x2, int y2, int width2, int height2)
9072 // do not join with "off-screen" rectangle
9073 if (x2 == -1 || y2 == -1)
9078 *width = MAX(*width, width2);
9079 *height = MAX(*height, height2);
9082 void SetAnimStatus(int anim_status_new)
9084 if (anim_status_new == GAME_MODE_MAIN)
9085 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9086 else if (anim_status_new == GAME_MODE_SCORES)
9087 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9089 global.anim_status_next = anim_status_new;
9091 // directly set screen modes that are entered without fading
9092 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9093 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9094 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9095 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9096 global.anim_status = global.anim_status_next;
9099 void SetGameStatus(int game_status_new)
9101 if (game_status_new != game_status)
9102 game_status_last_screen = game_status;
9104 game_status = game_status_new;
9106 SetAnimStatus(game_status_new);
9109 void SetFontStatus(int game_status_new)
9111 static int last_game_status = -1;
9113 if (game_status_new != -1)
9115 // set game status for font use after storing last game status
9116 last_game_status = game_status;
9117 game_status = game_status_new;
9121 // reset game status after font use from last stored game status
9122 game_status = last_game_status;
9126 void ResetFontStatus()
9131 boolean CheckIfPlayfieldViewportHasChanged()
9133 // if game status has not changed, playfield viewport has not changed either
9134 if (game_status == game_status_last)
9137 // check if playfield viewport has changed with current game status
9138 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9139 int new_real_sx = vp_playfield->x;
9140 int new_real_sy = vp_playfield->y;
9141 int new_full_sxsize = vp_playfield->width;
9142 int new_full_sysize = vp_playfield->height;
9144 return (new_real_sx != REAL_SX ||
9145 new_real_sy != REAL_SY ||
9146 new_full_sxsize != FULL_SXSIZE ||
9147 new_full_sysize != FULL_SYSIZE);
9150 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
9152 return (CheckIfGlobalBorderHasChanged() ||
9153 CheckIfPlayfieldViewportHasChanged());
9156 void ChangeViewportPropertiesIfNeeded()
9158 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9159 FALSE : setup.small_game_graphics);
9160 int gfx_game_mode = game_status;
9161 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9163 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9164 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9165 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9166 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9167 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9168 int new_win_xsize = vp_window->width;
9169 int new_win_ysize = vp_window->height;
9170 int border_size = vp_playfield->border_size;
9171 int new_sx = vp_playfield->x + border_size;
9172 int new_sy = vp_playfield->y + border_size;
9173 int new_sxsize = vp_playfield->width - 2 * border_size;
9174 int new_sysize = vp_playfield->height - 2 * border_size;
9175 int new_real_sx = vp_playfield->x;
9176 int new_real_sy = vp_playfield->y;
9177 int new_full_sxsize = vp_playfield->width;
9178 int new_full_sysize = vp_playfield->height;
9179 int new_dx = vp_door_1->x;
9180 int new_dy = vp_door_1->y;
9181 int new_dxsize = vp_door_1->width;
9182 int new_dysize = vp_door_1->height;
9183 int new_vx = vp_door_2->x;
9184 int new_vy = vp_door_2->y;
9185 int new_vxsize = vp_door_2->width;
9186 int new_vysize = vp_door_2->height;
9187 int new_ex = vp_door_3->x;
9188 int new_ey = vp_door_3->y;
9189 int new_exsize = vp_door_3->width;
9190 int new_eysize = vp_door_3->height;
9191 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9192 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9193 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9194 int new_scr_fieldx = new_sxsize / tilesize;
9195 int new_scr_fieldy = new_sysize / tilesize;
9196 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9197 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9198 boolean init_gfx_buffers = FALSE;
9199 boolean init_video_buffer = FALSE;
9200 boolean init_gadgets_and_anims = FALSE;
9201 boolean init_em_graphics = FALSE;
9203 if (new_win_xsize != WIN_XSIZE ||
9204 new_win_ysize != WIN_YSIZE)
9206 WIN_XSIZE = new_win_xsize;
9207 WIN_YSIZE = new_win_ysize;
9209 init_video_buffer = TRUE;
9210 init_gfx_buffers = TRUE;
9211 init_gadgets_and_anims = TRUE;
9213 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9216 if (new_scr_fieldx != SCR_FIELDX ||
9217 new_scr_fieldy != SCR_FIELDY)
9219 /* this always toggles between MAIN and GAME when using small tile size */
9221 SCR_FIELDX = new_scr_fieldx;
9222 SCR_FIELDY = new_scr_fieldy;
9224 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9235 new_sxsize != SXSIZE ||
9236 new_sysize != SYSIZE ||
9237 new_dxsize != DXSIZE ||
9238 new_dysize != DYSIZE ||
9239 new_vxsize != VXSIZE ||
9240 new_vysize != VYSIZE ||
9241 new_exsize != EXSIZE ||
9242 new_eysize != EYSIZE ||
9243 new_real_sx != REAL_SX ||
9244 new_real_sy != REAL_SY ||
9245 new_full_sxsize != FULL_SXSIZE ||
9246 new_full_sysize != FULL_SYSIZE ||
9247 new_tilesize_var != TILESIZE_VAR
9250 // ------------------------------------------------------------------------
9251 // determine next fading area for changed viewport definitions
9252 // ------------------------------------------------------------------------
9254 // start with current playfield area (default fading area)
9257 FADE_SXSIZE = FULL_SXSIZE;
9258 FADE_SYSIZE = FULL_SYSIZE;
9260 // add new playfield area if position or size has changed
9261 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9262 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9264 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9265 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9268 // add current and new door 1 area if position or size has changed
9269 if (new_dx != DX || new_dy != DY ||
9270 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9272 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9273 DX, DY, DXSIZE, DYSIZE);
9274 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9275 new_dx, new_dy, new_dxsize, new_dysize);
9278 // add current and new door 2 area if position or size has changed
9279 if (new_dx != VX || new_dy != VY ||
9280 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9282 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9283 VX, VY, VXSIZE, VYSIZE);
9284 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9285 new_vx, new_vy, new_vxsize, new_vysize);
9288 // ------------------------------------------------------------------------
9289 // handle changed tile size
9290 // ------------------------------------------------------------------------
9292 if (new_tilesize_var != TILESIZE_VAR)
9294 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9296 // changing tile size invalidates scroll values of engine snapshots
9297 FreeEngineSnapshotSingle();
9299 // changing tile size requires update of graphic mapping for EM engine
9300 init_em_graphics = TRUE;
9311 SXSIZE = new_sxsize;
9312 SYSIZE = new_sysize;
9313 DXSIZE = new_dxsize;
9314 DYSIZE = new_dysize;
9315 VXSIZE = new_vxsize;
9316 VYSIZE = new_vysize;
9317 EXSIZE = new_exsize;
9318 EYSIZE = new_eysize;
9319 REAL_SX = new_real_sx;
9320 REAL_SY = new_real_sy;
9321 FULL_SXSIZE = new_full_sxsize;
9322 FULL_SYSIZE = new_full_sysize;
9323 TILESIZE_VAR = new_tilesize_var;
9325 init_gfx_buffers = TRUE;
9326 init_gadgets_and_anims = TRUE;
9328 // printf("::: viewports: init_gfx_buffers\n");
9329 // printf("::: viewports: init_gadgets_and_anims\n");
9332 if (init_gfx_buffers)
9334 // printf("::: init_gfx_buffers\n");
9336 SCR_FIELDX = new_scr_fieldx_buffers;
9337 SCR_FIELDY = new_scr_fieldy_buffers;
9341 SCR_FIELDX = new_scr_fieldx;
9342 SCR_FIELDY = new_scr_fieldy;
9344 SetDrawDeactivationMask(REDRAW_NONE);
9345 SetDrawBackgroundMask(REDRAW_FIELD);
9348 if (init_video_buffer)
9350 // printf("::: init_video_buffer\n");
9352 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9353 InitImageTextures();
9356 if (init_gadgets_and_anims)
9358 // printf("::: init_gadgets_and_anims\n");
9361 InitGlobalAnimations();
9364 if (init_em_graphics)
9366 InitGraphicInfo_EM();