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;
5314 if (global.use_envelope_request)
5315 setRequestPosition(&dx, &dy, TRUE);
5317 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5319 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5321 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5322 pos->size, &deco_bitmap, &deco_x, &deco_y);
5323 deco_xpos = (gfx->width - pos->size) / 2;
5324 deco_ypos = (gfx->height - pos->size) / 2;
5327 gi = CreateGadget(GDI_CUSTOM_ID, id,
5328 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5329 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5330 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5331 GDI_WIDTH, gfx->width,
5332 GDI_HEIGHT, gfx->height,
5333 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5334 GDI_STATE, GD_BUTTON_UNPRESSED,
5335 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5336 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5337 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5338 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5339 GDI_DECORATION_SIZE, pos->size, pos->size,
5340 GDI_DECORATION_SHIFTING, 1, 1,
5341 GDI_DIRECT_DRAW, FALSE,
5342 GDI_EVENT_MASK, event_mask,
5343 GDI_CALLBACK_ACTION, HandleToolButtons,
5347 Error(ERR_EXIT, "cannot create gadget");
5349 tool_gadget[id] = gi;
5353 void FreeToolButtons()
5357 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5358 FreeGadget(tool_gadget[i]);
5361 static void UnmapToolButtons()
5365 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5366 UnmapGadget(tool_gadget[i]);
5369 static void HandleToolButtons(struct GadgetInfo *gi)
5371 request_gadget_id = gi->custom_id;
5374 static struct Mapping_EM_to_RND_object
5377 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5378 boolean is_backside; /* backside of moving element */
5384 em_object_mapping_list[] =
5387 Xblank, TRUE, FALSE,
5391 Yacid_splash_eB, FALSE, FALSE,
5392 EL_ACID_SPLASH_RIGHT, -1, -1
5395 Yacid_splash_wB, FALSE, FALSE,
5396 EL_ACID_SPLASH_LEFT, -1, -1
5399 #ifdef EM_ENGINE_BAD_ROLL
5401 Xstone_force_e, FALSE, FALSE,
5402 EL_ROCK, -1, MV_BIT_RIGHT
5405 Xstone_force_w, FALSE, FALSE,
5406 EL_ROCK, -1, MV_BIT_LEFT
5409 Xnut_force_e, FALSE, FALSE,
5410 EL_NUT, -1, MV_BIT_RIGHT
5413 Xnut_force_w, FALSE, FALSE,
5414 EL_NUT, -1, MV_BIT_LEFT
5417 Xspring_force_e, FALSE, FALSE,
5418 EL_SPRING, -1, MV_BIT_RIGHT
5421 Xspring_force_w, FALSE, FALSE,
5422 EL_SPRING, -1, MV_BIT_LEFT
5425 Xemerald_force_e, FALSE, FALSE,
5426 EL_EMERALD, -1, MV_BIT_RIGHT
5429 Xemerald_force_w, FALSE, FALSE,
5430 EL_EMERALD, -1, MV_BIT_LEFT
5433 Xdiamond_force_e, FALSE, FALSE,
5434 EL_DIAMOND, -1, MV_BIT_RIGHT
5437 Xdiamond_force_w, FALSE, FALSE,
5438 EL_DIAMOND, -1, MV_BIT_LEFT
5441 Xbomb_force_e, FALSE, FALSE,
5442 EL_BOMB, -1, MV_BIT_RIGHT
5445 Xbomb_force_w, FALSE, FALSE,
5446 EL_BOMB, -1, MV_BIT_LEFT
5448 #endif /* EM_ENGINE_BAD_ROLL */
5451 Xstone, TRUE, FALSE,
5455 Xstone_pause, FALSE, FALSE,
5459 Xstone_fall, FALSE, FALSE,
5463 Ystone_s, FALSE, FALSE,
5464 EL_ROCK, ACTION_FALLING, -1
5467 Ystone_sB, FALSE, TRUE,
5468 EL_ROCK, ACTION_FALLING, -1
5471 Ystone_e, FALSE, FALSE,
5472 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5475 Ystone_eB, FALSE, TRUE,
5476 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5479 Ystone_w, FALSE, FALSE,
5480 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5483 Ystone_wB, FALSE, TRUE,
5484 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5491 Xnut_pause, FALSE, FALSE,
5495 Xnut_fall, FALSE, FALSE,
5499 Ynut_s, FALSE, FALSE,
5500 EL_NUT, ACTION_FALLING, -1
5503 Ynut_sB, FALSE, TRUE,
5504 EL_NUT, ACTION_FALLING, -1
5507 Ynut_e, FALSE, FALSE,
5508 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5511 Ynut_eB, FALSE, TRUE,
5512 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5515 Ynut_w, FALSE, FALSE,
5516 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5519 Ynut_wB, FALSE, TRUE,
5520 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5523 Xbug_n, TRUE, FALSE,
5527 Xbug_e, TRUE, FALSE,
5528 EL_BUG_RIGHT, -1, -1
5531 Xbug_s, TRUE, FALSE,
5535 Xbug_w, TRUE, FALSE,
5539 Xbug_gon, FALSE, FALSE,
5543 Xbug_goe, FALSE, FALSE,
5544 EL_BUG_RIGHT, -1, -1
5547 Xbug_gos, FALSE, FALSE,
5551 Xbug_gow, FALSE, FALSE,
5555 Ybug_n, FALSE, FALSE,
5556 EL_BUG, ACTION_MOVING, MV_BIT_UP
5559 Ybug_nB, FALSE, TRUE,
5560 EL_BUG, ACTION_MOVING, MV_BIT_UP
5563 Ybug_e, FALSE, FALSE,
5564 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5567 Ybug_eB, FALSE, TRUE,
5568 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5571 Ybug_s, FALSE, FALSE,
5572 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5575 Ybug_sB, FALSE, TRUE,
5576 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5579 Ybug_w, FALSE, FALSE,
5580 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5583 Ybug_wB, FALSE, TRUE,
5584 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5587 Ybug_w_n, FALSE, FALSE,
5588 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5591 Ybug_n_e, FALSE, FALSE,
5592 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5595 Ybug_e_s, FALSE, FALSE,
5596 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5599 Ybug_s_w, FALSE, FALSE,
5600 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5603 Ybug_e_n, FALSE, FALSE,
5604 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5607 Ybug_s_e, FALSE, FALSE,
5608 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5611 Ybug_w_s, FALSE, FALSE,
5612 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5615 Ybug_n_w, FALSE, FALSE,
5616 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5619 Ybug_stone, FALSE, FALSE,
5620 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5623 Ybug_spring, FALSE, FALSE,
5624 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5627 Xtank_n, TRUE, FALSE,
5628 EL_SPACESHIP_UP, -1, -1
5631 Xtank_e, TRUE, FALSE,
5632 EL_SPACESHIP_RIGHT, -1, -1
5635 Xtank_s, TRUE, FALSE,
5636 EL_SPACESHIP_DOWN, -1, -1
5639 Xtank_w, TRUE, FALSE,
5640 EL_SPACESHIP_LEFT, -1, -1
5643 Xtank_gon, FALSE, FALSE,
5644 EL_SPACESHIP_UP, -1, -1
5647 Xtank_goe, FALSE, FALSE,
5648 EL_SPACESHIP_RIGHT, -1, -1
5651 Xtank_gos, FALSE, FALSE,
5652 EL_SPACESHIP_DOWN, -1, -1
5655 Xtank_gow, FALSE, FALSE,
5656 EL_SPACESHIP_LEFT, -1, -1
5659 Ytank_n, FALSE, FALSE,
5660 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5663 Ytank_nB, FALSE, TRUE,
5664 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5667 Ytank_e, FALSE, FALSE,
5668 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5671 Ytank_eB, FALSE, TRUE,
5672 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5675 Ytank_s, FALSE, FALSE,
5676 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5679 Ytank_sB, FALSE, TRUE,
5680 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5683 Ytank_w, FALSE, FALSE,
5684 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5687 Ytank_wB, FALSE, TRUE,
5688 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5691 Ytank_w_n, FALSE, FALSE,
5692 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5695 Ytank_n_e, FALSE, FALSE,
5696 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5699 Ytank_e_s, FALSE, FALSE,
5700 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5703 Ytank_s_w, FALSE, FALSE,
5704 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5707 Ytank_e_n, FALSE, FALSE,
5708 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5711 Ytank_s_e, FALSE, FALSE,
5712 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5715 Ytank_w_s, FALSE, FALSE,
5716 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5719 Ytank_n_w, FALSE, FALSE,
5720 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5723 Ytank_stone, FALSE, FALSE,
5724 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5727 Ytank_spring, FALSE, FALSE,
5728 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5731 Xandroid, TRUE, FALSE,
5732 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5735 Xandroid_1_n, FALSE, FALSE,
5736 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5739 Xandroid_2_n, FALSE, FALSE,
5740 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5743 Xandroid_1_e, FALSE, FALSE,
5744 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5747 Xandroid_2_e, FALSE, FALSE,
5748 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5751 Xandroid_1_w, FALSE, FALSE,
5752 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5755 Xandroid_2_w, FALSE, FALSE,
5756 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5759 Xandroid_1_s, FALSE, FALSE,
5760 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5763 Xandroid_2_s, FALSE, FALSE,
5764 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5767 Yandroid_n, FALSE, FALSE,
5768 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5771 Yandroid_nB, FALSE, TRUE,
5772 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5775 Yandroid_ne, FALSE, FALSE,
5776 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5779 Yandroid_neB, FALSE, TRUE,
5780 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5783 Yandroid_e, FALSE, FALSE,
5784 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5787 Yandroid_eB, FALSE, TRUE,
5788 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5791 Yandroid_se, FALSE, FALSE,
5792 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5795 Yandroid_seB, FALSE, TRUE,
5796 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5799 Yandroid_s, FALSE, FALSE,
5800 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5803 Yandroid_sB, FALSE, TRUE,
5804 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5807 Yandroid_sw, FALSE, FALSE,
5808 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5811 Yandroid_swB, FALSE, TRUE,
5812 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5815 Yandroid_w, FALSE, FALSE,
5816 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5819 Yandroid_wB, FALSE, TRUE,
5820 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5823 Yandroid_nw, FALSE, FALSE,
5824 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5827 Yandroid_nwB, FALSE, TRUE,
5828 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5831 Xspring, TRUE, FALSE,
5835 Xspring_pause, FALSE, FALSE,
5839 Xspring_e, FALSE, FALSE,
5843 Xspring_w, FALSE, FALSE,
5847 Xspring_fall, FALSE, FALSE,
5851 Yspring_s, FALSE, FALSE,
5852 EL_SPRING, ACTION_FALLING, -1
5855 Yspring_sB, FALSE, TRUE,
5856 EL_SPRING, ACTION_FALLING, -1
5859 Yspring_e, FALSE, FALSE,
5860 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5863 Yspring_eB, FALSE, TRUE,
5864 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5867 Yspring_w, FALSE, FALSE,
5868 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5871 Yspring_wB, FALSE, TRUE,
5872 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5875 Yspring_kill_e, FALSE, FALSE,
5876 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5879 Yspring_kill_eB, FALSE, TRUE,
5880 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5883 Yspring_kill_w, FALSE, FALSE,
5884 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5887 Yspring_kill_wB, FALSE, TRUE,
5888 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5891 Xeater_n, TRUE, FALSE,
5892 EL_YAMYAM_UP, -1, -1
5895 Xeater_e, TRUE, FALSE,
5896 EL_YAMYAM_RIGHT, -1, -1
5899 Xeater_w, TRUE, FALSE,
5900 EL_YAMYAM_LEFT, -1, -1
5903 Xeater_s, TRUE, FALSE,
5904 EL_YAMYAM_DOWN, -1, -1
5907 Yeater_n, FALSE, FALSE,
5908 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5911 Yeater_nB, FALSE, TRUE,
5912 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5915 Yeater_e, FALSE, FALSE,
5916 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5919 Yeater_eB, FALSE, TRUE,
5920 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5923 Yeater_s, FALSE, FALSE,
5924 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5927 Yeater_sB, FALSE, TRUE,
5928 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5931 Yeater_w, FALSE, FALSE,
5932 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5935 Yeater_wB, FALSE, TRUE,
5936 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5939 Yeater_stone, FALSE, FALSE,
5940 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5943 Yeater_spring, FALSE, FALSE,
5944 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5947 Xalien, TRUE, FALSE,
5951 Xalien_pause, FALSE, FALSE,
5955 Yalien_n, FALSE, FALSE,
5956 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5959 Yalien_nB, FALSE, TRUE,
5960 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5963 Yalien_e, FALSE, FALSE,
5964 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5967 Yalien_eB, FALSE, TRUE,
5968 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5971 Yalien_s, FALSE, FALSE,
5972 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5975 Yalien_sB, FALSE, TRUE,
5976 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5979 Yalien_w, FALSE, FALSE,
5980 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5983 Yalien_wB, FALSE, TRUE,
5984 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5987 Yalien_stone, FALSE, FALSE,
5988 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5991 Yalien_spring, FALSE, FALSE,
5992 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5995 Xemerald, TRUE, FALSE,
5999 Xemerald_pause, FALSE, FALSE,
6003 Xemerald_fall, FALSE, FALSE,
6007 Xemerald_shine, FALSE, FALSE,
6008 EL_EMERALD, ACTION_TWINKLING, -1
6011 Yemerald_s, FALSE, FALSE,
6012 EL_EMERALD, ACTION_FALLING, -1
6015 Yemerald_sB, FALSE, TRUE,
6016 EL_EMERALD, ACTION_FALLING, -1
6019 Yemerald_e, FALSE, FALSE,
6020 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6023 Yemerald_eB, FALSE, TRUE,
6024 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6027 Yemerald_w, FALSE, FALSE,
6028 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6031 Yemerald_wB, FALSE, TRUE,
6032 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6035 Yemerald_eat, FALSE, FALSE,
6036 EL_EMERALD, ACTION_COLLECTING, -1
6039 Yemerald_stone, FALSE, FALSE,
6040 EL_NUT, ACTION_BREAKING, -1
6043 Xdiamond, TRUE, FALSE,
6047 Xdiamond_pause, FALSE, FALSE,
6051 Xdiamond_fall, FALSE, FALSE,
6055 Xdiamond_shine, FALSE, FALSE,
6056 EL_DIAMOND, ACTION_TWINKLING, -1
6059 Ydiamond_s, FALSE, FALSE,
6060 EL_DIAMOND, ACTION_FALLING, -1
6063 Ydiamond_sB, FALSE, TRUE,
6064 EL_DIAMOND, ACTION_FALLING, -1
6067 Ydiamond_e, FALSE, FALSE,
6068 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6071 Ydiamond_eB, FALSE, TRUE,
6072 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6075 Ydiamond_w, FALSE, FALSE,
6076 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6079 Ydiamond_wB, FALSE, TRUE,
6080 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6083 Ydiamond_eat, FALSE, FALSE,
6084 EL_DIAMOND, ACTION_COLLECTING, -1
6087 Ydiamond_stone, FALSE, FALSE,
6088 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6091 Xdrip_fall, TRUE, FALSE,
6092 EL_AMOEBA_DROP, -1, -1
6095 Xdrip_stretch, FALSE, FALSE,
6096 EL_AMOEBA_DROP, ACTION_FALLING, -1
6099 Xdrip_stretchB, FALSE, TRUE,
6100 EL_AMOEBA_DROP, ACTION_FALLING, -1
6103 Xdrip_eat, FALSE, FALSE,
6104 EL_AMOEBA_DROP, ACTION_GROWING, -1
6107 Ydrip_s1, FALSE, FALSE,
6108 EL_AMOEBA_DROP, ACTION_FALLING, -1
6111 Ydrip_s1B, FALSE, TRUE,
6112 EL_AMOEBA_DROP, ACTION_FALLING, -1
6115 Ydrip_s2, FALSE, FALSE,
6116 EL_AMOEBA_DROP, ACTION_FALLING, -1
6119 Ydrip_s2B, FALSE, TRUE,
6120 EL_AMOEBA_DROP, ACTION_FALLING, -1
6127 Xbomb_pause, FALSE, FALSE,
6131 Xbomb_fall, FALSE, FALSE,
6135 Ybomb_s, FALSE, FALSE,
6136 EL_BOMB, ACTION_FALLING, -1
6139 Ybomb_sB, FALSE, TRUE,
6140 EL_BOMB, ACTION_FALLING, -1
6143 Ybomb_e, FALSE, FALSE,
6144 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6147 Ybomb_eB, FALSE, TRUE,
6148 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6151 Ybomb_w, FALSE, FALSE,
6152 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6155 Ybomb_wB, FALSE, TRUE,
6156 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6159 Ybomb_eat, FALSE, FALSE,
6160 EL_BOMB, ACTION_ACTIVATING, -1
6163 Xballoon, TRUE, FALSE,
6167 Yballoon_n, FALSE, FALSE,
6168 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6171 Yballoon_nB, FALSE, TRUE,
6172 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6175 Yballoon_e, FALSE, FALSE,
6176 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6179 Yballoon_eB, FALSE, TRUE,
6180 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6183 Yballoon_s, FALSE, FALSE,
6184 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6187 Yballoon_sB, FALSE, TRUE,
6188 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6191 Yballoon_w, FALSE, FALSE,
6192 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6195 Yballoon_wB, FALSE, TRUE,
6196 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6199 Xgrass, TRUE, FALSE,
6200 EL_EMC_GRASS, -1, -1
6203 Ygrass_nB, FALSE, FALSE,
6204 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6207 Ygrass_eB, FALSE, FALSE,
6208 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6211 Ygrass_sB, FALSE, FALSE,
6212 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6215 Ygrass_wB, FALSE, FALSE,
6216 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6223 Ydirt_nB, FALSE, FALSE,
6224 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6227 Ydirt_eB, FALSE, FALSE,
6228 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6231 Ydirt_sB, FALSE, FALSE,
6232 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6235 Ydirt_wB, FALSE, FALSE,
6236 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6239 Xacid_ne, TRUE, FALSE,
6240 EL_ACID_POOL_TOPRIGHT, -1, -1
6243 Xacid_se, TRUE, FALSE,
6244 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6247 Xacid_s, TRUE, FALSE,
6248 EL_ACID_POOL_BOTTOM, -1, -1
6251 Xacid_sw, TRUE, FALSE,
6252 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6255 Xacid_nw, TRUE, FALSE,
6256 EL_ACID_POOL_TOPLEFT, -1, -1
6259 Xacid_1, TRUE, FALSE,
6263 Xacid_2, FALSE, FALSE,
6267 Xacid_3, FALSE, FALSE,
6271 Xacid_4, FALSE, FALSE,
6275 Xacid_5, FALSE, FALSE,
6279 Xacid_6, FALSE, FALSE,
6283 Xacid_7, FALSE, FALSE,
6287 Xacid_8, FALSE, FALSE,
6291 Xball_1, TRUE, FALSE,
6292 EL_EMC_MAGIC_BALL, -1, -1
6295 Xball_1B, FALSE, FALSE,
6296 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6299 Xball_2, FALSE, FALSE,
6300 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6303 Xball_2B, FALSE, FALSE,
6304 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6307 Yball_eat, FALSE, FALSE,
6308 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6311 Ykey_1_eat, FALSE, FALSE,
6312 EL_EM_KEY_1, ACTION_COLLECTING, -1
6315 Ykey_2_eat, FALSE, FALSE,
6316 EL_EM_KEY_2, ACTION_COLLECTING, -1
6319 Ykey_3_eat, FALSE, FALSE,
6320 EL_EM_KEY_3, ACTION_COLLECTING, -1
6323 Ykey_4_eat, FALSE, FALSE,
6324 EL_EM_KEY_4, ACTION_COLLECTING, -1
6327 Ykey_5_eat, FALSE, FALSE,
6328 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6331 Ykey_6_eat, FALSE, FALSE,
6332 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6335 Ykey_7_eat, FALSE, FALSE,
6336 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6339 Ykey_8_eat, FALSE, FALSE,
6340 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6343 Ylenses_eat, FALSE, FALSE,
6344 EL_EMC_LENSES, ACTION_COLLECTING, -1
6347 Ymagnify_eat, FALSE, FALSE,
6348 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6351 Ygrass_eat, FALSE, FALSE,
6352 EL_EMC_GRASS, ACTION_SNAPPING, -1
6355 Ydirt_eat, FALSE, FALSE,
6356 EL_SAND, ACTION_SNAPPING, -1
6359 Xgrow_ns, TRUE, FALSE,
6360 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6363 Ygrow_ns_eat, FALSE, FALSE,
6364 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6367 Xgrow_ew, TRUE, FALSE,
6368 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6371 Ygrow_ew_eat, FALSE, FALSE,
6372 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6375 Xwonderwall, TRUE, FALSE,
6376 EL_MAGIC_WALL, -1, -1
6379 XwonderwallB, FALSE, FALSE,
6380 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6383 Xamoeba_1, TRUE, FALSE,
6384 EL_AMOEBA_DRY, ACTION_OTHER, -1
6387 Xamoeba_2, FALSE, FALSE,
6388 EL_AMOEBA_DRY, ACTION_OTHER, -1
6391 Xamoeba_3, FALSE, FALSE,
6392 EL_AMOEBA_DRY, ACTION_OTHER, -1
6395 Xamoeba_4, FALSE, FALSE,
6396 EL_AMOEBA_DRY, ACTION_OTHER, -1
6399 Xamoeba_5, TRUE, FALSE,
6400 EL_AMOEBA_WET, ACTION_OTHER, -1
6403 Xamoeba_6, FALSE, FALSE,
6404 EL_AMOEBA_WET, ACTION_OTHER, -1
6407 Xamoeba_7, FALSE, FALSE,
6408 EL_AMOEBA_WET, ACTION_OTHER, -1
6411 Xamoeba_8, FALSE, FALSE,
6412 EL_AMOEBA_WET, ACTION_OTHER, -1
6415 Xdoor_1, TRUE, FALSE,
6416 EL_EM_GATE_1, -1, -1
6419 Xdoor_2, TRUE, FALSE,
6420 EL_EM_GATE_2, -1, -1
6423 Xdoor_3, TRUE, FALSE,
6424 EL_EM_GATE_3, -1, -1
6427 Xdoor_4, TRUE, FALSE,
6428 EL_EM_GATE_4, -1, -1
6431 Xdoor_5, TRUE, FALSE,
6432 EL_EMC_GATE_5, -1, -1
6435 Xdoor_6, TRUE, FALSE,
6436 EL_EMC_GATE_6, -1, -1
6439 Xdoor_7, TRUE, FALSE,
6440 EL_EMC_GATE_7, -1, -1
6443 Xdoor_8, TRUE, FALSE,
6444 EL_EMC_GATE_8, -1, -1
6447 Xkey_1, TRUE, FALSE,
6451 Xkey_2, TRUE, FALSE,
6455 Xkey_3, TRUE, FALSE,
6459 Xkey_4, TRUE, FALSE,
6463 Xkey_5, TRUE, FALSE,
6464 EL_EMC_KEY_5, -1, -1
6467 Xkey_6, TRUE, FALSE,
6468 EL_EMC_KEY_6, -1, -1
6471 Xkey_7, TRUE, FALSE,
6472 EL_EMC_KEY_7, -1, -1
6475 Xkey_8, TRUE, FALSE,
6476 EL_EMC_KEY_8, -1, -1
6479 Xwind_n, TRUE, FALSE,
6480 EL_BALLOON_SWITCH_UP, -1, -1
6483 Xwind_e, TRUE, FALSE,
6484 EL_BALLOON_SWITCH_RIGHT, -1, -1
6487 Xwind_s, TRUE, FALSE,
6488 EL_BALLOON_SWITCH_DOWN, -1, -1
6491 Xwind_w, TRUE, FALSE,
6492 EL_BALLOON_SWITCH_LEFT, -1, -1
6495 Xwind_nesw, TRUE, FALSE,
6496 EL_BALLOON_SWITCH_ANY, -1, -1
6499 Xwind_stop, TRUE, FALSE,
6500 EL_BALLOON_SWITCH_NONE, -1, -1
6504 EL_EM_EXIT_CLOSED, -1, -1
6507 Xexit_1, TRUE, FALSE,
6508 EL_EM_EXIT_OPEN, -1, -1
6511 Xexit_2, FALSE, FALSE,
6512 EL_EM_EXIT_OPEN, -1, -1
6515 Xexit_3, FALSE, FALSE,
6516 EL_EM_EXIT_OPEN, -1, -1
6519 Xdynamite, TRUE, FALSE,
6520 EL_EM_DYNAMITE, -1, -1
6523 Ydynamite_eat, FALSE, FALSE,
6524 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6527 Xdynamite_1, TRUE, FALSE,
6528 EL_EM_DYNAMITE_ACTIVE, -1, -1
6531 Xdynamite_2, FALSE, FALSE,
6532 EL_EM_DYNAMITE_ACTIVE, -1, -1
6535 Xdynamite_3, FALSE, FALSE,
6536 EL_EM_DYNAMITE_ACTIVE, -1, -1
6539 Xdynamite_4, FALSE, FALSE,
6540 EL_EM_DYNAMITE_ACTIVE, -1, -1
6543 Xbumper, TRUE, FALSE,
6544 EL_EMC_SPRING_BUMPER, -1, -1
6547 XbumperB, FALSE, FALSE,
6548 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6551 Xwheel, TRUE, FALSE,
6552 EL_ROBOT_WHEEL, -1, -1
6555 XwheelB, FALSE, FALSE,
6556 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6559 Xswitch, TRUE, FALSE,
6560 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6563 XswitchB, FALSE, FALSE,
6564 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6568 EL_QUICKSAND_EMPTY, -1, -1
6571 Xsand_stone, TRUE, FALSE,
6572 EL_QUICKSAND_FULL, -1, -1
6575 Xsand_stonein_1, FALSE, TRUE,
6576 EL_ROCK, ACTION_FILLING, -1
6579 Xsand_stonein_2, FALSE, TRUE,
6580 EL_ROCK, ACTION_FILLING, -1
6583 Xsand_stonein_3, FALSE, TRUE,
6584 EL_ROCK, ACTION_FILLING, -1
6587 Xsand_stonein_4, FALSE, TRUE,
6588 EL_ROCK, ACTION_FILLING, -1
6591 Xsand_stonesand_1, FALSE, FALSE,
6592 EL_QUICKSAND_EMPTYING, -1, -1
6595 Xsand_stonesand_2, FALSE, FALSE,
6596 EL_QUICKSAND_EMPTYING, -1, -1
6599 Xsand_stonesand_3, FALSE, FALSE,
6600 EL_QUICKSAND_EMPTYING, -1, -1
6603 Xsand_stonesand_4, FALSE, FALSE,
6604 EL_QUICKSAND_EMPTYING, -1, -1
6607 Xsand_stonesand_quickout_1, FALSE, FALSE,
6608 EL_QUICKSAND_EMPTYING, -1, -1
6611 Xsand_stonesand_quickout_2, FALSE, FALSE,
6612 EL_QUICKSAND_EMPTYING, -1, -1
6615 Xsand_stoneout_1, FALSE, FALSE,
6616 EL_ROCK, ACTION_EMPTYING, -1
6619 Xsand_stoneout_2, FALSE, FALSE,
6620 EL_ROCK, ACTION_EMPTYING, -1
6623 Xsand_sandstone_1, FALSE, FALSE,
6624 EL_QUICKSAND_FILLING, -1, -1
6627 Xsand_sandstone_2, FALSE, FALSE,
6628 EL_QUICKSAND_FILLING, -1, -1
6631 Xsand_sandstone_3, FALSE, FALSE,
6632 EL_QUICKSAND_FILLING, -1, -1
6635 Xsand_sandstone_4, FALSE, FALSE,
6636 EL_QUICKSAND_FILLING, -1, -1
6639 Xplant, TRUE, FALSE,
6640 EL_EMC_PLANT, -1, -1
6643 Yplant, FALSE, FALSE,
6644 EL_EMC_PLANT, -1, -1
6647 Xlenses, TRUE, FALSE,
6648 EL_EMC_LENSES, -1, -1
6651 Xmagnify, TRUE, FALSE,
6652 EL_EMC_MAGNIFIER, -1, -1
6655 Xdripper, TRUE, FALSE,
6656 EL_EMC_DRIPPER, -1, -1
6659 XdripperB, FALSE, FALSE,
6660 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6663 Xfake_blank, TRUE, FALSE,
6664 EL_INVISIBLE_WALL, -1, -1
6667 Xfake_blankB, FALSE, FALSE,
6668 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6671 Xfake_grass, TRUE, FALSE,
6672 EL_EMC_FAKE_GRASS, -1, -1
6675 Xfake_grassB, FALSE, FALSE,
6676 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6679 Xfake_door_1, TRUE, FALSE,
6680 EL_EM_GATE_1_GRAY, -1, -1
6683 Xfake_door_2, TRUE, FALSE,
6684 EL_EM_GATE_2_GRAY, -1, -1
6687 Xfake_door_3, TRUE, FALSE,
6688 EL_EM_GATE_3_GRAY, -1, -1
6691 Xfake_door_4, TRUE, FALSE,
6692 EL_EM_GATE_4_GRAY, -1, -1
6695 Xfake_door_5, TRUE, FALSE,
6696 EL_EMC_GATE_5_GRAY, -1, -1
6699 Xfake_door_6, TRUE, FALSE,
6700 EL_EMC_GATE_6_GRAY, -1, -1
6703 Xfake_door_7, TRUE, FALSE,
6704 EL_EMC_GATE_7_GRAY, -1, -1
6707 Xfake_door_8, TRUE, FALSE,
6708 EL_EMC_GATE_8_GRAY, -1, -1
6711 Xfake_acid_1, TRUE, FALSE,
6712 EL_EMC_FAKE_ACID, -1, -1
6715 Xfake_acid_2, FALSE, FALSE,
6716 EL_EMC_FAKE_ACID, -1, -1
6719 Xfake_acid_3, FALSE, FALSE,
6720 EL_EMC_FAKE_ACID, -1, -1
6723 Xfake_acid_4, FALSE, FALSE,
6724 EL_EMC_FAKE_ACID, -1, -1
6727 Xfake_acid_5, FALSE, FALSE,
6728 EL_EMC_FAKE_ACID, -1, -1
6731 Xfake_acid_6, FALSE, FALSE,
6732 EL_EMC_FAKE_ACID, -1, -1
6735 Xfake_acid_7, FALSE, FALSE,
6736 EL_EMC_FAKE_ACID, -1, -1
6739 Xfake_acid_8, FALSE, FALSE,
6740 EL_EMC_FAKE_ACID, -1, -1
6743 Xsteel_1, TRUE, FALSE,
6744 EL_STEELWALL, -1, -1
6747 Xsteel_2, TRUE, FALSE,
6748 EL_EMC_STEELWALL_2, -1, -1
6751 Xsteel_3, TRUE, FALSE,
6752 EL_EMC_STEELWALL_3, -1, -1
6755 Xsteel_4, TRUE, FALSE,
6756 EL_EMC_STEELWALL_4, -1, -1
6759 Xwall_1, TRUE, FALSE,
6763 Xwall_2, TRUE, FALSE,
6764 EL_EMC_WALL_14, -1, -1
6767 Xwall_3, TRUE, FALSE,
6768 EL_EMC_WALL_15, -1, -1
6771 Xwall_4, TRUE, FALSE,
6772 EL_EMC_WALL_16, -1, -1
6775 Xround_wall_1, TRUE, FALSE,
6776 EL_WALL_SLIPPERY, -1, -1
6779 Xround_wall_2, TRUE, FALSE,
6780 EL_EMC_WALL_SLIPPERY_2, -1, -1
6783 Xround_wall_3, TRUE, FALSE,
6784 EL_EMC_WALL_SLIPPERY_3, -1, -1
6787 Xround_wall_4, TRUE, FALSE,
6788 EL_EMC_WALL_SLIPPERY_4, -1, -1
6791 Xdecor_1, TRUE, FALSE,
6792 EL_EMC_WALL_8, -1, -1
6795 Xdecor_2, TRUE, FALSE,
6796 EL_EMC_WALL_6, -1, -1
6799 Xdecor_3, TRUE, FALSE,
6800 EL_EMC_WALL_4, -1, -1
6803 Xdecor_4, TRUE, FALSE,
6804 EL_EMC_WALL_7, -1, -1
6807 Xdecor_5, TRUE, FALSE,
6808 EL_EMC_WALL_5, -1, -1
6811 Xdecor_6, TRUE, FALSE,
6812 EL_EMC_WALL_9, -1, -1
6815 Xdecor_7, TRUE, FALSE,
6816 EL_EMC_WALL_10, -1, -1
6819 Xdecor_8, TRUE, FALSE,
6820 EL_EMC_WALL_1, -1, -1
6823 Xdecor_9, TRUE, FALSE,
6824 EL_EMC_WALL_2, -1, -1
6827 Xdecor_10, TRUE, FALSE,
6828 EL_EMC_WALL_3, -1, -1
6831 Xdecor_11, TRUE, FALSE,
6832 EL_EMC_WALL_11, -1, -1
6835 Xdecor_12, TRUE, FALSE,
6836 EL_EMC_WALL_12, -1, -1
6839 Xalpha_0, TRUE, FALSE,
6840 EL_CHAR('0'), -1, -1
6843 Xalpha_1, TRUE, FALSE,
6844 EL_CHAR('1'), -1, -1
6847 Xalpha_2, TRUE, FALSE,
6848 EL_CHAR('2'), -1, -1
6851 Xalpha_3, TRUE, FALSE,
6852 EL_CHAR('3'), -1, -1
6855 Xalpha_4, TRUE, FALSE,
6856 EL_CHAR('4'), -1, -1
6859 Xalpha_5, TRUE, FALSE,
6860 EL_CHAR('5'), -1, -1
6863 Xalpha_6, TRUE, FALSE,
6864 EL_CHAR('6'), -1, -1
6867 Xalpha_7, TRUE, FALSE,
6868 EL_CHAR('7'), -1, -1
6871 Xalpha_8, TRUE, FALSE,
6872 EL_CHAR('8'), -1, -1
6875 Xalpha_9, TRUE, FALSE,
6876 EL_CHAR('9'), -1, -1
6879 Xalpha_excla, TRUE, FALSE,
6880 EL_CHAR('!'), -1, -1
6883 Xalpha_quote, TRUE, FALSE,
6884 EL_CHAR('"'), -1, -1
6887 Xalpha_comma, TRUE, FALSE,
6888 EL_CHAR(','), -1, -1
6891 Xalpha_minus, TRUE, FALSE,
6892 EL_CHAR('-'), -1, -1
6895 Xalpha_perio, TRUE, FALSE,
6896 EL_CHAR('.'), -1, -1
6899 Xalpha_colon, TRUE, FALSE,
6900 EL_CHAR(':'), -1, -1
6903 Xalpha_quest, TRUE, FALSE,
6904 EL_CHAR('?'), -1, -1
6907 Xalpha_a, TRUE, FALSE,
6908 EL_CHAR('A'), -1, -1
6911 Xalpha_b, TRUE, FALSE,
6912 EL_CHAR('B'), -1, -1
6915 Xalpha_c, TRUE, FALSE,
6916 EL_CHAR('C'), -1, -1
6919 Xalpha_d, TRUE, FALSE,
6920 EL_CHAR('D'), -1, -1
6923 Xalpha_e, TRUE, FALSE,
6924 EL_CHAR('E'), -1, -1
6927 Xalpha_f, TRUE, FALSE,
6928 EL_CHAR('F'), -1, -1
6931 Xalpha_g, TRUE, FALSE,
6932 EL_CHAR('G'), -1, -1
6935 Xalpha_h, TRUE, FALSE,
6936 EL_CHAR('H'), -1, -1
6939 Xalpha_i, TRUE, FALSE,
6940 EL_CHAR('I'), -1, -1
6943 Xalpha_j, TRUE, FALSE,
6944 EL_CHAR('J'), -1, -1
6947 Xalpha_k, TRUE, FALSE,
6948 EL_CHAR('K'), -1, -1
6951 Xalpha_l, TRUE, FALSE,
6952 EL_CHAR('L'), -1, -1
6955 Xalpha_m, TRUE, FALSE,
6956 EL_CHAR('M'), -1, -1
6959 Xalpha_n, TRUE, FALSE,
6960 EL_CHAR('N'), -1, -1
6963 Xalpha_o, TRUE, FALSE,
6964 EL_CHAR('O'), -1, -1
6967 Xalpha_p, TRUE, FALSE,
6968 EL_CHAR('P'), -1, -1
6971 Xalpha_q, TRUE, FALSE,
6972 EL_CHAR('Q'), -1, -1
6975 Xalpha_r, TRUE, FALSE,
6976 EL_CHAR('R'), -1, -1
6979 Xalpha_s, TRUE, FALSE,
6980 EL_CHAR('S'), -1, -1
6983 Xalpha_t, TRUE, FALSE,
6984 EL_CHAR('T'), -1, -1
6987 Xalpha_u, TRUE, FALSE,
6988 EL_CHAR('U'), -1, -1
6991 Xalpha_v, TRUE, FALSE,
6992 EL_CHAR('V'), -1, -1
6995 Xalpha_w, TRUE, FALSE,
6996 EL_CHAR('W'), -1, -1
6999 Xalpha_x, TRUE, FALSE,
7000 EL_CHAR('X'), -1, -1
7003 Xalpha_y, TRUE, FALSE,
7004 EL_CHAR('Y'), -1, -1
7007 Xalpha_z, TRUE, FALSE,
7008 EL_CHAR('Z'), -1, -1
7011 Xalpha_arrow_e, TRUE, FALSE,
7012 EL_CHAR('>'), -1, -1
7015 Xalpha_arrow_w, TRUE, FALSE,
7016 EL_CHAR('<'), -1, -1
7019 Xalpha_copyr, TRUE, FALSE,
7020 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7024 Xboom_bug, FALSE, FALSE,
7025 EL_BUG, ACTION_EXPLODING, -1
7028 Xboom_bomb, FALSE, FALSE,
7029 EL_BOMB, ACTION_EXPLODING, -1
7032 Xboom_android, FALSE, FALSE,
7033 EL_EMC_ANDROID, ACTION_OTHER, -1
7036 Xboom_1, FALSE, FALSE,
7037 EL_DEFAULT, ACTION_EXPLODING, -1
7040 Xboom_2, FALSE, FALSE,
7041 EL_DEFAULT, ACTION_EXPLODING, -1
7044 Znormal, FALSE, FALSE,
7048 Zdynamite, FALSE, FALSE,
7052 Zplayer, FALSE, FALSE,
7056 ZBORDER, FALSE, FALSE,
7066 static struct Mapping_EM_to_RND_player
7075 em_player_mapping_list[] =
7079 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7083 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7087 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7091 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7095 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7099 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7103 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7107 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7111 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7115 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7119 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7123 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7127 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7131 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7135 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7139 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7143 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7147 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7151 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7155 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7159 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7163 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7167 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7171 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7175 EL_PLAYER_1, ACTION_DEFAULT, -1,
7179 EL_PLAYER_2, ACTION_DEFAULT, -1,
7183 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7187 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7191 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7195 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7199 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7203 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7207 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7211 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7215 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7219 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7223 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7227 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7231 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7235 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7239 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7243 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7247 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7251 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7255 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7259 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7263 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7267 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7271 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7275 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7279 EL_PLAYER_3, ACTION_DEFAULT, -1,
7283 EL_PLAYER_4, ACTION_DEFAULT, -1,
7292 int map_element_RND_to_EM(int element_rnd)
7294 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7295 static boolean mapping_initialized = FALSE;
7297 if (!mapping_initialized)
7301 /* return "Xalpha_quest" for all undefined elements in mapping array */
7302 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7303 mapping_RND_to_EM[i] = Xalpha_quest;
7305 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7306 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7307 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7308 em_object_mapping_list[i].element_em;
7310 mapping_initialized = TRUE;
7313 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7314 return mapping_RND_to_EM[element_rnd];
7316 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7321 int map_element_EM_to_RND(int element_em)
7323 static unsigned short mapping_EM_to_RND[TILE_MAX];
7324 static boolean mapping_initialized = FALSE;
7326 if (!mapping_initialized)
7330 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7331 for (i = 0; i < TILE_MAX; i++)
7332 mapping_EM_to_RND[i] = EL_UNKNOWN;
7334 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7335 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7336 em_object_mapping_list[i].element_rnd;
7338 mapping_initialized = TRUE;
7341 if (element_em >= 0 && element_em < TILE_MAX)
7342 return mapping_EM_to_RND[element_em];
7344 Error(ERR_WARN, "invalid EM level element %d", element_em);
7349 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7351 struct LevelInfo_EM *level_em = level->native_em_level;
7352 struct LEVEL *lev = level_em->lev;
7355 for (i = 0; i < TILE_MAX; i++)
7356 lev->android_array[i] = Xblank;
7358 for (i = 0; i < level->num_android_clone_elements; i++)
7360 int element_rnd = level->android_clone_element[i];
7361 int element_em = map_element_RND_to_EM(element_rnd);
7363 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7364 if (em_object_mapping_list[j].element_rnd == element_rnd)
7365 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7369 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7371 struct LevelInfo_EM *level_em = level->native_em_level;
7372 struct LEVEL *lev = level_em->lev;
7375 level->num_android_clone_elements = 0;
7377 for (i = 0; i < TILE_MAX; i++)
7379 int element_em = lev->android_array[i];
7381 boolean element_found = FALSE;
7383 if (element_em == Xblank)
7386 element_rnd = map_element_EM_to_RND(element_em);
7388 for (j = 0; j < level->num_android_clone_elements; j++)
7389 if (level->android_clone_element[j] == element_rnd)
7390 element_found = TRUE;
7394 level->android_clone_element[level->num_android_clone_elements++] =
7397 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7402 if (level->num_android_clone_elements == 0)
7404 level->num_android_clone_elements = 1;
7405 level->android_clone_element[0] = EL_EMPTY;
7409 int map_direction_RND_to_EM(int direction)
7411 return (direction == MV_UP ? 0 :
7412 direction == MV_RIGHT ? 1 :
7413 direction == MV_DOWN ? 2 :
7414 direction == MV_LEFT ? 3 :
7418 int map_direction_EM_to_RND(int direction)
7420 return (direction == 0 ? MV_UP :
7421 direction == 1 ? MV_RIGHT :
7422 direction == 2 ? MV_DOWN :
7423 direction == 3 ? MV_LEFT :
7427 int map_element_RND_to_SP(int element_rnd)
7429 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7431 if (element_rnd >= EL_SP_START &&
7432 element_rnd <= EL_SP_END)
7433 element_sp = element_rnd - EL_SP_START;
7434 else if (element_rnd == EL_EMPTY_SPACE)
7436 else if (element_rnd == EL_INVISIBLE_WALL)
7442 int map_element_SP_to_RND(int element_sp)
7444 int element_rnd = EL_UNKNOWN;
7446 if (element_sp >= 0x00 &&
7448 element_rnd = EL_SP_START + element_sp;
7449 else if (element_sp == 0x28)
7450 element_rnd = EL_INVISIBLE_WALL;
7455 int map_action_SP_to_RND(int action_sp)
7459 case actActive: return ACTION_ACTIVE;
7460 case actImpact: return ACTION_IMPACT;
7461 case actExploding: return ACTION_EXPLODING;
7462 case actDigging: return ACTION_DIGGING;
7463 case actSnapping: return ACTION_SNAPPING;
7464 case actCollecting: return ACTION_COLLECTING;
7465 case actPassing: return ACTION_PASSING;
7466 case actPushing: return ACTION_PUSHING;
7467 case actDropping: return ACTION_DROPPING;
7469 default: return ACTION_DEFAULT;
7473 int map_element_RND_to_MM(int element_rnd)
7475 return (element_rnd >= EL_MM_START_1 &&
7476 element_rnd <= EL_MM_END_1 ?
7477 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7479 element_rnd >= EL_MM_START_2 &&
7480 element_rnd <= EL_MM_END_2 ?
7481 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7483 element_rnd >= EL_CHAR_START &&
7484 element_rnd <= EL_CHAR_END ?
7485 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7487 element_rnd >= EL_MM_RUNTIME_START &&
7488 element_rnd <= EL_MM_RUNTIME_END ?
7489 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7491 element_rnd >= EL_MM_DUMMY_START &&
7492 element_rnd <= EL_MM_DUMMY_END ?
7493 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7495 EL_MM_EMPTY_NATIVE);
7498 int map_element_MM_to_RND(int element_mm)
7500 return (element_mm == EL_MM_EMPTY_NATIVE ||
7501 element_mm == EL_DF_EMPTY_NATIVE ?
7504 element_mm >= EL_MM_START_1_NATIVE &&
7505 element_mm <= EL_MM_END_1_NATIVE ?
7506 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7508 element_mm >= EL_MM_START_2_NATIVE &&
7509 element_mm <= EL_MM_END_2_NATIVE ?
7510 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7512 element_mm >= EL_MM_CHAR_START_NATIVE &&
7513 element_mm <= EL_MM_CHAR_END_NATIVE ?
7514 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7516 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7517 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7518 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7520 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7521 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7522 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7527 int map_action_MM_to_RND(int action_mm)
7529 /* all MM actions are defined to exactly match their RND counterparts */
7533 int map_sound_MM_to_RND(int sound_mm)
7537 case SND_MM_GAME_LEVELTIME_CHARGING:
7538 return SND_GAME_LEVELTIME_CHARGING;
7540 case SND_MM_GAME_HEALTH_CHARGING:
7541 return SND_GAME_HEALTH_CHARGING;
7544 return SND_UNDEFINED;
7548 int map_mm_wall_element(int element)
7550 return (element >= EL_MM_STEEL_WALL_START &&
7551 element <= EL_MM_STEEL_WALL_END ?
7554 element >= EL_MM_WOODEN_WALL_START &&
7555 element <= EL_MM_WOODEN_WALL_END ?
7558 element >= EL_MM_ICE_WALL_START &&
7559 element <= EL_MM_ICE_WALL_END ?
7562 element >= EL_MM_AMOEBA_WALL_START &&
7563 element <= EL_MM_AMOEBA_WALL_END ?
7566 element >= EL_DF_STEEL_WALL_START &&
7567 element <= EL_DF_STEEL_WALL_END ?
7570 element >= EL_DF_WOODEN_WALL_START &&
7571 element <= EL_DF_WOODEN_WALL_END ?
7577 int map_mm_wall_element_editor(int element)
7581 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7582 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7583 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7584 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7585 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7586 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7588 default: return element;
7592 int get_next_element(int element)
7596 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7597 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7598 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7599 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7600 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7601 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7602 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7603 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7604 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7605 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7606 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7608 default: return element;
7612 int el2img_mm(int element_mm)
7614 return el2img(map_element_MM_to_RND(element_mm));
7617 int el_act_dir2img(int element, int action, int direction)
7619 element = GFX_ELEMENT(element);
7620 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7622 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7623 return element_info[element].direction_graphic[action][direction];
7626 static int el_act_dir2crm(int element, int action, int direction)
7628 element = GFX_ELEMENT(element);
7629 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7631 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7632 return element_info[element].direction_crumbled[action][direction];
7635 int el_act2img(int element, int action)
7637 element = GFX_ELEMENT(element);
7639 return element_info[element].graphic[action];
7642 int el_act2crm(int element, int action)
7644 element = GFX_ELEMENT(element);
7646 return element_info[element].crumbled[action];
7649 int el_dir2img(int element, int direction)
7651 element = GFX_ELEMENT(element);
7653 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7656 int el2baseimg(int element)
7658 return element_info[element].graphic[ACTION_DEFAULT];
7661 int el2img(int element)
7663 element = GFX_ELEMENT(element);
7665 return element_info[element].graphic[ACTION_DEFAULT];
7668 int el2edimg(int element)
7670 element = GFX_ELEMENT(element);
7672 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7675 int el2preimg(int element)
7677 element = GFX_ELEMENT(element);
7679 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7682 int el2panelimg(int element)
7684 element = GFX_ELEMENT(element);
7686 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7689 int font2baseimg(int font_nr)
7691 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7694 int getBeltNrFromBeltElement(int element)
7696 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7697 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7698 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7701 int getBeltNrFromBeltActiveElement(int element)
7703 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7704 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7705 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7708 int getBeltNrFromBeltSwitchElement(int element)
7710 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7711 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7712 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7715 int getBeltDirNrFromBeltElement(int element)
7717 static int belt_base_element[4] =
7719 EL_CONVEYOR_BELT_1_LEFT,
7720 EL_CONVEYOR_BELT_2_LEFT,
7721 EL_CONVEYOR_BELT_3_LEFT,
7722 EL_CONVEYOR_BELT_4_LEFT
7725 int belt_nr = getBeltNrFromBeltElement(element);
7726 int belt_dir_nr = element - belt_base_element[belt_nr];
7728 return (belt_dir_nr % 3);
7731 int getBeltDirNrFromBeltSwitchElement(int element)
7733 static int belt_base_element[4] =
7735 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7736 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7737 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7738 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7741 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7742 int belt_dir_nr = element - belt_base_element[belt_nr];
7744 return (belt_dir_nr % 3);
7747 int getBeltDirFromBeltElement(int element)
7749 static int belt_move_dir[3] =
7756 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7758 return belt_move_dir[belt_dir_nr];
7761 int getBeltDirFromBeltSwitchElement(int element)
7763 static int belt_move_dir[3] =
7770 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7772 return belt_move_dir[belt_dir_nr];
7775 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7777 static int belt_base_element[4] =
7779 EL_CONVEYOR_BELT_1_LEFT,
7780 EL_CONVEYOR_BELT_2_LEFT,
7781 EL_CONVEYOR_BELT_3_LEFT,
7782 EL_CONVEYOR_BELT_4_LEFT
7785 return belt_base_element[belt_nr] + belt_dir_nr;
7788 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7790 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7792 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7795 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7797 static int belt_base_element[4] =
7799 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7800 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7801 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7802 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7805 return belt_base_element[belt_nr] + belt_dir_nr;
7808 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7810 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7812 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7815 boolean getTeamMode_EM()
7817 return game.team_mode;
7820 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7822 int game_frame_delay_value;
7824 game_frame_delay_value =
7825 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7826 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7829 if (tape.playing && tape.warp_forward && !tape.pausing)
7830 game_frame_delay_value = 0;
7832 return game_frame_delay_value;
7835 unsigned int InitRND(int seed)
7837 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7838 return InitEngineRandom_EM(seed);
7839 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7840 return InitEngineRandom_SP(seed);
7841 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7842 return InitEngineRandom_MM(seed);
7844 return InitEngineRandom_RND(seed);
7847 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7848 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7850 inline static int get_effective_element_EM(int tile, int frame_em)
7852 int element = object_mapping[tile].element_rnd;
7853 int action = object_mapping[tile].action;
7854 boolean is_backside = object_mapping[tile].is_backside;
7855 boolean action_removing = (action == ACTION_DIGGING ||
7856 action == ACTION_SNAPPING ||
7857 action == ACTION_COLLECTING);
7863 case Yacid_splash_eB:
7864 case Yacid_splash_wB:
7865 return (frame_em > 5 ? EL_EMPTY : element);
7871 else /* frame_em == 7 */
7875 case Yacid_splash_eB:
7876 case Yacid_splash_wB:
7879 case Yemerald_stone:
7882 case Ydiamond_stone:
7886 case Xdrip_stretchB:
7905 case Xsand_stonein_1:
7906 case Xsand_stonein_2:
7907 case Xsand_stonein_3:
7908 case Xsand_stonein_4:
7912 return (is_backside || action_removing ? EL_EMPTY : element);
7917 inline static boolean check_linear_animation_EM(int tile)
7921 case Xsand_stonesand_1:
7922 case Xsand_stonesand_quickout_1:
7923 case Xsand_sandstone_1:
7924 case Xsand_stonein_1:
7925 case Xsand_stoneout_1:
7944 case Yacid_splash_eB:
7945 case Yacid_splash_wB:
7946 case Yemerald_stone:
7953 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7954 boolean has_crumbled_graphics,
7955 int crumbled, int sync_frame)
7957 /* if element can be crumbled, but certain action graphics are just empty
7958 space (like instantly snapping sand to empty space in 1 frame), do not
7959 treat these empty space graphics as crumbled graphics in EMC engine */
7960 if (crumbled == IMG_EMPTY_SPACE)
7961 has_crumbled_graphics = FALSE;
7963 if (has_crumbled_graphics)
7965 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7966 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7967 g_crumbled->anim_delay,
7968 g_crumbled->anim_mode,
7969 g_crumbled->anim_start_frame,
7972 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7973 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7975 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7976 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7978 g_em->has_crumbled_graphics = TRUE;
7982 g_em->crumbled_bitmap = NULL;
7983 g_em->crumbled_src_x = 0;
7984 g_em->crumbled_src_y = 0;
7985 g_em->crumbled_border_size = 0;
7986 g_em->crumbled_tile_size = 0;
7988 g_em->has_crumbled_graphics = FALSE;
7992 void ResetGfxAnimation_EM(int x, int y, int tile)
7997 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7998 int tile, int frame_em, int x, int y)
8000 int action = object_mapping[tile].action;
8001 int direction = object_mapping[tile].direction;
8002 int effective_element = get_effective_element_EM(tile, frame_em);
8003 int graphic = (direction == MV_NONE ?
8004 el_act2img(effective_element, action) :
8005 el_act_dir2img(effective_element, action, direction));
8006 struct GraphicInfo *g = &graphic_info[graphic];
8008 boolean action_removing = (action == ACTION_DIGGING ||
8009 action == ACTION_SNAPPING ||
8010 action == ACTION_COLLECTING);
8011 boolean action_moving = (action == ACTION_FALLING ||
8012 action == ACTION_MOVING ||
8013 action == ACTION_PUSHING ||
8014 action == ACTION_EATING ||
8015 action == ACTION_FILLING ||
8016 action == ACTION_EMPTYING);
8017 boolean action_falling = (action == ACTION_FALLING ||
8018 action == ACTION_FILLING ||
8019 action == ACTION_EMPTYING);
8021 /* special case: graphic uses "2nd movement tile" and has defined
8022 7 frames for movement animation (or less) => use default graphic
8023 for last (8th) frame which ends the movement animation */
8024 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8026 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
8027 graphic = (direction == MV_NONE ?
8028 el_act2img(effective_element, action) :
8029 el_act_dir2img(effective_element, action, direction));
8031 g = &graphic_info[graphic];
8034 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8038 else if (action_moving)
8040 boolean is_backside = object_mapping[tile].is_backside;
8044 int direction = object_mapping[tile].direction;
8045 int move_dir = (action_falling ? MV_DOWN : direction);
8050 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
8051 if (g->double_movement && frame_em == 0)
8055 if (move_dir == MV_LEFT)
8056 GfxFrame[x - 1][y] = GfxFrame[x][y];
8057 else if (move_dir == MV_RIGHT)
8058 GfxFrame[x + 1][y] = GfxFrame[x][y];
8059 else if (move_dir == MV_UP)
8060 GfxFrame[x][y - 1] = GfxFrame[x][y];
8061 else if (move_dir == MV_DOWN)
8062 GfxFrame[x][y + 1] = GfxFrame[x][y];
8069 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
8070 if (tile == Xsand_stonesand_quickout_1 ||
8071 tile == Xsand_stonesand_quickout_2)
8075 if (graphic_info[graphic].anim_global_sync)
8076 sync_frame = FrameCounter;
8077 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8078 sync_frame = GfxFrame[x][y];
8080 sync_frame = 0; /* playfield border (pseudo steel) */
8082 SetRandomAnimationValue(x, y);
8084 int frame = getAnimationFrame(g->anim_frames,
8087 g->anim_start_frame,
8090 g_em->unique_identifier =
8091 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8094 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8095 int tile, int frame_em, int x, int y)
8097 int action = object_mapping[tile].action;
8098 int direction = object_mapping[tile].direction;
8099 boolean is_backside = object_mapping[tile].is_backside;
8100 int effective_element = get_effective_element_EM(tile, frame_em);
8101 int effective_action = action;
8102 int graphic = (direction == MV_NONE ?
8103 el_act2img(effective_element, effective_action) :
8104 el_act_dir2img(effective_element, effective_action,
8106 int crumbled = (direction == MV_NONE ?
8107 el_act2crm(effective_element, effective_action) :
8108 el_act_dir2crm(effective_element, effective_action,
8110 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8111 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8112 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8113 struct GraphicInfo *g = &graphic_info[graphic];
8116 /* special case: graphic uses "2nd movement tile" and has defined
8117 7 frames for movement animation (or less) => use default graphic
8118 for last (8th) frame which ends the movement animation */
8119 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8121 effective_action = ACTION_DEFAULT;
8122 graphic = (direction == MV_NONE ?
8123 el_act2img(effective_element, effective_action) :
8124 el_act_dir2img(effective_element, effective_action,
8126 crumbled = (direction == MV_NONE ?
8127 el_act2crm(effective_element, effective_action) :
8128 el_act_dir2crm(effective_element, effective_action,
8131 g = &graphic_info[graphic];
8134 if (graphic_info[graphic].anim_global_sync)
8135 sync_frame = FrameCounter;
8136 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8137 sync_frame = GfxFrame[x][y];
8139 sync_frame = 0; /* playfield border (pseudo steel) */
8141 SetRandomAnimationValue(x, y);
8143 int frame = getAnimationFrame(g->anim_frames,
8146 g->anim_start_frame,
8149 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8150 g->double_movement && is_backside);
8152 /* (updating the "crumbled" graphic definitions is probably not really needed,
8153 as animations for crumbled graphics can't be longer than one EMC cycle) */
8154 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8158 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8159 int player_nr, int anim, int frame_em)
8161 int element = player_mapping[player_nr][anim].element_rnd;
8162 int action = player_mapping[player_nr][anim].action;
8163 int direction = player_mapping[player_nr][anim].direction;
8164 int graphic = (direction == MV_NONE ?
8165 el_act2img(element, action) :
8166 el_act_dir2img(element, action, direction));
8167 struct GraphicInfo *g = &graphic_info[graphic];
8170 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8172 stored_player[player_nr].StepFrame = frame_em;
8174 sync_frame = stored_player[player_nr].Frame;
8176 int frame = getAnimationFrame(g->anim_frames,
8179 g->anim_start_frame,
8182 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8183 &g_em->src_x, &g_em->src_y, FALSE);
8186 void InitGraphicInfo_EM(void)
8191 int num_em_gfx_errors = 0;
8193 if (graphic_info_em_object[0][0].bitmap == NULL)
8195 /* EM graphics not yet initialized in em_open_all() */
8200 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8203 /* always start with reliable default values */
8204 for (i = 0; i < TILE_MAX; i++)
8206 object_mapping[i].element_rnd = EL_UNKNOWN;
8207 object_mapping[i].is_backside = FALSE;
8208 object_mapping[i].action = ACTION_DEFAULT;
8209 object_mapping[i].direction = MV_NONE;
8212 /* always start with reliable default values */
8213 for (p = 0; p < MAX_PLAYERS; p++)
8215 for (i = 0; i < SPR_MAX; i++)
8217 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8218 player_mapping[p][i].action = ACTION_DEFAULT;
8219 player_mapping[p][i].direction = MV_NONE;
8223 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8225 int e = em_object_mapping_list[i].element_em;
8227 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8228 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8230 if (em_object_mapping_list[i].action != -1)
8231 object_mapping[e].action = em_object_mapping_list[i].action;
8233 if (em_object_mapping_list[i].direction != -1)
8234 object_mapping[e].direction =
8235 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8238 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8240 int a = em_player_mapping_list[i].action_em;
8241 int p = em_player_mapping_list[i].player_nr;
8243 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8245 if (em_player_mapping_list[i].action != -1)
8246 player_mapping[p][a].action = em_player_mapping_list[i].action;
8248 if (em_player_mapping_list[i].direction != -1)
8249 player_mapping[p][a].direction =
8250 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8253 for (i = 0; i < TILE_MAX; i++)
8255 int element = object_mapping[i].element_rnd;
8256 int action = object_mapping[i].action;
8257 int direction = object_mapping[i].direction;
8258 boolean is_backside = object_mapping[i].is_backside;
8259 boolean action_exploding = ((action == ACTION_EXPLODING ||
8260 action == ACTION_SMASHED_BY_ROCK ||
8261 action == ACTION_SMASHED_BY_SPRING) &&
8262 element != EL_DIAMOND);
8263 boolean action_active = (action == ACTION_ACTIVE);
8264 boolean action_other = (action == ACTION_OTHER);
8266 for (j = 0; j < 8; j++)
8268 int effective_element = get_effective_element_EM(i, j);
8269 int effective_action = (j < 7 ? action :
8270 i == Xdrip_stretch ? action :
8271 i == Xdrip_stretchB ? action :
8272 i == Ydrip_s1 ? action :
8273 i == Ydrip_s1B ? action :
8274 i == Xball_1B ? action :
8275 i == Xball_2 ? action :
8276 i == Xball_2B ? action :
8277 i == Yball_eat ? action :
8278 i == Ykey_1_eat ? action :
8279 i == Ykey_2_eat ? action :
8280 i == Ykey_3_eat ? action :
8281 i == Ykey_4_eat ? action :
8282 i == Ykey_5_eat ? action :
8283 i == Ykey_6_eat ? action :
8284 i == Ykey_7_eat ? action :
8285 i == Ykey_8_eat ? action :
8286 i == Ylenses_eat ? action :
8287 i == Ymagnify_eat ? action :
8288 i == Ygrass_eat ? action :
8289 i == Ydirt_eat ? action :
8290 i == Xsand_stonein_1 ? action :
8291 i == Xsand_stonein_2 ? action :
8292 i == Xsand_stonein_3 ? action :
8293 i == Xsand_stonein_4 ? action :
8294 i == Xsand_stoneout_1 ? action :
8295 i == Xsand_stoneout_2 ? action :
8296 i == Xboom_android ? ACTION_EXPLODING :
8297 action_exploding ? ACTION_EXPLODING :
8298 action_active ? action :
8299 action_other ? action :
8301 int graphic = (el_act_dir2img(effective_element, effective_action,
8303 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8305 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8306 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8307 boolean has_action_graphics = (graphic != base_graphic);
8308 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8309 struct GraphicInfo *g = &graphic_info[graphic];
8310 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8313 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8314 boolean special_animation = (action != ACTION_DEFAULT &&
8315 g->anim_frames == 3 &&
8316 g->anim_delay == 2 &&
8317 g->anim_mode & ANIM_LINEAR);
8318 int sync_frame = (i == Xdrip_stretch ? 7 :
8319 i == Xdrip_stretchB ? 7 :
8320 i == Ydrip_s2 ? j + 8 :
8321 i == Ydrip_s2B ? j + 8 :
8330 i == Xfake_acid_1 ? 0 :
8331 i == Xfake_acid_2 ? 10 :
8332 i == Xfake_acid_3 ? 20 :
8333 i == Xfake_acid_4 ? 30 :
8334 i == Xfake_acid_5 ? 40 :
8335 i == Xfake_acid_6 ? 50 :
8336 i == Xfake_acid_7 ? 60 :
8337 i == Xfake_acid_8 ? 70 :
8339 i == Xball_2B ? j + 8 :
8340 i == Yball_eat ? j + 1 :
8341 i == Ykey_1_eat ? j + 1 :
8342 i == Ykey_2_eat ? j + 1 :
8343 i == Ykey_3_eat ? j + 1 :
8344 i == Ykey_4_eat ? j + 1 :
8345 i == Ykey_5_eat ? j + 1 :
8346 i == Ykey_6_eat ? j + 1 :
8347 i == Ykey_7_eat ? j + 1 :
8348 i == Ykey_8_eat ? j + 1 :
8349 i == Ylenses_eat ? j + 1 :
8350 i == Ymagnify_eat ? j + 1 :
8351 i == Ygrass_eat ? j + 1 :
8352 i == Ydirt_eat ? j + 1 :
8353 i == Xamoeba_1 ? 0 :
8354 i == Xamoeba_2 ? 1 :
8355 i == Xamoeba_3 ? 2 :
8356 i == Xamoeba_4 ? 3 :
8357 i == Xamoeba_5 ? 0 :
8358 i == Xamoeba_6 ? 1 :
8359 i == Xamoeba_7 ? 2 :
8360 i == Xamoeba_8 ? 3 :
8361 i == Xexit_2 ? j + 8 :
8362 i == Xexit_3 ? j + 16 :
8363 i == Xdynamite_1 ? 0 :
8364 i == Xdynamite_2 ? 8 :
8365 i == Xdynamite_3 ? 16 :
8366 i == Xdynamite_4 ? 24 :
8367 i == Xsand_stonein_1 ? j + 1 :
8368 i == Xsand_stonein_2 ? j + 9 :
8369 i == Xsand_stonein_3 ? j + 17 :
8370 i == Xsand_stonein_4 ? j + 25 :
8371 i == Xsand_stoneout_1 && j == 0 ? 0 :
8372 i == Xsand_stoneout_1 && j == 1 ? 0 :
8373 i == Xsand_stoneout_1 && j == 2 ? 1 :
8374 i == Xsand_stoneout_1 && j == 3 ? 2 :
8375 i == Xsand_stoneout_1 && j == 4 ? 2 :
8376 i == Xsand_stoneout_1 && j == 5 ? 3 :
8377 i == Xsand_stoneout_1 && j == 6 ? 4 :
8378 i == Xsand_stoneout_1 && j == 7 ? 4 :
8379 i == Xsand_stoneout_2 && j == 0 ? 5 :
8380 i == Xsand_stoneout_2 && j == 1 ? 6 :
8381 i == Xsand_stoneout_2 && j == 2 ? 7 :
8382 i == Xsand_stoneout_2 && j == 3 ? 8 :
8383 i == Xsand_stoneout_2 && j == 4 ? 9 :
8384 i == Xsand_stoneout_2 && j == 5 ? 11 :
8385 i == Xsand_stoneout_2 && j == 6 ? 13 :
8386 i == Xsand_stoneout_2 && j == 7 ? 15 :
8387 i == Xboom_bug && j == 1 ? 2 :
8388 i == Xboom_bug && j == 2 ? 2 :
8389 i == Xboom_bug && j == 3 ? 4 :
8390 i == Xboom_bug && j == 4 ? 4 :
8391 i == Xboom_bug && j == 5 ? 2 :
8392 i == Xboom_bug && j == 6 ? 2 :
8393 i == Xboom_bug && j == 7 ? 0 :
8394 i == Xboom_bomb && j == 1 ? 2 :
8395 i == Xboom_bomb && j == 2 ? 2 :
8396 i == Xboom_bomb && j == 3 ? 4 :
8397 i == Xboom_bomb && j == 4 ? 4 :
8398 i == Xboom_bomb && j == 5 ? 2 :
8399 i == Xboom_bomb && j == 6 ? 2 :
8400 i == Xboom_bomb && j == 7 ? 0 :
8401 i == Xboom_android && j == 7 ? 6 :
8402 i == Xboom_1 && j == 1 ? 2 :
8403 i == Xboom_1 && j == 2 ? 2 :
8404 i == Xboom_1 && j == 3 ? 4 :
8405 i == Xboom_1 && j == 4 ? 4 :
8406 i == Xboom_1 && j == 5 ? 6 :
8407 i == Xboom_1 && j == 6 ? 6 :
8408 i == Xboom_1 && j == 7 ? 8 :
8409 i == Xboom_2 && j == 0 ? 8 :
8410 i == Xboom_2 && j == 1 ? 8 :
8411 i == Xboom_2 && j == 2 ? 10 :
8412 i == Xboom_2 && j == 3 ? 10 :
8413 i == Xboom_2 && j == 4 ? 10 :
8414 i == Xboom_2 && j == 5 ? 12 :
8415 i == Xboom_2 && j == 6 ? 12 :
8416 i == Xboom_2 && j == 7 ? 12 :
8417 special_animation && j == 4 ? 3 :
8418 effective_action != action ? 0 :
8422 Bitmap *debug_bitmap = g_em->bitmap;
8423 int debug_src_x = g_em->src_x;
8424 int debug_src_y = g_em->src_y;
8427 int frame = getAnimationFrame(g->anim_frames,
8430 g->anim_start_frame,
8433 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8434 g->double_movement && is_backside);
8436 g_em->bitmap = src_bitmap;
8437 g_em->src_x = src_x;
8438 g_em->src_y = src_y;
8439 g_em->src_offset_x = 0;
8440 g_em->src_offset_y = 0;
8441 g_em->dst_offset_x = 0;
8442 g_em->dst_offset_y = 0;
8443 g_em->width = TILEX;
8444 g_em->height = TILEY;
8446 g_em->preserve_background = FALSE;
8448 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8451 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8452 effective_action == ACTION_MOVING ||
8453 effective_action == ACTION_PUSHING ||
8454 effective_action == ACTION_EATING)) ||
8455 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8456 effective_action == ACTION_EMPTYING)))
8459 (effective_action == ACTION_FALLING ||
8460 effective_action == ACTION_FILLING ||
8461 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8462 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8463 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8464 int num_steps = (i == Ydrip_s1 ? 16 :
8465 i == Ydrip_s1B ? 16 :
8466 i == Ydrip_s2 ? 16 :
8467 i == Ydrip_s2B ? 16 :
8468 i == Xsand_stonein_1 ? 32 :
8469 i == Xsand_stonein_2 ? 32 :
8470 i == Xsand_stonein_3 ? 32 :
8471 i == Xsand_stonein_4 ? 32 :
8472 i == Xsand_stoneout_1 ? 16 :
8473 i == Xsand_stoneout_2 ? 16 : 8);
8474 int cx = ABS(dx) * (TILEX / num_steps);
8475 int cy = ABS(dy) * (TILEY / num_steps);
8476 int step_frame = (i == Ydrip_s2 ? j + 8 :
8477 i == Ydrip_s2B ? j + 8 :
8478 i == Xsand_stonein_2 ? j + 8 :
8479 i == Xsand_stonein_3 ? j + 16 :
8480 i == Xsand_stonein_4 ? j + 24 :
8481 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8482 int step = (is_backside ? step_frame : num_steps - step_frame);
8484 if (is_backside) /* tile where movement starts */
8486 if (dx < 0 || dy < 0)
8488 g_em->src_offset_x = cx * step;
8489 g_em->src_offset_y = cy * step;
8493 g_em->dst_offset_x = cx * step;
8494 g_em->dst_offset_y = cy * step;
8497 else /* tile where movement ends */
8499 if (dx < 0 || dy < 0)
8501 g_em->dst_offset_x = cx * step;
8502 g_em->dst_offset_y = cy * step;
8506 g_em->src_offset_x = cx * step;
8507 g_em->src_offset_y = cy * step;
8511 g_em->width = TILEX - cx * step;
8512 g_em->height = TILEY - cy * step;
8515 /* create unique graphic identifier to decide if tile must be redrawn */
8516 /* bit 31 - 16 (16 bit): EM style graphic
8517 bit 15 - 12 ( 4 bit): EM style frame
8518 bit 11 - 6 ( 6 bit): graphic width
8519 bit 5 - 0 ( 6 bit): graphic height */
8520 g_em->unique_identifier =
8521 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8525 /* skip check for EMC elements not contained in original EMC artwork */
8526 if (element == EL_EMC_FAKE_ACID)
8529 if (g_em->bitmap != debug_bitmap ||
8530 g_em->src_x != debug_src_x ||
8531 g_em->src_y != debug_src_y ||
8532 g_em->src_offset_x != 0 ||
8533 g_em->src_offset_y != 0 ||
8534 g_em->dst_offset_x != 0 ||
8535 g_em->dst_offset_y != 0 ||
8536 g_em->width != TILEX ||
8537 g_em->height != TILEY)
8539 static int last_i = -1;
8547 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8548 i, element, element_info[element].token_name,
8549 element_action_info[effective_action].suffix, direction);
8551 if (element != effective_element)
8552 printf(" [%d ('%s')]",
8554 element_info[effective_element].token_name);
8558 if (g_em->bitmap != debug_bitmap)
8559 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8560 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8562 if (g_em->src_x != debug_src_x ||
8563 g_em->src_y != debug_src_y)
8564 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8565 j, (is_backside ? 'B' : 'F'),
8566 g_em->src_x, g_em->src_y,
8567 g_em->src_x / 32, g_em->src_y / 32,
8568 debug_src_x, debug_src_y,
8569 debug_src_x / 32, debug_src_y / 32);
8571 if (g_em->src_offset_x != 0 ||
8572 g_em->src_offset_y != 0 ||
8573 g_em->dst_offset_x != 0 ||
8574 g_em->dst_offset_y != 0)
8575 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8577 g_em->src_offset_x, g_em->src_offset_y,
8578 g_em->dst_offset_x, g_em->dst_offset_y);
8580 if (g_em->width != TILEX ||
8581 g_em->height != TILEY)
8582 printf(" %d (%d): size %d,%d should be %d,%d\n",
8584 g_em->width, g_em->height, TILEX, TILEY);
8586 num_em_gfx_errors++;
8593 for (i = 0; i < TILE_MAX; i++)
8595 for (j = 0; j < 8; j++)
8597 int element = object_mapping[i].element_rnd;
8598 int action = object_mapping[i].action;
8599 int direction = object_mapping[i].direction;
8600 boolean is_backside = object_mapping[i].is_backside;
8601 int graphic_action = el_act_dir2img(element, action, direction);
8602 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8604 if ((action == ACTION_SMASHED_BY_ROCK ||
8605 action == ACTION_SMASHED_BY_SPRING ||
8606 action == ACTION_EATING) &&
8607 graphic_action == graphic_default)
8609 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8610 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8611 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8612 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8615 /* no separate animation for "smashed by rock" -- use rock instead */
8616 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8617 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8619 g_em->bitmap = g_xx->bitmap;
8620 g_em->src_x = g_xx->src_x;
8621 g_em->src_y = g_xx->src_y;
8622 g_em->src_offset_x = g_xx->src_offset_x;
8623 g_em->src_offset_y = g_xx->src_offset_y;
8624 g_em->dst_offset_x = g_xx->dst_offset_x;
8625 g_em->dst_offset_y = g_xx->dst_offset_y;
8626 g_em->width = g_xx->width;
8627 g_em->height = g_xx->height;
8628 g_em->unique_identifier = g_xx->unique_identifier;
8631 g_em->preserve_background = TRUE;
8636 for (p = 0; p < MAX_PLAYERS; p++)
8638 for (i = 0; i < SPR_MAX; i++)
8640 int element = player_mapping[p][i].element_rnd;
8641 int action = player_mapping[p][i].action;
8642 int direction = player_mapping[p][i].direction;
8644 for (j = 0; j < 8; j++)
8646 int effective_element = element;
8647 int effective_action = action;
8648 int graphic = (direction == MV_NONE ?
8649 el_act2img(effective_element, effective_action) :
8650 el_act_dir2img(effective_element, effective_action,
8652 struct GraphicInfo *g = &graphic_info[graphic];
8653 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8659 Bitmap *debug_bitmap = g_em->bitmap;
8660 int debug_src_x = g_em->src_x;
8661 int debug_src_y = g_em->src_y;
8664 int frame = getAnimationFrame(g->anim_frames,
8667 g->anim_start_frame,
8670 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8672 g_em->bitmap = src_bitmap;
8673 g_em->src_x = src_x;
8674 g_em->src_y = src_y;
8675 g_em->src_offset_x = 0;
8676 g_em->src_offset_y = 0;
8677 g_em->dst_offset_x = 0;
8678 g_em->dst_offset_y = 0;
8679 g_em->width = TILEX;
8680 g_em->height = TILEY;
8684 /* skip check for EMC elements not contained in original EMC artwork */
8685 if (element == EL_PLAYER_3 ||
8686 element == EL_PLAYER_4)
8689 if (g_em->bitmap != debug_bitmap ||
8690 g_em->src_x != debug_src_x ||
8691 g_em->src_y != debug_src_y)
8693 static int last_i = -1;
8701 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8702 p, i, element, element_info[element].token_name,
8703 element_action_info[effective_action].suffix, direction);
8705 if (element != effective_element)
8706 printf(" [%d ('%s')]",
8708 element_info[effective_element].token_name);
8712 if (g_em->bitmap != debug_bitmap)
8713 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8714 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8716 if (g_em->src_x != debug_src_x ||
8717 g_em->src_y != debug_src_y)
8718 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8720 g_em->src_x, g_em->src_y,
8721 g_em->src_x / 32, g_em->src_y / 32,
8722 debug_src_x, debug_src_y,
8723 debug_src_x / 32, debug_src_y / 32);
8725 num_em_gfx_errors++;
8735 printf("::: [%d errors found]\n", num_em_gfx_errors);
8741 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8742 boolean any_player_moving,
8743 boolean any_player_snapping,
8744 boolean any_player_dropping)
8746 if (frame == 0 && !any_player_dropping)
8748 if (!local_player->was_waiting)
8750 if (!CheckSaveEngineSnapshotToList())
8753 local_player->was_waiting = TRUE;
8756 else if (any_player_moving || any_player_snapping || any_player_dropping)
8758 local_player->was_waiting = FALSE;
8762 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8763 boolean murphy_is_dropping)
8765 if (murphy_is_waiting)
8767 if (!local_player->was_waiting)
8769 if (!CheckSaveEngineSnapshotToList())
8772 local_player->was_waiting = TRUE;
8777 local_player->was_waiting = FALSE;
8781 void CheckSaveEngineSnapshot_MM(boolean element_clicked,
8782 boolean button_released)
8784 if (button_released)
8786 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
8787 CheckSaveEngineSnapshotToList();
8789 else if (element_clicked)
8791 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
8792 CheckSaveEngineSnapshotToList();
8794 game.snapshot.changed_action = TRUE;
8798 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8799 boolean any_player_moving,
8800 boolean any_player_snapping,
8801 boolean any_player_dropping)
8803 if (tape.single_step && tape.recording && !tape.pausing)
8804 if (frame == 0 && !any_player_dropping)
8805 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8807 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8808 any_player_snapping, any_player_dropping);
8811 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8812 boolean murphy_is_dropping)
8814 boolean murphy_starts_dropping = FALSE;
8817 for (i = 0; i < MAX_PLAYERS; i++)
8818 if (stored_player[i].force_dropping)
8819 murphy_starts_dropping = TRUE;
8821 if (tape.single_step && tape.recording && !tape.pausing)
8822 if (murphy_is_waiting && !murphy_starts_dropping)
8823 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8825 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8828 void CheckSingleStepMode_MM(boolean element_clicked,
8829 boolean button_released)
8831 if (tape.single_step && tape.recording && !tape.pausing)
8832 if (button_released)
8833 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8835 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
8838 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8839 int graphic, int sync_frame, int x, int y)
8841 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8843 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8846 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8848 return (IS_NEXT_FRAME(sync_frame, graphic));
8851 int getGraphicInfo_Delay(int graphic)
8853 return graphic_info[graphic].anim_delay;
8856 void PlayMenuSoundExt(int sound)
8858 if (sound == SND_UNDEFINED)
8861 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8862 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8865 if (IS_LOOP_SOUND(sound))
8866 PlaySoundLoop(sound);
8871 void PlayMenuSound()
8873 PlayMenuSoundExt(menu.sound[game_status]);
8876 void PlayMenuSoundStereo(int sound, int stereo_position)
8878 if (sound == SND_UNDEFINED)
8881 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8882 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8885 if (IS_LOOP_SOUND(sound))
8886 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8888 PlaySoundStereo(sound, stereo_position);
8891 void PlayMenuSoundIfLoopExt(int sound)
8893 if (sound == SND_UNDEFINED)
8896 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8897 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8900 if (IS_LOOP_SOUND(sound))
8901 PlaySoundLoop(sound);
8904 void PlayMenuSoundIfLoop()
8906 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8909 void PlayMenuMusicExt(int music)
8911 if (music == MUS_UNDEFINED)
8914 if (!setup.sound_music)
8920 void PlayMenuMusic()
8922 char *curr_music = getCurrentlyPlayingMusicFilename();
8923 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8925 if (!strEqual(curr_music, next_music))
8926 PlayMenuMusicExt(menu.music[game_status]);
8929 void PlayMenuSoundsAndMusic()
8935 static void FadeMenuSounds()
8940 static void FadeMenuMusic()
8942 char *curr_music = getCurrentlyPlayingMusicFilename();
8943 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8945 if (!strEqual(curr_music, next_music))
8949 void FadeMenuSoundsAndMusic()
8955 void PlaySoundActivating()
8958 PlaySound(SND_MENU_ITEM_ACTIVATING);
8962 void PlaySoundSelecting()
8965 PlaySound(SND_MENU_ITEM_SELECTING);
8969 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8971 boolean change_fullscreen = (setup.fullscreen !=
8972 video.fullscreen_enabled);
8973 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8974 setup.window_scaling_percent !=
8975 video.window_scaling_percent);
8977 if (change_window_scaling_percent && video.fullscreen_enabled)
8980 if (!change_window_scaling_percent && !video.fullscreen_available)
8983 #if defined(TARGET_SDL2)
8984 if (change_window_scaling_percent)
8986 SDLSetWindowScaling(setup.window_scaling_percent);
8990 else if (change_fullscreen)
8992 SDLSetWindowFullscreen(setup.fullscreen);
8994 /* set setup value according to successfully changed fullscreen mode */
8995 setup.fullscreen = video.fullscreen_enabled;
9001 if (change_fullscreen ||
9002 change_window_scaling_percent)
9004 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9006 /* save backbuffer content which gets lost when toggling fullscreen mode */
9007 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9009 if (change_window_scaling_percent)
9011 /* keep window mode, but change window scaling */
9012 video.fullscreen_enabled = TRUE; /* force new window scaling */
9015 /* toggle fullscreen */
9016 ChangeVideoModeIfNeeded(setup.fullscreen);
9018 /* set setup value according to successfully changed fullscreen mode */
9019 setup.fullscreen = video.fullscreen_enabled;
9021 /* restore backbuffer content from temporary backbuffer backup bitmap */
9022 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9024 FreeBitmap(tmp_backbuffer);
9026 /* update visible window/screen */
9027 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9031 void JoinRectangles(int *x, int *y, int *width, int *height,
9032 int x2, int y2, int width2, int height2)
9034 // do not join with "off-screen" rectangle
9035 if (x2 == -1 || y2 == -1)
9040 *width = MAX(*width, width2);
9041 *height = MAX(*height, height2);
9044 void SetAnimStatus(int anim_status_new)
9046 if (anim_status_new == GAME_MODE_MAIN)
9047 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9048 else if (anim_status_new == GAME_MODE_SCORES)
9049 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9051 global.anim_status_next = anim_status_new;
9053 // directly set screen modes that are entered without fading
9054 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9055 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9056 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9057 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9058 global.anim_status = global.anim_status_next;
9061 void SetGameStatus(int game_status_new)
9063 if (game_status_new != game_status)
9064 game_status_last_screen = game_status;
9066 game_status = game_status_new;
9068 SetAnimStatus(game_status_new);
9071 void SetFontStatus(int game_status_new)
9073 static int last_game_status = -1;
9075 if (game_status_new != -1)
9077 // set game status for font use after storing last game status
9078 last_game_status = game_status;
9079 game_status = game_status_new;
9083 // reset game status after font use from last stored game status
9084 game_status = last_game_status;
9088 void ResetFontStatus()
9093 boolean CheckIfPlayfieldViewportHasChanged()
9095 // if game status has not changed, playfield viewport has not changed either
9096 if (game_status == game_status_last)
9099 // check if playfield viewport has changed with current game status
9100 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9101 int new_real_sx = vp_playfield->x;
9102 int new_real_sy = vp_playfield->y;
9103 int new_full_sxsize = vp_playfield->width;
9104 int new_full_sysize = vp_playfield->height;
9106 return (new_real_sx != REAL_SX ||
9107 new_real_sy != REAL_SY ||
9108 new_full_sxsize != FULL_SXSIZE ||
9109 new_full_sysize != FULL_SYSIZE);
9112 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
9114 return (CheckIfGlobalBorderHasChanged() ||
9115 CheckIfPlayfieldViewportHasChanged());
9118 void ChangeViewportPropertiesIfNeeded()
9120 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9121 FALSE : setup.small_game_graphics);
9122 int gfx_game_mode = game_status;
9123 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9125 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9126 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9127 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9128 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9129 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9130 int new_win_xsize = vp_window->width;
9131 int new_win_ysize = vp_window->height;
9132 int border_size = vp_playfield->border_size;
9133 int new_sx = vp_playfield->x + border_size;
9134 int new_sy = vp_playfield->y + border_size;
9135 int new_sxsize = vp_playfield->width - 2 * border_size;
9136 int new_sysize = vp_playfield->height - 2 * border_size;
9137 int new_real_sx = vp_playfield->x;
9138 int new_real_sy = vp_playfield->y;
9139 int new_full_sxsize = vp_playfield->width;
9140 int new_full_sysize = vp_playfield->height;
9141 int new_dx = vp_door_1->x;
9142 int new_dy = vp_door_1->y;
9143 int new_dxsize = vp_door_1->width;
9144 int new_dysize = vp_door_1->height;
9145 int new_vx = vp_door_2->x;
9146 int new_vy = vp_door_2->y;
9147 int new_vxsize = vp_door_2->width;
9148 int new_vysize = vp_door_2->height;
9149 int new_ex = vp_door_3->x;
9150 int new_ey = vp_door_3->y;
9151 int new_exsize = vp_door_3->width;
9152 int new_eysize = vp_door_3->height;
9153 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9154 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9155 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9156 int new_scr_fieldx = new_sxsize / tilesize;
9157 int new_scr_fieldy = new_sysize / tilesize;
9158 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9159 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9160 boolean init_gfx_buffers = FALSE;
9161 boolean init_video_buffer = FALSE;
9162 boolean init_gadgets_and_anims = FALSE;
9163 boolean init_em_graphics = FALSE;
9165 if (new_win_xsize != WIN_XSIZE ||
9166 new_win_ysize != WIN_YSIZE)
9168 WIN_XSIZE = new_win_xsize;
9169 WIN_YSIZE = new_win_ysize;
9171 init_video_buffer = TRUE;
9172 init_gfx_buffers = TRUE;
9173 init_gadgets_and_anims = TRUE;
9175 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9178 if (new_scr_fieldx != SCR_FIELDX ||
9179 new_scr_fieldy != SCR_FIELDY)
9181 /* this always toggles between MAIN and GAME when using small tile size */
9183 SCR_FIELDX = new_scr_fieldx;
9184 SCR_FIELDY = new_scr_fieldy;
9186 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9197 new_sxsize != SXSIZE ||
9198 new_sysize != SYSIZE ||
9199 new_dxsize != DXSIZE ||
9200 new_dysize != DYSIZE ||
9201 new_vxsize != VXSIZE ||
9202 new_vysize != VYSIZE ||
9203 new_exsize != EXSIZE ||
9204 new_eysize != EYSIZE ||
9205 new_real_sx != REAL_SX ||
9206 new_real_sy != REAL_SY ||
9207 new_full_sxsize != FULL_SXSIZE ||
9208 new_full_sysize != FULL_SYSIZE ||
9209 new_tilesize_var != TILESIZE_VAR
9212 // ------------------------------------------------------------------------
9213 // determine next fading area for changed viewport definitions
9214 // ------------------------------------------------------------------------
9216 // start with current playfield area (default fading area)
9219 FADE_SXSIZE = FULL_SXSIZE;
9220 FADE_SYSIZE = FULL_SYSIZE;
9222 // add new playfield area if position or size has changed
9223 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9224 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9226 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9227 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9230 // add current and new door 1 area if position or size has changed
9231 if (new_dx != DX || new_dy != DY ||
9232 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9234 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9235 DX, DY, DXSIZE, DYSIZE);
9236 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9237 new_dx, new_dy, new_dxsize, new_dysize);
9240 // add current and new door 2 area if position or size has changed
9241 if (new_dx != VX || new_dy != VY ||
9242 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9244 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9245 VX, VY, VXSIZE, VYSIZE);
9246 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9247 new_vx, new_vy, new_vxsize, new_vysize);
9250 // ------------------------------------------------------------------------
9251 // handle changed tile size
9252 // ------------------------------------------------------------------------
9254 if (new_tilesize_var != TILESIZE_VAR)
9256 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9258 // changing tile size invalidates scroll values of engine snapshots
9259 FreeEngineSnapshotSingle();
9261 // changing tile size requires update of graphic mapping for EM engine
9262 init_em_graphics = TRUE;
9273 SXSIZE = new_sxsize;
9274 SYSIZE = new_sysize;
9275 DXSIZE = new_dxsize;
9276 DYSIZE = new_dysize;
9277 VXSIZE = new_vxsize;
9278 VYSIZE = new_vysize;
9279 EXSIZE = new_exsize;
9280 EYSIZE = new_eysize;
9281 REAL_SX = new_real_sx;
9282 REAL_SY = new_real_sy;
9283 FULL_SXSIZE = new_full_sxsize;
9284 FULL_SYSIZE = new_full_sysize;
9285 TILESIZE_VAR = new_tilesize_var;
9287 init_gfx_buffers = TRUE;
9288 init_gadgets_and_anims = TRUE;
9290 // printf("::: viewports: init_gfx_buffers\n");
9291 // printf("::: viewports: init_gadgets_and_anims\n");
9294 if (init_gfx_buffers)
9296 // printf("::: init_gfx_buffers\n");
9298 SCR_FIELDX = new_scr_fieldx_buffers;
9299 SCR_FIELDY = new_scr_fieldy_buffers;
9303 SCR_FIELDX = new_scr_fieldx;
9304 SCR_FIELDY = new_scr_fieldy;
9306 SetDrawDeactivationMask(REDRAW_NONE);
9307 SetDrawBackgroundMask(REDRAW_FIELD);
9310 if (init_video_buffer)
9312 // printf("::: init_video_buffer\n");
9314 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9315 InitImageTextures();
9318 if (init_gadgets_and_anims)
9320 // printf("::: init_gadgets_and_anims\n");
9323 InitGlobalAnimations();
9326 if (init_em_graphics)
9328 InitGraphicInfo_EM();