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)
4184 #if defined(TARGET_SDL2)
4187 #if defined(KSYM_Rewind)
4188 case KSYM_Rewind: /* for Amazon Fire TV remote */
4195 #if defined(TARGET_SDL2)
4197 #if defined(KSYM_FastForward)
4198 case KSYM_FastForward: /* for Amazon Fire TV remote */
4205 HandleKeysDebug(key);
4209 if (req_state & REQ_PLAYER)
4215 case EVENT_KEYRELEASE:
4216 ClearPlayerAction();
4219 #if defined(TARGET_SDL2)
4220 case SDL_CONTROLLERBUTTONDOWN:
4221 switch (event.cbutton.button)
4223 case SDL_CONTROLLER_BUTTON_A:
4224 case SDL_CONTROLLER_BUTTON_X:
4225 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4229 case SDL_CONTROLLER_BUTTON_B:
4230 case SDL_CONTROLLER_BUTTON_Y:
4231 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4232 case SDL_CONTROLLER_BUTTON_BACK:
4237 if (req_state & REQ_PLAYER)
4242 case SDL_CONTROLLERBUTTONUP:
4243 HandleJoystickEvent(&event);
4244 ClearPlayerAction();
4249 HandleOtherEvents(&event);
4254 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4256 int joy = AnyJoystick();
4258 if (joy & JOY_BUTTON_1)
4260 else if (joy & JOY_BUTTON_2)
4266 if (global.use_envelope_request)
4268 /* copy back current state of pressed buttons inside request area */
4269 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4279 static boolean RequestDoor(char *text, unsigned int req_state)
4281 unsigned int old_door_state;
4282 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4283 int font_nr = FONT_TEXT_2;
4288 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4290 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4291 font_nr = FONT_TEXT_1;
4294 if (game_status == GAME_MODE_PLAYING)
4295 BlitScreenToBitmap(backbuffer);
4297 /* disable deactivated drawing when quick-loading level tape recording */
4298 if (tape.playing && tape.deactivate_display)
4299 TapeDeactivateDisplayOff(TRUE);
4301 SetMouseCursor(CURSOR_DEFAULT);
4303 #if defined(NETWORK_AVALIABLE)
4304 /* pause network game while waiting for request to answer */
4305 if (options.network &&
4306 game_status == GAME_MODE_PLAYING &&
4307 req_state & REQUEST_WAIT_FOR_INPUT)
4308 SendToServer_PausePlaying();
4311 old_door_state = GetDoorState();
4313 /* simulate releasing mouse button over last gadget, if still pressed */
4315 HandleGadgets(-1, -1, 0);
4319 /* draw released gadget before proceeding */
4322 if (old_door_state & DOOR_OPEN_1)
4324 CloseDoor(DOOR_CLOSE_1);
4326 /* save old door content */
4327 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4328 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4331 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4332 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4334 /* clear door drawing field */
4335 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4337 /* force DOOR font inside door area */
4338 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4340 /* write text for request */
4341 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4343 char text_line[max_request_line_len + 1];
4349 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4351 tc = *(text_ptr + tx);
4352 // if (!tc || tc == ' ')
4353 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4357 if ((tc == '?' || tc == '!') && tl == 0)
4367 strncpy(text_line, text_ptr, tl);
4370 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4371 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4372 text_line, font_nr);
4374 text_ptr += tl + (tc == ' ' ? 1 : 0);
4375 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4380 if (req_state & REQ_ASK)
4382 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4383 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4385 else if (req_state & REQ_CONFIRM)
4387 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4389 else if (req_state & REQ_PLAYER)
4391 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4392 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4393 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4394 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4397 /* copy request gadgets to door backbuffer */
4398 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4400 OpenDoor(DOOR_OPEN_1);
4402 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4404 if (game_status == GAME_MODE_PLAYING)
4406 SetPanelBackground();
4407 SetDrawBackgroundMask(REDRAW_DOOR_1);
4411 SetDrawBackgroundMask(REDRAW_FIELD);
4417 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4419 // ---------- handle request buttons ----------
4420 result = RequestHandleEvents(req_state);
4424 if (!(req_state & REQ_STAY_OPEN))
4426 CloseDoor(DOOR_CLOSE_1);
4428 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4429 (req_state & REQ_REOPEN))
4430 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4435 if (game_status == GAME_MODE_PLAYING)
4437 SetPanelBackground();
4438 SetDrawBackgroundMask(REDRAW_DOOR_1);
4442 SetDrawBackgroundMask(REDRAW_FIELD);
4445 #if defined(NETWORK_AVALIABLE)
4446 /* continue network game after request */
4447 if (options.network &&
4448 game_status == GAME_MODE_PLAYING &&
4449 req_state & REQUEST_WAIT_FOR_INPUT)
4450 SendToServer_ContinuePlaying();
4453 /* restore deactivated drawing when quick-loading level tape recording */
4454 if (tape.playing && tape.deactivate_display)
4455 TapeDeactivateDisplayOn();
4460 static boolean RequestEnvelope(char *text, unsigned int req_state)
4464 if (game_status == GAME_MODE_PLAYING)
4465 BlitScreenToBitmap(backbuffer);
4467 /* disable deactivated drawing when quick-loading level tape recording */
4468 if (tape.playing && tape.deactivate_display)
4469 TapeDeactivateDisplayOff(TRUE);
4471 SetMouseCursor(CURSOR_DEFAULT);
4473 #if defined(NETWORK_AVALIABLE)
4474 /* pause network game while waiting for request to answer */
4475 if (options.network &&
4476 game_status == GAME_MODE_PLAYING &&
4477 req_state & REQUEST_WAIT_FOR_INPUT)
4478 SendToServer_PausePlaying();
4481 /* simulate releasing mouse button over last gadget, if still pressed */
4483 HandleGadgets(-1, -1, 0);
4487 // (replace with setting corresponding request background)
4488 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4489 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4491 /* clear door drawing field */
4492 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4494 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4496 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4498 if (game_status == GAME_MODE_PLAYING)
4500 SetPanelBackground();
4501 SetDrawBackgroundMask(REDRAW_DOOR_1);
4505 SetDrawBackgroundMask(REDRAW_FIELD);
4511 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4513 // ---------- handle request buttons ----------
4514 result = RequestHandleEvents(req_state);
4518 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4522 if (game_status == GAME_MODE_PLAYING)
4524 SetPanelBackground();
4525 SetDrawBackgroundMask(REDRAW_DOOR_1);
4529 SetDrawBackgroundMask(REDRAW_FIELD);
4532 #if defined(NETWORK_AVALIABLE)
4533 /* continue network game after request */
4534 if (options.network &&
4535 game_status == GAME_MODE_PLAYING &&
4536 req_state & REQUEST_WAIT_FOR_INPUT)
4537 SendToServer_ContinuePlaying();
4540 /* restore deactivated drawing when quick-loading level tape recording */
4541 if (tape.playing && tape.deactivate_display)
4542 TapeDeactivateDisplayOn();
4547 boolean Request(char *text, unsigned int req_state)
4549 boolean overlay_active = GetOverlayActive();
4552 SetOverlayActive(FALSE);
4554 if (global.use_envelope_request)
4555 result = RequestEnvelope(text, req_state);
4557 result = RequestDoor(text, req_state);
4559 SetOverlayActive(overlay_active);
4564 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4566 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4567 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4570 if (dpo1->sort_priority != dpo2->sort_priority)
4571 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4573 compare_result = dpo1->nr - dpo2->nr;
4575 return compare_result;
4578 void InitGraphicCompatibilityInfo_Doors()
4584 struct DoorInfo *door;
4588 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4589 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4591 { -1, -1, -1, NULL }
4593 struct Rect door_rect_list[] =
4595 { DX, DY, DXSIZE, DYSIZE },
4596 { VX, VY, VXSIZE, VYSIZE }
4600 for (i = 0; doors[i].door_token != -1; i++)
4602 int door_token = doors[i].door_token;
4603 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4604 int part_1 = doors[i].part_1;
4605 int part_8 = doors[i].part_8;
4606 int part_2 = part_1 + 1;
4607 int part_3 = part_1 + 2;
4608 struct DoorInfo *door = doors[i].door;
4609 struct Rect *door_rect = &door_rect_list[door_index];
4610 boolean door_gfx_redefined = FALSE;
4612 /* check if any door part graphic definitions have been redefined */
4614 for (j = 0; door_part_controls[j].door_token != -1; j++)
4616 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4617 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4619 if (dpc->door_token == door_token && fi->redefined)
4620 door_gfx_redefined = TRUE;
4623 /* check for old-style door graphic/animation modifications */
4625 if (!door_gfx_redefined)
4627 if (door->anim_mode & ANIM_STATIC_PANEL)
4629 door->panel.step_xoffset = 0;
4630 door->panel.step_yoffset = 0;
4633 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4635 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4636 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4637 int num_door_steps, num_panel_steps;
4639 /* remove door part graphics other than the two default wings */
4641 for (j = 0; door_part_controls[j].door_token != -1; j++)
4643 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4644 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4646 if (dpc->graphic >= part_3 &&
4647 dpc->graphic <= part_8)
4651 /* set graphics and screen positions of the default wings */
4653 g_part_1->width = door_rect->width;
4654 g_part_1->height = door_rect->height;
4655 g_part_2->width = door_rect->width;
4656 g_part_2->height = door_rect->height;
4657 g_part_2->src_x = door_rect->width;
4658 g_part_2->src_y = g_part_1->src_y;
4660 door->part_2.x = door->part_1.x;
4661 door->part_2.y = door->part_1.y;
4663 if (door->width != -1)
4665 g_part_1->width = door->width;
4666 g_part_2->width = door->width;
4668 // special treatment for graphics and screen position of right wing
4669 g_part_2->src_x += door_rect->width - door->width;
4670 door->part_2.x += door_rect->width - door->width;
4673 if (door->height != -1)
4675 g_part_1->height = door->height;
4676 g_part_2->height = door->height;
4678 // special treatment for graphics and screen position of bottom wing
4679 g_part_2->src_y += door_rect->height - door->height;
4680 door->part_2.y += door_rect->height - door->height;
4683 /* set animation delays for the default wings and panels */
4685 door->part_1.step_delay = door->step_delay;
4686 door->part_2.step_delay = door->step_delay;
4687 door->panel.step_delay = door->step_delay;
4689 /* set animation draw order for the default wings */
4691 door->part_1.sort_priority = 2; /* draw left wing over ... */
4692 door->part_2.sort_priority = 1; /* ... right wing */
4694 /* set animation draw offset for the default wings */
4696 if (door->anim_mode & ANIM_HORIZONTAL)
4698 door->part_1.step_xoffset = door->step_offset;
4699 door->part_1.step_yoffset = 0;
4700 door->part_2.step_xoffset = door->step_offset * -1;
4701 door->part_2.step_yoffset = 0;
4703 num_door_steps = g_part_1->width / door->step_offset;
4705 else // ANIM_VERTICAL
4707 door->part_1.step_xoffset = 0;
4708 door->part_1.step_yoffset = door->step_offset;
4709 door->part_2.step_xoffset = 0;
4710 door->part_2.step_yoffset = door->step_offset * -1;
4712 num_door_steps = g_part_1->height / door->step_offset;
4715 /* set animation draw offset for the default panels */
4717 if (door->step_offset > 1)
4719 num_panel_steps = 2 * door_rect->height / door->step_offset;
4720 door->panel.start_step = num_panel_steps - num_door_steps;
4721 door->panel.start_step_closing = door->panel.start_step;
4725 num_panel_steps = door_rect->height / door->step_offset;
4726 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4727 door->panel.start_step_closing = door->panel.start_step;
4728 door->panel.step_delay *= 2;
4739 for (i = 0; door_part_controls[i].door_token != -1; i++)
4741 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4742 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4744 /* initialize "start_step_opening" and "start_step_closing", if needed */
4745 if (dpc->pos->start_step_opening == 0 &&
4746 dpc->pos->start_step_closing == 0)
4748 // dpc->pos->start_step_opening = dpc->pos->start_step;
4749 dpc->pos->start_step_closing = dpc->pos->start_step;
4752 /* fill structure for door part draw order (sorted below) */
4754 dpo->sort_priority = dpc->pos->sort_priority;
4757 /* sort door part controls according to sort_priority and graphic number */
4758 qsort(door_part_order, MAX_DOOR_PARTS,
4759 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4762 unsigned int OpenDoor(unsigned int door_state)
4764 if (door_state & DOOR_COPY_BACK)
4766 if (door_state & DOOR_OPEN_1)
4767 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4768 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4770 if (door_state & DOOR_OPEN_2)
4771 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4772 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4774 door_state &= ~DOOR_COPY_BACK;
4777 return MoveDoor(door_state);
4780 unsigned int CloseDoor(unsigned int door_state)
4782 unsigned int old_door_state = GetDoorState();
4784 if (!(door_state & DOOR_NO_COPY_BACK))
4786 if (old_door_state & DOOR_OPEN_1)
4787 BlitBitmap(backbuffer, bitmap_db_door_1,
4788 DX, DY, DXSIZE, DYSIZE, 0, 0);
4790 if (old_door_state & DOOR_OPEN_2)
4791 BlitBitmap(backbuffer, bitmap_db_door_2,
4792 VX, VY, VXSIZE, VYSIZE, 0, 0);
4794 door_state &= ~DOOR_NO_COPY_BACK;
4797 return MoveDoor(door_state);
4800 unsigned int GetDoorState()
4802 return MoveDoor(DOOR_GET_STATE);
4805 unsigned int SetDoorState(unsigned int door_state)
4807 return MoveDoor(door_state | DOOR_SET_STATE);
4810 int euclid(int a, int b)
4812 return (b ? euclid(b, a % b) : a);
4815 unsigned int MoveDoor(unsigned int door_state)
4817 struct Rect door_rect_list[] =
4819 { DX, DY, DXSIZE, DYSIZE },
4820 { VX, VY, VXSIZE, VYSIZE }
4822 static int door1 = DOOR_CLOSE_1;
4823 static int door2 = DOOR_CLOSE_2;
4824 unsigned int door_delay = 0;
4825 unsigned int door_delay_value;
4828 if (door_state == DOOR_GET_STATE)
4829 return (door1 | door2);
4831 if (door_state & DOOR_SET_STATE)
4833 if (door_state & DOOR_ACTION_1)
4834 door1 = door_state & DOOR_ACTION_1;
4835 if (door_state & DOOR_ACTION_2)
4836 door2 = door_state & DOOR_ACTION_2;
4838 return (door1 | door2);
4841 if (!(door_state & DOOR_FORCE_REDRAW))
4843 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4844 door_state &= ~DOOR_OPEN_1;
4845 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4846 door_state &= ~DOOR_CLOSE_1;
4847 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4848 door_state &= ~DOOR_OPEN_2;
4849 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4850 door_state &= ~DOOR_CLOSE_2;
4853 if (global.autoplay_leveldir)
4855 door_state |= DOOR_NO_DELAY;
4856 door_state &= ~DOOR_CLOSE_ALL;
4859 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
4860 door_state |= DOOR_NO_DELAY;
4862 if (door_state & DOOR_ACTION)
4864 boolean door_panel_drawn[NUM_DOORS];
4865 boolean panel_has_doors[NUM_DOORS];
4866 boolean door_part_skip[MAX_DOOR_PARTS];
4867 boolean door_part_done[MAX_DOOR_PARTS];
4868 boolean door_part_done_all;
4869 int num_steps[MAX_DOOR_PARTS];
4870 int max_move_delay = 0; // delay for complete animations of all doors
4871 int max_step_delay = 0; // delay (ms) between two animation frames
4872 int num_move_steps = 0; // number of animation steps for all doors
4873 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4874 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4875 int current_move_delay = 0;
4879 for (i = 0; i < NUM_DOORS; i++)
4880 panel_has_doors[i] = FALSE;
4882 for (i = 0; i < MAX_DOOR_PARTS; i++)
4884 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4885 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4886 int door_token = dpc->door_token;
4888 door_part_done[i] = FALSE;
4889 door_part_skip[i] = (!(door_state & door_token) ||
4893 for (i = 0; i < MAX_DOOR_PARTS; i++)
4895 int nr = door_part_order[i].nr;
4896 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4897 struct DoorPartPosInfo *pos = dpc->pos;
4898 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4899 int door_token = dpc->door_token;
4900 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4901 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4902 int step_xoffset = ABS(pos->step_xoffset);
4903 int step_yoffset = ABS(pos->step_yoffset);
4904 int step_delay = pos->step_delay;
4905 int current_door_state = door_state & door_token;
4906 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4907 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4908 boolean part_opening = (is_panel ? door_closing : door_opening);
4909 int start_step = (part_opening ? pos->start_step_opening :
4910 pos->start_step_closing);
4911 float move_xsize = (step_xoffset ? g->width : 0);
4912 float move_ysize = (step_yoffset ? g->height : 0);
4913 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4914 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4915 int move_steps = (move_xsteps && move_ysteps ?
4916 MIN(move_xsteps, move_ysteps) :
4917 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4918 int move_delay = move_steps * step_delay;
4920 if (door_part_skip[nr])
4923 max_move_delay = MAX(max_move_delay, move_delay);
4924 max_step_delay = (max_step_delay == 0 ? step_delay :
4925 euclid(max_step_delay, step_delay));
4926 num_steps[nr] = move_steps;
4930 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4932 panel_has_doors[door_index] = TRUE;
4936 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4938 num_move_steps = max_move_delay / max_step_delay;
4939 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4941 door_delay_value = max_step_delay;
4943 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4945 start = num_move_steps - 1;
4949 /* opening door sound has priority over simultaneously closing door */
4950 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4952 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4954 if (door_state & DOOR_OPEN_1)
4955 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
4956 if (door_state & DOOR_OPEN_2)
4957 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
4959 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4961 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4963 if (door_state & DOOR_CLOSE_1)
4964 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
4965 if (door_state & DOOR_CLOSE_2)
4966 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
4970 for (k = start; k < num_move_steps; k++)
4972 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4974 door_part_done_all = TRUE;
4976 for (i = 0; i < NUM_DOORS; i++)
4977 door_panel_drawn[i] = FALSE;
4979 for (i = 0; i < MAX_DOOR_PARTS; i++)
4981 int nr = door_part_order[i].nr;
4982 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4983 struct DoorPartPosInfo *pos = dpc->pos;
4984 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4985 int door_token = dpc->door_token;
4986 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4987 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4988 boolean is_panel_and_door_has_closed = FALSE;
4989 struct Rect *door_rect = &door_rect_list[door_index];
4990 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4992 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4993 int current_door_state = door_state & door_token;
4994 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4995 boolean door_closing = !door_opening;
4996 boolean part_opening = (is_panel ? door_closing : door_opening);
4997 boolean part_closing = !part_opening;
4998 int start_step = (part_opening ? pos->start_step_opening :
4999 pos->start_step_closing);
5000 int step_delay = pos->step_delay;
5001 int step_factor = step_delay / max_step_delay;
5002 int k1 = (step_factor ? k / step_factor + 1 : k);
5003 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5004 int kk = MAX(0, k2);
5007 int src_x, src_y, src_xx, src_yy;
5008 int dst_x, dst_y, dst_xx, dst_yy;
5011 if (door_part_skip[nr])
5014 if (!(door_state & door_token))
5022 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5023 int kk_door = MAX(0, k2_door);
5024 int sync_frame = kk_door * door_delay_value;
5025 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5027 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5028 &g_src_x, &g_src_y);
5033 if (!door_panel_drawn[door_index])
5035 ClearRectangle(drawto, door_rect->x, door_rect->y,
5036 door_rect->width, door_rect->height);
5038 door_panel_drawn[door_index] = TRUE;
5041 // draw opening or closing door parts
5043 if (pos->step_xoffset < 0) // door part on right side
5046 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5049 if (dst_xx + width > door_rect->width)
5050 width = door_rect->width - dst_xx;
5052 else // door part on left side
5055 dst_xx = pos->x - kk * pos->step_xoffset;
5059 src_xx = ABS(dst_xx);
5063 width = g->width - src_xx;
5065 if (width > door_rect->width)
5066 width = door_rect->width;
5068 // printf("::: k == %d [%d] \n", k, start_step);
5071 if (pos->step_yoffset < 0) // door part on bottom side
5074 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5077 if (dst_yy + height > door_rect->height)
5078 height = door_rect->height - dst_yy;
5080 else // door part on top side
5083 dst_yy = pos->y - kk * pos->step_yoffset;
5087 src_yy = ABS(dst_yy);
5091 height = g->height - src_yy;
5094 src_x = g_src_x + src_xx;
5095 src_y = g_src_y + src_yy;
5097 dst_x = door_rect->x + dst_xx;
5098 dst_y = door_rect->y + dst_yy;
5100 is_panel_and_door_has_closed =
5103 panel_has_doors[door_index] &&
5104 k >= num_move_steps_doors_only - 1);
5106 if (width >= 0 && width <= g->width &&
5107 height >= 0 && height <= g->height &&
5108 !is_panel_and_door_has_closed)
5110 if (is_panel || !pos->draw_masked)
5111 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5114 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5118 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5120 if ((part_opening && (width < 0 || height < 0)) ||
5121 (part_closing && (width >= g->width && height >= g->height)))
5122 door_part_done[nr] = TRUE;
5124 // continue door part animations, but not panel after door has closed
5125 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5126 door_part_done_all = FALSE;
5129 if (!(door_state & DOOR_NO_DELAY))
5133 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5135 current_move_delay += max_step_delay;
5137 /* prevent OS (Windows) from complaining about program not responding */
5141 if (door_part_done_all)
5145 if (!(door_state & DOOR_NO_DELAY))
5147 /* wait for specified door action post delay */
5148 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5149 Delay(MAX(door_1.post_delay, door_2.post_delay));
5150 else if (door_state & DOOR_ACTION_1)
5151 Delay(door_1.post_delay);
5152 else if (door_state & DOOR_ACTION_2)
5153 Delay(door_2.post_delay);
5157 if (door_state & DOOR_ACTION_1)
5158 door1 = door_state & DOOR_ACTION_1;
5159 if (door_state & DOOR_ACTION_2)
5160 door2 = door_state & DOOR_ACTION_2;
5162 // draw masked border over door area
5163 DrawMaskedBorder(REDRAW_DOOR_1);
5164 DrawMaskedBorder(REDRAW_DOOR_2);
5166 return (door1 | door2);
5169 static boolean useSpecialEditorDoor()
5171 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5172 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5174 // do not draw special editor door if editor border defined or redefined
5175 if (graphic_info[graphic].bitmap != NULL || redefined)
5178 // do not draw special editor door if global border defined to be empty
5179 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5182 // do not draw special editor door if viewport definitions do not match
5186 EY + EYSIZE != VY + VYSIZE)
5192 void DrawSpecialEditorDoor()
5194 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5195 int top_border_width = gfx1->width;
5196 int top_border_height = gfx1->height;
5197 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5198 int ex = EX - outer_border;
5199 int ey = EY - outer_border;
5200 int vy = VY - outer_border;
5201 int exsize = EXSIZE + 2 * outer_border;
5203 if (!useSpecialEditorDoor())
5206 /* draw bigger level editor toolbox window */
5207 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5208 top_border_width, top_border_height, ex, ey - top_border_height);
5209 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5210 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5212 redraw_mask |= REDRAW_ALL;
5215 void UndrawSpecialEditorDoor()
5217 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5218 int top_border_width = gfx1->width;
5219 int top_border_height = gfx1->height;
5220 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5221 int ex = EX - outer_border;
5222 int ey = EY - outer_border;
5223 int ey_top = ey - top_border_height;
5224 int exsize = EXSIZE + 2 * outer_border;
5225 int eysize = EYSIZE + 2 * outer_border;
5227 if (!useSpecialEditorDoor())
5230 /* draw normal tape recorder window */
5231 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5233 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5234 ex, ey_top, top_border_width, top_border_height,
5236 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5237 ex, ey, exsize, eysize, ex, ey);
5241 // if screen background is set to "[NONE]", clear editor toolbox window
5242 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5243 ClearRectangle(drawto, ex, ey, exsize, eysize);
5246 redraw_mask |= REDRAW_ALL;
5250 /* ---------- new tool button stuff ---------------------------------------- */
5255 struct TextPosInfo *pos;
5258 } toolbutton_info[NUM_TOOL_BUTTONS] =
5261 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5262 TOOL_CTRL_ID_YES, "yes"
5265 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5266 TOOL_CTRL_ID_NO, "no"
5269 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5270 TOOL_CTRL_ID_CONFIRM, "confirm"
5273 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5274 TOOL_CTRL_ID_PLAYER_1, "player 1"
5277 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5278 TOOL_CTRL_ID_PLAYER_2, "player 2"
5281 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5282 TOOL_CTRL_ID_PLAYER_3, "player 3"
5285 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5286 TOOL_CTRL_ID_PLAYER_4, "player 4"
5290 void CreateToolButtons()
5294 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5296 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5297 struct TextPosInfo *pos = toolbutton_info[i].pos;
5298 struct GadgetInfo *gi;
5299 Bitmap *deco_bitmap = None;
5300 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5301 unsigned int event_mask = GD_EVENT_RELEASED;
5304 int gd_x = gfx->src_x;
5305 int gd_y = gfx->src_y;
5306 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5307 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5310 if (global.use_envelope_request)
5311 setRequestPosition(&dx, &dy, TRUE);
5313 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5315 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5317 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5318 pos->size, &deco_bitmap, &deco_x, &deco_y);
5319 deco_xpos = (gfx->width - pos->size) / 2;
5320 deco_ypos = (gfx->height - pos->size) / 2;
5323 gi = CreateGadget(GDI_CUSTOM_ID, id,
5324 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5325 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5326 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5327 GDI_WIDTH, gfx->width,
5328 GDI_HEIGHT, gfx->height,
5329 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5330 GDI_STATE, GD_BUTTON_UNPRESSED,
5331 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5332 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5333 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5334 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5335 GDI_DECORATION_SIZE, pos->size, pos->size,
5336 GDI_DECORATION_SHIFTING, 1, 1,
5337 GDI_DIRECT_DRAW, FALSE,
5338 GDI_EVENT_MASK, event_mask,
5339 GDI_CALLBACK_ACTION, HandleToolButtons,
5343 Error(ERR_EXIT, "cannot create gadget");
5345 tool_gadget[id] = gi;
5349 void FreeToolButtons()
5353 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5354 FreeGadget(tool_gadget[i]);
5357 static void UnmapToolButtons()
5361 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5362 UnmapGadget(tool_gadget[i]);
5365 static void HandleToolButtons(struct GadgetInfo *gi)
5367 request_gadget_id = gi->custom_id;
5370 static struct Mapping_EM_to_RND_object
5373 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5374 boolean is_backside; /* backside of moving element */
5380 em_object_mapping_list[] =
5383 Xblank, TRUE, FALSE,
5387 Yacid_splash_eB, FALSE, FALSE,
5388 EL_ACID_SPLASH_RIGHT, -1, -1
5391 Yacid_splash_wB, FALSE, FALSE,
5392 EL_ACID_SPLASH_LEFT, -1, -1
5395 #ifdef EM_ENGINE_BAD_ROLL
5397 Xstone_force_e, FALSE, FALSE,
5398 EL_ROCK, -1, MV_BIT_RIGHT
5401 Xstone_force_w, FALSE, FALSE,
5402 EL_ROCK, -1, MV_BIT_LEFT
5405 Xnut_force_e, FALSE, FALSE,
5406 EL_NUT, -1, MV_BIT_RIGHT
5409 Xnut_force_w, FALSE, FALSE,
5410 EL_NUT, -1, MV_BIT_LEFT
5413 Xspring_force_e, FALSE, FALSE,
5414 EL_SPRING, -1, MV_BIT_RIGHT
5417 Xspring_force_w, FALSE, FALSE,
5418 EL_SPRING, -1, MV_BIT_LEFT
5421 Xemerald_force_e, FALSE, FALSE,
5422 EL_EMERALD, -1, MV_BIT_RIGHT
5425 Xemerald_force_w, FALSE, FALSE,
5426 EL_EMERALD, -1, MV_BIT_LEFT
5429 Xdiamond_force_e, FALSE, FALSE,
5430 EL_DIAMOND, -1, MV_BIT_RIGHT
5433 Xdiamond_force_w, FALSE, FALSE,
5434 EL_DIAMOND, -1, MV_BIT_LEFT
5437 Xbomb_force_e, FALSE, FALSE,
5438 EL_BOMB, -1, MV_BIT_RIGHT
5441 Xbomb_force_w, FALSE, FALSE,
5442 EL_BOMB, -1, MV_BIT_LEFT
5444 #endif /* EM_ENGINE_BAD_ROLL */
5447 Xstone, TRUE, FALSE,
5451 Xstone_pause, FALSE, FALSE,
5455 Xstone_fall, FALSE, FALSE,
5459 Ystone_s, FALSE, FALSE,
5460 EL_ROCK, ACTION_FALLING, -1
5463 Ystone_sB, FALSE, TRUE,
5464 EL_ROCK, ACTION_FALLING, -1
5467 Ystone_e, FALSE, FALSE,
5468 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5471 Ystone_eB, FALSE, TRUE,
5472 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5475 Ystone_w, FALSE, FALSE,
5476 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5479 Ystone_wB, FALSE, TRUE,
5480 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5487 Xnut_pause, FALSE, FALSE,
5491 Xnut_fall, FALSE, FALSE,
5495 Ynut_s, FALSE, FALSE,
5496 EL_NUT, ACTION_FALLING, -1
5499 Ynut_sB, FALSE, TRUE,
5500 EL_NUT, ACTION_FALLING, -1
5503 Ynut_e, FALSE, FALSE,
5504 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5507 Ynut_eB, FALSE, TRUE,
5508 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5511 Ynut_w, FALSE, FALSE,
5512 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5515 Ynut_wB, FALSE, TRUE,
5516 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5519 Xbug_n, TRUE, FALSE,
5523 Xbug_e, TRUE, FALSE,
5524 EL_BUG_RIGHT, -1, -1
5527 Xbug_s, TRUE, FALSE,
5531 Xbug_w, TRUE, FALSE,
5535 Xbug_gon, FALSE, FALSE,
5539 Xbug_goe, FALSE, FALSE,
5540 EL_BUG_RIGHT, -1, -1
5543 Xbug_gos, FALSE, FALSE,
5547 Xbug_gow, FALSE, FALSE,
5551 Ybug_n, FALSE, FALSE,
5552 EL_BUG, ACTION_MOVING, MV_BIT_UP
5555 Ybug_nB, FALSE, TRUE,
5556 EL_BUG, ACTION_MOVING, MV_BIT_UP
5559 Ybug_e, FALSE, FALSE,
5560 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5563 Ybug_eB, FALSE, TRUE,
5564 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5567 Ybug_s, FALSE, FALSE,
5568 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5571 Ybug_sB, FALSE, TRUE,
5572 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5575 Ybug_w, FALSE, FALSE,
5576 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5579 Ybug_wB, FALSE, TRUE,
5580 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5583 Ybug_w_n, FALSE, FALSE,
5584 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5587 Ybug_n_e, FALSE, FALSE,
5588 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5591 Ybug_e_s, FALSE, FALSE,
5592 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5595 Ybug_s_w, FALSE, FALSE,
5596 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5599 Ybug_e_n, FALSE, FALSE,
5600 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5603 Ybug_s_e, FALSE, FALSE,
5604 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5607 Ybug_w_s, FALSE, FALSE,
5608 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5611 Ybug_n_w, FALSE, FALSE,
5612 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5615 Ybug_stone, FALSE, FALSE,
5616 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5619 Ybug_spring, FALSE, FALSE,
5620 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5623 Xtank_n, TRUE, FALSE,
5624 EL_SPACESHIP_UP, -1, -1
5627 Xtank_e, TRUE, FALSE,
5628 EL_SPACESHIP_RIGHT, -1, -1
5631 Xtank_s, TRUE, FALSE,
5632 EL_SPACESHIP_DOWN, -1, -1
5635 Xtank_w, TRUE, FALSE,
5636 EL_SPACESHIP_LEFT, -1, -1
5639 Xtank_gon, FALSE, FALSE,
5640 EL_SPACESHIP_UP, -1, -1
5643 Xtank_goe, FALSE, FALSE,
5644 EL_SPACESHIP_RIGHT, -1, -1
5647 Xtank_gos, FALSE, FALSE,
5648 EL_SPACESHIP_DOWN, -1, -1
5651 Xtank_gow, FALSE, FALSE,
5652 EL_SPACESHIP_LEFT, -1, -1
5655 Ytank_n, FALSE, FALSE,
5656 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5659 Ytank_nB, FALSE, TRUE,
5660 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5663 Ytank_e, FALSE, FALSE,
5664 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5667 Ytank_eB, FALSE, TRUE,
5668 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5671 Ytank_s, FALSE, FALSE,
5672 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5675 Ytank_sB, FALSE, TRUE,
5676 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5679 Ytank_w, FALSE, FALSE,
5680 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5683 Ytank_wB, FALSE, TRUE,
5684 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5687 Ytank_w_n, FALSE, FALSE,
5688 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5691 Ytank_n_e, FALSE, FALSE,
5692 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5695 Ytank_e_s, FALSE, FALSE,
5696 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5699 Ytank_s_w, FALSE, FALSE,
5700 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5703 Ytank_e_n, FALSE, FALSE,
5704 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5707 Ytank_s_e, FALSE, FALSE,
5708 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5711 Ytank_w_s, FALSE, FALSE,
5712 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5715 Ytank_n_w, FALSE, FALSE,
5716 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5719 Ytank_stone, FALSE, FALSE,
5720 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5723 Ytank_spring, FALSE, FALSE,
5724 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5727 Xandroid, TRUE, FALSE,
5728 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5731 Xandroid_1_n, FALSE, FALSE,
5732 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5735 Xandroid_2_n, FALSE, FALSE,
5736 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5739 Xandroid_1_e, FALSE, FALSE,
5740 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5743 Xandroid_2_e, FALSE, FALSE,
5744 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5747 Xandroid_1_w, FALSE, FALSE,
5748 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5751 Xandroid_2_w, FALSE, FALSE,
5752 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5755 Xandroid_1_s, FALSE, FALSE,
5756 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5759 Xandroid_2_s, FALSE, FALSE,
5760 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5763 Yandroid_n, FALSE, FALSE,
5764 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5767 Yandroid_nB, FALSE, TRUE,
5768 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5771 Yandroid_ne, FALSE, FALSE,
5772 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5775 Yandroid_neB, FALSE, TRUE,
5776 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5779 Yandroid_e, FALSE, FALSE,
5780 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5783 Yandroid_eB, FALSE, TRUE,
5784 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5787 Yandroid_se, FALSE, FALSE,
5788 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5791 Yandroid_seB, FALSE, TRUE,
5792 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5795 Yandroid_s, FALSE, FALSE,
5796 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5799 Yandroid_sB, FALSE, TRUE,
5800 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5803 Yandroid_sw, FALSE, FALSE,
5804 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5807 Yandroid_swB, FALSE, TRUE,
5808 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5811 Yandroid_w, FALSE, FALSE,
5812 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5815 Yandroid_wB, FALSE, TRUE,
5816 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5819 Yandroid_nw, FALSE, FALSE,
5820 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5823 Yandroid_nwB, FALSE, TRUE,
5824 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5827 Xspring, TRUE, FALSE,
5831 Xspring_pause, FALSE, FALSE,
5835 Xspring_e, FALSE, FALSE,
5839 Xspring_w, FALSE, FALSE,
5843 Xspring_fall, FALSE, FALSE,
5847 Yspring_s, FALSE, FALSE,
5848 EL_SPRING, ACTION_FALLING, -1
5851 Yspring_sB, FALSE, TRUE,
5852 EL_SPRING, ACTION_FALLING, -1
5855 Yspring_e, FALSE, FALSE,
5856 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5859 Yspring_eB, FALSE, TRUE,
5860 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5863 Yspring_w, FALSE, FALSE,
5864 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5867 Yspring_wB, FALSE, TRUE,
5868 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5871 Yspring_kill_e, FALSE, FALSE,
5872 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5875 Yspring_kill_eB, FALSE, TRUE,
5876 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5879 Yspring_kill_w, FALSE, FALSE,
5880 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5883 Yspring_kill_wB, FALSE, TRUE,
5884 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5887 Xeater_n, TRUE, FALSE,
5888 EL_YAMYAM_UP, -1, -1
5891 Xeater_e, TRUE, FALSE,
5892 EL_YAMYAM_RIGHT, -1, -1
5895 Xeater_w, TRUE, FALSE,
5896 EL_YAMYAM_LEFT, -1, -1
5899 Xeater_s, TRUE, FALSE,
5900 EL_YAMYAM_DOWN, -1, -1
5903 Yeater_n, FALSE, FALSE,
5904 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5907 Yeater_nB, FALSE, TRUE,
5908 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5911 Yeater_e, FALSE, FALSE,
5912 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5915 Yeater_eB, FALSE, TRUE,
5916 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5919 Yeater_s, FALSE, FALSE,
5920 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5923 Yeater_sB, FALSE, TRUE,
5924 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5927 Yeater_w, FALSE, FALSE,
5928 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5931 Yeater_wB, FALSE, TRUE,
5932 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5935 Yeater_stone, FALSE, FALSE,
5936 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5939 Yeater_spring, FALSE, FALSE,
5940 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5943 Xalien, TRUE, FALSE,
5947 Xalien_pause, FALSE, FALSE,
5951 Yalien_n, FALSE, FALSE,
5952 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5955 Yalien_nB, FALSE, TRUE,
5956 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5959 Yalien_e, FALSE, FALSE,
5960 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5963 Yalien_eB, FALSE, TRUE,
5964 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5967 Yalien_s, FALSE, FALSE,
5968 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5971 Yalien_sB, FALSE, TRUE,
5972 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5975 Yalien_w, FALSE, FALSE,
5976 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5979 Yalien_wB, FALSE, TRUE,
5980 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5983 Yalien_stone, FALSE, FALSE,
5984 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5987 Yalien_spring, FALSE, FALSE,
5988 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5991 Xemerald, TRUE, FALSE,
5995 Xemerald_pause, FALSE, FALSE,
5999 Xemerald_fall, FALSE, FALSE,
6003 Xemerald_shine, FALSE, FALSE,
6004 EL_EMERALD, ACTION_TWINKLING, -1
6007 Yemerald_s, FALSE, FALSE,
6008 EL_EMERALD, ACTION_FALLING, -1
6011 Yemerald_sB, FALSE, TRUE,
6012 EL_EMERALD, ACTION_FALLING, -1
6015 Yemerald_e, FALSE, FALSE,
6016 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6019 Yemerald_eB, FALSE, TRUE,
6020 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6023 Yemerald_w, FALSE, FALSE,
6024 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6027 Yemerald_wB, FALSE, TRUE,
6028 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6031 Yemerald_eat, FALSE, FALSE,
6032 EL_EMERALD, ACTION_COLLECTING, -1
6035 Yemerald_stone, FALSE, FALSE,
6036 EL_NUT, ACTION_BREAKING, -1
6039 Xdiamond, TRUE, FALSE,
6043 Xdiamond_pause, FALSE, FALSE,
6047 Xdiamond_fall, FALSE, FALSE,
6051 Xdiamond_shine, FALSE, FALSE,
6052 EL_DIAMOND, ACTION_TWINKLING, -1
6055 Ydiamond_s, FALSE, FALSE,
6056 EL_DIAMOND, ACTION_FALLING, -1
6059 Ydiamond_sB, FALSE, TRUE,
6060 EL_DIAMOND, ACTION_FALLING, -1
6063 Ydiamond_e, FALSE, FALSE,
6064 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6067 Ydiamond_eB, FALSE, TRUE,
6068 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6071 Ydiamond_w, FALSE, FALSE,
6072 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6075 Ydiamond_wB, FALSE, TRUE,
6076 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6079 Ydiamond_eat, FALSE, FALSE,
6080 EL_DIAMOND, ACTION_COLLECTING, -1
6083 Ydiamond_stone, FALSE, FALSE,
6084 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6087 Xdrip_fall, TRUE, FALSE,
6088 EL_AMOEBA_DROP, -1, -1
6091 Xdrip_stretch, FALSE, FALSE,
6092 EL_AMOEBA_DROP, ACTION_FALLING, -1
6095 Xdrip_stretchB, FALSE, TRUE,
6096 EL_AMOEBA_DROP, ACTION_FALLING, -1
6099 Xdrip_eat, FALSE, FALSE,
6100 EL_AMOEBA_DROP, ACTION_GROWING, -1
6103 Ydrip_s1, FALSE, FALSE,
6104 EL_AMOEBA_DROP, ACTION_FALLING, -1
6107 Ydrip_s1B, FALSE, TRUE,
6108 EL_AMOEBA_DROP, ACTION_FALLING, -1
6111 Ydrip_s2, FALSE, FALSE,
6112 EL_AMOEBA_DROP, ACTION_FALLING, -1
6115 Ydrip_s2B, FALSE, TRUE,
6116 EL_AMOEBA_DROP, ACTION_FALLING, -1
6123 Xbomb_pause, FALSE, FALSE,
6127 Xbomb_fall, FALSE, FALSE,
6131 Ybomb_s, FALSE, FALSE,
6132 EL_BOMB, ACTION_FALLING, -1
6135 Ybomb_sB, FALSE, TRUE,
6136 EL_BOMB, ACTION_FALLING, -1
6139 Ybomb_e, FALSE, FALSE,
6140 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6143 Ybomb_eB, FALSE, TRUE,
6144 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6147 Ybomb_w, FALSE, FALSE,
6148 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6151 Ybomb_wB, FALSE, TRUE,
6152 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6155 Ybomb_eat, FALSE, FALSE,
6156 EL_BOMB, ACTION_ACTIVATING, -1
6159 Xballoon, TRUE, FALSE,
6163 Yballoon_n, FALSE, FALSE,
6164 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6167 Yballoon_nB, FALSE, TRUE,
6168 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6171 Yballoon_e, FALSE, FALSE,
6172 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6175 Yballoon_eB, FALSE, TRUE,
6176 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6179 Yballoon_s, FALSE, FALSE,
6180 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6183 Yballoon_sB, FALSE, TRUE,
6184 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6187 Yballoon_w, FALSE, FALSE,
6188 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6191 Yballoon_wB, FALSE, TRUE,
6192 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6195 Xgrass, TRUE, FALSE,
6196 EL_EMC_GRASS, -1, -1
6199 Ygrass_nB, FALSE, FALSE,
6200 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6203 Ygrass_eB, FALSE, FALSE,
6204 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6207 Ygrass_sB, FALSE, FALSE,
6208 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6211 Ygrass_wB, FALSE, FALSE,
6212 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6219 Ydirt_nB, FALSE, FALSE,
6220 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6223 Ydirt_eB, FALSE, FALSE,
6224 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6227 Ydirt_sB, FALSE, FALSE,
6228 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6231 Ydirt_wB, FALSE, FALSE,
6232 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6235 Xacid_ne, TRUE, FALSE,
6236 EL_ACID_POOL_TOPRIGHT, -1, -1
6239 Xacid_se, TRUE, FALSE,
6240 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6243 Xacid_s, TRUE, FALSE,
6244 EL_ACID_POOL_BOTTOM, -1, -1
6247 Xacid_sw, TRUE, FALSE,
6248 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6251 Xacid_nw, TRUE, FALSE,
6252 EL_ACID_POOL_TOPLEFT, -1, -1
6255 Xacid_1, TRUE, FALSE,
6259 Xacid_2, FALSE, FALSE,
6263 Xacid_3, FALSE, FALSE,
6267 Xacid_4, FALSE, FALSE,
6271 Xacid_5, FALSE, FALSE,
6275 Xacid_6, FALSE, FALSE,
6279 Xacid_7, FALSE, FALSE,
6283 Xacid_8, FALSE, FALSE,
6287 Xball_1, TRUE, FALSE,
6288 EL_EMC_MAGIC_BALL, -1, -1
6291 Xball_1B, FALSE, FALSE,
6292 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6295 Xball_2, FALSE, FALSE,
6296 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6299 Xball_2B, FALSE, FALSE,
6300 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6303 Yball_eat, FALSE, FALSE,
6304 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6307 Ykey_1_eat, FALSE, FALSE,
6308 EL_EM_KEY_1, ACTION_COLLECTING, -1
6311 Ykey_2_eat, FALSE, FALSE,
6312 EL_EM_KEY_2, ACTION_COLLECTING, -1
6315 Ykey_3_eat, FALSE, FALSE,
6316 EL_EM_KEY_3, ACTION_COLLECTING, -1
6319 Ykey_4_eat, FALSE, FALSE,
6320 EL_EM_KEY_4, ACTION_COLLECTING, -1
6323 Ykey_5_eat, FALSE, FALSE,
6324 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6327 Ykey_6_eat, FALSE, FALSE,
6328 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6331 Ykey_7_eat, FALSE, FALSE,
6332 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6335 Ykey_8_eat, FALSE, FALSE,
6336 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6339 Ylenses_eat, FALSE, FALSE,
6340 EL_EMC_LENSES, ACTION_COLLECTING, -1
6343 Ymagnify_eat, FALSE, FALSE,
6344 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6347 Ygrass_eat, FALSE, FALSE,
6348 EL_EMC_GRASS, ACTION_SNAPPING, -1
6351 Ydirt_eat, FALSE, FALSE,
6352 EL_SAND, ACTION_SNAPPING, -1
6355 Xgrow_ns, TRUE, FALSE,
6356 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6359 Ygrow_ns_eat, FALSE, FALSE,
6360 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6363 Xgrow_ew, TRUE, FALSE,
6364 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6367 Ygrow_ew_eat, FALSE, FALSE,
6368 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6371 Xwonderwall, TRUE, FALSE,
6372 EL_MAGIC_WALL, -1, -1
6375 XwonderwallB, FALSE, FALSE,
6376 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6379 Xamoeba_1, TRUE, FALSE,
6380 EL_AMOEBA_DRY, ACTION_OTHER, -1
6383 Xamoeba_2, FALSE, FALSE,
6384 EL_AMOEBA_DRY, ACTION_OTHER, -1
6387 Xamoeba_3, FALSE, FALSE,
6388 EL_AMOEBA_DRY, ACTION_OTHER, -1
6391 Xamoeba_4, FALSE, FALSE,
6392 EL_AMOEBA_DRY, ACTION_OTHER, -1
6395 Xamoeba_5, TRUE, FALSE,
6396 EL_AMOEBA_WET, ACTION_OTHER, -1
6399 Xamoeba_6, FALSE, FALSE,
6400 EL_AMOEBA_WET, ACTION_OTHER, -1
6403 Xamoeba_7, FALSE, FALSE,
6404 EL_AMOEBA_WET, ACTION_OTHER, -1
6407 Xamoeba_8, FALSE, FALSE,
6408 EL_AMOEBA_WET, ACTION_OTHER, -1
6411 Xdoor_1, TRUE, FALSE,
6412 EL_EM_GATE_1, -1, -1
6415 Xdoor_2, TRUE, FALSE,
6416 EL_EM_GATE_2, -1, -1
6419 Xdoor_3, TRUE, FALSE,
6420 EL_EM_GATE_3, -1, -1
6423 Xdoor_4, TRUE, FALSE,
6424 EL_EM_GATE_4, -1, -1
6427 Xdoor_5, TRUE, FALSE,
6428 EL_EMC_GATE_5, -1, -1
6431 Xdoor_6, TRUE, FALSE,
6432 EL_EMC_GATE_6, -1, -1
6435 Xdoor_7, TRUE, FALSE,
6436 EL_EMC_GATE_7, -1, -1
6439 Xdoor_8, TRUE, FALSE,
6440 EL_EMC_GATE_8, -1, -1
6443 Xkey_1, TRUE, FALSE,
6447 Xkey_2, TRUE, FALSE,
6451 Xkey_3, TRUE, FALSE,
6455 Xkey_4, TRUE, FALSE,
6459 Xkey_5, TRUE, FALSE,
6460 EL_EMC_KEY_5, -1, -1
6463 Xkey_6, TRUE, FALSE,
6464 EL_EMC_KEY_6, -1, -1
6467 Xkey_7, TRUE, FALSE,
6468 EL_EMC_KEY_7, -1, -1
6471 Xkey_8, TRUE, FALSE,
6472 EL_EMC_KEY_8, -1, -1
6475 Xwind_n, TRUE, FALSE,
6476 EL_BALLOON_SWITCH_UP, -1, -1
6479 Xwind_e, TRUE, FALSE,
6480 EL_BALLOON_SWITCH_RIGHT, -1, -1
6483 Xwind_s, TRUE, FALSE,
6484 EL_BALLOON_SWITCH_DOWN, -1, -1
6487 Xwind_w, TRUE, FALSE,
6488 EL_BALLOON_SWITCH_LEFT, -1, -1
6491 Xwind_nesw, TRUE, FALSE,
6492 EL_BALLOON_SWITCH_ANY, -1, -1
6495 Xwind_stop, TRUE, FALSE,
6496 EL_BALLOON_SWITCH_NONE, -1, -1
6500 EL_EM_EXIT_CLOSED, -1, -1
6503 Xexit_1, TRUE, FALSE,
6504 EL_EM_EXIT_OPEN, -1, -1
6507 Xexit_2, FALSE, FALSE,
6508 EL_EM_EXIT_OPEN, -1, -1
6511 Xexit_3, FALSE, FALSE,
6512 EL_EM_EXIT_OPEN, -1, -1
6515 Xdynamite, TRUE, FALSE,
6516 EL_EM_DYNAMITE, -1, -1
6519 Ydynamite_eat, FALSE, FALSE,
6520 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6523 Xdynamite_1, TRUE, FALSE,
6524 EL_EM_DYNAMITE_ACTIVE, -1, -1
6527 Xdynamite_2, FALSE, FALSE,
6528 EL_EM_DYNAMITE_ACTIVE, -1, -1
6531 Xdynamite_3, FALSE, FALSE,
6532 EL_EM_DYNAMITE_ACTIVE, -1, -1
6535 Xdynamite_4, FALSE, FALSE,
6536 EL_EM_DYNAMITE_ACTIVE, -1, -1
6539 Xbumper, TRUE, FALSE,
6540 EL_EMC_SPRING_BUMPER, -1, -1
6543 XbumperB, FALSE, FALSE,
6544 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6547 Xwheel, TRUE, FALSE,
6548 EL_ROBOT_WHEEL, -1, -1
6551 XwheelB, FALSE, FALSE,
6552 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6555 Xswitch, TRUE, FALSE,
6556 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6559 XswitchB, FALSE, FALSE,
6560 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6564 EL_QUICKSAND_EMPTY, -1, -1
6567 Xsand_stone, TRUE, FALSE,
6568 EL_QUICKSAND_FULL, -1, -1
6571 Xsand_stonein_1, FALSE, TRUE,
6572 EL_ROCK, ACTION_FILLING, -1
6575 Xsand_stonein_2, FALSE, TRUE,
6576 EL_ROCK, ACTION_FILLING, -1
6579 Xsand_stonein_3, FALSE, TRUE,
6580 EL_ROCK, ACTION_FILLING, -1
6583 Xsand_stonein_4, FALSE, TRUE,
6584 EL_ROCK, ACTION_FILLING, -1
6587 Xsand_stonesand_1, FALSE, FALSE,
6588 EL_QUICKSAND_EMPTYING, -1, -1
6591 Xsand_stonesand_2, FALSE, FALSE,
6592 EL_QUICKSAND_EMPTYING, -1, -1
6595 Xsand_stonesand_3, FALSE, FALSE,
6596 EL_QUICKSAND_EMPTYING, -1, -1
6599 Xsand_stonesand_4, FALSE, FALSE,
6600 EL_QUICKSAND_EMPTYING, -1, -1
6603 Xsand_stonesand_quickout_1, FALSE, FALSE,
6604 EL_QUICKSAND_EMPTYING, -1, -1
6607 Xsand_stonesand_quickout_2, FALSE, FALSE,
6608 EL_QUICKSAND_EMPTYING, -1, -1
6611 Xsand_stoneout_1, FALSE, FALSE,
6612 EL_ROCK, ACTION_EMPTYING, -1
6615 Xsand_stoneout_2, FALSE, FALSE,
6616 EL_ROCK, ACTION_EMPTYING, -1
6619 Xsand_sandstone_1, FALSE, FALSE,
6620 EL_QUICKSAND_FILLING, -1, -1
6623 Xsand_sandstone_2, FALSE, FALSE,
6624 EL_QUICKSAND_FILLING, -1, -1
6627 Xsand_sandstone_3, FALSE, FALSE,
6628 EL_QUICKSAND_FILLING, -1, -1
6631 Xsand_sandstone_4, FALSE, FALSE,
6632 EL_QUICKSAND_FILLING, -1, -1
6635 Xplant, TRUE, FALSE,
6636 EL_EMC_PLANT, -1, -1
6639 Yplant, FALSE, FALSE,
6640 EL_EMC_PLANT, -1, -1
6643 Xlenses, TRUE, FALSE,
6644 EL_EMC_LENSES, -1, -1
6647 Xmagnify, TRUE, FALSE,
6648 EL_EMC_MAGNIFIER, -1, -1
6651 Xdripper, TRUE, FALSE,
6652 EL_EMC_DRIPPER, -1, -1
6655 XdripperB, FALSE, FALSE,
6656 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6659 Xfake_blank, TRUE, FALSE,
6660 EL_INVISIBLE_WALL, -1, -1
6663 Xfake_blankB, FALSE, FALSE,
6664 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6667 Xfake_grass, TRUE, FALSE,
6668 EL_EMC_FAKE_GRASS, -1, -1
6671 Xfake_grassB, FALSE, FALSE,
6672 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6675 Xfake_door_1, TRUE, FALSE,
6676 EL_EM_GATE_1_GRAY, -1, -1
6679 Xfake_door_2, TRUE, FALSE,
6680 EL_EM_GATE_2_GRAY, -1, -1
6683 Xfake_door_3, TRUE, FALSE,
6684 EL_EM_GATE_3_GRAY, -1, -1
6687 Xfake_door_4, TRUE, FALSE,
6688 EL_EM_GATE_4_GRAY, -1, -1
6691 Xfake_door_5, TRUE, FALSE,
6692 EL_EMC_GATE_5_GRAY, -1, -1
6695 Xfake_door_6, TRUE, FALSE,
6696 EL_EMC_GATE_6_GRAY, -1, -1
6699 Xfake_door_7, TRUE, FALSE,
6700 EL_EMC_GATE_7_GRAY, -1, -1
6703 Xfake_door_8, TRUE, FALSE,
6704 EL_EMC_GATE_8_GRAY, -1, -1
6707 Xfake_acid_1, TRUE, FALSE,
6708 EL_EMC_FAKE_ACID, -1, -1
6711 Xfake_acid_2, FALSE, FALSE,
6712 EL_EMC_FAKE_ACID, -1, -1
6715 Xfake_acid_3, FALSE, FALSE,
6716 EL_EMC_FAKE_ACID, -1, -1
6719 Xfake_acid_4, FALSE, FALSE,
6720 EL_EMC_FAKE_ACID, -1, -1
6723 Xfake_acid_5, FALSE, FALSE,
6724 EL_EMC_FAKE_ACID, -1, -1
6727 Xfake_acid_6, FALSE, FALSE,
6728 EL_EMC_FAKE_ACID, -1, -1
6731 Xfake_acid_7, FALSE, FALSE,
6732 EL_EMC_FAKE_ACID, -1, -1
6735 Xfake_acid_8, FALSE, FALSE,
6736 EL_EMC_FAKE_ACID, -1, -1
6739 Xsteel_1, TRUE, FALSE,
6740 EL_STEELWALL, -1, -1
6743 Xsteel_2, TRUE, FALSE,
6744 EL_EMC_STEELWALL_2, -1, -1
6747 Xsteel_3, TRUE, FALSE,
6748 EL_EMC_STEELWALL_3, -1, -1
6751 Xsteel_4, TRUE, FALSE,
6752 EL_EMC_STEELWALL_4, -1, -1
6755 Xwall_1, TRUE, FALSE,
6759 Xwall_2, TRUE, FALSE,
6760 EL_EMC_WALL_14, -1, -1
6763 Xwall_3, TRUE, FALSE,
6764 EL_EMC_WALL_15, -1, -1
6767 Xwall_4, TRUE, FALSE,
6768 EL_EMC_WALL_16, -1, -1
6771 Xround_wall_1, TRUE, FALSE,
6772 EL_WALL_SLIPPERY, -1, -1
6775 Xround_wall_2, TRUE, FALSE,
6776 EL_EMC_WALL_SLIPPERY_2, -1, -1
6779 Xround_wall_3, TRUE, FALSE,
6780 EL_EMC_WALL_SLIPPERY_3, -1, -1
6783 Xround_wall_4, TRUE, FALSE,
6784 EL_EMC_WALL_SLIPPERY_4, -1, -1
6787 Xdecor_1, TRUE, FALSE,
6788 EL_EMC_WALL_8, -1, -1
6791 Xdecor_2, TRUE, FALSE,
6792 EL_EMC_WALL_6, -1, -1
6795 Xdecor_3, TRUE, FALSE,
6796 EL_EMC_WALL_4, -1, -1
6799 Xdecor_4, TRUE, FALSE,
6800 EL_EMC_WALL_7, -1, -1
6803 Xdecor_5, TRUE, FALSE,
6804 EL_EMC_WALL_5, -1, -1
6807 Xdecor_6, TRUE, FALSE,
6808 EL_EMC_WALL_9, -1, -1
6811 Xdecor_7, TRUE, FALSE,
6812 EL_EMC_WALL_10, -1, -1
6815 Xdecor_8, TRUE, FALSE,
6816 EL_EMC_WALL_1, -1, -1
6819 Xdecor_9, TRUE, FALSE,
6820 EL_EMC_WALL_2, -1, -1
6823 Xdecor_10, TRUE, FALSE,
6824 EL_EMC_WALL_3, -1, -1
6827 Xdecor_11, TRUE, FALSE,
6828 EL_EMC_WALL_11, -1, -1
6831 Xdecor_12, TRUE, FALSE,
6832 EL_EMC_WALL_12, -1, -1
6835 Xalpha_0, TRUE, FALSE,
6836 EL_CHAR('0'), -1, -1
6839 Xalpha_1, TRUE, FALSE,
6840 EL_CHAR('1'), -1, -1
6843 Xalpha_2, TRUE, FALSE,
6844 EL_CHAR('2'), -1, -1
6847 Xalpha_3, TRUE, FALSE,
6848 EL_CHAR('3'), -1, -1
6851 Xalpha_4, TRUE, FALSE,
6852 EL_CHAR('4'), -1, -1
6855 Xalpha_5, TRUE, FALSE,
6856 EL_CHAR('5'), -1, -1
6859 Xalpha_6, TRUE, FALSE,
6860 EL_CHAR('6'), -1, -1
6863 Xalpha_7, TRUE, FALSE,
6864 EL_CHAR('7'), -1, -1
6867 Xalpha_8, TRUE, FALSE,
6868 EL_CHAR('8'), -1, -1
6871 Xalpha_9, TRUE, FALSE,
6872 EL_CHAR('9'), -1, -1
6875 Xalpha_excla, TRUE, FALSE,
6876 EL_CHAR('!'), -1, -1
6879 Xalpha_quote, TRUE, FALSE,
6880 EL_CHAR('"'), -1, -1
6883 Xalpha_comma, TRUE, FALSE,
6884 EL_CHAR(','), -1, -1
6887 Xalpha_minus, TRUE, FALSE,
6888 EL_CHAR('-'), -1, -1
6891 Xalpha_perio, TRUE, FALSE,
6892 EL_CHAR('.'), -1, -1
6895 Xalpha_colon, TRUE, FALSE,
6896 EL_CHAR(':'), -1, -1
6899 Xalpha_quest, TRUE, FALSE,
6900 EL_CHAR('?'), -1, -1
6903 Xalpha_a, TRUE, FALSE,
6904 EL_CHAR('A'), -1, -1
6907 Xalpha_b, TRUE, FALSE,
6908 EL_CHAR('B'), -1, -1
6911 Xalpha_c, TRUE, FALSE,
6912 EL_CHAR('C'), -1, -1
6915 Xalpha_d, TRUE, FALSE,
6916 EL_CHAR('D'), -1, -1
6919 Xalpha_e, TRUE, FALSE,
6920 EL_CHAR('E'), -1, -1
6923 Xalpha_f, TRUE, FALSE,
6924 EL_CHAR('F'), -1, -1
6927 Xalpha_g, TRUE, FALSE,
6928 EL_CHAR('G'), -1, -1
6931 Xalpha_h, TRUE, FALSE,
6932 EL_CHAR('H'), -1, -1
6935 Xalpha_i, TRUE, FALSE,
6936 EL_CHAR('I'), -1, -1
6939 Xalpha_j, TRUE, FALSE,
6940 EL_CHAR('J'), -1, -1
6943 Xalpha_k, TRUE, FALSE,
6944 EL_CHAR('K'), -1, -1
6947 Xalpha_l, TRUE, FALSE,
6948 EL_CHAR('L'), -1, -1
6951 Xalpha_m, TRUE, FALSE,
6952 EL_CHAR('M'), -1, -1
6955 Xalpha_n, TRUE, FALSE,
6956 EL_CHAR('N'), -1, -1
6959 Xalpha_o, TRUE, FALSE,
6960 EL_CHAR('O'), -1, -1
6963 Xalpha_p, TRUE, FALSE,
6964 EL_CHAR('P'), -1, -1
6967 Xalpha_q, TRUE, FALSE,
6968 EL_CHAR('Q'), -1, -1
6971 Xalpha_r, TRUE, FALSE,
6972 EL_CHAR('R'), -1, -1
6975 Xalpha_s, TRUE, FALSE,
6976 EL_CHAR('S'), -1, -1
6979 Xalpha_t, TRUE, FALSE,
6980 EL_CHAR('T'), -1, -1
6983 Xalpha_u, TRUE, FALSE,
6984 EL_CHAR('U'), -1, -1
6987 Xalpha_v, TRUE, FALSE,
6988 EL_CHAR('V'), -1, -1
6991 Xalpha_w, TRUE, FALSE,
6992 EL_CHAR('W'), -1, -1
6995 Xalpha_x, TRUE, FALSE,
6996 EL_CHAR('X'), -1, -1
6999 Xalpha_y, TRUE, FALSE,
7000 EL_CHAR('Y'), -1, -1
7003 Xalpha_z, TRUE, FALSE,
7004 EL_CHAR('Z'), -1, -1
7007 Xalpha_arrow_e, TRUE, FALSE,
7008 EL_CHAR('>'), -1, -1
7011 Xalpha_arrow_w, TRUE, FALSE,
7012 EL_CHAR('<'), -1, -1
7015 Xalpha_copyr, TRUE, FALSE,
7016 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7020 Xboom_bug, FALSE, FALSE,
7021 EL_BUG, ACTION_EXPLODING, -1
7024 Xboom_bomb, FALSE, FALSE,
7025 EL_BOMB, ACTION_EXPLODING, -1
7028 Xboom_android, FALSE, FALSE,
7029 EL_EMC_ANDROID, ACTION_OTHER, -1
7032 Xboom_1, FALSE, FALSE,
7033 EL_DEFAULT, ACTION_EXPLODING, -1
7036 Xboom_2, FALSE, FALSE,
7037 EL_DEFAULT, ACTION_EXPLODING, -1
7040 Znormal, FALSE, FALSE,
7044 Zdynamite, FALSE, FALSE,
7048 Zplayer, FALSE, FALSE,
7052 ZBORDER, FALSE, FALSE,
7062 static struct Mapping_EM_to_RND_player
7071 em_player_mapping_list[] =
7075 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7079 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7083 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7087 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7091 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7095 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7099 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7103 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7107 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7111 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7115 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7119 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7123 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7127 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7131 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7135 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7139 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7143 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7147 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7151 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7155 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7159 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7163 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7167 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7171 EL_PLAYER_1, ACTION_DEFAULT, -1,
7175 EL_PLAYER_2, ACTION_DEFAULT, -1,
7179 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7183 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7187 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7191 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7195 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7199 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7203 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7207 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7211 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7215 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7219 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7223 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7227 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7231 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7235 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7239 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7243 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7247 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7251 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7255 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7259 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7263 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7267 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7271 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7275 EL_PLAYER_3, ACTION_DEFAULT, -1,
7279 EL_PLAYER_4, ACTION_DEFAULT, -1,
7288 int map_element_RND_to_EM(int element_rnd)
7290 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7291 static boolean mapping_initialized = FALSE;
7293 if (!mapping_initialized)
7297 /* return "Xalpha_quest" for all undefined elements in mapping array */
7298 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7299 mapping_RND_to_EM[i] = Xalpha_quest;
7301 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7302 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7303 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7304 em_object_mapping_list[i].element_em;
7306 mapping_initialized = TRUE;
7309 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7310 return mapping_RND_to_EM[element_rnd];
7312 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7317 int map_element_EM_to_RND(int element_em)
7319 static unsigned short mapping_EM_to_RND[TILE_MAX];
7320 static boolean mapping_initialized = FALSE;
7322 if (!mapping_initialized)
7326 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7327 for (i = 0; i < TILE_MAX; i++)
7328 mapping_EM_to_RND[i] = EL_UNKNOWN;
7330 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7331 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7332 em_object_mapping_list[i].element_rnd;
7334 mapping_initialized = TRUE;
7337 if (element_em >= 0 && element_em < TILE_MAX)
7338 return mapping_EM_to_RND[element_em];
7340 Error(ERR_WARN, "invalid EM level element %d", element_em);
7345 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7347 struct LevelInfo_EM *level_em = level->native_em_level;
7348 struct LEVEL *lev = level_em->lev;
7351 for (i = 0; i < TILE_MAX; i++)
7352 lev->android_array[i] = Xblank;
7354 for (i = 0; i < level->num_android_clone_elements; i++)
7356 int element_rnd = level->android_clone_element[i];
7357 int element_em = map_element_RND_to_EM(element_rnd);
7359 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7360 if (em_object_mapping_list[j].element_rnd == element_rnd)
7361 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7365 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7367 struct LevelInfo_EM *level_em = level->native_em_level;
7368 struct LEVEL *lev = level_em->lev;
7371 level->num_android_clone_elements = 0;
7373 for (i = 0; i < TILE_MAX; i++)
7375 int element_em = lev->android_array[i];
7377 boolean element_found = FALSE;
7379 if (element_em == Xblank)
7382 element_rnd = map_element_EM_to_RND(element_em);
7384 for (j = 0; j < level->num_android_clone_elements; j++)
7385 if (level->android_clone_element[j] == element_rnd)
7386 element_found = TRUE;
7390 level->android_clone_element[level->num_android_clone_elements++] =
7393 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7398 if (level->num_android_clone_elements == 0)
7400 level->num_android_clone_elements = 1;
7401 level->android_clone_element[0] = EL_EMPTY;
7405 int map_direction_RND_to_EM(int direction)
7407 return (direction == MV_UP ? 0 :
7408 direction == MV_RIGHT ? 1 :
7409 direction == MV_DOWN ? 2 :
7410 direction == MV_LEFT ? 3 :
7414 int map_direction_EM_to_RND(int direction)
7416 return (direction == 0 ? MV_UP :
7417 direction == 1 ? MV_RIGHT :
7418 direction == 2 ? MV_DOWN :
7419 direction == 3 ? MV_LEFT :
7423 int map_element_RND_to_SP(int element_rnd)
7425 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7427 if (element_rnd >= EL_SP_START &&
7428 element_rnd <= EL_SP_END)
7429 element_sp = element_rnd - EL_SP_START;
7430 else if (element_rnd == EL_EMPTY_SPACE)
7432 else if (element_rnd == EL_INVISIBLE_WALL)
7438 int map_element_SP_to_RND(int element_sp)
7440 int element_rnd = EL_UNKNOWN;
7442 if (element_sp >= 0x00 &&
7444 element_rnd = EL_SP_START + element_sp;
7445 else if (element_sp == 0x28)
7446 element_rnd = EL_INVISIBLE_WALL;
7451 int map_action_SP_to_RND(int action_sp)
7455 case actActive: return ACTION_ACTIVE;
7456 case actImpact: return ACTION_IMPACT;
7457 case actExploding: return ACTION_EXPLODING;
7458 case actDigging: return ACTION_DIGGING;
7459 case actSnapping: return ACTION_SNAPPING;
7460 case actCollecting: return ACTION_COLLECTING;
7461 case actPassing: return ACTION_PASSING;
7462 case actPushing: return ACTION_PUSHING;
7463 case actDropping: return ACTION_DROPPING;
7465 default: return ACTION_DEFAULT;
7469 int map_element_RND_to_MM(int element_rnd)
7471 return (element_rnd >= EL_MM_START_1 &&
7472 element_rnd <= EL_MM_END_1 ?
7473 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7475 element_rnd >= EL_MM_START_2 &&
7476 element_rnd <= EL_MM_END_2 ?
7477 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7479 element_rnd >= EL_CHAR_START &&
7480 element_rnd <= EL_CHAR_END ?
7481 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7483 element_rnd >= EL_MM_RUNTIME_START &&
7484 element_rnd <= EL_MM_RUNTIME_END ?
7485 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7487 element_rnd >= EL_MM_DUMMY_START &&
7488 element_rnd <= EL_MM_DUMMY_END ?
7489 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7491 EL_MM_EMPTY_NATIVE);
7494 int map_element_MM_to_RND(int element_mm)
7496 return (element_mm == EL_MM_EMPTY_NATIVE ||
7497 element_mm == EL_DF_EMPTY_NATIVE ?
7500 element_mm >= EL_MM_START_1_NATIVE &&
7501 element_mm <= EL_MM_END_1_NATIVE ?
7502 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7504 element_mm >= EL_MM_START_2_NATIVE &&
7505 element_mm <= EL_MM_END_2_NATIVE ?
7506 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7508 element_mm >= EL_MM_CHAR_START_NATIVE &&
7509 element_mm <= EL_MM_CHAR_END_NATIVE ?
7510 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7512 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7513 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7514 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7516 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7517 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7518 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7523 int map_action_MM_to_RND(int action_mm)
7525 /* all MM actions are defined to exactly match their RND counterparts */
7529 int map_sound_MM_to_RND(int sound_mm)
7533 case SND_MM_GAME_LEVELTIME_CHARGING:
7534 return SND_GAME_LEVELTIME_CHARGING;
7536 case SND_MM_GAME_HEALTH_CHARGING:
7537 return SND_GAME_HEALTH_CHARGING;
7540 return SND_UNDEFINED;
7544 int map_mm_wall_element(int element)
7546 return (element >= EL_MM_STEEL_WALL_START &&
7547 element <= EL_MM_STEEL_WALL_END ?
7550 element >= EL_MM_WOODEN_WALL_START &&
7551 element <= EL_MM_WOODEN_WALL_END ?
7554 element >= EL_MM_ICE_WALL_START &&
7555 element <= EL_MM_ICE_WALL_END ?
7558 element >= EL_MM_AMOEBA_WALL_START &&
7559 element <= EL_MM_AMOEBA_WALL_END ?
7562 element >= EL_DF_STEEL_WALL_START &&
7563 element <= EL_DF_STEEL_WALL_END ?
7566 element >= EL_DF_WOODEN_WALL_START &&
7567 element <= EL_DF_WOODEN_WALL_END ?
7573 int map_mm_wall_element_editor(int element)
7577 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7578 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7579 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7580 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7581 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7582 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7584 default: return element;
7588 int get_next_element(int element)
7592 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7593 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7594 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7595 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7596 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7597 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7598 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7599 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7600 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7601 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7602 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7604 default: return element;
7608 int el2img_mm(int element_mm)
7610 return el2img(map_element_MM_to_RND(element_mm));
7613 int el_act_dir2img(int element, int action, int direction)
7615 element = GFX_ELEMENT(element);
7616 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7618 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7619 return element_info[element].direction_graphic[action][direction];
7622 static int el_act_dir2crm(int element, int action, int direction)
7624 element = GFX_ELEMENT(element);
7625 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7627 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7628 return element_info[element].direction_crumbled[action][direction];
7631 int el_act2img(int element, int action)
7633 element = GFX_ELEMENT(element);
7635 return element_info[element].graphic[action];
7638 int el_act2crm(int element, int action)
7640 element = GFX_ELEMENT(element);
7642 return element_info[element].crumbled[action];
7645 int el_dir2img(int element, int direction)
7647 element = GFX_ELEMENT(element);
7649 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7652 int el2baseimg(int element)
7654 return element_info[element].graphic[ACTION_DEFAULT];
7657 int el2img(int element)
7659 element = GFX_ELEMENT(element);
7661 return element_info[element].graphic[ACTION_DEFAULT];
7664 int el2edimg(int element)
7666 element = GFX_ELEMENT(element);
7668 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7671 int el2preimg(int element)
7673 element = GFX_ELEMENT(element);
7675 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7678 int el2panelimg(int element)
7680 element = GFX_ELEMENT(element);
7682 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7685 int font2baseimg(int font_nr)
7687 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7690 int getBeltNrFromBeltElement(int element)
7692 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7693 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7694 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7697 int getBeltNrFromBeltActiveElement(int element)
7699 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7700 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7701 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7704 int getBeltNrFromBeltSwitchElement(int element)
7706 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7707 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7708 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7711 int getBeltDirNrFromBeltElement(int element)
7713 static int belt_base_element[4] =
7715 EL_CONVEYOR_BELT_1_LEFT,
7716 EL_CONVEYOR_BELT_2_LEFT,
7717 EL_CONVEYOR_BELT_3_LEFT,
7718 EL_CONVEYOR_BELT_4_LEFT
7721 int belt_nr = getBeltNrFromBeltElement(element);
7722 int belt_dir_nr = element - belt_base_element[belt_nr];
7724 return (belt_dir_nr % 3);
7727 int getBeltDirNrFromBeltSwitchElement(int element)
7729 static int belt_base_element[4] =
7731 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7732 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7733 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7734 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7737 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7738 int belt_dir_nr = element - belt_base_element[belt_nr];
7740 return (belt_dir_nr % 3);
7743 int getBeltDirFromBeltElement(int element)
7745 static int belt_move_dir[3] =
7752 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7754 return belt_move_dir[belt_dir_nr];
7757 int getBeltDirFromBeltSwitchElement(int element)
7759 static int belt_move_dir[3] =
7766 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7768 return belt_move_dir[belt_dir_nr];
7771 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7773 static int belt_base_element[4] =
7775 EL_CONVEYOR_BELT_1_LEFT,
7776 EL_CONVEYOR_BELT_2_LEFT,
7777 EL_CONVEYOR_BELT_3_LEFT,
7778 EL_CONVEYOR_BELT_4_LEFT
7781 return belt_base_element[belt_nr] + belt_dir_nr;
7784 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7786 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7788 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7791 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7793 static int belt_base_element[4] =
7795 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7796 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7797 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7798 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7801 return belt_base_element[belt_nr] + belt_dir_nr;
7804 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7806 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7808 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7811 boolean getTeamMode_EM()
7813 return game.team_mode;
7816 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7818 int game_frame_delay_value;
7820 game_frame_delay_value =
7821 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7822 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7825 if (tape.playing && tape.warp_forward && !tape.pausing)
7826 game_frame_delay_value = 0;
7828 return game_frame_delay_value;
7831 unsigned int InitRND(int seed)
7833 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7834 return InitEngineRandom_EM(seed);
7835 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7836 return InitEngineRandom_SP(seed);
7837 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7838 return InitEngineRandom_MM(seed);
7840 return InitEngineRandom_RND(seed);
7843 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7844 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7846 inline static int get_effective_element_EM(int tile, int frame_em)
7848 int element = object_mapping[tile].element_rnd;
7849 int action = object_mapping[tile].action;
7850 boolean is_backside = object_mapping[tile].is_backside;
7851 boolean action_removing = (action == ACTION_DIGGING ||
7852 action == ACTION_SNAPPING ||
7853 action == ACTION_COLLECTING);
7859 case Yacid_splash_eB:
7860 case Yacid_splash_wB:
7861 return (frame_em > 5 ? EL_EMPTY : element);
7867 else /* frame_em == 7 */
7871 case Yacid_splash_eB:
7872 case Yacid_splash_wB:
7875 case Yemerald_stone:
7878 case Ydiamond_stone:
7882 case Xdrip_stretchB:
7901 case Xsand_stonein_1:
7902 case Xsand_stonein_2:
7903 case Xsand_stonein_3:
7904 case Xsand_stonein_4:
7908 return (is_backside || action_removing ? EL_EMPTY : element);
7913 inline static boolean check_linear_animation_EM(int tile)
7917 case Xsand_stonesand_1:
7918 case Xsand_stonesand_quickout_1:
7919 case Xsand_sandstone_1:
7920 case Xsand_stonein_1:
7921 case Xsand_stoneout_1:
7940 case Yacid_splash_eB:
7941 case Yacid_splash_wB:
7942 case Yemerald_stone:
7949 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7950 boolean has_crumbled_graphics,
7951 int crumbled, int sync_frame)
7953 /* if element can be crumbled, but certain action graphics are just empty
7954 space (like instantly snapping sand to empty space in 1 frame), do not
7955 treat these empty space graphics as crumbled graphics in EMC engine */
7956 if (crumbled == IMG_EMPTY_SPACE)
7957 has_crumbled_graphics = FALSE;
7959 if (has_crumbled_graphics)
7961 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7962 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7963 g_crumbled->anim_delay,
7964 g_crumbled->anim_mode,
7965 g_crumbled->anim_start_frame,
7968 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7969 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7971 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7972 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7974 g_em->has_crumbled_graphics = TRUE;
7978 g_em->crumbled_bitmap = NULL;
7979 g_em->crumbled_src_x = 0;
7980 g_em->crumbled_src_y = 0;
7981 g_em->crumbled_border_size = 0;
7982 g_em->crumbled_tile_size = 0;
7984 g_em->has_crumbled_graphics = FALSE;
7988 void ResetGfxAnimation_EM(int x, int y, int tile)
7993 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7994 int tile, int frame_em, int x, int y)
7996 int action = object_mapping[tile].action;
7997 int direction = object_mapping[tile].direction;
7998 int effective_element = get_effective_element_EM(tile, frame_em);
7999 int graphic = (direction == MV_NONE ?
8000 el_act2img(effective_element, action) :
8001 el_act_dir2img(effective_element, action, direction));
8002 struct GraphicInfo *g = &graphic_info[graphic];
8004 boolean action_removing = (action == ACTION_DIGGING ||
8005 action == ACTION_SNAPPING ||
8006 action == ACTION_COLLECTING);
8007 boolean action_moving = (action == ACTION_FALLING ||
8008 action == ACTION_MOVING ||
8009 action == ACTION_PUSHING ||
8010 action == ACTION_EATING ||
8011 action == ACTION_FILLING ||
8012 action == ACTION_EMPTYING);
8013 boolean action_falling = (action == ACTION_FALLING ||
8014 action == ACTION_FILLING ||
8015 action == ACTION_EMPTYING);
8017 /* special case: graphic uses "2nd movement tile" and has defined
8018 7 frames for movement animation (or less) => use default graphic
8019 for last (8th) frame which ends the movement animation */
8020 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8022 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
8023 graphic = (direction == MV_NONE ?
8024 el_act2img(effective_element, action) :
8025 el_act_dir2img(effective_element, action, direction));
8027 g = &graphic_info[graphic];
8030 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8034 else if (action_moving)
8036 boolean is_backside = object_mapping[tile].is_backside;
8040 int direction = object_mapping[tile].direction;
8041 int move_dir = (action_falling ? MV_DOWN : direction);
8046 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
8047 if (g->double_movement && frame_em == 0)
8051 if (move_dir == MV_LEFT)
8052 GfxFrame[x - 1][y] = GfxFrame[x][y];
8053 else if (move_dir == MV_RIGHT)
8054 GfxFrame[x + 1][y] = GfxFrame[x][y];
8055 else if (move_dir == MV_UP)
8056 GfxFrame[x][y - 1] = GfxFrame[x][y];
8057 else if (move_dir == MV_DOWN)
8058 GfxFrame[x][y + 1] = GfxFrame[x][y];
8065 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
8066 if (tile == Xsand_stonesand_quickout_1 ||
8067 tile == Xsand_stonesand_quickout_2)
8071 if (graphic_info[graphic].anim_global_sync)
8072 sync_frame = FrameCounter;
8073 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8074 sync_frame = GfxFrame[x][y];
8076 sync_frame = 0; /* playfield border (pseudo steel) */
8078 SetRandomAnimationValue(x, y);
8080 int frame = getAnimationFrame(g->anim_frames,
8083 g->anim_start_frame,
8086 g_em->unique_identifier =
8087 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8090 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8091 int tile, int frame_em, int x, int y)
8093 int action = object_mapping[tile].action;
8094 int direction = object_mapping[tile].direction;
8095 boolean is_backside = object_mapping[tile].is_backside;
8096 int effective_element = get_effective_element_EM(tile, frame_em);
8097 int effective_action = action;
8098 int graphic = (direction == MV_NONE ?
8099 el_act2img(effective_element, effective_action) :
8100 el_act_dir2img(effective_element, effective_action,
8102 int crumbled = (direction == MV_NONE ?
8103 el_act2crm(effective_element, effective_action) :
8104 el_act_dir2crm(effective_element, effective_action,
8106 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8107 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8108 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8109 struct GraphicInfo *g = &graphic_info[graphic];
8112 /* special case: graphic uses "2nd movement tile" and has defined
8113 7 frames for movement animation (or less) => use default graphic
8114 for last (8th) frame which ends the movement animation */
8115 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8117 effective_action = ACTION_DEFAULT;
8118 graphic = (direction == MV_NONE ?
8119 el_act2img(effective_element, effective_action) :
8120 el_act_dir2img(effective_element, effective_action,
8122 crumbled = (direction == MV_NONE ?
8123 el_act2crm(effective_element, effective_action) :
8124 el_act_dir2crm(effective_element, effective_action,
8127 g = &graphic_info[graphic];
8130 if (graphic_info[graphic].anim_global_sync)
8131 sync_frame = FrameCounter;
8132 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8133 sync_frame = GfxFrame[x][y];
8135 sync_frame = 0; /* playfield border (pseudo steel) */
8137 SetRandomAnimationValue(x, y);
8139 int frame = getAnimationFrame(g->anim_frames,
8142 g->anim_start_frame,
8145 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8146 g->double_movement && is_backside);
8148 /* (updating the "crumbled" graphic definitions is probably not really needed,
8149 as animations for crumbled graphics can't be longer than one EMC cycle) */
8150 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8154 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8155 int player_nr, int anim, int frame_em)
8157 int element = player_mapping[player_nr][anim].element_rnd;
8158 int action = player_mapping[player_nr][anim].action;
8159 int direction = player_mapping[player_nr][anim].direction;
8160 int graphic = (direction == MV_NONE ?
8161 el_act2img(element, action) :
8162 el_act_dir2img(element, action, direction));
8163 struct GraphicInfo *g = &graphic_info[graphic];
8166 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8168 stored_player[player_nr].StepFrame = frame_em;
8170 sync_frame = stored_player[player_nr].Frame;
8172 int frame = getAnimationFrame(g->anim_frames,
8175 g->anim_start_frame,
8178 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8179 &g_em->src_x, &g_em->src_y, FALSE);
8182 void InitGraphicInfo_EM(void)
8187 int num_em_gfx_errors = 0;
8189 if (graphic_info_em_object[0][0].bitmap == NULL)
8191 /* EM graphics not yet initialized in em_open_all() */
8196 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8199 /* always start with reliable default values */
8200 for (i = 0; i < TILE_MAX; i++)
8202 object_mapping[i].element_rnd = EL_UNKNOWN;
8203 object_mapping[i].is_backside = FALSE;
8204 object_mapping[i].action = ACTION_DEFAULT;
8205 object_mapping[i].direction = MV_NONE;
8208 /* always start with reliable default values */
8209 for (p = 0; p < MAX_PLAYERS; p++)
8211 for (i = 0; i < SPR_MAX; i++)
8213 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8214 player_mapping[p][i].action = ACTION_DEFAULT;
8215 player_mapping[p][i].direction = MV_NONE;
8219 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8221 int e = em_object_mapping_list[i].element_em;
8223 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8224 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8226 if (em_object_mapping_list[i].action != -1)
8227 object_mapping[e].action = em_object_mapping_list[i].action;
8229 if (em_object_mapping_list[i].direction != -1)
8230 object_mapping[e].direction =
8231 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8234 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8236 int a = em_player_mapping_list[i].action_em;
8237 int p = em_player_mapping_list[i].player_nr;
8239 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8241 if (em_player_mapping_list[i].action != -1)
8242 player_mapping[p][a].action = em_player_mapping_list[i].action;
8244 if (em_player_mapping_list[i].direction != -1)
8245 player_mapping[p][a].direction =
8246 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8249 for (i = 0; i < TILE_MAX; i++)
8251 int element = object_mapping[i].element_rnd;
8252 int action = object_mapping[i].action;
8253 int direction = object_mapping[i].direction;
8254 boolean is_backside = object_mapping[i].is_backside;
8255 boolean action_exploding = ((action == ACTION_EXPLODING ||
8256 action == ACTION_SMASHED_BY_ROCK ||
8257 action == ACTION_SMASHED_BY_SPRING) &&
8258 element != EL_DIAMOND);
8259 boolean action_active = (action == ACTION_ACTIVE);
8260 boolean action_other = (action == ACTION_OTHER);
8262 for (j = 0; j < 8; j++)
8264 int effective_element = get_effective_element_EM(i, j);
8265 int effective_action = (j < 7 ? action :
8266 i == Xdrip_stretch ? action :
8267 i == Xdrip_stretchB ? action :
8268 i == Ydrip_s1 ? action :
8269 i == Ydrip_s1B ? action :
8270 i == Xball_1B ? action :
8271 i == Xball_2 ? action :
8272 i == Xball_2B ? action :
8273 i == Yball_eat ? action :
8274 i == Ykey_1_eat ? action :
8275 i == Ykey_2_eat ? action :
8276 i == Ykey_3_eat ? action :
8277 i == Ykey_4_eat ? action :
8278 i == Ykey_5_eat ? action :
8279 i == Ykey_6_eat ? action :
8280 i == Ykey_7_eat ? action :
8281 i == Ykey_8_eat ? action :
8282 i == Ylenses_eat ? action :
8283 i == Ymagnify_eat ? action :
8284 i == Ygrass_eat ? action :
8285 i == Ydirt_eat ? action :
8286 i == Xsand_stonein_1 ? action :
8287 i == Xsand_stonein_2 ? action :
8288 i == Xsand_stonein_3 ? action :
8289 i == Xsand_stonein_4 ? action :
8290 i == Xsand_stoneout_1 ? action :
8291 i == Xsand_stoneout_2 ? action :
8292 i == Xboom_android ? ACTION_EXPLODING :
8293 action_exploding ? ACTION_EXPLODING :
8294 action_active ? action :
8295 action_other ? action :
8297 int graphic = (el_act_dir2img(effective_element, effective_action,
8299 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8301 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8302 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8303 boolean has_action_graphics = (graphic != base_graphic);
8304 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8305 struct GraphicInfo *g = &graphic_info[graphic];
8306 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8309 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8310 boolean special_animation = (action != ACTION_DEFAULT &&
8311 g->anim_frames == 3 &&
8312 g->anim_delay == 2 &&
8313 g->anim_mode & ANIM_LINEAR);
8314 int sync_frame = (i == Xdrip_stretch ? 7 :
8315 i == Xdrip_stretchB ? 7 :
8316 i == Ydrip_s2 ? j + 8 :
8317 i == Ydrip_s2B ? j + 8 :
8326 i == Xfake_acid_1 ? 0 :
8327 i == Xfake_acid_2 ? 10 :
8328 i == Xfake_acid_3 ? 20 :
8329 i == Xfake_acid_4 ? 30 :
8330 i == Xfake_acid_5 ? 40 :
8331 i == Xfake_acid_6 ? 50 :
8332 i == Xfake_acid_7 ? 60 :
8333 i == Xfake_acid_8 ? 70 :
8335 i == Xball_2B ? j + 8 :
8336 i == Yball_eat ? j + 1 :
8337 i == Ykey_1_eat ? j + 1 :
8338 i == Ykey_2_eat ? j + 1 :
8339 i == Ykey_3_eat ? j + 1 :
8340 i == Ykey_4_eat ? j + 1 :
8341 i == Ykey_5_eat ? j + 1 :
8342 i == Ykey_6_eat ? j + 1 :
8343 i == Ykey_7_eat ? j + 1 :
8344 i == Ykey_8_eat ? j + 1 :
8345 i == Ylenses_eat ? j + 1 :
8346 i == Ymagnify_eat ? j + 1 :
8347 i == Ygrass_eat ? j + 1 :
8348 i == Ydirt_eat ? j + 1 :
8349 i == Xamoeba_1 ? 0 :
8350 i == Xamoeba_2 ? 1 :
8351 i == Xamoeba_3 ? 2 :
8352 i == Xamoeba_4 ? 3 :
8353 i == Xamoeba_5 ? 0 :
8354 i == Xamoeba_6 ? 1 :
8355 i == Xamoeba_7 ? 2 :
8356 i == Xamoeba_8 ? 3 :
8357 i == Xexit_2 ? j + 8 :
8358 i == Xexit_3 ? j + 16 :
8359 i == Xdynamite_1 ? 0 :
8360 i == Xdynamite_2 ? 8 :
8361 i == Xdynamite_3 ? 16 :
8362 i == Xdynamite_4 ? 24 :
8363 i == Xsand_stonein_1 ? j + 1 :
8364 i == Xsand_stonein_2 ? j + 9 :
8365 i == Xsand_stonein_3 ? j + 17 :
8366 i == Xsand_stonein_4 ? j + 25 :
8367 i == Xsand_stoneout_1 && j == 0 ? 0 :
8368 i == Xsand_stoneout_1 && j == 1 ? 0 :
8369 i == Xsand_stoneout_1 && j == 2 ? 1 :
8370 i == Xsand_stoneout_1 && j == 3 ? 2 :
8371 i == Xsand_stoneout_1 && j == 4 ? 2 :
8372 i == Xsand_stoneout_1 && j == 5 ? 3 :
8373 i == Xsand_stoneout_1 && j == 6 ? 4 :
8374 i == Xsand_stoneout_1 && j == 7 ? 4 :
8375 i == Xsand_stoneout_2 && j == 0 ? 5 :
8376 i == Xsand_stoneout_2 && j == 1 ? 6 :
8377 i == Xsand_stoneout_2 && j == 2 ? 7 :
8378 i == Xsand_stoneout_2 && j == 3 ? 8 :
8379 i == Xsand_stoneout_2 && j == 4 ? 9 :
8380 i == Xsand_stoneout_2 && j == 5 ? 11 :
8381 i == Xsand_stoneout_2 && j == 6 ? 13 :
8382 i == Xsand_stoneout_2 && j == 7 ? 15 :
8383 i == Xboom_bug && j == 1 ? 2 :
8384 i == Xboom_bug && j == 2 ? 2 :
8385 i == Xboom_bug && j == 3 ? 4 :
8386 i == Xboom_bug && j == 4 ? 4 :
8387 i == Xboom_bug && j == 5 ? 2 :
8388 i == Xboom_bug && j == 6 ? 2 :
8389 i == Xboom_bug && j == 7 ? 0 :
8390 i == Xboom_bomb && j == 1 ? 2 :
8391 i == Xboom_bomb && j == 2 ? 2 :
8392 i == Xboom_bomb && j == 3 ? 4 :
8393 i == Xboom_bomb && j == 4 ? 4 :
8394 i == Xboom_bomb && j == 5 ? 2 :
8395 i == Xboom_bomb && j == 6 ? 2 :
8396 i == Xboom_bomb && j == 7 ? 0 :
8397 i == Xboom_android && j == 7 ? 6 :
8398 i == Xboom_1 && j == 1 ? 2 :
8399 i == Xboom_1 && j == 2 ? 2 :
8400 i == Xboom_1 && j == 3 ? 4 :
8401 i == Xboom_1 && j == 4 ? 4 :
8402 i == Xboom_1 && j == 5 ? 6 :
8403 i == Xboom_1 && j == 6 ? 6 :
8404 i == Xboom_1 && j == 7 ? 8 :
8405 i == Xboom_2 && j == 0 ? 8 :
8406 i == Xboom_2 && j == 1 ? 8 :
8407 i == Xboom_2 && j == 2 ? 10 :
8408 i == Xboom_2 && j == 3 ? 10 :
8409 i == Xboom_2 && j == 4 ? 10 :
8410 i == Xboom_2 && j == 5 ? 12 :
8411 i == Xboom_2 && j == 6 ? 12 :
8412 i == Xboom_2 && j == 7 ? 12 :
8413 special_animation && j == 4 ? 3 :
8414 effective_action != action ? 0 :
8418 Bitmap *debug_bitmap = g_em->bitmap;
8419 int debug_src_x = g_em->src_x;
8420 int debug_src_y = g_em->src_y;
8423 int frame = getAnimationFrame(g->anim_frames,
8426 g->anim_start_frame,
8429 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8430 g->double_movement && is_backside);
8432 g_em->bitmap = src_bitmap;
8433 g_em->src_x = src_x;
8434 g_em->src_y = src_y;
8435 g_em->src_offset_x = 0;
8436 g_em->src_offset_y = 0;
8437 g_em->dst_offset_x = 0;
8438 g_em->dst_offset_y = 0;
8439 g_em->width = TILEX;
8440 g_em->height = TILEY;
8442 g_em->preserve_background = FALSE;
8444 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8447 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8448 effective_action == ACTION_MOVING ||
8449 effective_action == ACTION_PUSHING ||
8450 effective_action == ACTION_EATING)) ||
8451 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8452 effective_action == ACTION_EMPTYING)))
8455 (effective_action == ACTION_FALLING ||
8456 effective_action == ACTION_FILLING ||
8457 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8458 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8459 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8460 int num_steps = (i == Ydrip_s1 ? 16 :
8461 i == Ydrip_s1B ? 16 :
8462 i == Ydrip_s2 ? 16 :
8463 i == Ydrip_s2B ? 16 :
8464 i == Xsand_stonein_1 ? 32 :
8465 i == Xsand_stonein_2 ? 32 :
8466 i == Xsand_stonein_3 ? 32 :
8467 i == Xsand_stonein_4 ? 32 :
8468 i == Xsand_stoneout_1 ? 16 :
8469 i == Xsand_stoneout_2 ? 16 : 8);
8470 int cx = ABS(dx) * (TILEX / num_steps);
8471 int cy = ABS(dy) * (TILEY / num_steps);
8472 int step_frame = (i == Ydrip_s2 ? j + 8 :
8473 i == Ydrip_s2B ? j + 8 :
8474 i == Xsand_stonein_2 ? j + 8 :
8475 i == Xsand_stonein_3 ? j + 16 :
8476 i == Xsand_stonein_4 ? j + 24 :
8477 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8478 int step = (is_backside ? step_frame : num_steps - step_frame);
8480 if (is_backside) /* tile where movement starts */
8482 if (dx < 0 || dy < 0)
8484 g_em->src_offset_x = cx * step;
8485 g_em->src_offset_y = cy * step;
8489 g_em->dst_offset_x = cx * step;
8490 g_em->dst_offset_y = cy * step;
8493 else /* tile where movement ends */
8495 if (dx < 0 || dy < 0)
8497 g_em->dst_offset_x = cx * step;
8498 g_em->dst_offset_y = cy * step;
8502 g_em->src_offset_x = cx * step;
8503 g_em->src_offset_y = cy * step;
8507 g_em->width = TILEX - cx * step;
8508 g_em->height = TILEY - cy * step;
8511 /* create unique graphic identifier to decide if tile must be redrawn */
8512 /* bit 31 - 16 (16 bit): EM style graphic
8513 bit 15 - 12 ( 4 bit): EM style frame
8514 bit 11 - 6 ( 6 bit): graphic width
8515 bit 5 - 0 ( 6 bit): graphic height */
8516 g_em->unique_identifier =
8517 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8521 /* skip check for EMC elements not contained in original EMC artwork */
8522 if (element == EL_EMC_FAKE_ACID)
8525 if (g_em->bitmap != debug_bitmap ||
8526 g_em->src_x != debug_src_x ||
8527 g_em->src_y != debug_src_y ||
8528 g_em->src_offset_x != 0 ||
8529 g_em->src_offset_y != 0 ||
8530 g_em->dst_offset_x != 0 ||
8531 g_em->dst_offset_y != 0 ||
8532 g_em->width != TILEX ||
8533 g_em->height != TILEY)
8535 static int last_i = -1;
8543 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8544 i, element, element_info[element].token_name,
8545 element_action_info[effective_action].suffix, direction);
8547 if (element != effective_element)
8548 printf(" [%d ('%s')]",
8550 element_info[effective_element].token_name);
8554 if (g_em->bitmap != debug_bitmap)
8555 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8556 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8558 if (g_em->src_x != debug_src_x ||
8559 g_em->src_y != debug_src_y)
8560 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8561 j, (is_backside ? 'B' : 'F'),
8562 g_em->src_x, g_em->src_y,
8563 g_em->src_x / 32, g_em->src_y / 32,
8564 debug_src_x, debug_src_y,
8565 debug_src_x / 32, debug_src_y / 32);
8567 if (g_em->src_offset_x != 0 ||
8568 g_em->src_offset_y != 0 ||
8569 g_em->dst_offset_x != 0 ||
8570 g_em->dst_offset_y != 0)
8571 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8573 g_em->src_offset_x, g_em->src_offset_y,
8574 g_em->dst_offset_x, g_em->dst_offset_y);
8576 if (g_em->width != TILEX ||
8577 g_em->height != TILEY)
8578 printf(" %d (%d): size %d,%d should be %d,%d\n",
8580 g_em->width, g_em->height, TILEX, TILEY);
8582 num_em_gfx_errors++;
8589 for (i = 0; i < TILE_MAX; i++)
8591 for (j = 0; j < 8; j++)
8593 int element = object_mapping[i].element_rnd;
8594 int action = object_mapping[i].action;
8595 int direction = object_mapping[i].direction;
8596 boolean is_backside = object_mapping[i].is_backside;
8597 int graphic_action = el_act_dir2img(element, action, direction);
8598 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8600 if ((action == ACTION_SMASHED_BY_ROCK ||
8601 action == ACTION_SMASHED_BY_SPRING ||
8602 action == ACTION_EATING) &&
8603 graphic_action == graphic_default)
8605 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8606 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8607 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8608 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8611 /* no separate animation for "smashed by rock" -- use rock instead */
8612 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8613 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8615 g_em->bitmap = g_xx->bitmap;
8616 g_em->src_x = g_xx->src_x;
8617 g_em->src_y = g_xx->src_y;
8618 g_em->src_offset_x = g_xx->src_offset_x;
8619 g_em->src_offset_y = g_xx->src_offset_y;
8620 g_em->dst_offset_x = g_xx->dst_offset_x;
8621 g_em->dst_offset_y = g_xx->dst_offset_y;
8622 g_em->width = g_xx->width;
8623 g_em->height = g_xx->height;
8624 g_em->unique_identifier = g_xx->unique_identifier;
8627 g_em->preserve_background = TRUE;
8632 for (p = 0; p < MAX_PLAYERS; p++)
8634 for (i = 0; i < SPR_MAX; i++)
8636 int element = player_mapping[p][i].element_rnd;
8637 int action = player_mapping[p][i].action;
8638 int direction = player_mapping[p][i].direction;
8640 for (j = 0; j < 8; j++)
8642 int effective_element = element;
8643 int effective_action = action;
8644 int graphic = (direction == MV_NONE ?
8645 el_act2img(effective_element, effective_action) :
8646 el_act_dir2img(effective_element, effective_action,
8648 struct GraphicInfo *g = &graphic_info[graphic];
8649 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8655 Bitmap *debug_bitmap = g_em->bitmap;
8656 int debug_src_x = g_em->src_x;
8657 int debug_src_y = g_em->src_y;
8660 int frame = getAnimationFrame(g->anim_frames,
8663 g->anim_start_frame,
8666 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8668 g_em->bitmap = src_bitmap;
8669 g_em->src_x = src_x;
8670 g_em->src_y = src_y;
8671 g_em->src_offset_x = 0;
8672 g_em->src_offset_y = 0;
8673 g_em->dst_offset_x = 0;
8674 g_em->dst_offset_y = 0;
8675 g_em->width = TILEX;
8676 g_em->height = TILEY;
8680 /* skip check for EMC elements not contained in original EMC artwork */
8681 if (element == EL_PLAYER_3 ||
8682 element == EL_PLAYER_4)
8685 if (g_em->bitmap != debug_bitmap ||
8686 g_em->src_x != debug_src_x ||
8687 g_em->src_y != debug_src_y)
8689 static int last_i = -1;
8697 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8698 p, i, element, element_info[element].token_name,
8699 element_action_info[effective_action].suffix, direction);
8701 if (element != effective_element)
8702 printf(" [%d ('%s')]",
8704 element_info[effective_element].token_name);
8708 if (g_em->bitmap != debug_bitmap)
8709 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8710 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8712 if (g_em->src_x != debug_src_x ||
8713 g_em->src_y != debug_src_y)
8714 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8716 g_em->src_x, g_em->src_y,
8717 g_em->src_x / 32, g_em->src_y / 32,
8718 debug_src_x, debug_src_y,
8719 debug_src_x / 32, debug_src_y / 32);
8721 num_em_gfx_errors++;
8731 printf("::: [%d errors found]\n", num_em_gfx_errors);
8737 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8738 boolean any_player_moving,
8739 boolean any_player_snapping,
8740 boolean any_player_dropping)
8742 if (frame == 0 && !any_player_dropping)
8744 if (!local_player->was_waiting)
8746 if (!CheckSaveEngineSnapshotToList())
8749 local_player->was_waiting = TRUE;
8752 else if (any_player_moving || any_player_snapping || any_player_dropping)
8754 local_player->was_waiting = FALSE;
8758 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8759 boolean murphy_is_dropping)
8761 if (murphy_is_waiting)
8763 if (!local_player->was_waiting)
8765 if (!CheckSaveEngineSnapshotToList())
8768 local_player->was_waiting = TRUE;
8773 local_player->was_waiting = FALSE;
8777 void CheckSaveEngineSnapshot_MM(boolean element_clicked,
8778 boolean button_released)
8780 if (button_released)
8782 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
8783 CheckSaveEngineSnapshotToList();
8785 else if (element_clicked)
8787 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
8788 CheckSaveEngineSnapshotToList();
8790 game.snapshot.changed_action = TRUE;
8794 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8795 boolean any_player_moving,
8796 boolean any_player_snapping,
8797 boolean any_player_dropping)
8799 if (tape.single_step && tape.recording && !tape.pausing)
8800 if (frame == 0 && !any_player_dropping)
8801 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8803 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8804 any_player_snapping, any_player_dropping);
8807 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8808 boolean murphy_is_dropping)
8810 boolean murphy_starts_dropping = FALSE;
8813 for (i = 0; i < MAX_PLAYERS; i++)
8814 if (stored_player[i].force_dropping)
8815 murphy_starts_dropping = TRUE;
8817 if (tape.single_step && tape.recording && !tape.pausing)
8818 if (murphy_is_waiting && !murphy_starts_dropping)
8819 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8821 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8824 void CheckSingleStepMode_MM(boolean element_clicked,
8825 boolean button_released)
8827 if (tape.single_step && tape.recording && !tape.pausing)
8828 if (button_released)
8829 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8831 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
8834 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8835 int graphic, int sync_frame, int x, int y)
8837 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8839 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8842 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8844 return (IS_NEXT_FRAME(sync_frame, graphic));
8847 int getGraphicInfo_Delay(int graphic)
8849 return graphic_info[graphic].anim_delay;
8852 void PlayMenuSoundExt(int sound)
8854 if (sound == SND_UNDEFINED)
8857 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8858 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8861 if (IS_LOOP_SOUND(sound))
8862 PlaySoundLoop(sound);
8867 void PlayMenuSound()
8869 PlayMenuSoundExt(menu.sound[game_status]);
8872 void PlayMenuSoundStereo(int sound, int stereo_position)
8874 if (sound == SND_UNDEFINED)
8877 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8878 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8881 if (IS_LOOP_SOUND(sound))
8882 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8884 PlaySoundStereo(sound, stereo_position);
8887 void PlayMenuSoundIfLoopExt(int sound)
8889 if (sound == SND_UNDEFINED)
8892 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8893 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8896 if (IS_LOOP_SOUND(sound))
8897 PlaySoundLoop(sound);
8900 void PlayMenuSoundIfLoop()
8902 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8905 void PlayMenuMusicExt(int music)
8907 if (music == MUS_UNDEFINED)
8910 if (!setup.sound_music)
8916 void PlayMenuMusic()
8918 char *curr_music = getCurrentlyPlayingMusicFilename();
8919 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8921 if (!strEqual(curr_music, next_music))
8922 PlayMenuMusicExt(menu.music[game_status]);
8925 void PlayMenuSoundsAndMusic()
8931 static void FadeMenuSounds()
8936 static void FadeMenuMusic()
8938 char *curr_music = getCurrentlyPlayingMusicFilename();
8939 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8941 if (!strEqual(curr_music, next_music))
8945 void FadeMenuSoundsAndMusic()
8951 void PlaySoundActivating()
8954 PlaySound(SND_MENU_ITEM_ACTIVATING);
8958 void PlaySoundSelecting()
8961 PlaySound(SND_MENU_ITEM_SELECTING);
8965 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8967 boolean change_fullscreen = (setup.fullscreen !=
8968 video.fullscreen_enabled);
8969 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8970 setup.window_scaling_percent !=
8971 video.window_scaling_percent);
8973 if (change_window_scaling_percent && video.fullscreen_enabled)
8976 if (!change_window_scaling_percent && !video.fullscreen_available)
8979 #if defined(TARGET_SDL2)
8980 if (change_window_scaling_percent)
8982 SDLSetWindowScaling(setup.window_scaling_percent);
8986 else if (change_fullscreen)
8988 SDLSetWindowFullscreen(setup.fullscreen);
8990 /* set setup value according to successfully changed fullscreen mode */
8991 setup.fullscreen = video.fullscreen_enabled;
8997 if (change_fullscreen ||
8998 change_window_scaling_percent)
9000 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9002 /* save backbuffer content which gets lost when toggling fullscreen mode */
9003 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9005 if (change_window_scaling_percent)
9007 /* keep window mode, but change window scaling */
9008 video.fullscreen_enabled = TRUE; /* force new window scaling */
9011 /* toggle fullscreen */
9012 ChangeVideoModeIfNeeded(setup.fullscreen);
9014 /* set setup value according to successfully changed fullscreen mode */
9015 setup.fullscreen = video.fullscreen_enabled;
9017 /* restore backbuffer content from temporary backbuffer backup bitmap */
9018 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9020 FreeBitmap(tmp_backbuffer);
9022 /* update visible window/screen */
9023 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9027 void JoinRectangles(int *x, int *y, int *width, int *height,
9028 int x2, int y2, int width2, int height2)
9030 // do not join with "off-screen" rectangle
9031 if (x2 == -1 || y2 == -1)
9036 *width = MAX(*width, width2);
9037 *height = MAX(*height, height2);
9040 void SetAnimStatus(int anim_status_new)
9042 if (anim_status_new == GAME_MODE_MAIN)
9043 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9044 else if (anim_status_new == GAME_MODE_SCORES)
9045 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9047 global.anim_status_next = anim_status_new;
9049 // directly set screen modes that are entered without fading
9050 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9051 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9052 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9053 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9054 global.anim_status = global.anim_status_next;
9057 void SetGameStatus(int game_status_new)
9059 if (game_status_new != game_status)
9060 game_status_last_screen = game_status;
9062 game_status = game_status_new;
9064 SetAnimStatus(game_status_new);
9067 void SetFontStatus(int game_status_new)
9069 static int last_game_status = -1;
9071 if (game_status_new != -1)
9073 // set game status for font use after storing last game status
9074 last_game_status = game_status;
9075 game_status = game_status_new;
9079 // reset game status after font use from last stored game status
9080 game_status = last_game_status;
9084 void ResetFontStatus()
9089 boolean CheckIfPlayfieldViewportHasChanged()
9091 // if game status has not changed, playfield viewport has not changed either
9092 if (game_status == game_status_last)
9095 // check if playfield viewport has changed with current game status
9096 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9097 int new_real_sx = vp_playfield->x;
9098 int new_real_sy = vp_playfield->y;
9099 int new_full_sxsize = vp_playfield->width;
9100 int new_full_sysize = vp_playfield->height;
9102 return (new_real_sx != REAL_SX ||
9103 new_real_sy != REAL_SY ||
9104 new_full_sxsize != FULL_SXSIZE ||
9105 new_full_sysize != FULL_SYSIZE);
9108 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
9110 return (CheckIfGlobalBorderHasChanged() ||
9111 CheckIfPlayfieldViewportHasChanged());
9114 void ChangeViewportPropertiesIfNeeded()
9116 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9117 FALSE : setup.small_game_graphics);
9118 int gfx_game_mode = game_status;
9119 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9121 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9122 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9123 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9124 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9125 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9126 int new_win_xsize = vp_window->width;
9127 int new_win_ysize = vp_window->height;
9128 int border_size = vp_playfield->border_size;
9129 int new_sx = vp_playfield->x + border_size;
9130 int new_sy = vp_playfield->y + border_size;
9131 int new_sxsize = vp_playfield->width - 2 * border_size;
9132 int new_sysize = vp_playfield->height - 2 * border_size;
9133 int new_real_sx = vp_playfield->x;
9134 int new_real_sy = vp_playfield->y;
9135 int new_full_sxsize = vp_playfield->width;
9136 int new_full_sysize = vp_playfield->height;
9137 int new_dx = vp_door_1->x;
9138 int new_dy = vp_door_1->y;
9139 int new_dxsize = vp_door_1->width;
9140 int new_dysize = vp_door_1->height;
9141 int new_vx = vp_door_2->x;
9142 int new_vy = vp_door_2->y;
9143 int new_vxsize = vp_door_2->width;
9144 int new_vysize = vp_door_2->height;
9145 int new_ex = vp_door_3->x;
9146 int new_ey = vp_door_3->y;
9147 int new_exsize = vp_door_3->width;
9148 int new_eysize = vp_door_3->height;
9149 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9150 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9151 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9152 int new_scr_fieldx = new_sxsize / tilesize;
9153 int new_scr_fieldy = new_sysize / tilesize;
9154 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9155 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9156 boolean init_gfx_buffers = FALSE;
9157 boolean init_video_buffer = FALSE;
9158 boolean init_gadgets_and_anims = FALSE;
9159 boolean init_em_graphics = FALSE;
9161 if (new_win_xsize != WIN_XSIZE ||
9162 new_win_ysize != WIN_YSIZE)
9164 WIN_XSIZE = new_win_xsize;
9165 WIN_YSIZE = new_win_ysize;
9167 init_video_buffer = TRUE;
9168 init_gfx_buffers = TRUE;
9169 init_gadgets_and_anims = TRUE;
9171 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9174 if (new_scr_fieldx != SCR_FIELDX ||
9175 new_scr_fieldy != SCR_FIELDY)
9177 /* this always toggles between MAIN and GAME when using small tile size */
9179 SCR_FIELDX = new_scr_fieldx;
9180 SCR_FIELDY = new_scr_fieldy;
9182 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9193 new_sxsize != SXSIZE ||
9194 new_sysize != SYSIZE ||
9195 new_dxsize != DXSIZE ||
9196 new_dysize != DYSIZE ||
9197 new_vxsize != VXSIZE ||
9198 new_vysize != VYSIZE ||
9199 new_exsize != EXSIZE ||
9200 new_eysize != EYSIZE ||
9201 new_real_sx != REAL_SX ||
9202 new_real_sy != REAL_SY ||
9203 new_full_sxsize != FULL_SXSIZE ||
9204 new_full_sysize != FULL_SYSIZE ||
9205 new_tilesize_var != TILESIZE_VAR
9208 // ------------------------------------------------------------------------
9209 // determine next fading area for changed viewport definitions
9210 // ------------------------------------------------------------------------
9212 // start with current playfield area (default fading area)
9215 FADE_SXSIZE = FULL_SXSIZE;
9216 FADE_SYSIZE = FULL_SYSIZE;
9218 // add new playfield area if position or size has changed
9219 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9220 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9222 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9223 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9226 // add current and new door 1 area if position or size has changed
9227 if (new_dx != DX || new_dy != DY ||
9228 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9230 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9231 DX, DY, DXSIZE, DYSIZE);
9232 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9233 new_dx, new_dy, new_dxsize, new_dysize);
9236 // add current and new door 2 area if position or size has changed
9237 if (new_dx != VX || new_dy != VY ||
9238 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9240 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9241 VX, VY, VXSIZE, VYSIZE);
9242 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9243 new_vx, new_vy, new_vxsize, new_vysize);
9246 // ------------------------------------------------------------------------
9247 // handle changed tile size
9248 // ------------------------------------------------------------------------
9250 if (new_tilesize_var != TILESIZE_VAR)
9252 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9254 // changing tile size invalidates scroll values of engine snapshots
9255 FreeEngineSnapshotSingle();
9257 // changing tile size requires update of graphic mapping for EM engine
9258 init_em_graphics = TRUE;
9269 SXSIZE = new_sxsize;
9270 SYSIZE = new_sysize;
9271 DXSIZE = new_dxsize;
9272 DYSIZE = new_dysize;
9273 VXSIZE = new_vxsize;
9274 VYSIZE = new_vysize;
9275 EXSIZE = new_exsize;
9276 EYSIZE = new_eysize;
9277 REAL_SX = new_real_sx;
9278 REAL_SY = new_real_sy;
9279 FULL_SXSIZE = new_full_sxsize;
9280 FULL_SYSIZE = new_full_sysize;
9281 TILESIZE_VAR = new_tilesize_var;
9283 init_gfx_buffers = TRUE;
9284 init_gadgets_and_anims = TRUE;
9286 // printf("::: viewports: init_gfx_buffers\n");
9287 // printf("::: viewports: init_gadgets_and_anims\n");
9290 if (init_gfx_buffers)
9292 // printf("::: init_gfx_buffers\n");
9294 SCR_FIELDX = new_scr_fieldx_buffers;
9295 SCR_FIELDY = new_scr_fieldy_buffers;
9299 SCR_FIELDX = new_scr_fieldx;
9300 SCR_FIELDY = new_scr_fieldy;
9302 SetDrawDeactivationMask(REDRAW_NONE);
9303 SetDrawBackgroundMask(REDRAW_FIELD);
9306 if (init_video_buffer)
9308 // printf("::: init_video_buffer\n");
9310 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9311 InitImageTextures();
9314 if (init_gadgets_and_anims)
9316 // printf("::: init_gadgets_and_anims\n");
9319 InitGlobalAnimations();
9322 if (init_em_graphics)
9324 InitGraphicInfo_EM();