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(void);
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(void)
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(void)
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(void)
484 if (game.envelope_active)
487 DrawLevel(REDRAW_ALL);
491 void RedrawPlayfield(void)
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(void)
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 static void DrawFramesPerSecond(void)
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(void)
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);
793 void BackToFront(void)
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
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;
985 ClearAutoRepeatKeyEvents();
988 static void SetScreenStates_BeforeFadingIn(void)
990 // temporarily set screen mode for animations to screen after fading in
991 global.anim_status = global.anim_status_next;
993 // store backbuffer with all animations that will be started after fading in
994 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
995 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
997 // set screen mode for animations back to fading
998 global.anim_status = GAME_MODE_PSEUDO_FADING;
1001 static void SetScreenStates_AfterFadingIn(void)
1003 // store new source screen (to use correct masked border for fading)
1004 gfx.fade_border_source_status = global.border_status;
1006 global.anim_status = global.anim_status_next;
1009 static void SetScreenStates_BeforeFadingOut(void)
1011 // store new target screen (to use correct masked border for fading)
1012 gfx.fade_border_target_status = game_status;
1014 // set screen mode for animations to fading
1015 global.anim_status = GAME_MODE_PSEUDO_FADING;
1017 // store backbuffer with all animations that will be stopped for fading out
1018 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1019 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1022 static void SetScreenStates_AfterFadingOut(void)
1024 global.border_status = game_status;
1027 void FadeIn(int fade_mask)
1029 SetScreenStates_BeforeFadingIn();
1032 DrawMaskedBorder(REDRAW_ALL);
1035 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1036 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1038 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1042 FADE_SXSIZE = FULL_SXSIZE;
1043 FADE_SYSIZE = FULL_SYSIZE;
1045 // activate virtual buttons depending on upcoming game status
1046 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1047 game_status == GAME_MODE_PLAYING && !tape.playing)
1048 SetOverlayActive(TRUE);
1050 SetScreenStates_AfterFadingIn();
1052 // force update of global animation status in case of rapid screen changes
1053 redraw_mask = REDRAW_ALL;
1057 void FadeOut(int fade_mask)
1059 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1060 if (!equalRedrawMasks(fade_mask, redraw_mask))
1063 SetScreenStates_BeforeFadingOut();
1065 SetTileCursorActive(FALSE);
1066 SetOverlayActive(FALSE);
1069 DrawMaskedBorder(REDRAW_ALL);
1072 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1073 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1075 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1077 SetScreenStates_AfterFadingOut();
1080 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1082 static struct TitleFadingInfo fading_leave_stored;
1085 fading_leave_stored = fading_leave;
1087 fading = fading_leave_stored;
1090 void FadeSetEnterMenu(void)
1092 fading = menu.enter_menu;
1094 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1097 void FadeSetLeaveMenu(void)
1099 fading = menu.leave_menu;
1101 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1104 void FadeSetEnterScreen(void)
1106 fading = menu.enter_screen[game_status];
1108 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1111 void FadeSetNextScreen(void)
1113 fading = menu.next_screen[game_status];
1115 // (do not overwrite fade mode set by FadeSetEnterScreen)
1116 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1119 void FadeSetLeaveScreen(void)
1121 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1124 void FadeSetFromType(int type)
1126 if (type & TYPE_ENTER_SCREEN)
1127 FadeSetEnterScreen();
1128 else if (type & TYPE_ENTER)
1130 else if (type & TYPE_LEAVE)
1134 void FadeSetDisabled(void)
1136 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1138 fading = fading_none;
1141 void FadeSkipNextFadeIn(void)
1143 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1146 void FadeSkipNextFadeOut(void)
1148 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1151 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1153 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1155 return (graphic == IMG_UNDEFINED ? NULL :
1156 graphic_info[graphic].bitmap != NULL || redefined ?
1157 graphic_info[graphic].bitmap :
1158 graphic_info[default_graphic].bitmap);
1161 static Bitmap *getBackgroundBitmap(int graphic)
1163 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1166 static Bitmap *getGlobalBorderBitmap(int graphic)
1168 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1171 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1174 (status == GAME_MODE_MAIN ||
1175 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1176 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1177 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1178 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1181 return getGlobalBorderBitmap(graphic);
1184 void SetWindowBackgroundImageIfDefined(int graphic)
1186 if (graphic_info[graphic].bitmap)
1187 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1190 void SetMainBackgroundImageIfDefined(int graphic)
1192 if (graphic_info[graphic].bitmap)
1193 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1196 void SetDoorBackgroundImageIfDefined(int graphic)
1198 if (graphic_info[graphic].bitmap)
1199 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1202 void SetWindowBackgroundImage(int graphic)
1204 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1207 void SetMainBackgroundImage(int graphic)
1209 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1212 void SetDoorBackgroundImage(int graphic)
1214 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1217 void SetPanelBackground(void)
1219 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1221 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1222 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1224 SetDoorBackgroundBitmap(bitmap_db_panel);
1227 void DrawBackground(int x, int y, int width, int height)
1229 // "drawto" might still point to playfield buffer here (hall of fame)
1230 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1232 if (IN_GFX_FIELD_FULL(x, y))
1233 redraw_mask |= REDRAW_FIELD;
1234 else if (IN_GFX_DOOR_1(x, y))
1235 redraw_mask |= REDRAW_DOOR_1;
1236 else if (IN_GFX_DOOR_2(x, y))
1237 redraw_mask |= REDRAW_DOOR_2;
1238 else if (IN_GFX_DOOR_3(x, y))
1239 redraw_mask |= REDRAW_DOOR_3;
1242 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1244 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1246 if (font->bitmap == NULL)
1249 DrawBackground(x, y, width, height);
1252 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1254 struct GraphicInfo *g = &graphic_info[graphic];
1256 if (g->bitmap == NULL)
1259 DrawBackground(x, y, width, height);
1262 static int game_status_last = -1;
1263 static Bitmap *global_border_bitmap_last = NULL;
1264 static Bitmap *global_border_bitmap = NULL;
1265 static int real_sx_last = -1, real_sy_last = -1;
1266 static int full_sxsize_last = -1, full_sysize_last = -1;
1267 static int dx_last = -1, dy_last = -1;
1268 static int dxsize_last = -1, dysize_last = -1;
1269 static int vx_last = -1, vy_last = -1;
1270 static int vxsize_last = -1, vysize_last = -1;
1271 static int ex_last = -1, ey_last = -1;
1272 static int exsize_last = -1, eysize_last = -1;
1274 boolean CheckIfGlobalBorderHasChanged(void)
1276 // if game status has not changed, global border has not changed either
1277 if (game_status == game_status_last)
1280 // determine and store new global border bitmap for current game status
1281 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1283 return (global_border_bitmap_last != global_border_bitmap);
1286 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1288 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1289 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1291 // if game status has not changed, nothing has to be redrawn
1292 if (game_status == game_status_last)
1295 // redraw if last screen was title screen
1296 if (game_status_last == GAME_MODE_TITLE)
1299 // redraw if global screen border has changed
1300 if (CheckIfGlobalBorderHasChanged())
1303 // redraw if position or size of playfield area has changed
1304 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1305 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1308 // redraw if position or size of door area has changed
1309 if (dx_last != DX || dy_last != DY ||
1310 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1313 // redraw if position or size of tape area has changed
1314 if (vx_last != VX || vy_last != VY ||
1315 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1318 // redraw if position or size of editor area has changed
1319 if (ex_last != EX || ey_last != EY ||
1320 exsize_last != EXSIZE || eysize_last != EYSIZE)
1327 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1330 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1332 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1335 void RedrawGlobalBorder(void)
1337 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1339 RedrawGlobalBorderFromBitmap(bitmap);
1341 redraw_mask = REDRAW_ALL;
1344 static void RedrawGlobalBorderIfNeeded(void)
1346 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1347 if (game_status == game_status_last)
1351 // copy current draw buffer to later copy back areas that have not changed
1352 if (game_status_last != GAME_MODE_TITLE)
1353 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1355 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1356 if (CheckIfGlobalBorderRedrawIsNeeded())
1359 // redraw global screen border (or clear, if defined to be empty)
1360 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1362 if (game_status == GAME_MODE_EDITOR)
1363 DrawSpecialEditorDoor();
1365 // copy previous playfield and door areas, if they are defined on both
1366 // previous and current screen and if they still have the same size
1368 if (real_sx_last != -1 && real_sy_last != -1 &&
1369 REAL_SX != -1 && REAL_SY != -1 &&
1370 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1371 BlitBitmap(bitmap_db_store_1, backbuffer,
1372 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1375 if (dx_last != -1 && dy_last != -1 &&
1376 DX != -1 && DY != -1 &&
1377 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1378 BlitBitmap(bitmap_db_store_1, backbuffer,
1379 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1381 if (game_status != GAME_MODE_EDITOR)
1383 if (vx_last != -1 && vy_last != -1 &&
1384 VX != -1 && VY != -1 &&
1385 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1386 BlitBitmap(bitmap_db_store_1, backbuffer,
1387 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1391 if (ex_last != -1 && ey_last != -1 &&
1392 EX != -1 && EY != -1 &&
1393 exsize_last == EXSIZE && eysize_last == EYSIZE)
1394 BlitBitmap(bitmap_db_store_1, backbuffer,
1395 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1398 redraw_mask = REDRAW_ALL;
1401 game_status_last = game_status;
1403 global_border_bitmap_last = global_border_bitmap;
1405 real_sx_last = REAL_SX;
1406 real_sy_last = REAL_SY;
1407 full_sxsize_last = FULL_SXSIZE;
1408 full_sysize_last = FULL_SYSIZE;
1411 dxsize_last = DXSIZE;
1412 dysize_last = DYSIZE;
1415 vxsize_last = VXSIZE;
1416 vysize_last = VYSIZE;
1419 exsize_last = EXSIZE;
1420 eysize_last = EYSIZE;
1423 void ClearField(void)
1425 RedrawGlobalBorderIfNeeded();
1427 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1428 // (when entering hall of fame after playing)
1429 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1431 // !!! maybe this should be done before clearing the background !!!
1432 if (game_status == GAME_MODE_PLAYING)
1434 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1435 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1439 SetDrawtoField(DRAW_TO_BACKBUFFER);
1443 void MarkTileDirty(int x, int y)
1445 redraw_mask |= REDRAW_FIELD;
1448 void SetBorderElement(void)
1452 BorderElement = EL_EMPTY;
1454 // the MM game engine does not use a visible border element
1455 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1458 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1460 for (x = 0; x < lev_fieldx; x++)
1462 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1463 BorderElement = EL_STEELWALL;
1465 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1471 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1472 int max_array_fieldx, int max_array_fieldy,
1473 short field[max_array_fieldx][max_array_fieldy],
1474 int max_fieldx, int max_fieldy)
1478 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1479 static int safety = 0;
1481 // check if starting field still has the desired content
1482 if (field[from_x][from_y] == fill_element)
1487 if (safety > max_fieldx * max_fieldy)
1488 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1490 old_element = field[from_x][from_y];
1491 field[from_x][from_y] = fill_element;
1493 for (i = 0; i < 4; i++)
1495 x = from_x + check[i][0];
1496 y = from_y + check[i][1];
1498 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1499 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1500 field, max_fieldx, max_fieldy);
1506 void FloodFillLevel(int from_x, int from_y, int fill_element,
1507 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1508 int max_fieldx, int max_fieldy)
1510 FloodFillLevelExt(from_x, from_y, fill_element,
1511 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1512 max_fieldx, max_fieldy);
1515 void SetRandomAnimationValue(int x, int y)
1517 gfx.anim_random_frame = GfxRandom[x][y];
1520 int getGraphicAnimationFrame(int graphic, int sync_frame)
1522 // animation synchronized with global frame counter, not move position
1523 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1524 sync_frame = FrameCounter;
1526 return getAnimationFrame(graphic_info[graphic].anim_frames,
1527 graphic_info[graphic].anim_delay,
1528 graphic_info[graphic].anim_mode,
1529 graphic_info[graphic].anim_start_frame,
1533 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1535 struct GraphicInfo *g = &graphic_info[graphic];
1536 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1538 if (tilesize == gfx.standard_tile_size)
1539 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1540 else if (tilesize == game.tile_size)
1541 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1543 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1546 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1547 boolean get_backside)
1549 struct GraphicInfo *g = &graphic_info[graphic];
1550 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1551 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1553 if (g->offset_y == 0) // frames are ordered horizontally
1555 int max_width = g->anim_frames_per_line * g->width;
1556 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1558 *x = pos % max_width;
1559 *y = src_y % g->height + pos / max_width * g->height;
1561 else if (g->offset_x == 0) // frames are ordered vertically
1563 int max_height = g->anim_frames_per_line * g->height;
1564 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1566 *x = src_x % g->width + pos / max_height * g->width;
1567 *y = pos % max_height;
1569 else // frames are ordered diagonally
1571 *x = src_x + frame * g->offset_x;
1572 *y = src_y + frame * g->offset_y;
1576 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1577 Bitmap **bitmap, int *x, int *y,
1578 boolean get_backside)
1580 struct GraphicInfo *g = &graphic_info[graphic];
1582 // if no graphics defined at all, use fallback graphics
1583 if (g->bitmaps == NULL)
1584 *g = graphic_info[IMG_CHAR_EXCLAM];
1586 // if no in-game graphics defined, always use standard graphic size
1587 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1588 tilesize = TILESIZE;
1590 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1591 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1593 *x = *x * tilesize / g->tile_size;
1594 *y = *y * tilesize / g->tile_size;
1597 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1598 Bitmap **bitmap, int *x, int *y)
1600 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1603 void getFixedGraphicSource(int graphic, int frame,
1604 Bitmap **bitmap, int *x, int *y)
1606 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1609 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1611 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1614 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1615 int *x, int *y, boolean get_backside)
1617 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1621 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1623 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1626 void DrawGraphic(int x, int y, int graphic, int frame)
1629 if (!IN_SCR_FIELD(x, y))
1631 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1632 printf("DrawGraphic(): This should never happen!\n");
1637 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1640 MarkTileDirty(x, y);
1643 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1646 if (!IN_SCR_FIELD(x, y))
1648 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1649 printf("DrawGraphic(): This should never happen!\n");
1654 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1656 MarkTileDirty(x, y);
1659 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1665 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1667 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1670 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1676 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1677 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1680 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1683 if (!IN_SCR_FIELD(x, y))
1685 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1686 printf("DrawGraphicThruMask(): This should never happen!\n");
1691 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1694 MarkTileDirty(x, y);
1697 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1700 if (!IN_SCR_FIELD(x, y))
1702 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1703 printf("DrawGraphicThruMask(): This should never happen!\n");
1708 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1710 MarkTileDirty(x, y);
1713 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1719 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1721 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1725 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1726 int graphic, int frame)
1731 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1733 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1737 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1739 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1741 MarkTileDirty(x / tilesize, y / tilesize);
1744 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1747 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1748 graphic, frame, tilesize);
1749 MarkTileDirty(x / tilesize, y / tilesize);
1752 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1758 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1759 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1762 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1763 int frame, int tilesize)
1768 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1769 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1772 void DrawMiniGraphic(int x, int y, int graphic)
1774 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1775 MarkTileDirty(x / 2, y / 2);
1778 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1783 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1784 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1787 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1788 int graphic, int frame,
1789 int cut_mode, int mask_mode)
1794 int width = TILEX, height = TILEY;
1797 if (dx || dy) // shifted graphic
1799 if (x < BX1) // object enters playfield from the left
1806 else if (x > BX2) // object enters playfield from the right
1812 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1818 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1820 else if (dx) // general horizontal movement
1821 MarkTileDirty(x + SIGN(dx), y);
1823 if (y < BY1) // object enters playfield from the top
1825 if (cut_mode == CUT_BELOW) // object completely above top border
1833 else if (y > BY2) // object enters playfield from the bottom
1839 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1845 else if (dy > 0 && cut_mode == CUT_ABOVE)
1847 if (y == BY2) // object completely above bottom border
1853 MarkTileDirty(x, y + 1);
1854 } // object leaves playfield to the bottom
1855 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1857 else if (dy) // general vertical movement
1858 MarkTileDirty(x, y + SIGN(dy));
1862 if (!IN_SCR_FIELD(x, y))
1864 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1865 printf("DrawGraphicShifted(): This should never happen!\n");
1870 width = width * TILESIZE_VAR / TILESIZE;
1871 height = height * TILESIZE_VAR / TILESIZE;
1872 cx = cx * TILESIZE_VAR / TILESIZE;
1873 cy = cy * TILESIZE_VAR / TILESIZE;
1874 dx = dx * TILESIZE_VAR / TILESIZE;
1875 dy = dy * TILESIZE_VAR / TILESIZE;
1877 if (width > 0 && height > 0)
1879 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1884 dst_x = FX + x * TILEX_VAR + dx;
1885 dst_y = FY + y * TILEY_VAR + dy;
1887 if (mask_mode == USE_MASKING)
1888 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1891 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1894 MarkTileDirty(x, y);
1898 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1899 int graphic, int frame,
1900 int cut_mode, int mask_mode)
1905 int width = TILEX_VAR, height = TILEY_VAR;
1908 int x2 = x + SIGN(dx);
1909 int y2 = y + SIGN(dy);
1911 // movement with two-tile animations must be sync'ed with movement position,
1912 // not with current GfxFrame (which can be higher when using slow movement)
1913 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1914 int anim_frames = graphic_info[graphic].anim_frames;
1916 // (we also need anim_delay here for movement animations with less frames)
1917 int anim_delay = graphic_info[graphic].anim_delay;
1918 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1920 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1921 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1923 // re-calculate animation frame for two-tile movement animation
1924 frame = getGraphicAnimationFrame(graphic, sync_frame);
1926 // check if movement start graphic inside screen area and should be drawn
1927 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1929 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1931 dst_x = FX + x1 * TILEX_VAR;
1932 dst_y = FY + y1 * TILEY_VAR;
1934 if (mask_mode == USE_MASKING)
1935 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1938 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1941 MarkTileDirty(x1, y1);
1944 // check if movement end graphic inside screen area and should be drawn
1945 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1947 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1949 dst_x = FX + x2 * TILEX_VAR;
1950 dst_y = FY + y2 * TILEY_VAR;
1952 if (mask_mode == USE_MASKING)
1953 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1956 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1959 MarkTileDirty(x2, y2);
1963 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1964 int graphic, int frame,
1965 int cut_mode, int mask_mode)
1969 DrawGraphic(x, y, graphic, frame);
1974 if (graphic_info[graphic].double_movement) // EM style movement images
1975 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1977 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1980 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1981 int graphic, int frame, int cut_mode)
1983 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1986 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1987 int cut_mode, int mask_mode)
1989 int lx = LEVELX(x), ly = LEVELY(y);
1993 if (IN_LEV_FIELD(lx, ly))
1995 SetRandomAnimationValue(lx, ly);
1997 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1998 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2000 // do not use double (EM style) movement graphic when not moving
2001 if (graphic_info[graphic].double_movement && !dx && !dy)
2003 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2004 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2007 else // border element
2009 graphic = el2img(element);
2010 frame = getGraphicAnimationFrame(graphic, -1);
2013 if (element == EL_EXPANDABLE_WALL)
2015 boolean left_stopped = FALSE, right_stopped = FALSE;
2017 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2018 left_stopped = TRUE;
2019 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2020 right_stopped = TRUE;
2022 if (left_stopped && right_stopped)
2024 else if (left_stopped)
2026 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2027 frame = graphic_info[graphic].anim_frames - 1;
2029 else if (right_stopped)
2031 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2032 frame = graphic_info[graphic].anim_frames - 1;
2037 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2038 else if (mask_mode == USE_MASKING)
2039 DrawGraphicThruMask(x, y, graphic, frame);
2041 DrawGraphic(x, y, graphic, frame);
2044 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2045 int cut_mode, int mask_mode)
2047 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2048 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2049 cut_mode, mask_mode);
2052 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2055 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2058 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2061 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2064 void DrawLevelElementThruMask(int x, int y, int element)
2066 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2069 void DrawLevelFieldThruMask(int x, int y)
2071 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2074 // !!! implementation of quicksand is totally broken !!!
2075 #define IS_CRUMBLED_TILE(x, y, e) \
2076 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2077 !IS_MOVING(x, y) || \
2078 (e) == EL_QUICKSAND_EMPTYING || \
2079 (e) == EL_QUICKSAND_FAST_EMPTYING))
2081 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2086 int width, height, cx, cy;
2087 int sx = SCREENX(x), sy = SCREENY(y);
2088 int crumbled_border_size = graphic_info[graphic].border_size;
2089 int crumbled_tile_size = graphic_info[graphic].tile_size;
2090 int crumbled_border_size_var =
2091 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2094 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2096 for (i = 1; i < 4; i++)
2098 int dxx = (i & 1 ? dx : 0);
2099 int dyy = (i & 2 ? dy : 0);
2102 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2105 // check if neighbour field is of same crumble type
2106 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2107 graphic_info[graphic].class ==
2108 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2110 // return if check prevents inner corner
2111 if (same == (dxx == dx && dyy == dy))
2115 // if we reach this point, we have an inner corner
2117 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2119 width = crumbled_border_size_var;
2120 height = crumbled_border_size_var;
2121 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2122 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2124 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2125 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2128 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2133 int width, height, bx, by, cx, cy;
2134 int sx = SCREENX(x), sy = SCREENY(y);
2135 int crumbled_border_size = graphic_info[graphic].border_size;
2136 int crumbled_tile_size = graphic_info[graphic].tile_size;
2137 int crumbled_border_size_var =
2138 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2139 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2142 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2144 // draw simple, sloppy, non-corner-accurate crumbled border
2146 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2147 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2148 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2149 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2151 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2152 FX + sx * TILEX_VAR + cx,
2153 FY + sy * TILEY_VAR + cy);
2155 // (remaining middle border part must be at least as big as corner part)
2156 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2157 crumbled_border_size_var >= TILESIZE_VAR / 3)
2160 // correct corners of crumbled border, if needed
2162 for (i = -1; i <= 1; i += 2)
2164 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2165 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2166 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2169 // check if neighbour field is of same crumble type
2170 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2171 graphic_info[graphic].class ==
2172 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2174 // no crumbled corner, but continued crumbled border
2176 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2177 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2178 int b1 = (i == 1 ? crumbled_border_size_var :
2179 TILESIZE_VAR - 2 * crumbled_border_size_var);
2181 width = crumbled_border_size_var;
2182 height = crumbled_border_size_var;
2184 if (dir == 1 || dir == 2)
2199 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2201 FX + sx * TILEX_VAR + cx,
2202 FY + sy * TILEY_VAR + cy);
2207 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2209 int sx = SCREENX(x), sy = SCREENY(y);
2212 static int xy[4][2] =
2220 if (!IN_LEV_FIELD(x, y))
2223 element = TILE_GFX_ELEMENT(x, y);
2225 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2227 if (!IN_SCR_FIELD(sx, sy))
2230 // crumble field borders towards direct neighbour fields
2231 for (i = 0; i < 4; i++)
2233 int xx = x + xy[i][0];
2234 int yy = y + xy[i][1];
2236 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2239 // check if neighbour field is of same crumble type
2240 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2241 graphic_info[graphic].class ==
2242 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2245 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2248 // crumble inner field corners towards corner neighbour fields
2249 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2250 graphic_info[graphic].anim_frames == 2)
2252 for (i = 0; i < 4; i++)
2254 int dx = (i & 1 ? +1 : -1);
2255 int dy = (i & 2 ? +1 : -1);
2257 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2261 MarkTileDirty(sx, sy);
2263 else // center field is not crumbled -- crumble neighbour fields
2265 // crumble field borders of direct neighbour fields
2266 for (i = 0; i < 4; i++)
2268 int xx = x + xy[i][0];
2269 int yy = y + xy[i][1];
2270 int sxx = sx + xy[i][0];
2271 int syy = sy + xy[i][1];
2273 if (!IN_LEV_FIELD(xx, yy) ||
2274 !IN_SCR_FIELD(sxx, syy))
2277 // do not crumble fields that are being digged or snapped
2278 if (Feld[xx][yy] == EL_EMPTY ||
2279 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2282 element = TILE_GFX_ELEMENT(xx, yy);
2284 if (!IS_CRUMBLED_TILE(xx, yy, element))
2287 graphic = el_act2crm(element, ACTION_DEFAULT);
2289 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2291 MarkTileDirty(sxx, syy);
2294 // crumble inner field corners of corner neighbour fields
2295 for (i = 0; i < 4; i++)
2297 int dx = (i & 1 ? +1 : -1);
2298 int dy = (i & 2 ? +1 : -1);
2304 if (!IN_LEV_FIELD(xx, yy) ||
2305 !IN_SCR_FIELD(sxx, syy))
2308 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2311 element = TILE_GFX_ELEMENT(xx, yy);
2313 if (!IS_CRUMBLED_TILE(xx, yy, element))
2316 graphic = el_act2crm(element, ACTION_DEFAULT);
2318 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2319 graphic_info[graphic].anim_frames == 2)
2320 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2322 MarkTileDirty(sxx, syy);
2327 void DrawLevelFieldCrumbled(int x, int y)
2331 if (!IN_LEV_FIELD(x, y))
2334 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2335 GfxElement[x][y] != EL_UNDEFINED &&
2336 GFX_CRUMBLED(GfxElement[x][y]))
2338 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2343 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2345 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2348 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2351 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2352 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2353 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2354 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2355 int sx = SCREENX(x), sy = SCREENY(y);
2357 DrawGraphic(sx, sy, graphic1, frame1);
2358 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2361 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2363 int sx = SCREENX(x), sy = SCREENY(y);
2364 static int xy[4][2] =
2373 // crumble direct neighbour fields (required for field borders)
2374 for (i = 0; i < 4; i++)
2376 int xx = x + xy[i][0];
2377 int yy = y + xy[i][1];
2378 int sxx = sx + xy[i][0];
2379 int syy = sy + xy[i][1];
2381 if (!IN_LEV_FIELD(xx, yy) ||
2382 !IN_SCR_FIELD(sxx, syy) ||
2383 !GFX_CRUMBLED(Feld[xx][yy]) ||
2387 DrawLevelField(xx, yy);
2390 // crumble corner neighbour fields (required for inner field corners)
2391 for (i = 0; i < 4; i++)
2393 int dx = (i & 1 ? +1 : -1);
2394 int dy = (i & 2 ? +1 : -1);
2400 if (!IN_LEV_FIELD(xx, yy) ||
2401 !IN_SCR_FIELD(sxx, syy) ||
2402 !GFX_CRUMBLED(Feld[xx][yy]) ||
2406 int element = TILE_GFX_ELEMENT(xx, yy);
2407 int graphic = el_act2crm(element, ACTION_DEFAULT);
2409 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2410 graphic_info[graphic].anim_frames == 2)
2411 DrawLevelField(xx, yy);
2415 static int getBorderElement(int x, int y)
2419 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2420 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2421 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2422 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2423 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2424 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2425 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2427 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2428 int steel_position = (x == -1 && y == -1 ? 0 :
2429 x == lev_fieldx && y == -1 ? 1 :
2430 x == -1 && y == lev_fieldy ? 2 :
2431 x == lev_fieldx && y == lev_fieldy ? 3 :
2432 x == -1 || x == lev_fieldx ? 4 :
2433 y == -1 || y == lev_fieldy ? 5 : 6);
2435 return border[steel_position][steel_type];
2438 void DrawScreenElement(int x, int y, int element)
2440 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2441 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2444 void DrawLevelElement(int x, int y, int element)
2446 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2447 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2450 void DrawScreenField(int x, int y)
2452 int lx = LEVELX(x), ly = LEVELY(y);
2453 int element, content;
2455 if (!IN_LEV_FIELD(lx, ly))
2457 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2460 element = getBorderElement(lx, ly);
2462 DrawScreenElement(x, y, element);
2467 element = Feld[lx][ly];
2468 content = Store[lx][ly];
2470 if (IS_MOVING(lx, ly))
2472 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2473 boolean cut_mode = NO_CUTTING;
2475 if (element == EL_QUICKSAND_EMPTYING ||
2476 element == EL_QUICKSAND_FAST_EMPTYING ||
2477 element == EL_MAGIC_WALL_EMPTYING ||
2478 element == EL_BD_MAGIC_WALL_EMPTYING ||
2479 element == EL_DC_MAGIC_WALL_EMPTYING ||
2480 element == EL_AMOEBA_DROPPING)
2481 cut_mode = CUT_ABOVE;
2482 else if (element == EL_QUICKSAND_FILLING ||
2483 element == EL_QUICKSAND_FAST_FILLING ||
2484 element == EL_MAGIC_WALL_FILLING ||
2485 element == EL_BD_MAGIC_WALL_FILLING ||
2486 element == EL_DC_MAGIC_WALL_FILLING)
2487 cut_mode = CUT_BELOW;
2489 if (cut_mode == CUT_ABOVE)
2490 DrawScreenElement(x, y, element);
2492 DrawScreenElement(x, y, EL_EMPTY);
2495 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2496 else if (cut_mode == NO_CUTTING)
2497 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2500 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2502 if (cut_mode == CUT_BELOW &&
2503 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2504 DrawLevelElement(lx, ly + 1, element);
2507 if (content == EL_ACID)
2509 int dir = MovDir[lx][ly];
2510 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2511 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2513 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2515 // prevent target field from being drawn again (but without masking)
2516 // (this would happen if target field is scanned after moving element)
2517 Stop[newlx][newly] = TRUE;
2520 else if (IS_BLOCKED(lx, ly))
2525 boolean cut_mode = NO_CUTTING;
2526 int element_old, content_old;
2528 Blocked2Moving(lx, ly, &oldx, &oldy);
2531 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2532 MovDir[oldx][oldy] == MV_RIGHT);
2534 element_old = Feld[oldx][oldy];
2535 content_old = Store[oldx][oldy];
2537 if (element_old == EL_QUICKSAND_EMPTYING ||
2538 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2539 element_old == EL_MAGIC_WALL_EMPTYING ||
2540 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2541 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2542 element_old == EL_AMOEBA_DROPPING)
2543 cut_mode = CUT_ABOVE;
2545 DrawScreenElement(x, y, EL_EMPTY);
2548 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2550 else if (cut_mode == NO_CUTTING)
2551 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2554 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2557 else if (IS_DRAWABLE(element))
2558 DrawScreenElement(x, y, element);
2560 DrawScreenElement(x, y, EL_EMPTY);
2563 void DrawLevelField(int x, int y)
2565 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2566 DrawScreenField(SCREENX(x), SCREENY(y));
2567 else if (IS_MOVING(x, y))
2571 Moving2Blocked(x, y, &newx, &newy);
2572 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2573 DrawScreenField(SCREENX(newx), SCREENY(newy));
2575 else if (IS_BLOCKED(x, y))
2579 Blocked2Moving(x, y, &oldx, &oldy);
2580 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2581 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2585 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2586 int (*el2img_function)(int), boolean masked,
2587 int element_bits_draw)
2589 int element_base = map_mm_wall_element(element);
2590 int element_bits = (IS_DF_WALL(element) ?
2591 element - EL_DF_WALL_START :
2592 IS_MM_WALL(element) ?
2593 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2594 int graphic = el2img_function(element_base);
2595 int tilesize_draw = tilesize / 2;
2600 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2602 for (i = 0; i < 4; i++)
2604 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2605 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2607 if (!(element_bits_draw & (1 << i)))
2610 if (element_bits & (1 << i))
2613 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2614 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2616 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2617 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2622 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2623 tilesize_draw, tilesize_draw);
2628 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2629 boolean masked, int element_bits_draw)
2631 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2632 element, tilesize, el2edimg, masked, element_bits_draw);
2635 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2636 int (*el2img_function)(int))
2638 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2642 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2645 if (IS_MM_WALL(element))
2647 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2648 element, tilesize, el2edimg, masked, 0x000f);
2652 int graphic = el2edimg(element);
2655 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2657 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2661 void DrawSizedElement(int x, int y, int element, int tilesize)
2663 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2666 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2668 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2671 void DrawMiniElement(int x, int y, int element)
2675 graphic = el2edimg(element);
2676 DrawMiniGraphic(x, y, graphic);
2679 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2682 int x = sx + scroll_x, y = sy + scroll_y;
2684 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2685 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2686 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2687 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2689 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2692 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2694 int x = sx + scroll_x, y = sy + scroll_y;
2696 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2697 DrawMiniElement(sx, sy, EL_EMPTY);
2698 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2699 DrawMiniElement(sx, sy, Feld[x][y]);
2701 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2704 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2705 int x, int y, int xsize, int ysize,
2706 int tile_width, int tile_height)
2710 int dst_x = startx + x * tile_width;
2711 int dst_y = starty + y * tile_height;
2712 int width = graphic_info[graphic].width;
2713 int height = graphic_info[graphic].height;
2714 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2715 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2716 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2717 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2718 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2719 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2720 boolean draw_masked = graphic_info[graphic].draw_masked;
2722 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2724 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2726 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2730 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2731 inner_sx + (x - 1) * tile_width % inner_width);
2732 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2733 inner_sy + (y - 1) * tile_height % inner_height);
2736 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2739 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2743 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2744 int x, int y, int xsize, int ysize,
2747 int font_width = getFontWidth(font_nr);
2748 int font_height = getFontHeight(font_nr);
2750 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2751 font_width, font_height);
2754 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2756 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2757 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2758 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2759 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2760 boolean no_delay = (tape.warp_forward);
2761 unsigned int anim_delay = 0;
2762 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2763 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2764 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2765 int font_width = getFontWidth(font_nr);
2766 int font_height = getFontHeight(font_nr);
2767 int max_xsize = level.envelope[envelope_nr].xsize;
2768 int max_ysize = level.envelope[envelope_nr].ysize;
2769 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2770 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2771 int xend = max_xsize;
2772 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2773 int xstep = (xstart < xend ? 1 : 0);
2774 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2776 int end = MAX(xend - xstart, yend - ystart);
2779 for (i = start; i <= end; i++)
2781 int last_frame = end; // last frame of this "for" loop
2782 int x = xstart + i * xstep;
2783 int y = ystart + i * ystep;
2784 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2785 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2786 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2787 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2790 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2792 BlitScreenToBitmap(backbuffer);
2794 SetDrawtoField(DRAW_TO_BACKBUFFER);
2796 for (yy = 0; yy < ysize; yy++)
2797 for (xx = 0; xx < xsize; xx++)
2798 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2800 DrawTextBuffer(sx + font_width, sy + font_height,
2801 level.envelope[envelope_nr].text, font_nr, max_xsize,
2802 xsize - 2, ysize - 2, 0, mask_mode,
2803 level.envelope[envelope_nr].autowrap,
2804 level.envelope[envelope_nr].centered, FALSE);
2806 redraw_mask |= REDRAW_FIELD;
2809 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2812 ClearAutoRepeatKeyEvents();
2815 void ShowEnvelope(int envelope_nr)
2817 int element = EL_ENVELOPE_1 + envelope_nr;
2818 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2819 int sound_opening = element_info[element].sound[ACTION_OPENING];
2820 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2821 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2822 boolean no_delay = (tape.warp_forward);
2823 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2824 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2825 int anim_mode = graphic_info[graphic].anim_mode;
2826 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2827 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2829 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2831 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2833 if (anim_mode == ANIM_DEFAULT)
2834 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2836 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2839 Delay(wait_delay_value);
2841 WaitForEventToContinue();
2843 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2845 if (anim_mode != ANIM_NONE)
2846 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2848 if (anim_mode == ANIM_DEFAULT)
2849 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2851 game.envelope_active = FALSE;
2853 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2855 redraw_mask |= REDRAW_FIELD;
2859 static void setRequestBasePosition(int *x, int *y)
2861 int sx_base, sy_base;
2863 if (request.x != -1)
2864 sx_base = request.x;
2865 else if (request.align == ALIGN_LEFT)
2867 else if (request.align == ALIGN_RIGHT)
2868 sx_base = SX + SXSIZE;
2870 sx_base = SX + SXSIZE / 2;
2872 if (request.y != -1)
2873 sy_base = request.y;
2874 else if (request.valign == VALIGN_TOP)
2876 else if (request.valign == VALIGN_BOTTOM)
2877 sy_base = SY + SYSIZE;
2879 sy_base = SY + SYSIZE / 2;
2885 static void setRequestPositionExt(int *x, int *y, int width, int height,
2886 boolean add_border_size)
2888 int border_size = request.border_size;
2889 int sx_base, sy_base;
2892 setRequestBasePosition(&sx_base, &sy_base);
2894 if (request.align == ALIGN_LEFT)
2896 else if (request.align == ALIGN_RIGHT)
2897 sx = sx_base - width;
2899 sx = sx_base - width / 2;
2901 if (request.valign == VALIGN_TOP)
2903 else if (request.valign == VALIGN_BOTTOM)
2904 sy = sy_base - height;
2906 sy = sy_base - height / 2;
2908 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2909 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2911 if (add_border_size)
2921 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2923 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2926 static void DrawEnvelopeRequest(char *text)
2928 char *text_final = text;
2929 char *text_door_style = NULL;
2930 int graphic = IMG_BACKGROUND_REQUEST;
2931 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2932 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2933 int font_nr = FONT_REQUEST;
2934 int font_width = getFontWidth(font_nr);
2935 int font_height = getFontHeight(font_nr);
2936 int border_size = request.border_size;
2937 int line_spacing = request.line_spacing;
2938 int line_height = font_height + line_spacing;
2939 int max_text_width = request.width - 2 * border_size;
2940 int max_text_height = request.height - 2 * border_size;
2941 int line_length = max_text_width / font_width;
2942 int max_lines = max_text_height / line_height;
2943 int text_width = line_length * font_width;
2944 int width = request.width;
2945 int height = request.height;
2946 int tile_size = MAX(request.step_offset, 1);
2947 int x_steps = width / tile_size;
2948 int y_steps = height / tile_size;
2949 int sx_offset = border_size;
2950 int sy_offset = border_size;
2954 if (request.centered)
2955 sx_offset = (request.width - text_width) / 2;
2957 if (request.wrap_single_words && !request.autowrap)
2959 char *src_text_ptr, *dst_text_ptr;
2961 text_door_style = checked_malloc(2 * strlen(text) + 1);
2963 src_text_ptr = text;
2964 dst_text_ptr = text_door_style;
2966 while (*src_text_ptr)
2968 if (*src_text_ptr == ' ' ||
2969 *src_text_ptr == '?' ||
2970 *src_text_ptr == '!')
2971 *dst_text_ptr++ = '\n';
2973 if (*src_text_ptr != ' ')
2974 *dst_text_ptr++ = *src_text_ptr;
2979 *dst_text_ptr = '\0';
2981 text_final = text_door_style;
2984 setRequestPosition(&sx, &sy, FALSE);
2986 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2988 for (y = 0; y < y_steps; y++)
2989 for (x = 0; x < x_steps; x++)
2990 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2991 x, y, x_steps, y_steps,
2992 tile_size, tile_size);
2994 // force DOOR font inside door area
2995 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2997 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2998 line_length, -1, max_lines, line_spacing, mask_mode,
2999 request.autowrap, request.centered, FALSE);
3003 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3004 RedrawGadget(tool_gadget[i]);
3006 // store readily prepared envelope request for later use when animating
3007 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3009 if (text_door_style)
3010 free(text_door_style);
3013 static void AnimateEnvelopeRequest(int anim_mode, int action)
3015 int graphic = IMG_BACKGROUND_REQUEST;
3016 boolean draw_masked = graphic_info[graphic].draw_masked;
3017 int delay_value_normal = request.step_delay;
3018 int delay_value_fast = delay_value_normal / 2;
3019 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3020 boolean no_delay = (tape.warp_forward);
3021 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3022 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3023 unsigned int anim_delay = 0;
3025 int tile_size = MAX(request.step_offset, 1);
3026 int max_xsize = request.width / tile_size;
3027 int max_ysize = request.height / tile_size;
3028 int max_xsize_inner = max_xsize - 2;
3029 int max_ysize_inner = max_ysize - 2;
3031 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3032 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3033 int xend = max_xsize_inner;
3034 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3035 int xstep = (xstart < xend ? 1 : 0);
3036 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3038 int end = MAX(xend - xstart, yend - ystart);
3041 if (setup.quick_doors)
3048 for (i = start; i <= end; i++)
3050 int last_frame = end; // last frame of this "for" loop
3051 int x = xstart + i * xstep;
3052 int y = ystart + i * ystep;
3053 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3054 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3055 int xsize_size_left = (xsize - 1) * tile_size;
3056 int ysize_size_top = (ysize - 1) * tile_size;
3057 int max_xsize_pos = (max_xsize - 1) * tile_size;
3058 int max_ysize_pos = (max_ysize - 1) * tile_size;
3059 int width = xsize * tile_size;
3060 int height = ysize * tile_size;
3065 setRequestPosition(&src_x, &src_y, FALSE);
3066 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3068 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3070 for (yy = 0; yy < 2; yy++)
3072 for (xx = 0; xx < 2; xx++)
3074 int src_xx = src_x + xx * max_xsize_pos;
3075 int src_yy = src_y + yy * max_ysize_pos;
3076 int dst_xx = dst_x + xx * xsize_size_left;
3077 int dst_yy = dst_y + yy * ysize_size_top;
3078 int xx_size = (xx ? tile_size : xsize_size_left);
3079 int yy_size = (yy ? tile_size : ysize_size_top);
3082 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3083 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3085 BlitBitmap(bitmap_db_store_2, backbuffer,
3086 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3090 redraw_mask |= REDRAW_FIELD;
3094 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3097 ClearAutoRepeatKeyEvents();
3100 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3102 int graphic = IMG_BACKGROUND_REQUEST;
3103 int sound_opening = SND_REQUEST_OPENING;
3104 int sound_closing = SND_REQUEST_CLOSING;
3105 int anim_mode_1 = request.anim_mode; // (higher priority)
3106 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3107 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3108 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3109 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3111 if (game_status == GAME_MODE_PLAYING)
3112 BlitScreenToBitmap(backbuffer);
3114 SetDrawtoField(DRAW_TO_BACKBUFFER);
3116 // SetDrawBackgroundMask(REDRAW_NONE);
3118 if (action == ACTION_OPENING)
3120 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3122 if (req_state & REQ_ASK)
3124 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3125 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3127 else if (req_state & REQ_CONFIRM)
3129 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3131 else if (req_state & REQ_PLAYER)
3133 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3134 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3135 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3136 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3139 DrawEnvelopeRequest(text);
3142 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3144 if (action == ACTION_OPENING)
3146 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3148 if (anim_mode == ANIM_DEFAULT)
3149 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3151 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3155 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3157 if (anim_mode != ANIM_NONE)
3158 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3160 if (anim_mode == ANIM_DEFAULT)
3161 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3164 game.envelope_active = FALSE;
3166 if (action == ACTION_CLOSING)
3167 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3169 // SetDrawBackgroundMask(last_draw_background_mask);
3171 redraw_mask |= REDRAW_FIELD;
3175 if (action == ACTION_CLOSING &&
3176 game_status == GAME_MODE_PLAYING &&
3177 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3178 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3181 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3183 if (IS_MM_WALL(element))
3185 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3191 int graphic = el2preimg(element);
3193 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3194 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3199 void DrawLevel(int draw_background_mask)
3203 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3204 SetDrawBackgroundMask(draw_background_mask);
3208 for (x = BX1; x <= BX2; x++)
3209 for (y = BY1; y <= BY2; y++)
3210 DrawScreenField(x, y);
3212 redraw_mask |= REDRAW_FIELD;
3215 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3220 for (x = 0; x < size_x; x++)
3221 for (y = 0; y < size_y; y++)
3222 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3224 redraw_mask |= REDRAW_FIELD;
3227 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3231 for (x = 0; x < size_x; x++)
3232 for (y = 0; y < size_y; y++)
3233 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3235 redraw_mask |= REDRAW_FIELD;
3238 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3240 boolean show_level_border = (BorderElement != EL_EMPTY);
3241 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3242 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3243 int tile_size = preview.tile_size;
3244 int preview_width = preview.xsize * tile_size;
3245 int preview_height = preview.ysize * tile_size;
3246 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3247 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3248 int real_preview_width = real_preview_xsize * tile_size;
3249 int real_preview_height = real_preview_ysize * tile_size;
3250 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3251 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3254 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3257 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3259 dst_x += (preview_width - real_preview_width) / 2;
3260 dst_y += (preview_height - real_preview_height) / 2;
3262 for (x = 0; x < real_preview_xsize; x++)
3264 for (y = 0; y < real_preview_ysize; y++)
3266 int lx = from_x + x + (show_level_border ? -1 : 0);
3267 int ly = from_y + y + (show_level_border ? -1 : 0);
3268 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3269 getBorderElement(lx, ly));
3271 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3272 element, tile_size);
3276 redraw_mask |= REDRAW_FIELD;
3279 #define MICROLABEL_EMPTY 0
3280 #define MICROLABEL_LEVEL_NAME 1
3281 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3282 #define MICROLABEL_LEVEL_AUTHOR 3
3283 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3284 #define MICROLABEL_IMPORTED_FROM 5
3285 #define MICROLABEL_IMPORTED_BY_HEAD 6
3286 #define MICROLABEL_IMPORTED_BY 7
3288 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3290 int max_text_width = SXSIZE;
3291 int font_width = getFontWidth(font_nr);
3293 if (pos->align == ALIGN_CENTER)
3294 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3295 else if (pos->align == ALIGN_RIGHT)
3296 max_text_width = pos->x;
3298 max_text_width = SXSIZE - pos->x;
3300 return max_text_width / font_width;
3303 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3305 char label_text[MAX_OUTPUT_LINESIZE + 1];
3306 int max_len_label_text;
3307 int font_nr = pos->font;
3310 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3313 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3314 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3315 mode == MICROLABEL_IMPORTED_BY_HEAD)
3316 font_nr = pos->font_alt;
3318 max_len_label_text = getMaxTextLength(pos, font_nr);
3320 if (pos->size != -1)
3321 max_len_label_text = pos->size;
3323 for (i = 0; i < max_len_label_text; i++)
3324 label_text[i] = ' ';
3325 label_text[max_len_label_text] = '\0';
3327 if (strlen(label_text) > 0)
3328 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3331 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3332 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3333 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3334 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3335 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3336 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3337 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3338 max_len_label_text);
3339 label_text[max_len_label_text] = '\0';
3341 if (strlen(label_text) > 0)
3342 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3344 redraw_mask |= REDRAW_FIELD;
3347 static void DrawPreviewLevelLabel(int mode)
3349 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3352 static void DrawPreviewLevelInfo(int mode)
3354 if (mode == MICROLABEL_LEVEL_NAME)
3355 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3356 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3357 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3360 static void DrawPreviewLevelExt(boolean restart)
3362 static unsigned int scroll_delay = 0;
3363 static unsigned int label_delay = 0;
3364 static int from_x, from_y, scroll_direction;
3365 static int label_state, label_counter;
3366 unsigned int scroll_delay_value = preview.step_delay;
3367 boolean show_level_border = (BorderElement != EL_EMPTY);
3368 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3369 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3376 if (preview.anim_mode == ANIM_CENTERED)
3378 if (level_xsize > preview.xsize)
3379 from_x = (level_xsize - preview.xsize) / 2;
3380 if (level_ysize > preview.ysize)
3381 from_y = (level_ysize - preview.ysize) / 2;
3384 from_x += preview.xoffset;
3385 from_y += preview.yoffset;
3387 scroll_direction = MV_RIGHT;
3391 DrawPreviewLevelPlayfield(from_x, from_y);
3392 DrawPreviewLevelLabel(label_state);
3394 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3395 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3397 // initialize delay counters
3398 DelayReached(&scroll_delay, 0);
3399 DelayReached(&label_delay, 0);
3401 if (leveldir_current->name)
3403 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3404 char label_text[MAX_OUTPUT_LINESIZE + 1];
3405 int font_nr = pos->font;
3406 int max_len_label_text = getMaxTextLength(pos, font_nr);
3408 if (pos->size != -1)
3409 max_len_label_text = pos->size;
3411 strncpy(label_text, leveldir_current->name, max_len_label_text);
3412 label_text[max_len_label_text] = '\0';
3414 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3415 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3421 // scroll preview level, if needed
3422 if (preview.anim_mode != ANIM_NONE &&
3423 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3424 DelayReached(&scroll_delay, scroll_delay_value))
3426 switch (scroll_direction)
3431 from_x -= preview.step_offset;
3432 from_x = (from_x < 0 ? 0 : from_x);
3435 scroll_direction = MV_UP;
3439 if (from_x < level_xsize - preview.xsize)
3441 from_x += preview.step_offset;
3442 from_x = (from_x > level_xsize - preview.xsize ?
3443 level_xsize - preview.xsize : from_x);
3446 scroll_direction = MV_DOWN;
3452 from_y -= preview.step_offset;
3453 from_y = (from_y < 0 ? 0 : from_y);
3456 scroll_direction = MV_RIGHT;
3460 if (from_y < level_ysize - preview.ysize)
3462 from_y += preview.step_offset;
3463 from_y = (from_y > level_ysize - preview.ysize ?
3464 level_ysize - preview.ysize : from_y);
3467 scroll_direction = MV_LEFT;
3474 DrawPreviewLevelPlayfield(from_x, from_y);
3477 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3478 // redraw micro level label, if needed
3479 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3480 !strEqual(level.author, ANONYMOUS_NAME) &&
3481 !strEqual(level.author, leveldir_current->name) &&
3482 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3484 int max_label_counter = 23;
3486 if (leveldir_current->imported_from != NULL &&
3487 strlen(leveldir_current->imported_from) > 0)
3488 max_label_counter += 14;
3489 if (leveldir_current->imported_by != NULL &&
3490 strlen(leveldir_current->imported_by) > 0)
3491 max_label_counter += 14;
3493 label_counter = (label_counter + 1) % max_label_counter;
3494 label_state = (label_counter >= 0 && label_counter <= 7 ?
3495 MICROLABEL_LEVEL_NAME :
3496 label_counter >= 9 && label_counter <= 12 ?
3497 MICROLABEL_LEVEL_AUTHOR_HEAD :
3498 label_counter >= 14 && label_counter <= 21 ?
3499 MICROLABEL_LEVEL_AUTHOR :
3500 label_counter >= 23 && label_counter <= 26 ?
3501 MICROLABEL_IMPORTED_FROM_HEAD :
3502 label_counter >= 28 && label_counter <= 35 ?
3503 MICROLABEL_IMPORTED_FROM :
3504 label_counter >= 37 && label_counter <= 40 ?
3505 MICROLABEL_IMPORTED_BY_HEAD :
3506 label_counter >= 42 && label_counter <= 49 ?
3507 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3509 if (leveldir_current->imported_from == NULL &&
3510 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3511 label_state == MICROLABEL_IMPORTED_FROM))
3512 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3513 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3515 DrawPreviewLevelLabel(label_state);
3519 static void DrawPreviewPlayers(void)
3521 if (game_status != GAME_MODE_MAIN)
3524 if (!network.enabled && !setup.team_mode)
3527 boolean player_found[MAX_PLAYERS];
3528 int num_players = 0;
3531 for (i = 0; i < MAX_PLAYERS; i++)
3532 player_found[i] = FALSE;
3534 // check which players can be found in the level (simple approach)
3535 for (x = 0; x < lev_fieldx; x++)
3537 for (y = 0; y < lev_fieldy; y++)
3539 int element = level.field[x][y];
3541 if (ELEM_IS_PLAYER(element))
3543 int player_nr = GET_PLAYER_NR(element);
3545 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3547 if (!player_found[player_nr])
3550 player_found[player_nr] = TRUE;
3555 struct TextPosInfo *pos = &menu.main.preview_players;
3556 int tile_size = pos->tile_size;
3557 int border_size = pos->border_size;
3558 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3559 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3560 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3561 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3562 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3563 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3564 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3565 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3566 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3567 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3568 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3569 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3571 // clear area in which the players will be drawn
3572 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3573 max_players_width, max_players_height);
3575 // only draw players if level is suited for team mode
3576 if (num_players < 2)
3579 // draw all players that were found in the level
3580 for (i = 0; i < MAX_PLAYERS; i++)
3582 if (player_found[i])
3584 int graphic = el2img(EL_PLAYER_1 + i);
3586 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3588 xpos += player_xoffset;
3589 ypos += player_yoffset;
3594 void DrawPreviewLevelInitial(void)
3596 DrawPreviewLevelExt(TRUE);
3597 DrawPreviewPlayers();
3600 void DrawPreviewLevelAnimation(void)
3602 DrawPreviewLevelExt(FALSE);
3605 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3606 int border_size, int font_nr)
3608 int graphic = el2img(EL_PLAYER_1 + player_nr);
3609 int font_height = getFontHeight(font_nr);
3610 int player_height = MAX(tile_size, font_height);
3611 int xoffset_text = tile_size + border_size;
3612 int yoffset_text = (player_height - font_height) / 2;
3613 int yoffset_graphic = (player_height - tile_size) / 2;
3614 char *player_name = getNetworkPlayerName(player_nr + 1);
3616 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3618 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3621 static void DrawNetworkPlayersExt(boolean force)
3623 if (game_status != GAME_MODE_MAIN)
3626 if (!network.connected && !force)
3629 int num_players = 0;
3632 for (i = 0; i < MAX_PLAYERS; i++)
3633 if (stored_player[i].connected_network)
3636 struct TextPosInfo *pos = &menu.main.network_players;
3637 int tile_size = pos->tile_size;
3638 int border_size = pos->border_size;
3639 int xoffset_text = tile_size + border_size;
3640 int font_nr = pos->font;
3641 int font_width = getFontWidth(font_nr);
3642 int font_height = getFontHeight(font_nr);
3643 int player_height = MAX(tile_size, font_height);
3644 int player_yoffset = player_height + border_size;
3645 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3646 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3647 int all_players_height = num_players * player_yoffset - border_size;
3648 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3649 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3650 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3652 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3653 max_players_width, max_players_height);
3655 // first draw local network player ...
3656 for (i = 0; i < MAX_PLAYERS; i++)
3658 if (stored_player[i].connected_network &&
3659 stored_player[i].connected_locally)
3661 char *player_name = getNetworkPlayerName(i + 1);
3662 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3663 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3665 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3667 ypos += player_yoffset;
3671 // ... then draw all other network players
3672 for (i = 0; i < MAX_PLAYERS; i++)
3674 if (stored_player[i].connected_network &&
3675 !stored_player[i].connected_locally)
3677 char *player_name = getNetworkPlayerName(i + 1);
3678 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3679 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3681 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3683 ypos += player_yoffset;
3688 void DrawNetworkPlayers(void)
3690 DrawNetworkPlayersExt(FALSE);
3693 void ClearNetworkPlayers(void)
3695 DrawNetworkPlayersExt(TRUE);
3698 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3699 int graphic, int sync_frame,
3702 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3704 if (mask_mode == USE_MASKING)
3705 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3707 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3710 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3711 int graphic, int sync_frame, int mask_mode)
3713 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3715 if (mask_mode == USE_MASKING)
3716 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3718 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3721 static void DrawGraphicAnimation(int x, int y, int graphic)
3723 int lx = LEVELX(x), ly = LEVELY(y);
3725 if (!IN_SCR_FIELD(x, y))
3728 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3729 graphic, GfxFrame[lx][ly], NO_MASKING);
3731 MarkTileDirty(x, y);
3734 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3736 int lx = LEVELX(x), ly = LEVELY(y);
3738 if (!IN_SCR_FIELD(x, y))
3741 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3742 graphic, GfxFrame[lx][ly], NO_MASKING);
3743 MarkTileDirty(x, y);
3746 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3748 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3751 void DrawLevelElementAnimation(int x, int y, int element)
3753 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3755 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3758 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3760 int sx = SCREENX(x), sy = SCREENY(y);
3762 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3765 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3768 DrawGraphicAnimation(sx, sy, graphic);
3771 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3772 DrawLevelFieldCrumbled(x, y);
3774 if (GFX_CRUMBLED(Feld[x][y]))
3775 DrawLevelFieldCrumbled(x, y);
3779 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3781 int sx = SCREENX(x), sy = SCREENY(y);
3784 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3787 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3789 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3792 DrawGraphicAnimation(sx, sy, graphic);
3794 if (GFX_CRUMBLED(element))
3795 DrawLevelFieldCrumbled(x, y);
3798 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3800 if (player->use_murphy)
3802 // this works only because currently only one player can be "murphy" ...
3803 static int last_horizontal_dir = MV_LEFT;
3804 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3806 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3807 last_horizontal_dir = move_dir;
3809 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3811 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3813 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3819 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3822 static boolean equalGraphics(int graphic1, int graphic2)
3824 struct GraphicInfo *g1 = &graphic_info[graphic1];
3825 struct GraphicInfo *g2 = &graphic_info[graphic2];
3827 return (g1->bitmap == g2->bitmap &&
3828 g1->src_x == g2->src_x &&
3829 g1->src_y == g2->src_y &&
3830 g1->anim_frames == g2->anim_frames &&
3831 g1->anim_delay == g2->anim_delay &&
3832 g1->anim_mode == g2->anim_mode);
3835 void DrawAllPlayers(void)
3839 for (i = 0; i < MAX_PLAYERS; i++)
3840 if (stored_player[i].active)
3841 DrawPlayer(&stored_player[i]);
3844 void DrawPlayerField(int x, int y)
3846 if (!IS_PLAYER(x, y))
3849 DrawPlayer(PLAYERINFO(x, y));
3852 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3854 void DrawPlayer(struct PlayerInfo *player)
3856 int jx = player->jx;
3857 int jy = player->jy;
3858 int move_dir = player->MovDir;
3859 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3860 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3861 int last_jx = (player->is_moving ? jx - dx : jx);
3862 int last_jy = (player->is_moving ? jy - dy : jy);
3863 int next_jx = jx + dx;
3864 int next_jy = jy + dy;
3865 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3866 boolean player_is_opaque = FALSE;
3867 int sx = SCREENX(jx), sy = SCREENY(jy);
3868 int sxx = 0, syy = 0;
3869 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3871 int action = ACTION_DEFAULT;
3872 int last_player_graphic = getPlayerGraphic(player, move_dir);
3873 int last_player_frame = player->Frame;
3876 // GfxElement[][] is set to the element the player is digging or collecting;
3877 // remove also for off-screen player if the player is not moving anymore
3878 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3879 GfxElement[jx][jy] = EL_UNDEFINED;
3881 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3885 if (!IN_LEV_FIELD(jx, jy))
3887 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3888 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3889 printf("DrawPlayerField(): This should never happen!\n");
3894 if (element == EL_EXPLOSION)
3897 action = (player->is_pushing ? ACTION_PUSHING :
3898 player->is_digging ? ACTION_DIGGING :
3899 player->is_collecting ? ACTION_COLLECTING :
3900 player->is_moving ? ACTION_MOVING :
3901 player->is_snapping ? ACTION_SNAPPING :
3902 player->is_dropping ? ACTION_DROPPING :
3903 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3905 if (player->is_waiting)
3906 move_dir = player->dir_waiting;
3908 InitPlayerGfxAnimation(player, action, move_dir);
3910 // --------------------------------------------------------------------------
3911 // draw things in the field the player is leaving, if needed
3912 // --------------------------------------------------------------------------
3914 if (player->is_moving)
3916 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3918 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3920 if (last_element == EL_DYNAMITE_ACTIVE ||
3921 last_element == EL_EM_DYNAMITE_ACTIVE ||
3922 last_element == EL_SP_DISK_RED_ACTIVE)
3923 DrawDynamite(last_jx, last_jy);
3925 DrawLevelFieldThruMask(last_jx, last_jy);
3927 else if (last_element == EL_DYNAMITE_ACTIVE ||
3928 last_element == EL_EM_DYNAMITE_ACTIVE ||
3929 last_element == EL_SP_DISK_RED_ACTIVE)
3930 DrawDynamite(last_jx, last_jy);
3932 /* !!! this is not enough to prevent flickering of players which are
3933 moving next to each others without a free tile between them -- this
3934 can only be solved by drawing all players layer by layer (first the
3935 background, then the foreground etc.) !!! => TODO */
3936 else if (!IS_PLAYER(last_jx, last_jy))
3937 DrawLevelField(last_jx, last_jy);
3940 DrawLevelField(last_jx, last_jy);
3943 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3944 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3947 if (!IN_SCR_FIELD(sx, sy))
3950 // --------------------------------------------------------------------------
3951 // draw things behind the player, if needed
3952 // --------------------------------------------------------------------------
3955 DrawLevelElement(jx, jy, Back[jx][jy]);
3956 else if (IS_ACTIVE_BOMB(element))
3957 DrawLevelElement(jx, jy, EL_EMPTY);
3960 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3962 int old_element = GfxElement[jx][jy];
3963 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3964 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3966 if (GFX_CRUMBLED(old_element))
3967 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3969 DrawGraphic(sx, sy, old_graphic, frame);
3971 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3972 player_is_opaque = TRUE;
3976 GfxElement[jx][jy] = EL_UNDEFINED;
3978 // make sure that pushed elements are drawn with correct frame rate
3979 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3981 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3982 GfxFrame[jx][jy] = player->StepFrame;
3984 DrawLevelField(jx, jy);
3988 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3989 // -----------------------------------------------------------------------
3990 // draw player himself
3991 // -----------------------------------------------------------------------
3993 graphic = getPlayerGraphic(player, move_dir);
3995 // in the case of changed player action or direction, prevent the current
3996 // animation frame from being restarted for identical animations
3997 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3998 player->Frame = last_player_frame;
4000 frame = getGraphicAnimationFrame(graphic, player->Frame);
4004 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4005 sxx = player->GfxPos;
4007 syy = player->GfxPos;
4010 if (player_is_opaque)
4011 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4013 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4015 if (SHIELD_ON(player))
4017 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4018 IMG_SHIELD_NORMAL_ACTIVE);
4019 int frame = getGraphicAnimationFrame(graphic, -1);
4021 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4025 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4028 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4029 sxx = player->GfxPos;
4031 syy = player->GfxPos;
4035 // --------------------------------------------------------------------------
4036 // draw things the player is pushing, if needed
4037 // --------------------------------------------------------------------------
4039 if (player->is_pushing && player->is_moving)
4041 int px = SCREENX(jx), py = SCREENY(jy);
4042 int pxx = (TILEX - ABS(sxx)) * dx;
4043 int pyy = (TILEY - ABS(syy)) * dy;
4044 int gfx_frame = GfxFrame[jx][jy];
4050 if (!IS_MOVING(jx, jy)) // push movement already finished
4052 element = Feld[next_jx][next_jy];
4053 gfx_frame = GfxFrame[next_jx][next_jy];
4056 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4058 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4059 frame = getGraphicAnimationFrame(graphic, sync_frame);
4061 // draw background element under pushed element (like the Sokoban field)
4062 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4064 // this allows transparent pushing animation over non-black background
4067 DrawLevelElement(jx, jy, Back[jx][jy]);
4069 DrawLevelElement(jx, jy, EL_EMPTY);
4071 if (Back[next_jx][next_jy])
4072 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4074 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4076 else if (Back[next_jx][next_jy])
4077 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4080 // do not draw (EM style) pushing animation when pushing is finished
4081 // (two-tile animations usually do not contain start and end frame)
4082 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4083 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4085 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4087 // masked drawing is needed for EMC style (double) movement graphics
4088 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4089 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4093 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4094 // -----------------------------------------------------------------------
4095 // draw player himself
4096 // -----------------------------------------------------------------------
4098 graphic = getPlayerGraphic(player, move_dir);
4100 // in the case of changed player action or direction, prevent the current
4101 // animation frame from being restarted for identical animations
4102 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4103 player->Frame = last_player_frame;
4105 frame = getGraphicAnimationFrame(graphic, player->Frame);
4109 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4110 sxx = player->GfxPos;
4112 syy = player->GfxPos;
4115 if (player_is_opaque)
4116 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4118 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4120 if (SHIELD_ON(player))
4122 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4123 IMG_SHIELD_NORMAL_ACTIVE);
4124 int frame = getGraphicAnimationFrame(graphic, -1);
4126 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4130 // --------------------------------------------------------------------------
4131 // draw things in front of player (active dynamite or dynabombs)
4132 // --------------------------------------------------------------------------
4134 if (IS_ACTIVE_BOMB(element))
4136 graphic = el2img(element);
4137 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4139 if (game.emulation == EMU_SUPAPLEX)
4140 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4142 DrawGraphicThruMask(sx, sy, graphic, frame);
4145 if (player_is_moving && last_element == EL_EXPLOSION)
4147 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4148 GfxElement[last_jx][last_jy] : EL_EMPTY);
4149 int graphic = el_act2img(element, ACTION_EXPLODING);
4150 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4151 int phase = ExplodePhase[last_jx][last_jy] - 1;
4152 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4155 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4158 // --------------------------------------------------------------------------
4159 // draw elements the player is just walking/passing through/under
4160 // --------------------------------------------------------------------------
4162 if (player_is_moving)
4164 // handle the field the player is leaving ...
4165 if (IS_ACCESSIBLE_INSIDE(last_element))
4166 DrawLevelField(last_jx, last_jy);
4167 else if (IS_ACCESSIBLE_UNDER(last_element))
4168 DrawLevelFieldThruMask(last_jx, last_jy);
4171 // do not redraw accessible elements if the player is just pushing them
4172 if (!player_is_moving || !player->is_pushing)
4174 // ... and the field the player is entering
4175 if (IS_ACCESSIBLE_INSIDE(element))
4176 DrawLevelField(jx, jy);
4177 else if (IS_ACCESSIBLE_UNDER(element))
4178 DrawLevelFieldThruMask(jx, jy);
4181 MarkTileDirty(sx, sy);
4184 // ----------------------------------------------------------------------------
4186 void WaitForEventToContinue(void)
4188 boolean still_wait = TRUE;
4190 if (program.headless)
4193 // simulate releasing mouse button over last gadget, if still pressed
4195 HandleGadgets(-1, -1, 0);
4197 button_status = MB_RELEASED;
4205 if (NextValidEvent(&event))
4209 case EVENT_BUTTONRELEASE:
4210 case EVENT_KEYPRESS:
4211 case SDL_CONTROLLERBUTTONDOWN:
4212 case SDL_JOYBUTTONDOWN:
4216 case EVENT_KEYRELEASE:
4217 ClearPlayerAction();
4221 HandleOtherEvents(&event);
4225 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4234 #define MAX_REQUEST_LINES 13
4235 #define MAX_REQUEST_LINE_FONT1_LEN 7
4236 #define MAX_REQUEST_LINE_FONT2_LEN 10
4238 static int RequestHandleEvents(unsigned int req_state)
4240 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4242 int width = request.width;
4243 int height = request.height;
4247 // when showing request dialog after game ended, deactivate game panel
4248 if (game_just_ended)
4249 game.panel.active = FALSE;
4251 game.request_active = TRUE;
4253 setRequestPosition(&sx, &sy, FALSE);
4255 button_status = MB_RELEASED;
4257 request_gadget_id = -1;
4262 if (game_just_ended)
4264 // the MM game engine does not use a special (scrollable) field buffer
4265 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4266 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4268 HandleGameActions();
4270 SetDrawtoField(DRAW_TO_BACKBUFFER);
4272 if (global.use_envelope_request)
4274 // copy current state of request area to middle of playfield area
4275 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4283 while (NextValidEvent(&event))
4287 case EVENT_BUTTONPRESS:
4288 case EVENT_BUTTONRELEASE:
4289 case EVENT_MOTIONNOTIFY:
4293 if (event.type == EVENT_MOTIONNOTIFY)
4298 motion_status = TRUE;
4299 mx = ((MotionEvent *) &event)->x;
4300 my = ((MotionEvent *) &event)->y;
4304 motion_status = FALSE;
4305 mx = ((ButtonEvent *) &event)->x;
4306 my = ((ButtonEvent *) &event)->y;
4307 if (event.type == EVENT_BUTTONPRESS)
4308 button_status = ((ButtonEvent *) &event)->button;
4310 button_status = MB_RELEASED;
4313 // this sets 'request_gadget_id'
4314 HandleGadgets(mx, my, button_status);
4316 switch (request_gadget_id)
4318 case TOOL_CTRL_ID_YES:
4321 case TOOL_CTRL_ID_NO:
4324 case TOOL_CTRL_ID_CONFIRM:
4325 result = TRUE | FALSE;
4328 case TOOL_CTRL_ID_PLAYER_1:
4331 case TOOL_CTRL_ID_PLAYER_2:
4334 case TOOL_CTRL_ID_PLAYER_3:
4337 case TOOL_CTRL_ID_PLAYER_4:
4348 case SDL_WINDOWEVENT:
4349 HandleWindowEvent((WindowEvent *) &event);
4352 case SDL_APP_WILLENTERBACKGROUND:
4353 case SDL_APP_DIDENTERBACKGROUND:
4354 case SDL_APP_WILLENTERFOREGROUND:
4355 case SDL_APP_DIDENTERFOREGROUND:
4356 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4359 case EVENT_KEYPRESS:
4361 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4366 if (req_state & REQ_CONFIRM)
4375 #if defined(KSYM_Rewind)
4376 case KSYM_Rewind: // for Amazon Fire TV remote
4385 #if defined(KSYM_FastForward)
4386 case KSYM_FastForward: // for Amazon Fire TV remote
4392 HandleKeysDebug(key, KEY_PRESSED);
4396 if (req_state & REQ_PLAYER)
4398 int old_player_nr = setup.network_player_nr;
4401 result = old_player_nr + 1;
4406 result = old_player_nr + 1;
4437 case EVENT_KEYRELEASE:
4438 ClearPlayerAction();
4441 case SDL_CONTROLLERBUTTONDOWN:
4442 switch (event.cbutton.button)
4444 case SDL_CONTROLLER_BUTTON_A:
4445 case SDL_CONTROLLER_BUTTON_X:
4446 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4447 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4451 case SDL_CONTROLLER_BUTTON_B:
4452 case SDL_CONTROLLER_BUTTON_Y:
4453 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4454 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4455 case SDL_CONTROLLER_BUTTON_BACK:
4460 if (req_state & REQ_PLAYER)
4462 int old_player_nr = setup.network_player_nr;
4465 result = old_player_nr + 1;
4467 switch (event.cbutton.button)
4469 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4470 case SDL_CONTROLLER_BUTTON_Y:
4474 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4475 case SDL_CONTROLLER_BUTTON_B:
4479 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4480 case SDL_CONTROLLER_BUTTON_A:
4484 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4485 case SDL_CONTROLLER_BUTTON_X:
4496 case SDL_CONTROLLERBUTTONUP:
4497 HandleJoystickEvent(&event);
4498 ClearPlayerAction();
4502 HandleOtherEvents(&event);
4507 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4509 int joy = AnyJoystick();
4511 if (joy & JOY_BUTTON_1)
4513 else if (joy & JOY_BUTTON_2)
4516 else if (AnyJoystick())
4518 int joy = AnyJoystick();
4520 if (req_state & REQ_PLAYER)
4524 else if (joy & JOY_RIGHT)
4526 else if (joy & JOY_DOWN)
4528 else if (joy & JOY_LEFT)
4533 if (game_just_ended)
4535 if (global.use_envelope_request)
4537 // copy back current state of pressed buttons inside request area
4538 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4545 game.request_active = FALSE;
4550 static boolean RequestDoor(char *text, unsigned int req_state)
4552 unsigned int old_door_state;
4553 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4554 int font_nr = FONT_TEXT_2;
4559 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4561 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4562 font_nr = FONT_TEXT_1;
4565 if (game_status == GAME_MODE_PLAYING)
4566 BlitScreenToBitmap(backbuffer);
4568 // disable deactivated drawing when quick-loading level tape recording
4569 if (tape.playing && tape.deactivate_display)
4570 TapeDeactivateDisplayOff(TRUE);
4572 SetMouseCursor(CURSOR_DEFAULT);
4574 // pause network game while waiting for request to answer
4575 if (network.enabled &&
4576 game_status == GAME_MODE_PLAYING &&
4577 !game.all_players_gone &&
4578 req_state & REQUEST_WAIT_FOR_INPUT)
4579 SendToServer_PausePlaying();
4581 old_door_state = GetDoorState();
4583 // simulate releasing mouse button over last gadget, if still pressed
4585 HandleGadgets(-1, -1, 0);
4589 // draw released gadget before proceeding
4592 if (old_door_state & DOOR_OPEN_1)
4594 CloseDoor(DOOR_CLOSE_1);
4596 // save old door content
4597 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4598 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4601 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4602 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4604 // clear door drawing field
4605 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4607 // force DOOR font inside door area
4608 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4610 // write text for request
4611 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4613 char text_line[max_request_line_len + 1];
4619 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4621 tc = *(text_ptr + tx);
4622 // if (!tc || tc == ' ')
4623 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4627 if ((tc == '?' || tc == '!') && tl == 0)
4637 strncpy(text_line, text_ptr, tl);
4640 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4641 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4642 text_line, font_nr);
4644 text_ptr += tl + (tc == ' ' ? 1 : 0);
4645 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4650 if (req_state & REQ_ASK)
4652 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4653 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4655 else if (req_state & REQ_CONFIRM)
4657 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4659 else if (req_state & REQ_PLAYER)
4661 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4662 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4663 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4664 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4667 // copy request gadgets to door backbuffer
4668 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4670 OpenDoor(DOOR_OPEN_1);
4672 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4674 if (game_status == GAME_MODE_PLAYING)
4676 SetPanelBackground();
4677 SetDrawBackgroundMask(REDRAW_DOOR_1);
4681 SetDrawBackgroundMask(REDRAW_FIELD);
4687 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4689 // ---------- handle request buttons ----------
4690 result = RequestHandleEvents(req_state);
4694 if (!(req_state & REQ_STAY_OPEN))
4696 CloseDoor(DOOR_CLOSE_1);
4698 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4699 (req_state & REQ_REOPEN))
4700 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4705 if (game_status == GAME_MODE_PLAYING)
4707 SetPanelBackground();
4708 SetDrawBackgroundMask(REDRAW_DOOR_1);
4712 SetDrawBackgroundMask(REDRAW_FIELD);
4715 // continue network game after request
4716 if (network.enabled &&
4717 game_status == GAME_MODE_PLAYING &&
4718 !game.all_players_gone &&
4719 req_state & REQUEST_WAIT_FOR_INPUT)
4720 SendToServer_ContinuePlaying();
4722 // restore deactivated drawing when quick-loading level tape recording
4723 if (tape.playing && tape.deactivate_display)
4724 TapeDeactivateDisplayOn();
4729 static boolean RequestEnvelope(char *text, unsigned int req_state)
4733 if (game_status == GAME_MODE_PLAYING)
4734 BlitScreenToBitmap(backbuffer);
4736 // disable deactivated drawing when quick-loading level tape recording
4737 if (tape.playing && tape.deactivate_display)
4738 TapeDeactivateDisplayOff(TRUE);
4740 SetMouseCursor(CURSOR_DEFAULT);
4742 // pause network game while waiting for request to answer
4743 if (network.enabled &&
4744 game_status == GAME_MODE_PLAYING &&
4745 !game.all_players_gone &&
4746 req_state & REQUEST_WAIT_FOR_INPUT)
4747 SendToServer_PausePlaying();
4749 // simulate releasing mouse button over last gadget, if still pressed
4751 HandleGadgets(-1, -1, 0);
4755 // (replace with setting corresponding request background)
4756 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4757 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4759 // clear door drawing field
4760 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4762 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4764 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4766 if (game_status == GAME_MODE_PLAYING)
4768 SetPanelBackground();
4769 SetDrawBackgroundMask(REDRAW_DOOR_1);
4773 SetDrawBackgroundMask(REDRAW_FIELD);
4779 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4781 // ---------- handle request buttons ----------
4782 result = RequestHandleEvents(req_state);
4786 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4790 if (game_status == GAME_MODE_PLAYING)
4792 SetPanelBackground();
4793 SetDrawBackgroundMask(REDRAW_DOOR_1);
4797 SetDrawBackgroundMask(REDRAW_FIELD);
4800 // continue network game after request
4801 if (network.enabled &&
4802 game_status == GAME_MODE_PLAYING &&
4803 !game.all_players_gone &&
4804 req_state & REQUEST_WAIT_FOR_INPUT)
4805 SendToServer_ContinuePlaying();
4807 // restore deactivated drawing when quick-loading level tape recording
4808 if (tape.playing && tape.deactivate_display)
4809 TapeDeactivateDisplayOn();
4814 boolean Request(char *text, unsigned int req_state)
4816 boolean overlay_enabled = GetOverlayEnabled();
4819 SetOverlayEnabled(FALSE);
4821 if (global.use_envelope_request)
4822 result = RequestEnvelope(text, req_state);
4824 result = RequestDoor(text, req_state);
4826 SetOverlayEnabled(overlay_enabled);
4831 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4833 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4834 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4837 if (dpo1->sort_priority != dpo2->sort_priority)
4838 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4840 compare_result = dpo1->nr - dpo2->nr;
4842 return compare_result;
4845 void InitGraphicCompatibilityInfo_Doors(void)
4851 struct DoorInfo *door;
4855 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4856 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4858 { -1, -1, -1, NULL }
4860 struct Rect door_rect_list[] =
4862 { DX, DY, DXSIZE, DYSIZE },
4863 { VX, VY, VXSIZE, VYSIZE }
4867 for (i = 0; doors[i].door_token != -1; i++)
4869 int door_token = doors[i].door_token;
4870 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4871 int part_1 = doors[i].part_1;
4872 int part_8 = doors[i].part_8;
4873 int part_2 = part_1 + 1;
4874 int part_3 = part_1 + 2;
4875 struct DoorInfo *door = doors[i].door;
4876 struct Rect *door_rect = &door_rect_list[door_index];
4877 boolean door_gfx_redefined = FALSE;
4879 // check if any door part graphic definitions have been redefined
4881 for (j = 0; door_part_controls[j].door_token != -1; j++)
4883 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4884 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4886 if (dpc->door_token == door_token && fi->redefined)
4887 door_gfx_redefined = TRUE;
4890 // check for old-style door graphic/animation modifications
4892 if (!door_gfx_redefined)
4894 if (door->anim_mode & ANIM_STATIC_PANEL)
4896 door->panel.step_xoffset = 0;
4897 door->panel.step_yoffset = 0;
4900 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4902 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4903 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4904 int num_door_steps, num_panel_steps;
4906 // remove door part graphics other than the two default wings
4908 for (j = 0; door_part_controls[j].door_token != -1; j++)
4910 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4911 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4913 if (dpc->graphic >= part_3 &&
4914 dpc->graphic <= part_8)
4918 // set graphics and screen positions of the default wings
4920 g_part_1->width = door_rect->width;
4921 g_part_1->height = door_rect->height;
4922 g_part_2->width = door_rect->width;
4923 g_part_2->height = door_rect->height;
4924 g_part_2->src_x = door_rect->width;
4925 g_part_2->src_y = g_part_1->src_y;
4927 door->part_2.x = door->part_1.x;
4928 door->part_2.y = door->part_1.y;
4930 if (door->width != -1)
4932 g_part_1->width = door->width;
4933 g_part_2->width = door->width;
4935 // special treatment for graphics and screen position of right wing
4936 g_part_2->src_x += door_rect->width - door->width;
4937 door->part_2.x += door_rect->width - door->width;
4940 if (door->height != -1)
4942 g_part_1->height = door->height;
4943 g_part_2->height = door->height;
4945 // special treatment for graphics and screen position of bottom wing
4946 g_part_2->src_y += door_rect->height - door->height;
4947 door->part_2.y += door_rect->height - door->height;
4950 // set animation delays for the default wings and panels
4952 door->part_1.step_delay = door->step_delay;
4953 door->part_2.step_delay = door->step_delay;
4954 door->panel.step_delay = door->step_delay;
4956 // set animation draw order for the default wings
4958 door->part_1.sort_priority = 2; // draw left wing over ...
4959 door->part_2.sort_priority = 1; // ... right wing
4961 // set animation draw offset for the default wings
4963 if (door->anim_mode & ANIM_HORIZONTAL)
4965 door->part_1.step_xoffset = door->step_offset;
4966 door->part_1.step_yoffset = 0;
4967 door->part_2.step_xoffset = door->step_offset * -1;
4968 door->part_2.step_yoffset = 0;
4970 num_door_steps = g_part_1->width / door->step_offset;
4972 else // ANIM_VERTICAL
4974 door->part_1.step_xoffset = 0;
4975 door->part_1.step_yoffset = door->step_offset;
4976 door->part_2.step_xoffset = 0;
4977 door->part_2.step_yoffset = door->step_offset * -1;
4979 num_door_steps = g_part_1->height / door->step_offset;
4982 // set animation draw offset for the default panels
4984 if (door->step_offset > 1)
4986 num_panel_steps = 2 * door_rect->height / door->step_offset;
4987 door->panel.start_step = num_panel_steps - num_door_steps;
4988 door->panel.start_step_closing = door->panel.start_step;
4992 num_panel_steps = door_rect->height / door->step_offset;
4993 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4994 door->panel.start_step_closing = door->panel.start_step;
4995 door->panel.step_delay *= 2;
5002 void InitDoors(void)
5006 for (i = 0; door_part_controls[i].door_token != -1; i++)
5008 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5009 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5011 // initialize "start_step_opening" and "start_step_closing", if needed
5012 if (dpc->pos->start_step_opening == 0 &&
5013 dpc->pos->start_step_closing == 0)
5015 // dpc->pos->start_step_opening = dpc->pos->start_step;
5016 dpc->pos->start_step_closing = dpc->pos->start_step;
5019 // fill structure for door part draw order (sorted below)
5021 dpo->sort_priority = dpc->pos->sort_priority;
5024 // sort door part controls according to sort_priority and graphic number
5025 qsort(door_part_order, MAX_DOOR_PARTS,
5026 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5029 unsigned int OpenDoor(unsigned int door_state)
5031 if (door_state & DOOR_COPY_BACK)
5033 if (door_state & DOOR_OPEN_1)
5034 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5035 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5037 if (door_state & DOOR_OPEN_2)
5038 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5039 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5041 door_state &= ~DOOR_COPY_BACK;
5044 return MoveDoor(door_state);
5047 unsigned int CloseDoor(unsigned int door_state)
5049 unsigned int old_door_state = GetDoorState();
5051 if (!(door_state & DOOR_NO_COPY_BACK))
5053 if (old_door_state & DOOR_OPEN_1)
5054 BlitBitmap(backbuffer, bitmap_db_door_1,
5055 DX, DY, DXSIZE, DYSIZE, 0, 0);
5057 if (old_door_state & DOOR_OPEN_2)
5058 BlitBitmap(backbuffer, bitmap_db_door_2,
5059 VX, VY, VXSIZE, VYSIZE, 0, 0);
5061 door_state &= ~DOOR_NO_COPY_BACK;
5064 return MoveDoor(door_state);
5067 unsigned int GetDoorState(void)
5069 return MoveDoor(DOOR_GET_STATE);
5072 unsigned int SetDoorState(unsigned int door_state)
5074 return MoveDoor(door_state | DOOR_SET_STATE);
5077 static int euclid(int a, int b)
5079 return (b ? euclid(b, a % b) : a);
5082 unsigned int MoveDoor(unsigned int door_state)
5084 struct Rect door_rect_list[] =
5086 { DX, DY, DXSIZE, DYSIZE },
5087 { VX, VY, VXSIZE, VYSIZE }
5089 static int door1 = DOOR_CLOSE_1;
5090 static int door2 = DOOR_CLOSE_2;
5091 unsigned int door_delay = 0;
5092 unsigned int door_delay_value;
5095 if (door_state == DOOR_GET_STATE)
5096 return (door1 | door2);
5098 if (door_state & DOOR_SET_STATE)
5100 if (door_state & DOOR_ACTION_1)
5101 door1 = door_state & DOOR_ACTION_1;
5102 if (door_state & DOOR_ACTION_2)
5103 door2 = door_state & DOOR_ACTION_2;
5105 return (door1 | door2);
5108 if (!(door_state & DOOR_FORCE_REDRAW))
5110 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5111 door_state &= ~DOOR_OPEN_1;
5112 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5113 door_state &= ~DOOR_CLOSE_1;
5114 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5115 door_state &= ~DOOR_OPEN_2;
5116 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5117 door_state &= ~DOOR_CLOSE_2;
5120 if (global.autoplay_leveldir)
5122 door_state |= DOOR_NO_DELAY;
5123 door_state &= ~DOOR_CLOSE_ALL;
5126 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5127 door_state |= DOOR_NO_DELAY;
5129 if (door_state & DOOR_ACTION)
5131 boolean door_panel_drawn[NUM_DOORS];
5132 boolean panel_has_doors[NUM_DOORS];
5133 boolean door_part_skip[MAX_DOOR_PARTS];
5134 boolean door_part_done[MAX_DOOR_PARTS];
5135 boolean door_part_done_all;
5136 int num_steps[MAX_DOOR_PARTS];
5137 int max_move_delay = 0; // delay for complete animations of all doors
5138 int max_step_delay = 0; // delay (ms) between two animation frames
5139 int num_move_steps = 0; // number of animation steps for all doors
5140 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5141 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5142 int current_move_delay = 0;
5146 for (i = 0; i < NUM_DOORS; i++)
5147 panel_has_doors[i] = FALSE;
5149 for (i = 0; i < MAX_DOOR_PARTS; i++)
5151 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5152 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5153 int door_token = dpc->door_token;
5155 door_part_done[i] = FALSE;
5156 door_part_skip[i] = (!(door_state & door_token) ||
5160 for (i = 0; i < MAX_DOOR_PARTS; i++)
5162 int nr = door_part_order[i].nr;
5163 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5164 struct DoorPartPosInfo *pos = dpc->pos;
5165 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5166 int door_token = dpc->door_token;
5167 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5168 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5169 int step_xoffset = ABS(pos->step_xoffset);
5170 int step_yoffset = ABS(pos->step_yoffset);
5171 int step_delay = pos->step_delay;
5172 int current_door_state = door_state & door_token;
5173 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5174 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5175 boolean part_opening = (is_panel ? door_closing : door_opening);
5176 int start_step = (part_opening ? pos->start_step_opening :
5177 pos->start_step_closing);
5178 float move_xsize = (step_xoffset ? g->width : 0);
5179 float move_ysize = (step_yoffset ? g->height : 0);
5180 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5181 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5182 int move_steps = (move_xsteps && move_ysteps ?
5183 MIN(move_xsteps, move_ysteps) :
5184 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5185 int move_delay = move_steps * step_delay;
5187 if (door_part_skip[nr])
5190 max_move_delay = MAX(max_move_delay, move_delay);
5191 max_step_delay = (max_step_delay == 0 ? step_delay :
5192 euclid(max_step_delay, step_delay));
5193 num_steps[nr] = move_steps;
5197 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5199 panel_has_doors[door_index] = TRUE;
5203 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5205 num_move_steps = max_move_delay / max_step_delay;
5206 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5208 door_delay_value = max_step_delay;
5210 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5212 start = num_move_steps - 1;
5216 // opening door sound has priority over simultaneously closing door
5217 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5219 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5221 if (door_state & DOOR_OPEN_1)
5222 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5223 if (door_state & DOOR_OPEN_2)
5224 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5226 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5228 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5230 if (door_state & DOOR_CLOSE_1)
5231 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5232 if (door_state & DOOR_CLOSE_2)
5233 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5237 for (k = start; k < num_move_steps; k++)
5239 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5241 door_part_done_all = TRUE;
5243 for (i = 0; i < NUM_DOORS; i++)
5244 door_panel_drawn[i] = FALSE;
5246 for (i = 0; i < MAX_DOOR_PARTS; i++)
5248 int nr = door_part_order[i].nr;
5249 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5250 struct DoorPartPosInfo *pos = dpc->pos;
5251 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5252 int door_token = dpc->door_token;
5253 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5254 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5255 boolean is_panel_and_door_has_closed = FALSE;
5256 struct Rect *door_rect = &door_rect_list[door_index];
5257 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5259 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5260 int current_door_state = door_state & door_token;
5261 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5262 boolean door_closing = !door_opening;
5263 boolean part_opening = (is_panel ? door_closing : door_opening);
5264 boolean part_closing = !part_opening;
5265 int start_step = (part_opening ? pos->start_step_opening :
5266 pos->start_step_closing);
5267 int step_delay = pos->step_delay;
5268 int step_factor = step_delay / max_step_delay;
5269 int k1 = (step_factor ? k / step_factor + 1 : k);
5270 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5271 int kk = MAX(0, k2);
5274 int src_x, src_y, src_xx, src_yy;
5275 int dst_x, dst_y, dst_xx, dst_yy;
5278 if (door_part_skip[nr])
5281 if (!(door_state & door_token))
5289 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5290 int kk_door = MAX(0, k2_door);
5291 int sync_frame = kk_door * door_delay_value;
5292 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5294 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5295 &g_src_x, &g_src_y);
5300 if (!door_panel_drawn[door_index])
5302 ClearRectangle(drawto, door_rect->x, door_rect->y,
5303 door_rect->width, door_rect->height);
5305 door_panel_drawn[door_index] = TRUE;
5308 // draw opening or closing door parts
5310 if (pos->step_xoffset < 0) // door part on right side
5313 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5316 if (dst_xx + width > door_rect->width)
5317 width = door_rect->width - dst_xx;
5319 else // door part on left side
5322 dst_xx = pos->x - kk * pos->step_xoffset;
5326 src_xx = ABS(dst_xx);
5330 width = g->width - src_xx;
5332 if (width > door_rect->width)
5333 width = door_rect->width;
5335 // printf("::: k == %d [%d] \n", k, start_step);
5338 if (pos->step_yoffset < 0) // door part on bottom side
5341 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5344 if (dst_yy + height > door_rect->height)
5345 height = door_rect->height - dst_yy;
5347 else // door part on top side
5350 dst_yy = pos->y - kk * pos->step_yoffset;
5354 src_yy = ABS(dst_yy);
5358 height = g->height - src_yy;
5361 src_x = g_src_x + src_xx;
5362 src_y = g_src_y + src_yy;
5364 dst_x = door_rect->x + dst_xx;
5365 dst_y = door_rect->y + dst_yy;
5367 is_panel_and_door_has_closed =
5370 panel_has_doors[door_index] &&
5371 k >= num_move_steps_doors_only - 1);
5373 if (width >= 0 && width <= g->width &&
5374 height >= 0 && height <= g->height &&
5375 !is_panel_and_door_has_closed)
5377 if (is_panel || !pos->draw_masked)
5378 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5381 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5385 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5387 if ((part_opening && (width < 0 || height < 0)) ||
5388 (part_closing && (width >= g->width && height >= g->height)))
5389 door_part_done[nr] = TRUE;
5391 // continue door part animations, but not panel after door has closed
5392 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5393 door_part_done_all = FALSE;
5396 if (!(door_state & DOOR_NO_DELAY))
5400 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5402 current_move_delay += max_step_delay;
5404 // prevent OS (Windows) from complaining about program not responding
5408 if (door_part_done_all)
5412 if (!(door_state & DOOR_NO_DELAY))
5414 // wait for specified door action post delay
5415 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5416 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5417 else if (door_state & DOOR_ACTION_1)
5418 door_delay_value = door_1.post_delay;
5419 else if (door_state & DOOR_ACTION_2)
5420 door_delay_value = door_2.post_delay;
5422 while (!DelayReached(&door_delay, door_delay_value))
5427 if (door_state & DOOR_ACTION_1)
5428 door1 = door_state & DOOR_ACTION_1;
5429 if (door_state & DOOR_ACTION_2)
5430 door2 = door_state & DOOR_ACTION_2;
5432 // draw masked border over door area
5433 DrawMaskedBorder(REDRAW_DOOR_1);
5434 DrawMaskedBorder(REDRAW_DOOR_2);
5436 ClearAutoRepeatKeyEvents();
5438 return (door1 | door2);
5441 static boolean useSpecialEditorDoor(void)
5443 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5444 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5446 // do not draw special editor door if editor border defined or redefined
5447 if (graphic_info[graphic].bitmap != NULL || redefined)
5450 // do not draw special editor door if global border defined to be empty
5451 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5454 // do not draw special editor door if viewport definitions do not match
5458 EY + EYSIZE != VY + VYSIZE)
5464 void DrawSpecialEditorDoor(void)
5466 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5467 int top_border_width = gfx1->width;
5468 int top_border_height = gfx1->height;
5469 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5470 int ex = EX - outer_border;
5471 int ey = EY - outer_border;
5472 int vy = VY - outer_border;
5473 int exsize = EXSIZE + 2 * outer_border;
5475 if (!useSpecialEditorDoor())
5478 // draw bigger level editor toolbox window
5479 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5480 top_border_width, top_border_height, ex, ey - top_border_height);
5481 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5482 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5484 redraw_mask |= REDRAW_ALL;
5487 void UndrawSpecialEditorDoor(void)
5489 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5490 int top_border_width = gfx1->width;
5491 int top_border_height = gfx1->height;
5492 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5493 int ex = EX - outer_border;
5494 int ey = EY - outer_border;
5495 int ey_top = ey - top_border_height;
5496 int exsize = EXSIZE + 2 * outer_border;
5497 int eysize = EYSIZE + 2 * outer_border;
5499 if (!useSpecialEditorDoor())
5502 // draw normal tape recorder window
5503 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5505 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5506 ex, ey_top, top_border_width, top_border_height,
5508 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5509 ex, ey, exsize, eysize, ex, ey);
5513 // if screen background is set to "[NONE]", clear editor toolbox window
5514 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5515 ClearRectangle(drawto, ex, ey, exsize, eysize);
5518 redraw_mask |= REDRAW_ALL;
5522 // ---------- new tool button stuff -------------------------------------------
5527 struct TextPosInfo *pos;
5530 } toolbutton_info[NUM_TOOL_BUTTONS] =
5533 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5534 TOOL_CTRL_ID_YES, "yes"
5537 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5538 TOOL_CTRL_ID_NO, "no"
5541 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5542 TOOL_CTRL_ID_CONFIRM, "confirm"
5545 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5546 TOOL_CTRL_ID_PLAYER_1, "player 1"
5549 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5550 TOOL_CTRL_ID_PLAYER_2, "player 2"
5553 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5554 TOOL_CTRL_ID_PLAYER_3, "player 3"
5557 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5558 TOOL_CTRL_ID_PLAYER_4, "player 4"
5562 void CreateToolButtons(void)
5566 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5568 int graphic = toolbutton_info[i].graphic;
5569 struct GraphicInfo *gfx = &graphic_info[graphic];
5570 struct TextPosInfo *pos = toolbutton_info[i].pos;
5571 struct GadgetInfo *gi;
5572 Bitmap *deco_bitmap = None;
5573 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5574 unsigned int event_mask = GD_EVENT_RELEASED;
5577 int gd_x = gfx->src_x;
5578 int gd_y = gfx->src_y;
5579 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5580 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5585 if (global.use_envelope_request)
5587 setRequestPosition(&dx, &dy, TRUE);
5589 // check if request buttons are outside of envelope and fix, if needed
5590 if (x < 0 || x + gfx->width > request.width ||
5591 y < 0 || y + gfx->height > request.height)
5593 if (id == TOOL_CTRL_ID_YES)
5596 y = request.height - 2 * request.border_size - gfx->height;
5598 else if (id == TOOL_CTRL_ID_NO)
5600 x = request.width - 2 * request.border_size - gfx->width;
5601 y = request.height - 2 * request.border_size - gfx->height;
5603 else if (id == TOOL_CTRL_ID_CONFIRM)
5605 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5606 y = request.height - 2 * request.border_size - gfx->height;
5608 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5610 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5612 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5613 y = request.height - 2 * request.border_size - gfx->height * 2;
5615 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5616 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5621 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5623 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5625 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5626 pos->size, &deco_bitmap, &deco_x, &deco_y);
5627 deco_xpos = (gfx->width - pos->size) / 2;
5628 deco_ypos = (gfx->height - pos->size) / 2;
5631 gi = CreateGadget(GDI_CUSTOM_ID, id,
5632 GDI_IMAGE_ID, graphic,
5633 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5636 GDI_WIDTH, gfx->width,
5637 GDI_HEIGHT, gfx->height,
5638 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5639 GDI_STATE, GD_BUTTON_UNPRESSED,
5640 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5641 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5642 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5643 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5644 GDI_DECORATION_SIZE, pos->size, pos->size,
5645 GDI_DECORATION_SHIFTING, 1, 1,
5646 GDI_DIRECT_DRAW, FALSE,
5647 GDI_EVENT_MASK, event_mask,
5648 GDI_CALLBACK_ACTION, HandleToolButtons,
5652 Error(ERR_EXIT, "cannot create gadget");
5654 tool_gadget[id] = gi;
5658 void FreeToolButtons(void)
5662 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5663 FreeGadget(tool_gadget[i]);
5666 static void UnmapToolButtons(void)
5670 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5671 UnmapGadget(tool_gadget[i]);
5674 static void HandleToolButtons(struct GadgetInfo *gi)
5676 request_gadget_id = gi->custom_id;
5679 static struct Mapping_EM_to_RND_object
5682 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5683 boolean is_backside; // backside of moving element
5689 em_object_mapping_list[] =
5692 Xblank, TRUE, FALSE,
5696 Yacid_splash_eB, FALSE, FALSE,
5697 EL_ACID_SPLASH_RIGHT, -1, -1
5700 Yacid_splash_wB, FALSE, FALSE,
5701 EL_ACID_SPLASH_LEFT, -1, -1
5704 #ifdef EM_ENGINE_BAD_ROLL
5706 Xstone_force_e, FALSE, FALSE,
5707 EL_ROCK, -1, MV_BIT_RIGHT
5710 Xstone_force_w, FALSE, FALSE,
5711 EL_ROCK, -1, MV_BIT_LEFT
5714 Xnut_force_e, FALSE, FALSE,
5715 EL_NUT, -1, MV_BIT_RIGHT
5718 Xnut_force_w, FALSE, FALSE,
5719 EL_NUT, -1, MV_BIT_LEFT
5722 Xspring_force_e, FALSE, FALSE,
5723 EL_SPRING, -1, MV_BIT_RIGHT
5726 Xspring_force_w, FALSE, FALSE,
5727 EL_SPRING, -1, MV_BIT_LEFT
5730 Xemerald_force_e, FALSE, FALSE,
5731 EL_EMERALD, -1, MV_BIT_RIGHT
5734 Xemerald_force_w, FALSE, FALSE,
5735 EL_EMERALD, -1, MV_BIT_LEFT
5738 Xdiamond_force_e, FALSE, FALSE,
5739 EL_DIAMOND, -1, MV_BIT_RIGHT
5742 Xdiamond_force_w, FALSE, FALSE,
5743 EL_DIAMOND, -1, MV_BIT_LEFT
5746 Xbomb_force_e, FALSE, FALSE,
5747 EL_BOMB, -1, MV_BIT_RIGHT
5750 Xbomb_force_w, FALSE, FALSE,
5751 EL_BOMB, -1, MV_BIT_LEFT
5753 #endif // EM_ENGINE_BAD_ROLL
5756 Xstone, TRUE, FALSE,
5760 Xstone_pause, FALSE, FALSE,
5764 Xstone_fall, FALSE, FALSE,
5768 Ystone_s, FALSE, FALSE,
5769 EL_ROCK, ACTION_FALLING, -1
5772 Ystone_sB, FALSE, TRUE,
5773 EL_ROCK, ACTION_FALLING, -1
5776 Ystone_e, FALSE, FALSE,
5777 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5780 Ystone_eB, FALSE, TRUE,
5781 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5784 Ystone_w, FALSE, FALSE,
5785 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5788 Ystone_wB, FALSE, TRUE,
5789 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5796 Xnut_pause, FALSE, FALSE,
5800 Xnut_fall, FALSE, FALSE,
5804 Ynut_s, FALSE, FALSE,
5805 EL_NUT, ACTION_FALLING, -1
5808 Ynut_sB, FALSE, TRUE,
5809 EL_NUT, ACTION_FALLING, -1
5812 Ynut_e, FALSE, FALSE,
5813 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5816 Ynut_eB, FALSE, TRUE,
5817 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5820 Ynut_w, FALSE, FALSE,
5821 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5824 Ynut_wB, FALSE, TRUE,
5825 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5828 Xbug_n, TRUE, FALSE,
5832 Xbug_e, TRUE, FALSE,
5833 EL_BUG_RIGHT, -1, -1
5836 Xbug_s, TRUE, FALSE,
5840 Xbug_w, TRUE, FALSE,
5844 Xbug_gon, FALSE, FALSE,
5848 Xbug_goe, FALSE, FALSE,
5849 EL_BUG_RIGHT, -1, -1
5852 Xbug_gos, FALSE, FALSE,
5856 Xbug_gow, FALSE, FALSE,
5860 Ybug_n, FALSE, FALSE,
5861 EL_BUG, ACTION_MOVING, MV_BIT_UP
5864 Ybug_nB, FALSE, TRUE,
5865 EL_BUG, ACTION_MOVING, MV_BIT_UP
5868 Ybug_e, FALSE, FALSE,
5869 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5872 Ybug_eB, FALSE, TRUE,
5873 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5876 Ybug_s, FALSE, FALSE,
5877 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5880 Ybug_sB, FALSE, TRUE,
5881 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5884 Ybug_w, FALSE, FALSE,
5885 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5888 Ybug_wB, FALSE, TRUE,
5889 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5892 Ybug_w_n, FALSE, FALSE,
5893 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5896 Ybug_n_e, FALSE, FALSE,
5897 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5900 Ybug_e_s, FALSE, FALSE,
5901 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5904 Ybug_s_w, FALSE, FALSE,
5905 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5908 Ybug_e_n, FALSE, FALSE,
5909 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5912 Ybug_s_e, FALSE, FALSE,
5913 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5916 Ybug_w_s, FALSE, FALSE,
5917 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5920 Ybug_n_w, FALSE, FALSE,
5921 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5924 Ybug_stone, FALSE, FALSE,
5925 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5928 Ybug_spring, FALSE, FALSE,
5929 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5932 Xtank_n, TRUE, FALSE,
5933 EL_SPACESHIP_UP, -1, -1
5936 Xtank_e, TRUE, FALSE,
5937 EL_SPACESHIP_RIGHT, -1, -1
5940 Xtank_s, TRUE, FALSE,
5941 EL_SPACESHIP_DOWN, -1, -1
5944 Xtank_w, TRUE, FALSE,
5945 EL_SPACESHIP_LEFT, -1, -1
5948 Xtank_gon, FALSE, FALSE,
5949 EL_SPACESHIP_UP, -1, -1
5952 Xtank_goe, FALSE, FALSE,
5953 EL_SPACESHIP_RIGHT, -1, -1
5956 Xtank_gos, FALSE, FALSE,
5957 EL_SPACESHIP_DOWN, -1, -1
5960 Xtank_gow, FALSE, FALSE,
5961 EL_SPACESHIP_LEFT, -1, -1
5964 Ytank_n, FALSE, FALSE,
5965 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5968 Ytank_nB, FALSE, TRUE,
5969 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5972 Ytank_e, FALSE, FALSE,
5973 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5976 Ytank_eB, FALSE, TRUE,
5977 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5980 Ytank_s, FALSE, FALSE,
5981 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5984 Ytank_sB, FALSE, TRUE,
5985 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5988 Ytank_w, FALSE, FALSE,
5989 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5992 Ytank_wB, FALSE, TRUE,
5993 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5996 Ytank_w_n, FALSE, FALSE,
5997 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6000 Ytank_n_e, FALSE, FALSE,
6001 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6004 Ytank_e_s, FALSE, FALSE,
6005 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6008 Ytank_s_w, FALSE, FALSE,
6009 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6012 Ytank_e_n, FALSE, FALSE,
6013 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6016 Ytank_s_e, FALSE, FALSE,
6017 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6020 Ytank_w_s, FALSE, FALSE,
6021 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6024 Ytank_n_w, FALSE, FALSE,
6025 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6028 Ytank_stone, FALSE, FALSE,
6029 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6032 Ytank_spring, FALSE, FALSE,
6033 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6036 Xandroid, TRUE, FALSE,
6037 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6040 Xandroid_1_n, FALSE, FALSE,
6041 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6044 Xandroid_2_n, FALSE, FALSE,
6045 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6048 Xandroid_1_e, FALSE, FALSE,
6049 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6052 Xandroid_2_e, FALSE, FALSE,
6053 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6056 Xandroid_1_w, FALSE, FALSE,
6057 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6060 Xandroid_2_w, FALSE, FALSE,
6061 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6064 Xandroid_1_s, FALSE, FALSE,
6065 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6068 Xandroid_2_s, FALSE, FALSE,
6069 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6072 Yandroid_n, FALSE, FALSE,
6073 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6076 Yandroid_nB, FALSE, TRUE,
6077 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6080 Yandroid_ne, FALSE, FALSE,
6081 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6084 Yandroid_neB, FALSE, TRUE,
6085 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6088 Yandroid_e, FALSE, FALSE,
6089 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6092 Yandroid_eB, FALSE, TRUE,
6093 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6096 Yandroid_se, FALSE, FALSE,
6097 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6100 Yandroid_seB, FALSE, TRUE,
6101 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6104 Yandroid_s, FALSE, FALSE,
6105 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6108 Yandroid_sB, FALSE, TRUE,
6109 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6112 Yandroid_sw, FALSE, FALSE,
6113 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6116 Yandroid_swB, FALSE, TRUE,
6117 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6120 Yandroid_w, FALSE, FALSE,
6121 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6124 Yandroid_wB, FALSE, TRUE,
6125 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6128 Yandroid_nw, FALSE, FALSE,
6129 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6132 Yandroid_nwB, FALSE, TRUE,
6133 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6136 Xspring, TRUE, FALSE,
6140 Xspring_pause, FALSE, FALSE,
6144 Xspring_e, FALSE, FALSE,
6148 Xspring_w, FALSE, FALSE,
6152 Xspring_fall, FALSE, FALSE,
6156 Yspring_s, FALSE, FALSE,
6157 EL_SPRING, ACTION_FALLING, -1
6160 Yspring_sB, FALSE, TRUE,
6161 EL_SPRING, ACTION_FALLING, -1
6164 Yspring_e, FALSE, FALSE,
6165 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6168 Yspring_eB, FALSE, TRUE,
6169 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6172 Yspring_w, FALSE, FALSE,
6173 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6176 Yspring_wB, FALSE, TRUE,
6177 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6180 Yspring_kill_e, FALSE, FALSE,
6181 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6184 Yspring_kill_eB, FALSE, TRUE,
6185 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6188 Yspring_kill_w, FALSE, FALSE,
6189 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6192 Yspring_kill_wB, FALSE, TRUE,
6193 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6196 Xeater_n, TRUE, FALSE,
6197 EL_YAMYAM_UP, -1, -1
6200 Xeater_e, TRUE, FALSE,
6201 EL_YAMYAM_RIGHT, -1, -1
6204 Xeater_w, TRUE, FALSE,
6205 EL_YAMYAM_LEFT, -1, -1
6208 Xeater_s, TRUE, FALSE,
6209 EL_YAMYAM_DOWN, -1, -1
6212 Yeater_n, FALSE, FALSE,
6213 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6216 Yeater_nB, FALSE, TRUE,
6217 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6220 Yeater_e, FALSE, FALSE,
6221 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6224 Yeater_eB, FALSE, TRUE,
6225 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6228 Yeater_s, FALSE, FALSE,
6229 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6232 Yeater_sB, FALSE, TRUE,
6233 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6236 Yeater_w, FALSE, FALSE,
6237 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6240 Yeater_wB, FALSE, TRUE,
6241 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6244 Yeater_stone, FALSE, FALSE,
6245 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6248 Yeater_spring, FALSE, FALSE,
6249 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6252 Xalien, TRUE, FALSE,
6256 Xalien_pause, FALSE, FALSE,
6260 Yalien_n, FALSE, FALSE,
6261 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6264 Yalien_nB, FALSE, TRUE,
6265 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6268 Yalien_e, FALSE, FALSE,
6269 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6272 Yalien_eB, FALSE, TRUE,
6273 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6276 Yalien_s, FALSE, FALSE,
6277 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6280 Yalien_sB, FALSE, TRUE,
6281 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6284 Yalien_w, FALSE, FALSE,
6285 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6288 Yalien_wB, FALSE, TRUE,
6289 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6292 Yalien_stone, FALSE, FALSE,
6293 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6296 Yalien_spring, FALSE, FALSE,
6297 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6300 Xemerald, TRUE, FALSE,
6304 Xemerald_pause, FALSE, FALSE,
6308 Xemerald_fall, FALSE, FALSE,
6312 Xemerald_shine, FALSE, FALSE,
6313 EL_EMERALD, ACTION_TWINKLING, -1
6316 Yemerald_s, FALSE, FALSE,
6317 EL_EMERALD, ACTION_FALLING, -1
6320 Yemerald_sB, FALSE, TRUE,
6321 EL_EMERALD, ACTION_FALLING, -1
6324 Yemerald_e, FALSE, FALSE,
6325 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6328 Yemerald_eB, FALSE, TRUE,
6329 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6332 Yemerald_w, FALSE, FALSE,
6333 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6336 Yemerald_wB, FALSE, TRUE,
6337 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6340 Yemerald_eat, FALSE, FALSE,
6341 EL_EMERALD, ACTION_COLLECTING, -1
6344 Yemerald_stone, FALSE, FALSE,
6345 EL_NUT, ACTION_BREAKING, -1
6348 Xdiamond, TRUE, FALSE,
6352 Xdiamond_pause, FALSE, FALSE,
6356 Xdiamond_fall, FALSE, FALSE,
6360 Xdiamond_shine, FALSE, FALSE,
6361 EL_DIAMOND, ACTION_TWINKLING, -1
6364 Ydiamond_s, FALSE, FALSE,
6365 EL_DIAMOND, ACTION_FALLING, -1
6368 Ydiamond_sB, FALSE, TRUE,
6369 EL_DIAMOND, ACTION_FALLING, -1
6372 Ydiamond_e, FALSE, FALSE,
6373 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6376 Ydiamond_eB, FALSE, TRUE,
6377 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6380 Ydiamond_w, FALSE, FALSE,
6381 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6384 Ydiamond_wB, FALSE, TRUE,
6385 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6388 Ydiamond_eat, FALSE, FALSE,
6389 EL_DIAMOND, ACTION_COLLECTING, -1
6392 Ydiamond_stone, FALSE, FALSE,
6393 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6396 Xdrip_fall, TRUE, FALSE,
6397 EL_AMOEBA_DROP, -1, -1
6400 Xdrip_stretch, FALSE, FALSE,
6401 EL_AMOEBA_DROP, ACTION_FALLING, -1
6404 Xdrip_stretchB, FALSE, TRUE,
6405 EL_AMOEBA_DROP, ACTION_FALLING, -1
6408 Xdrip_eat, FALSE, FALSE,
6409 EL_AMOEBA_DROP, ACTION_GROWING, -1
6412 Ydrip_s1, FALSE, FALSE,
6413 EL_AMOEBA_DROP, ACTION_FALLING, -1
6416 Ydrip_s1B, FALSE, TRUE,
6417 EL_AMOEBA_DROP, ACTION_FALLING, -1
6420 Ydrip_s2, FALSE, FALSE,
6421 EL_AMOEBA_DROP, ACTION_FALLING, -1
6424 Ydrip_s2B, FALSE, TRUE,
6425 EL_AMOEBA_DROP, ACTION_FALLING, -1
6432 Xbomb_pause, FALSE, FALSE,
6436 Xbomb_fall, FALSE, FALSE,
6440 Ybomb_s, FALSE, FALSE,
6441 EL_BOMB, ACTION_FALLING, -1
6444 Ybomb_sB, FALSE, TRUE,
6445 EL_BOMB, ACTION_FALLING, -1
6448 Ybomb_e, FALSE, FALSE,
6449 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6452 Ybomb_eB, FALSE, TRUE,
6453 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6456 Ybomb_w, FALSE, FALSE,
6457 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6460 Ybomb_wB, FALSE, TRUE,
6461 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6464 Ybomb_eat, FALSE, FALSE,
6465 EL_BOMB, ACTION_ACTIVATING, -1
6468 Xballoon, TRUE, FALSE,
6472 Yballoon_n, FALSE, FALSE,
6473 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6476 Yballoon_nB, FALSE, TRUE,
6477 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6480 Yballoon_e, FALSE, FALSE,
6481 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6484 Yballoon_eB, FALSE, TRUE,
6485 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6488 Yballoon_s, FALSE, FALSE,
6489 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6492 Yballoon_sB, FALSE, TRUE,
6493 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6496 Yballoon_w, FALSE, FALSE,
6497 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6500 Yballoon_wB, FALSE, TRUE,
6501 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6504 Xgrass, TRUE, FALSE,
6505 EL_EMC_GRASS, -1, -1
6508 Ygrass_nB, FALSE, FALSE,
6509 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6512 Ygrass_eB, FALSE, FALSE,
6513 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6516 Ygrass_sB, FALSE, FALSE,
6517 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6520 Ygrass_wB, FALSE, FALSE,
6521 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6528 Ydirt_nB, FALSE, FALSE,
6529 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6532 Ydirt_eB, FALSE, FALSE,
6533 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6536 Ydirt_sB, FALSE, FALSE,
6537 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6540 Ydirt_wB, FALSE, FALSE,
6541 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6544 Xacid_ne, TRUE, FALSE,
6545 EL_ACID_POOL_TOPRIGHT, -1, -1
6548 Xacid_se, TRUE, FALSE,
6549 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6552 Xacid_s, TRUE, FALSE,
6553 EL_ACID_POOL_BOTTOM, -1, -1
6556 Xacid_sw, TRUE, FALSE,
6557 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6560 Xacid_nw, TRUE, FALSE,
6561 EL_ACID_POOL_TOPLEFT, -1, -1
6564 Xacid_1, TRUE, FALSE,
6568 Xacid_2, FALSE, FALSE,
6572 Xacid_3, FALSE, FALSE,
6576 Xacid_4, FALSE, FALSE,
6580 Xacid_5, FALSE, FALSE,
6584 Xacid_6, FALSE, FALSE,
6588 Xacid_7, FALSE, FALSE,
6592 Xacid_8, FALSE, FALSE,
6596 Xball_1, TRUE, FALSE,
6597 EL_EMC_MAGIC_BALL, -1, -1
6600 Xball_1B, FALSE, FALSE,
6601 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6604 Xball_2, FALSE, FALSE,
6605 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6608 Xball_2B, FALSE, FALSE,
6609 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6612 Yball_eat, FALSE, FALSE,
6613 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6616 Ykey_1_eat, FALSE, FALSE,
6617 EL_EM_KEY_1, ACTION_COLLECTING, -1
6620 Ykey_2_eat, FALSE, FALSE,
6621 EL_EM_KEY_2, ACTION_COLLECTING, -1
6624 Ykey_3_eat, FALSE, FALSE,
6625 EL_EM_KEY_3, ACTION_COLLECTING, -1
6628 Ykey_4_eat, FALSE, FALSE,
6629 EL_EM_KEY_4, ACTION_COLLECTING, -1
6632 Ykey_5_eat, FALSE, FALSE,
6633 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6636 Ykey_6_eat, FALSE, FALSE,
6637 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6640 Ykey_7_eat, FALSE, FALSE,
6641 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6644 Ykey_8_eat, FALSE, FALSE,
6645 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6648 Ylenses_eat, FALSE, FALSE,
6649 EL_EMC_LENSES, ACTION_COLLECTING, -1
6652 Ymagnify_eat, FALSE, FALSE,
6653 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6656 Ygrass_eat, FALSE, FALSE,
6657 EL_EMC_GRASS, ACTION_SNAPPING, -1
6660 Ydirt_eat, FALSE, FALSE,
6661 EL_SAND, ACTION_SNAPPING, -1
6664 Xgrow_ns, TRUE, FALSE,
6665 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6668 Ygrow_ns_eat, FALSE, FALSE,
6669 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6672 Xgrow_ew, TRUE, FALSE,
6673 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6676 Ygrow_ew_eat, FALSE, FALSE,
6677 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6680 Xwonderwall, TRUE, FALSE,
6681 EL_MAGIC_WALL, -1, -1
6684 XwonderwallB, FALSE, FALSE,
6685 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6688 Xamoeba_1, TRUE, FALSE,
6689 EL_AMOEBA_DRY, ACTION_OTHER, -1
6692 Xamoeba_2, FALSE, FALSE,
6693 EL_AMOEBA_DRY, ACTION_OTHER, -1
6696 Xamoeba_3, FALSE, FALSE,
6697 EL_AMOEBA_DRY, ACTION_OTHER, -1
6700 Xamoeba_4, FALSE, FALSE,
6701 EL_AMOEBA_DRY, ACTION_OTHER, -1
6704 Xamoeba_5, TRUE, FALSE,
6705 EL_AMOEBA_WET, ACTION_OTHER, -1
6708 Xamoeba_6, FALSE, FALSE,
6709 EL_AMOEBA_WET, ACTION_OTHER, -1
6712 Xamoeba_7, FALSE, FALSE,
6713 EL_AMOEBA_WET, ACTION_OTHER, -1
6716 Xamoeba_8, FALSE, FALSE,
6717 EL_AMOEBA_WET, ACTION_OTHER, -1
6720 Xdoor_1, TRUE, FALSE,
6721 EL_EM_GATE_1, -1, -1
6724 Xdoor_2, TRUE, FALSE,
6725 EL_EM_GATE_2, -1, -1
6728 Xdoor_3, TRUE, FALSE,
6729 EL_EM_GATE_3, -1, -1
6732 Xdoor_4, TRUE, FALSE,
6733 EL_EM_GATE_4, -1, -1
6736 Xdoor_5, TRUE, FALSE,
6737 EL_EMC_GATE_5, -1, -1
6740 Xdoor_6, TRUE, FALSE,
6741 EL_EMC_GATE_6, -1, -1
6744 Xdoor_7, TRUE, FALSE,
6745 EL_EMC_GATE_7, -1, -1
6748 Xdoor_8, TRUE, FALSE,
6749 EL_EMC_GATE_8, -1, -1
6752 Xkey_1, TRUE, FALSE,
6756 Xkey_2, TRUE, FALSE,
6760 Xkey_3, TRUE, FALSE,
6764 Xkey_4, TRUE, FALSE,
6768 Xkey_5, TRUE, FALSE,
6769 EL_EMC_KEY_5, -1, -1
6772 Xkey_6, TRUE, FALSE,
6773 EL_EMC_KEY_6, -1, -1
6776 Xkey_7, TRUE, FALSE,
6777 EL_EMC_KEY_7, -1, -1
6780 Xkey_8, TRUE, FALSE,
6781 EL_EMC_KEY_8, -1, -1
6784 Xwind_n, TRUE, FALSE,
6785 EL_BALLOON_SWITCH_UP, -1, -1
6788 Xwind_e, TRUE, FALSE,
6789 EL_BALLOON_SWITCH_RIGHT, -1, -1
6792 Xwind_s, TRUE, FALSE,
6793 EL_BALLOON_SWITCH_DOWN, -1, -1
6796 Xwind_w, TRUE, FALSE,
6797 EL_BALLOON_SWITCH_LEFT, -1, -1
6800 Xwind_nesw, TRUE, FALSE,
6801 EL_BALLOON_SWITCH_ANY, -1, -1
6804 Xwind_stop, TRUE, FALSE,
6805 EL_BALLOON_SWITCH_NONE, -1, -1
6809 EL_EM_EXIT_CLOSED, -1, -1
6812 Xexit_1, TRUE, FALSE,
6813 EL_EM_EXIT_OPEN, -1, -1
6816 Xexit_2, FALSE, FALSE,
6817 EL_EM_EXIT_OPEN, -1, -1
6820 Xexit_3, FALSE, FALSE,
6821 EL_EM_EXIT_OPEN, -1, -1
6824 Xdynamite, TRUE, FALSE,
6825 EL_EM_DYNAMITE, -1, -1
6828 Ydynamite_eat, FALSE, FALSE,
6829 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6832 Xdynamite_1, TRUE, FALSE,
6833 EL_EM_DYNAMITE_ACTIVE, -1, -1
6836 Xdynamite_2, FALSE, FALSE,
6837 EL_EM_DYNAMITE_ACTIVE, -1, -1
6840 Xdynamite_3, FALSE, FALSE,
6841 EL_EM_DYNAMITE_ACTIVE, -1, -1
6844 Xdynamite_4, FALSE, FALSE,
6845 EL_EM_DYNAMITE_ACTIVE, -1, -1
6848 Xbumper, TRUE, FALSE,
6849 EL_EMC_SPRING_BUMPER, -1, -1
6852 XbumperB, FALSE, FALSE,
6853 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6856 Xwheel, TRUE, FALSE,
6857 EL_ROBOT_WHEEL, -1, -1
6860 XwheelB, FALSE, FALSE,
6861 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6864 Xswitch, TRUE, FALSE,
6865 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6868 XswitchB, FALSE, FALSE,
6869 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6873 EL_QUICKSAND_EMPTY, -1, -1
6876 Xsand_stone, TRUE, FALSE,
6877 EL_QUICKSAND_FULL, -1, -1
6880 Xsand_stonein_1, FALSE, TRUE,
6881 EL_ROCK, ACTION_FILLING, -1
6884 Xsand_stonein_2, FALSE, TRUE,
6885 EL_ROCK, ACTION_FILLING, -1
6888 Xsand_stonein_3, FALSE, TRUE,
6889 EL_ROCK, ACTION_FILLING, -1
6892 Xsand_stonein_4, FALSE, TRUE,
6893 EL_ROCK, ACTION_FILLING, -1
6896 Xsand_stonesand_1, FALSE, FALSE,
6897 EL_QUICKSAND_EMPTYING, -1, -1
6900 Xsand_stonesand_2, FALSE, FALSE,
6901 EL_QUICKSAND_EMPTYING, -1, -1
6904 Xsand_stonesand_3, FALSE, FALSE,
6905 EL_QUICKSAND_EMPTYING, -1, -1
6908 Xsand_stonesand_4, FALSE, FALSE,
6909 EL_QUICKSAND_EMPTYING, -1, -1
6912 Xsand_stonesand_quickout_1, FALSE, FALSE,
6913 EL_QUICKSAND_EMPTYING, -1, -1
6916 Xsand_stonesand_quickout_2, FALSE, FALSE,
6917 EL_QUICKSAND_EMPTYING, -1, -1
6920 Xsand_stoneout_1, FALSE, FALSE,
6921 EL_ROCK, ACTION_EMPTYING, -1
6924 Xsand_stoneout_2, FALSE, FALSE,
6925 EL_ROCK, ACTION_EMPTYING, -1
6928 Xsand_sandstone_1, FALSE, FALSE,
6929 EL_QUICKSAND_FILLING, -1, -1
6932 Xsand_sandstone_2, FALSE, FALSE,
6933 EL_QUICKSAND_FILLING, -1, -1
6936 Xsand_sandstone_3, FALSE, FALSE,
6937 EL_QUICKSAND_FILLING, -1, -1
6940 Xsand_sandstone_4, FALSE, FALSE,
6941 EL_QUICKSAND_FILLING, -1, -1
6944 Xplant, TRUE, FALSE,
6945 EL_EMC_PLANT, -1, -1
6948 Yplant, FALSE, FALSE,
6949 EL_EMC_PLANT, -1, -1
6952 Xlenses, TRUE, FALSE,
6953 EL_EMC_LENSES, -1, -1
6956 Xmagnify, TRUE, FALSE,
6957 EL_EMC_MAGNIFIER, -1, -1
6960 Xdripper, TRUE, FALSE,
6961 EL_EMC_DRIPPER, -1, -1
6964 XdripperB, FALSE, FALSE,
6965 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6968 Xfake_blank, TRUE, FALSE,
6969 EL_INVISIBLE_WALL, -1, -1
6972 Xfake_blankB, FALSE, FALSE,
6973 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6976 Xfake_grass, TRUE, FALSE,
6977 EL_EMC_FAKE_GRASS, -1, -1
6980 Xfake_grassB, FALSE, FALSE,
6981 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6984 Xfake_door_1, TRUE, FALSE,
6985 EL_EM_GATE_1_GRAY, -1, -1
6988 Xfake_door_2, TRUE, FALSE,
6989 EL_EM_GATE_2_GRAY, -1, -1
6992 Xfake_door_3, TRUE, FALSE,
6993 EL_EM_GATE_3_GRAY, -1, -1
6996 Xfake_door_4, TRUE, FALSE,
6997 EL_EM_GATE_4_GRAY, -1, -1
7000 Xfake_door_5, TRUE, FALSE,
7001 EL_EMC_GATE_5_GRAY, -1, -1
7004 Xfake_door_6, TRUE, FALSE,
7005 EL_EMC_GATE_6_GRAY, -1, -1
7008 Xfake_door_7, TRUE, FALSE,
7009 EL_EMC_GATE_7_GRAY, -1, -1
7012 Xfake_door_8, TRUE, FALSE,
7013 EL_EMC_GATE_8_GRAY, -1, -1
7016 Xfake_acid_1, TRUE, FALSE,
7017 EL_EMC_FAKE_ACID, -1, -1
7020 Xfake_acid_2, FALSE, FALSE,
7021 EL_EMC_FAKE_ACID, -1, -1
7024 Xfake_acid_3, FALSE, FALSE,
7025 EL_EMC_FAKE_ACID, -1, -1
7028 Xfake_acid_4, FALSE, FALSE,
7029 EL_EMC_FAKE_ACID, -1, -1
7032 Xfake_acid_5, FALSE, FALSE,
7033 EL_EMC_FAKE_ACID, -1, -1
7036 Xfake_acid_6, FALSE, FALSE,
7037 EL_EMC_FAKE_ACID, -1, -1
7040 Xfake_acid_7, FALSE, FALSE,
7041 EL_EMC_FAKE_ACID, -1, -1
7044 Xfake_acid_8, FALSE, FALSE,
7045 EL_EMC_FAKE_ACID, -1, -1
7048 Xsteel_1, TRUE, FALSE,
7049 EL_STEELWALL, -1, -1
7052 Xsteel_2, TRUE, FALSE,
7053 EL_EMC_STEELWALL_2, -1, -1
7056 Xsteel_3, TRUE, FALSE,
7057 EL_EMC_STEELWALL_3, -1, -1
7060 Xsteel_4, TRUE, FALSE,
7061 EL_EMC_STEELWALL_4, -1, -1
7064 Xwall_1, TRUE, FALSE,
7068 Xwall_2, TRUE, FALSE,
7069 EL_EMC_WALL_14, -1, -1
7072 Xwall_3, TRUE, FALSE,
7073 EL_EMC_WALL_15, -1, -1
7076 Xwall_4, TRUE, FALSE,
7077 EL_EMC_WALL_16, -1, -1
7080 Xround_wall_1, TRUE, FALSE,
7081 EL_WALL_SLIPPERY, -1, -1
7084 Xround_wall_2, TRUE, FALSE,
7085 EL_EMC_WALL_SLIPPERY_2, -1, -1
7088 Xround_wall_3, TRUE, FALSE,
7089 EL_EMC_WALL_SLIPPERY_3, -1, -1
7092 Xround_wall_4, TRUE, FALSE,
7093 EL_EMC_WALL_SLIPPERY_4, -1, -1
7096 Xdecor_1, TRUE, FALSE,
7097 EL_EMC_WALL_8, -1, -1
7100 Xdecor_2, TRUE, FALSE,
7101 EL_EMC_WALL_6, -1, -1
7104 Xdecor_3, TRUE, FALSE,
7105 EL_EMC_WALL_4, -1, -1
7108 Xdecor_4, TRUE, FALSE,
7109 EL_EMC_WALL_7, -1, -1
7112 Xdecor_5, TRUE, FALSE,
7113 EL_EMC_WALL_5, -1, -1
7116 Xdecor_6, TRUE, FALSE,
7117 EL_EMC_WALL_9, -1, -1
7120 Xdecor_7, TRUE, FALSE,
7121 EL_EMC_WALL_10, -1, -1
7124 Xdecor_8, TRUE, FALSE,
7125 EL_EMC_WALL_1, -1, -1
7128 Xdecor_9, TRUE, FALSE,
7129 EL_EMC_WALL_2, -1, -1
7132 Xdecor_10, TRUE, FALSE,
7133 EL_EMC_WALL_3, -1, -1
7136 Xdecor_11, TRUE, FALSE,
7137 EL_EMC_WALL_11, -1, -1
7140 Xdecor_12, TRUE, FALSE,
7141 EL_EMC_WALL_12, -1, -1
7144 Xalpha_0, TRUE, FALSE,
7145 EL_CHAR('0'), -1, -1
7148 Xalpha_1, TRUE, FALSE,
7149 EL_CHAR('1'), -1, -1
7152 Xalpha_2, TRUE, FALSE,
7153 EL_CHAR('2'), -1, -1
7156 Xalpha_3, TRUE, FALSE,
7157 EL_CHAR('3'), -1, -1
7160 Xalpha_4, TRUE, FALSE,
7161 EL_CHAR('4'), -1, -1
7164 Xalpha_5, TRUE, FALSE,
7165 EL_CHAR('5'), -1, -1
7168 Xalpha_6, TRUE, FALSE,
7169 EL_CHAR('6'), -1, -1
7172 Xalpha_7, TRUE, FALSE,
7173 EL_CHAR('7'), -1, -1
7176 Xalpha_8, TRUE, FALSE,
7177 EL_CHAR('8'), -1, -1
7180 Xalpha_9, TRUE, FALSE,
7181 EL_CHAR('9'), -1, -1
7184 Xalpha_excla, TRUE, FALSE,
7185 EL_CHAR('!'), -1, -1
7188 Xalpha_quote, TRUE, FALSE,
7189 EL_CHAR('"'), -1, -1
7192 Xalpha_comma, TRUE, FALSE,
7193 EL_CHAR(','), -1, -1
7196 Xalpha_minus, TRUE, FALSE,
7197 EL_CHAR('-'), -1, -1
7200 Xalpha_perio, TRUE, FALSE,
7201 EL_CHAR('.'), -1, -1
7204 Xalpha_colon, TRUE, FALSE,
7205 EL_CHAR(':'), -1, -1
7208 Xalpha_quest, TRUE, FALSE,
7209 EL_CHAR('?'), -1, -1
7212 Xalpha_a, TRUE, FALSE,
7213 EL_CHAR('A'), -1, -1
7216 Xalpha_b, TRUE, FALSE,
7217 EL_CHAR('B'), -1, -1
7220 Xalpha_c, TRUE, FALSE,
7221 EL_CHAR('C'), -1, -1
7224 Xalpha_d, TRUE, FALSE,
7225 EL_CHAR('D'), -1, -1
7228 Xalpha_e, TRUE, FALSE,
7229 EL_CHAR('E'), -1, -1
7232 Xalpha_f, TRUE, FALSE,
7233 EL_CHAR('F'), -1, -1
7236 Xalpha_g, TRUE, FALSE,
7237 EL_CHAR('G'), -1, -1
7240 Xalpha_h, TRUE, FALSE,
7241 EL_CHAR('H'), -1, -1
7244 Xalpha_i, TRUE, FALSE,
7245 EL_CHAR('I'), -1, -1
7248 Xalpha_j, TRUE, FALSE,
7249 EL_CHAR('J'), -1, -1
7252 Xalpha_k, TRUE, FALSE,
7253 EL_CHAR('K'), -1, -1
7256 Xalpha_l, TRUE, FALSE,
7257 EL_CHAR('L'), -1, -1
7260 Xalpha_m, TRUE, FALSE,
7261 EL_CHAR('M'), -1, -1
7264 Xalpha_n, TRUE, FALSE,
7265 EL_CHAR('N'), -1, -1
7268 Xalpha_o, TRUE, FALSE,
7269 EL_CHAR('O'), -1, -1
7272 Xalpha_p, TRUE, FALSE,
7273 EL_CHAR('P'), -1, -1
7276 Xalpha_q, TRUE, FALSE,
7277 EL_CHAR('Q'), -1, -1
7280 Xalpha_r, TRUE, FALSE,
7281 EL_CHAR('R'), -1, -1
7284 Xalpha_s, TRUE, FALSE,
7285 EL_CHAR('S'), -1, -1
7288 Xalpha_t, TRUE, FALSE,
7289 EL_CHAR('T'), -1, -1
7292 Xalpha_u, TRUE, FALSE,
7293 EL_CHAR('U'), -1, -1
7296 Xalpha_v, TRUE, FALSE,
7297 EL_CHAR('V'), -1, -1
7300 Xalpha_w, TRUE, FALSE,
7301 EL_CHAR('W'), -1, -1
7304 Xalpha_x, TRUE, FALSE,
7305 EL_CHAR('X'), -1, -1
7308 Xalpha_y, TRUE, FALSE,
7309 EL_CHAR('Y'), -1, -1
7312 Xalpha_z, TRUE, FALSE,
7313 EL_CHAR('Z'), -1, -1
7316 Xalpha_arrow_e, TRUE, FALSE,
7317 EL_CHAR('>'), -1, -1
7320 Xalpha_arrow_w, TRUE, FALSE,
7321 EL_CHAR('<'), -1, -1
7324 Xalpha_copyr, TRUE, FALSE,
7325 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7329 Xboom_bug, FALSE, FALSE,
7330 EL_BUG, ACTION_EXPLODING, -1
7333 Xboom_bomb, FALSE, FALSE,
7334 EL_BOMB, ACTION_EXPLODING, -1
7337 Xboom_android, FALSE, FALSE,
7338 EL_EMC_ANDROID, ACTION_OTHER, -1
7341 Xboom_1, FALSE, FALSE,
7342 EL_DEFAULT, ACTION_EXPLODING, -1
7345 Xboom_2, FALSE, FALSE,
7346 EL_DEFAULT, ACTION_EXPLODING, -1
7349 Znormal, FALSE, FALSE,
7353 Zdynamite, FALSE, FALSE,
7357 Zplayer, FALSE, FALSE,
7361 ZBORDER, FALSE, FALSE,
7371 static struct Mapping_EM_to_RND_player
7380 em_player_mapping_list[] =
7384 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7388 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7392 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7396 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7400 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7404 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7408 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7412 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7416 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7420 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7424 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7428 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7432 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7436 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7440 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7444 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7448 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7452 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7456 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7460 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7464 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7468 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7472 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7476 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7480 EL_PLAYER_1, ACTION_DEFAULT, -1,
7484 EL_PLAYER_2, ACTION_DEFAULT, -1,
7488 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7492 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7496 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7500 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7504 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7508 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7512 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7516 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7520 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7524 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7528 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7532 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7536 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7540 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7544 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7548 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7552 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7556 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7560 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7564 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7568 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7572 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7576 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7580 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7584 EL_PLAYER_3, ACTION_DEFAULT, -1,
7588 EL_PLAYER_4, ACTION_DEFAULT, -1,
7597 int map_element_RND_to_EM(int element_rnd)
7599 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7600 static boolean mapping_initialized = FALSE;
7602 if (!mapping_initialized)
7606 // return "Xalpha_quest" for all undefined elements in mapping array
7607 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7608 mapping_RND_to_EM[i] = Xalpha_quest;
7610 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7611 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7612 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7613 em_object_mapping_list[i].element_em;
7615 mapping_initialized = TRUE;
7618 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7619 return mapping_RND_to_EM[element_rnd];
7621 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7626 int map_element_EM_to_RND(int element_em)
7628 static unsigned short mapping_EM_to_RND[TILE_MAX];
7629 static boolean mapping_initialized = FALSE;
7631 if (!mapping_initialized)
7635 // return "EL_UNKNOWN" for all undefined elements in mapping array
7636 for (i = 0; i < TILE_MAX; i++)
7637 mapping_EM_to_RND[i] = EL_UNKNOWN;
7639 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7640 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7641 em_object_mapping_list[i].element_rnd;
7643 mapping_initialized = TRUE;
7646 if (element_em >= 0 && element_em < TILE_MAX)
7647 return mapping_EM_to_RND[element_em];
7649 Error(ERR_WARN, "invalid EM level element %d", element_em);
7654 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7656 struct LevelInfo_EM *level_em = level->native_em_level;
7657 struct LEVEL *lev = level_em->lev;
7660 for (i = 0; i < TILE_MAX; i++)
7661 lev->android_array[i] = Xblank;
7663 for (i = 0; i < level->num_android_clone_elements; i++)
7665 int element_rnd = level->android_clone_element[i];
7666 int element_em = map_element_RND_to_EM(element_rnd);
7668 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7669 if (em_object_mapping_list[j].element_rnd == element_rnd)
7670 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7674 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7676 struct LevelInfo_EM *level_em = level->native_em_level;
7677 struct LEVEL *lev = level_em->lev;
7680 level->num_android_clone_elements = 0;
7682 for (i = 0; i < TILE_MAX; i++)
7684 int element_em = lev->android_array[i];
7686 boolean element_found = FALSE;
7688 if (element_em == Xblank)
7691 element_rnd = map_element_EM_to_RND(element_em);
7693 for (j = 0; j < level->num_android_clone_elements; j++)
7694 if (level->android_clone_element[j] == element_rnd)
7695 element_found = TRUE;
7699 level->android_clone_element[level->num_android_clone_elements++] =
7702 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7707 if (level->num_android_clone_elements == 0)
7709 level->num_android_clone_elements = 1;
7710 level->android_clone_element[0] = EL_EMPTY;
7714 int map_direction_RND_to_EM(int direction)
7716 return (direction == MV_UP ? 0 :
7717 direction == MV_RIGHT ? 1 :
7718 direction == MV_DOWN ? 2 :
7719 direction == MV_LEFT ? 3 :
7723 int map_direction_EM_to_RND(int direction)
7725 return (direction == 0 ? MV_UP :
7726 direction == 1 ? MV_RIGHT :
7727 direction == 2 ? MV_DOWN :
7728 direction == 3 ? MV_LEFT :
7732 int map_element_RND_to_SP(int element_rnd)
7734 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7736 if (element_rnd >= EL_SP_START &&
7737 element_rnd <= EL_SP_END)
7738 element_sp = element_rnd - EL_SP_START;
7739 else if (element_rnd == EL_EMPTY_SPACE)
7741 else if (element_rnd == EL_INVISIBLE_WALL)
7747 int map_element_SP_to_RND(int element_sp)
7749 int element_rnd = EL_UNKNOWN;
7751 if (element_sp >= 0x00 &&
7753 element_rnd = EL_SP_START + element_sp;
7754 else if (element_sp == 0x28)
7755 element_rnd = EL_INVISIBLE_WALL;
7760 int map_action_SP_to_RND(int action_sp)
7764 case actActive: return ACTION_ACTIVE;
7765 case actImpact: return ACTION_IMPACT;
7766 case actExploding: return ACTION_EXPLODING;
7767 case actDigging: return ACTION_DIGGING;
7768 case actSnapping: return ACTION_SNAPPING;
7769 case actCollecting: return ACTION_COLLECTING;
7770 case actPassing: return ACTION_PASSING;
7771 case actPushing: return ACTION_PUSHING;
7772 case actDropping: return ACTION_DROPPING;
7774 default: return ACTION_DEFAULT;
7778 int map_element_RND_to_MM(int element_rnd)
7780 return (element_rnd >= EL_MM_START_1 &&
7781 element_rnd <= EL_MM_END_1 ?
7782 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7784 element_rnd >= EL_MM_START_2 &&
7785 element_rnd <= EL_MM_END_2 ?
7786 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7788 element_rnd >= EL_CHAR_START &&
7789 element_rnd <= EL_CHAR_END ?
7790 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7792 element_rnd >= EL_MM_RUNTIME_START &&
7793 element_rnd <= EL_MM_RUNTIME_END ?
7794 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7796 element_rnd >= EL_MM_DUMMY_START &&
7797 element_rnd <= EL_MM_DUMMY_END ?
7798 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7800 EL_MM_EMPTY_NATIVE);
7803 int map_element_MM_to_RND(int element_mm)
7805 return (element_mm == EL_MM_EMPTY_NATIVE ||
7806 element_mm == EL_DF_EMPTY_NATIVE ?
7809 element_mm >= EL_MM_START_1_NATIVE &&
7810 element_mm <= EL_MM_END_1_NATIVE ?
7811 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7813 element_mm >= EL_MM_START_2_NATIVE &&
7814 element_mm <= EL_MM_END_2_NATIVE ?
7815 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7817 element_mm >= EL_MM_CHAR_START_NATIVE &&
7818 element_mm <= EL_MM_CHAR_END_NATIVE ?
7819 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7821 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7822 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7823 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7825 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7826 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7827 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7832 int map_action_MM_to_RND(int action_mm)
7834 // all MM actions are defined to exactly match their RND counterparts
7838 int map_sound_MM_to_RND(int sound_mm)
7842 case SND_MM_GAME_LEVELTIME_CHARGING:
7843 return SND_GAME_LEVELTIME_CHARGING;
7845 case SND_MM_GAME_HEALTH_CHARGING:
7846 return SND_GAME_HEALTH_CHARGING;
7849 return SND_UNDEFINED;
7853 int map_mm_wall_element(int element)
7855 return (element >= EL_MM_STEEL_WALL_START &&
7856 element <= EL_MM_STEEL_WALL_END ?
7859 element >= EL_MM_WOODEN_WALL_START &&
7860 element <= EL_MM_WOODEN_WALL_END ?
7863 element >= EL_MM_ICE_WALL_START &&
7864 element <= EL_MM_ICE_WALL_END ?
7867 element >= EL_MM_AMOEBA_WALL_START &&
7868 element <= EL_MM_AMOEBA_WALL_END ?
7871 element >= EL_DF_STEEL_WALL_START &&
7872 element <= EL_DF_STEEL_WALL_END ?
7875 element >= EL_DF_WOODEN_WALL_START &&
7876 element <= EL_DF_WOODEN_WALL_END ?
7882 int map_mm_wall_element_editor(int element)
7886 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7887 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7888 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7889 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7890 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7891 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7893 default: return element;
7897 int get_next_element(int element)
7901 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7902 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7903 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7904 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7905 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7906 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7907 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7908 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7909 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7910 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7911 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7913 default: return element;
7917 int el2img_mm(int element_mm)
7919 return el2img(map_element_MM_to_RND(element_mm));
7922 int el_act_dir2img(int element, int action, int direction)
7924 element = GFX_ELEMENT(element);
7925 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7927 // direction_graphic[][] == graphic[] for undefined direction graphics
7928 return element_info[element].direction_graphic[action][direction];
7931 static int el_act_dir2crm(int element, int action, int direction)
7933 element = GFX_ELEMENT(element);
7934 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7936 // direction_graphic[][] == graphic[] for undefined direction graphics
7937 return element_info[element].direction_crumbled[action][direction];
7940 int el_act2img(int element, int action)
7942 element = GFX_ELEMENT(element);
7944 return element_info[element].graphic[action];
7947 int el_act2crm(int element, int action)
7949 element = GFX_ELEMENT(element);
7951 return element_info[element].crumbled[action];
7954 int el_dir2img(int element, int direction)
7956 element = GFX_ELEMENT(element);
7958 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7961 int el2baseimg(int element)
7963 return element_info[element].graphic[ACTION_DEFAULT];
7966 int el2img(int element)
7968 element = GFX_ELEMENT(element);
7970 return element_info[element].graphic[ACTION_DEFAULT];
7973 int el2edimg(int element)
7975 element = GFX_ELEMENT(element);
7977 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7980 int el2preimg(int element)
7982 element = GFX_ELEMENT(element);
7984 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7987 int el2panelimg(int element)
7989 element = GFX_ELEMENT(element);
7991 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7994 int font2baseimg(int font_nr)
7996 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7999 int getBeltNrFromBeltElement(int element)
8001 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8002 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8003 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8006 int getBeltNrFromBeltActiveElement(int element)
8008 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8009 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8010 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8013 int getBeltNrFromBeltSwitchElement(int element)
8015 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8016 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8017 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8020 int getBeltDirNrFromBeltElement(int element)
8022 static int belt_base_element[4] =
8024 EL_CONVEYOR_BELT_1_LEFT,
8025 EL_CONVEYOR_BELT_2_LEFT,
8026 EL_CONVEYOR_BELT_3_LEFT,
8027 EL_CONVEYOR_BELT_4_LEFT
8030 int belt_nr = getBeltNrFromBeltElement(element);
8031 int belt_dir_nr = element - belt_base_element[belt_nr];
8033 return (belt_dir_nr % 3);
8036 int getBeltDirNrFromBeltSwitchElement(int element)
8038 static int belt_base_element[4] =
8040 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8041 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8042 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8043 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8046 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8047 int belt_dir_nr = element - belt_base_element[belt_nr];
8049 return (belt_dir_nr % 3);
8052 int getBeltDirFromBeltElement(int element)
8054 static int belt_move_dir[3] =
8061 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8063 return belt_move_dir[belt_dir_nr];
8066 int getBeltDirFromBeltSwitchElement(int element)
8068 static int belt_move_dir[3] =
8075 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8077 return belt_move_dir[belt_dir_nr];
8080 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8082 static int belt_base_element[4] =
8084 EL_CONVEYOR_BELT_1_LEFT,
8085 EL_CONVEYOR_BELT_2_LEFT,
8086 EL_CONVEYOR_BELT_3_LEFT,
8087 EL_CONVEYOR_BELT_4_LEFT
8090 return belt_base_element[belt_nr] + belt_dir_nr;
8093 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8095 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8097 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8100 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8102 static int belt_base_element[4] =
8104 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8105 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8106 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8107 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8110 return belt_base_element[belt_nr] + belt_dir_nr;
8113 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8115 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8117 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8120 boolean getTeamMode_EM(void)
8122 return game.team_mode || network_playing;
8125 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8127 int game_frame_delay_value;
8129 game_frame_delay_value =
8130 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8131 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8134 if (tape.playing && tape.warp_forward && !tape.pausing)
8135 game_frame_delay_value = 0;
8137 return game_frame_delay_value;
8140 unsigned int InitRND(int seed)
8142 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8143 return InitEngineRandom_EM(seed);
8144 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8145 return InitEngineRandom_SP(seed);
8146 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8147 return InitEngineRandom_MM(seed);
8149 return InitEngineRandom_RND(seed);
8152 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8153 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8155 static int get_effective_element_EM(int tile, int frame_em)
8157 int element = object_mapping[tile].element_rnd;
8158 int action = object_mapping[tile].action;
8159 boolean is_backside = object_mapping[tile].is_backside;
8160 boolean action_removing = (action == ACTION_DIGGING ||
8161 action == ACTION_SNAPPING ||
8162 action == ACTION_COLLECTING);
8168 case Yacid_splash_eB:
8169 case Yacid_splash_wB:
8170 return (frame_em > 5 ? EL_EMPTY : element);
8176 else // frame_em == 7
8180 case Yacid_splash_eB:
8181 case Yacid_splash_wB:
8184 case Yemerald_stone:
8187 case Ydiamond_stone:
8191 case Xdrip_stretchB:
8210 case Xsand_stonein_1:
8211 case Xsand_stonein_2:
8212 case Xsand_stonein_3:
8213 case Xsand_stonein_4:
8217 return (is_backside || action_removing ? EL_EMPTY : element);
8222 static boolean check_linear_animation_EM(int tile)
8226 case Xsand_stonesand_1:
8227 case Xsand_stonesand_quickout_1:
8228 case Xsand_sandstone_1:
8229 case Xsand_stonein_1:
8230 case Xsand_stoneout_1:
8249 case Yacid_splash_eB:
8250 case Yacid_splash_wB:
8251 case Yemerald_stone:
8258 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8259 boolean has_crumbled_graphics,
8260 int crumbled, int sync_frame)
8262 // if element can be crumbled, but certain action graphics are just empty
8263 // space (like instantly snapping sand to empty space in 1 frame), do not
8264 // treat these empty space graphics as crumbled graphics in EMC engine
8265 if (crumbled == IMG_EMPTY_SPACE)
8266 has_crumbled_graphics = FALSE;
8268 if (has_crumbled_graphics)
8270 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8271 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8272 g_crumbled->anim_delay,
8273 g_crumbled->anim_mode,
8274 g_crumbled->anim_start_frame,
8277 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8278 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8280 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8281 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8283 g_em->has_crumbled_graphics = TRUE;
8287 g_em->crumbled_bitmap = NULL;
8288 g_em->crumbled_src_x = 0;
8289 g_em->crumbled_src_y = 0;
8290 g_em->crumbled_border_size = 0;
8291 g_em->crumbled_tile_size = 0;
8293 g_em->has_crumbled_graphics = FALSE;
8298 void ResetGfxAnimation_EM(int x, int y, int tile)
8304 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8305 int tile, int frame_em, int x, int y)
8307 int action = object_mapping[tile].action;
8308 int direction = object_mapping[tile].direction;
8309 int effective_element = get_effective_element_EM(tile, frame_em);
8310 int graphic = (direction == MV_NONE ?
8311 el_act2img(effective_element, action) :
8312 el_act_dir2img(effective_element, action, direction));
8313 struct GraphicInfo *g = &graphic_info[graphic];
8315 boolean action_removing = (action == ACTION_DIGGING ||
8316 action == ACTION_SNAPPING ||
8317 action == ACTION_COLLECTING);
8318 boolean action_moving = (action == ACTION_FALLING ||
8319 action == ACTION_MOVING ||
8320 action == ACTION_PUSHING ||
8321 action == ACTION_EATING ||
8322 action == ACTION_FILLING ||
8323 action == ACTION_EMPTYING);
8324 boolean action_falling = (action == ACTION_FALLING ||
8325 action == ACTION_FILLING ||
8326 action == ACTION_EMPTYING);
8328 // special case: graphic uses "2nd movement tile" and has defined
8329 // 7 frames for movement animation (or less) => use default graphic
8330 // for last (8th) frame which ends the movement animation
8331 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8333 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8334 graphic = (direction == MV_NONE ?
8335 el_act2img(effective_element, action) :
8336 el_act_dir2img(effective_element, action, direction));
8338 g = &graphic_info[graphic];
8341 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8345 else if (action_moving)
8347 boolean is_backside = object_mapping[tile].is_backside;
8351 int direction = object_mapping[tile].direction;
8352 int move_dir = (action_falling ? MV_DOWN : direction);
8357 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8358 if (g->double_movement && frame_em == 0)
8362 if (move_dir == MV_LEFT)
8363 GfxFrame[x - 1][y] = GfxFrame[x][y];
8364 else if (move_dir == MV_RIGHT)
8365 GfxFrame[x + 1][y] = GfxFrame[x][y];
8366 else if (move_dir == MV_UP)
8367 GfxFrame[x][y - 1] = GfxFrame[x][y];
8368 else if (move_dir == MV_DOWN)
8369 GfxFrame[x][y + 1] = GfxFrame[x][y];
8376 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8377 if (tile == Xsand_stonesand_quickout_1 ||
8378 tile == Xsand_stonesand_quickout_2)
8382 if (graphic_info[graphic].anim_global_sync)
8383 sync_frame = FrameCounter;
8384 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8385 sync_frame = GfxFrame[x][y];
8387 sync_frame = 0; // playfield border (pseudo steel)
8389 SetRandomAnimationValue(x, y);
8391 int frame = getAnimationFrame(g->anim_frames,
8394 g->anim_start_frame,
8397 g_em->unique_identifier =
8398 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8401 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8402 int tile, int frame_em, int x, int y)
8404 int action = object_mapping[tile].action;
8405 int direction = object_mapping[tile].direction;
8406 boolean is_backside = object_mapping[tile].is_backside;
8407 int effective_element = get_effective_element_EM(tile, frame_em);
8408 int effective_action = action;
8409 int graphic = (direction == MV_NONE ?
8410 el_act2img(effective_element, effective_action) :
8411 el_act_dir2img(effective_element, effective_action,
8413 int crumbled = (direction == MV_NONE ?
8414 el_act2crm(effective_element, effective_action) :
8415 el_act_dir2crm(effective_element, effective_action,
8417 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8418 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8419 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8420 struct GraphicInfo *g = &graphic_info[graphic];
8423 // special case: graphic uses "2nd movement tile" and has defined
8424 // 7 frames for movement animation (or less) => use default graphic
8425 // for last (8th) frame which ends the movement animation
8426 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8428 effective_action = ACTION_DEFAULT;
8429 graphic = (direction == MV_NONE ?
8430 el_act2img(effective_element, effective_action) :
8431 el_act_dir2img(effective_element, effective_action,
8433 crumbled = (direction == MV_NONE ?
8434 el_act2crm(effective_element, effective_action) :
8435 el_act_dir2crm(effective_element, effective_action,
8438 g = &graphic_info[graphic];
8441 if (graphic_info[graphic].anim_global_sync)
8442 sync_frame = FrameCounter;
8443 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8444 sync_frame = GfxFrame[x][y];
8446 sync_frame = 0; // playfield border (pseudo steel)
8448 SetRandomAnimationValue(x, y);
8450 int frame = getAnimationFrame(g->anim_frames,
8453 g->anim_start_frame,
8456 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8457 g->double_movement && is_backside);
8459 // (updating the "crumbled" graphic definitions is probably not really needed,
8460 // as animations for crumbled graphics can't be longer than one EMC cycle)
8461 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8465 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8466 int player_nr, int anim, int frame_em)
8468 int element = player_mapping[player_nr][anim].element_rnd;
8469 int action = player_mapping[player_nr][anim].action;
8470 int direction = player_mapping[player_nr][anim].direction;
8471 int graphic = (direction == MV_NONE ?
8472 el_act2img(element, action) :
8473 el_act_dir2img(element, action, direction));
8474 struct GraphicInfo *g = &graphic_info[graphic];
8477 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8479 stored_player[player_nr].StepFrame = frame_em;
8481 sync_frame = stored_player[player_nr].Frame;
8483 int frame = getAnimationFrame(g->anim_frames,
8486 g->anim_start_frame,
8489 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8490 &g_em->src_x, &g_em->src_y, FALSE);
8493 void InitGraphicInfo_EM(void)
8498 int num_em_gfx_errors = 0;
8500 if (graphic_info_em_object[0][0].bitmap == NULL)
8502 // EM graphics not yet initialized in em_open_all()
8507 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8510 // always start with reliable default values
8511 for (i = 0; i < TILE_MAX; i++)
8513 object_mapping[i].element_rnd = EL_UNKNOWN;
8514 object_mapping[i].is_backside = FALSE;
8515 object_mapping[i].action = ACTION_DEFAULT;
8516 object_mapping[i].direction = MV_NONE;
8519 // always start with reliable default values
8520 for (p = 0; p < MAX_PLAYERS; p++)
8522 for (i = 0; i < SPR_MAX; i++)
8524 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8525 player_mapping[p][i].action = ACTION_DEFAULT;
8526 player_mapping[p][i].direction = MV_NONE;
8530 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8532 int e = em_object_mapping_list[i].element_em;
8534 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8535 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8537 if (em_object_mapping_list[i].action != -1)
8538 object_mapping[e].action = em_object_mapping_list[i].action;
8540 if (em_object_mapping_list[i].direction != -1)
8541 object_mapping[e].direction =
8542 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8545 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8547 int a = em_player_mapping_list[i].action_em;
8548 int p = em_player_mapping_list[i].player_nr;
8550 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8552 if (em_player_mapping_list[i].action != -1)
8553 player_mapping[p][a].action = em_player_mapping_list[i].action;
8555 if (em_player_mapping_list[i].direction != -1)
8556 player_mapping[p][a].direction =
8557 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8560 for (i = 0; i < TILE_MAX; i++)
8562 int element = object_mapping[i].element_rnd;
8563 int action = object_mapping[i].action;
8564 int direction = object_mapping[i].direction;
8565 boolean is_backside = object_mapping[i].is_backside;
8566 boolean action_exploding = ((action == ACTION_EXPLODING ||
8567 action == ACTION_SMASHED_BY_ROCK ||
8568 action == ACTION_SMASHED_BY_SPRING) &&
8569 element != EL_DIAMOND);
8570 boolean action_active = (action == ACTION_ACTIVE);
8571 boolean action_other = (action == ACTION_OTHER);
8573 for (j = 0; j < 8; j++)
8575 int effective_element = get_effective_element_EM(i, j);
8576 int effective_action = (j < 7 ? action :
8577 i == Xdrip_stretch ? action :
8578 i == Xdrip_stretchB ? action :
8579 i == Ydrip_s1 ? action :
8580 i == Ydrip_s1B ? action :
8581 i == Xball_1B ? action :
8582 i == Xball_2 ? action :
8583 i == Xball_2B ? action :
8584 i == Yball_eat ? action :
8585 i == Ykey_1_eat ? action :
8586 i == Ykey_2_eat ? action :
8587 i == Ykey_3_eat ? action :
8588 i == Ykey_4_eat ? action :
8589 i == Ykey_5_eat ? action :
8590 i == Ykey_6_eat ? action :
8591 i == Ykey_7_eat ? action :
8592 i == Ykey_8_eat ? action :
8593 i == Ylenses_eat ? action :
8594 i == Ymagnify_eat ? action :
8595 i == Ygrass_eat ? action :
8596 i == Ydirt_eat ? action :
8597 i == Xsand_stonein_1 ? action :
8598 i == Xsand_stonein_2 ? action :
8599 i == Xsand_stonein_3 ? action :
8600 i == Xsand_stonein_4 ? action :
8601 i == Xsand_stoneout_1 ? action :
8602 i == Xsand_stoneout_2 ? action :
8603 i == Xboom_android ? ACTION_EXPLODING :
8604 action_exploding ? ACTION_EXPLODING :
8605 action_active ? action :
8606 action_other ? action :
8608 int graphic = (el_act_dir2img(effective_element, effective_action,
8610 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8612 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8613 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8614 boolean has_action_graphics = (graphic != base_graphic);
8615 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8616 struct GraphicInfo *g = &graphic_info[graphic];
8617 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8620 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8621 boolean special_animation = (action != ACTION_DEFAULT &&
8622 g->anim_frames == 3 &&
8623 g->anim_delay == 2 &&
8624 g->anim_mode & ANIM_LINEAR);
8625 int sync_frame = (i == Xdrip_stretch ? 7 :
8626 i == Xdrip_stretchB ? 7 :
8627 i == Ydrip_s2 ? j + 8 :
8628 i == Ydrip_s2B ? j + 8 :
8637 i == Xfake_acid_1 ? 0 :
8638 i == Xfake_acid_2 ? 10 :
8639 i == Xfake_acid_3 ? 20 :
8640 i == Xfake_acid_4 ? 30 :
8641 i == Xfake_acid_5 ? 40 :
8642 i == Xfake_acid_6 ? 50 :
8643 i == Xfake_acid_7 ? 60 :
8644 i == Xfake_acid_8 ? 70 :
8646 i == Xball_2B ? j + 8 :
8647 i == Yball_eat ? j + 1 :
8648 i == Ykey_1_eat ? j + 1 :
8649 i == Ykey_2_eat ? j + 1 :
8650 i == Ykey_3_eat ? j + 1 :
8651 i == Ykey_4_eat ? j + 1 :
8652 i == Ykey_5_eat ? j + 1 :
8653 i == Ykey_6_eat ? j + 1 :
8654 i == Ykey_7_eat ? j + 1 :
8655 i == Ykey_8_eat ? j + 1 :
8656 i == Ylenses_eat ? j + 1 :
8657 i == Ymagnify_eat ? j + 1 :
8658 i == Ygrass_eat ? j + 1 :
8659 i == Ydirt_eat ? j + 1 :
8660 i == Xamoeba_1 ? 0 :
8661 i == Xamoeba_2 ? 1 :
8662 i == Xamoeba_3 ? 2 :
8663 i == Xamoeba_4 ? 3 :
8664 i == Xamoeba_5 ? 0 :
8665 i == Xamoeba_6 ? 1 :
8666 i == Xamoeba_7 ? 2 :
8667 i == Xamoeba_8 ? 3 :
8668 i == Xexit_2 ? j + 8 :
8669 i == Xexit_3 ? j + 16 :
8670 i == Xdynamite_1 ? 0 :
8671 i == Xdynamite_2 ? 8 :
8672 i == Xdynamite_3 ? 16 :
8673 i == Xdynamite_4 ? 24 :
8674 i == Xsand_stonein_1 ? j + 1 :
8675 i == Xsand_stonein_2 ? j + 9 :
8676 i == Xsand_stonein_3 ? j + 17 :
8677 i == Xsand_stonein_4 ? j + 25 :
8678 i == Xsand_stoneout_1 && j == 0 ? 0 :
8679 i == Xsand_stoneout_1 && j == 1 ? 0 :
8680 i == Xsand_stoneout_1 && j == 2 ? 1 :
8681 i == Xsand_stoneout_1 && j == 3 ? 2 :
8682 i == Xsand_stoneout_1 && j == 4 ? 2 :
8683 i == Xsand_stoneout_1 && j == 5 ? 3 :
8684 i == Xsand_stoneout_1 && j == 6 ? 4 :
8685 i == Xsand_stoneout_1 && j == 7 ? 4 :
8686 i == Xsand_stoneout_2 && j == 0 ? 5 :
8687 i == Xsand_stoneout_2 && j == 1 ? 6 :
8688 i == Xsand_stoneout_2 && j == 2 ? 7 :
8689 i == Xsand_stoneout_2 && j == 3 ? 8 :
8690 i == Xsand_stoneout_2 && j == 4 ? 9 :
8691 i == Xsand_stoneout_2 && j == 5 ? 11 :
8692 i == Xsand_stoneout_2 && j == 6 ? 13 :
8693 i == Xsand_stoneout_2 && j == 7 ? 15 :
8694 i == Xboom_bug && j == 1 ? 2 :
8695 i == Xboom_bug && j == 2 ? 2 :
8696 i == Xboom_bug && j == 3 ? 4 :
8697 i == Xboom_bug && j == 4 ? 4 :
8698 i == Xboom_bug && j == 5 ? 2 :
8699 i == Xboom_bug && j == 6 ? 2 :
8700 i == Xboom_bug && j == 7 ? 0 :
8701 i == Xboom_bomb && j == 1 ? 2 :
8702 i == Xboom_bomb && j == 2 ? 2 :
8703 i == Xboom_bomb && j == 3 ? 4 :
8704 i == Xboom_bomb && j == 4 ? 4 :
8705 i == Xboom_bomb && j == 5 ? 2 :
8706 i == Xboom_bomb && j == 6 ? 2 :
8707 i == Xboom_bomb && j == 7 ? 0 :
8708 i == Xboom_android && j == 7 ? 6 :
8709 i == Xboom_1 && j == 1 ? 2 :
8710 i == Xboom_1 && j == 2 ? 2 :
8711 i == Xboom_1 && j == 3 ? 4 :
8712 i == Xboom_1 && j == 4 ? 4 :
8713 i == Xboom_1 && j == 5 ? 6 :
8714 i == Xboom_1 && j == 6 ? 6 :
8715 i == Xboom_1 && j == 7 ? 8 :
8716 i == Xboom_2 && j == 0 ? 8 :
8717 i == Xboom_2 && j == 1 ? 8 :
8718 i == Xboom_2 && j == 2 ? 10 :
8719 i == Xboom_2 && j == 3 ? 10 :
8720 i == Xboom_2 && j == 4 ? 10 :
8721 i == Xboom_2 && j == 5 ? 12 :
8722 i == Xboom_2 && j == 6 ? 12 :
8723 i == Xboom_2 && j == 7 ? 12 :
8724 special_animation && j == 4 ? 3 :
8725 effective_action != action ? 0 :
8729 Bitmap *debug_bitmap = g_em->bitmap;
8730 int debug_src_x = g_em->src_x;
8731 int debug_src_y = g_em->src_y;
8734 int frame = getAnimationFrame(g->anim_frames,
8737 g->anim_start_frame,
8740 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8741 g->double_movement && is_backside);
8743 g_em->bitmap = src_bitmap;
8744 g_em->src_x = src_x;
8745 g_em->src_y = src_y;
8746 g_em->src_offset_x = 0;
8747 g_em->src_offset_y = 0;
8748 g_em->dst_offset_x = 0;
8749 g_em->dst_offset_y = 0;
8750 g_em->width = TILEX;
8751 g_em->height = TILEY;
8753 g_em->preserve_background = FALSE;
8755 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8758 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8759 effective_action == ACTION_MOVING ||
8760 effective_action == ACTION_PUSHING ||
8761 effective_action == ACTION_EATING)) ||
8762 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8763 effective_action == ACTION_EMPTYING)))
8766 (effective_action == ACTION_FALLING ||
8767 effective_action == ACTION_FILLING ||
8768 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8769 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8770 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8771 int num_steps = (i == Ydrip_s1 ? 16 :
8772 i == Ydrip_s1B ? 16 :
8773 i == Ydrip_s2 ? 16 :
8774 i == Ydrip_s2B ? 16 :
8775 i == Xsand_stonein_1 ? 32 :
8776 i == Xsand_stonein_2 ? 32 :
8777 i == Xsand_stonein_3 ? 32 :
8778 i == Xsand_stonein_4 ? 32 :
8779 i == Xsand_stoneout_1 ? 16 :
8780 i == Xsand_stoneout_2 ? 16 : 8);
8781 int cx = ABS(dx) * (TILEX / num_steps);
8782 int cy = ABS(dy) * (TILEY / num_steps);
8783 int step_frame = (i == Ydrip_s2 ? j + 8 :
8784 i == Ydrip_s2B ? j + 8 :
8785 i == Xsand_stonein_2 ? j + 8 :
8786 i == Xsand_stonein_3 ? j + 16 :
8787 i == Xsand_stonein_4 ? j + 24 :
8788 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8789 int step = (is_backside ? step_frame : num_steps - step_frame);
8791 if (is_backside) // tile where movement starts
8793 if (dx < 0 || dy < 0)
8795 g_em->src_offset_x = cx * step;
8796 g_em->src_offset_y = cy * step;
8800 g_em->dst_offset_x = cx * step;
8801 g_em->dst_offset_y = cy * step;
8804 else // tile where movement ends
8806 if (dx < 0 || dy < 0)
8808 g_em->dst_offset_x = cx * step;
8809 g_em->dst_offset_y = cy * step;
8813 g_em->src_offset_x = cx * step;
8814 g_em->src_offset_y = cy * step;
8818 g_em->width = TILEX - cx * step;
8819 g_em->height = TILEY - cy * step;
8822 // create unique graphic identifier to decide if tile must be redrawn
8823 /* bit 31 - 16 (16 bit): EM style graphic
8824 bit 15 - 12 ( 4 bit): EM style frame
8825 bit 11 - 6 ( 6 bit): graphic width
8826 bit 5 - 0 ( 6 bit): graphic height */
8827 g_em->unique_identifier =
8828 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8832 // skip check for EMC elements not contained in original EMC artwork
8833 if (element == EL_EMC_FAKE_ACID)
8836 if (g_em->bitmap != debug_bitmap ||
8837 g_em->src_x != debug_src_x ||
8838 g_em->src_y != debug_src_y ||
8839 g_em->src_offset_x != 0 ||
8840 g_em->src_offset_y != 0 ||
8841 g_em->dst_offset_x != 0 ||
8842 g_em->dst_offset_y != 0 ||
8843 g_em->width != TILEX ||
8844 g_em->height != TILEY)
8846 static int last_i = -1;
8854 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8855 i, element, element_info[element].token_name,
8856 element_action_info[effective_action].suffix, direction);
8858 if (element != effective_element)
8859 printf(" [%d ('%s')]",
8861 element_info[effective_element].token_name);
8865 if (g_em->bitmap != debug_bitmap)
8866 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8867 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8869 if (g_em->src_x != debug_src_x ||
8870 g_em->src_y != debug_src_y)
8871 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8872 j, (is_backside ? 'B' : 'F'),
8873 g_em->src_x, g_em->src_y,
8874 g_em->src_x / 32, g_em->src_y / 32,
8875 debug_src_x, debug_src_y,
8876 debug_src_x / 32, debug_src_y / 32);
8878 if (g_em->src_offset_x != 0 ||
8879 g_em->src_offset_y != 0 ||
8880 g_em->dst_offset_x != 0 ||
8881 g_em->dst_offset_y != 0)
8882 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8884 g_em->src_offset_x, g_em->src_offset_y,
8885 g_em->dst_offset_x, g_em->dst_offset_y);
8887 if (g_em->width != TILEX ||
8888 g_em->height != TILEY)
8889 printf(" %d (%d): size %d,%d should be %d,%d\n",
8891 g_em->width, g_em->height, TILEX, TILEY);
8893 num_em_gfx_errors++;
8900 for (i = 0; i < TILE_MAX; i++)
8902 for (j = 0; j < 8; j++)
8904 int element = object_mapping[i].element_rnd;
8905 int action = object_mapping[i].action;
8906 int direction = object_mapping[i].direction;
8907 boolean is_backside = object_mapping[i].is_backside;
8908 int graphic_action = el_act_dir2img(element, action, direction);
8909 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8911 if ((action == ACTION_SMASHED_BY_ROCK ||
8912 action == ACTION_SMASHED_BY_SPRING ||
8913 action == ACTION_EATING) &&
8914 graphic_action == graphic_default)
8916 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8917 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8918 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8919 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8922 // no separate animation for "smashed by rock" -- use rock instead
8923 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8924 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8926 g_em->bitmap = g_xx->bitmap;
8927 g_em->src_x = g_xx->src_x;
8928 g_em->src_y = g_xx->src_y;
8929 g_em->src_offset_x = g_xx->src_offset_x;
8930 g_em->src_offset_y = g_xx->src_offset_y;
8931 g_em->dst_offset_x = g_xx->dst_offset_x;
8932 g_em->dst_offset_y = g_xx->dst_offset_y;
8933 g_em->width = g_xx->width;
8934 g_em->height = g_xx->height;
8935 g_em->unique_identifier = g_xx->unique_identifier;
8938 g_em->preserve_background = TRUE;
8943 for (p = 0; p < MAX_PLAYERS; p++)
8945 for (i = 0; i < SPR_MAX; i++)
8947 int element = player_mapping[p][i].element_rnd;
8948 int action = player_mapping[p][i].action;
8949 int direction = player_mapping[p][i].direction;
8951 for (j = 0; j < 8; j++)
8953 int effective_element = element;
8954 int effective_action = action;
8955 int graphic = (direction == MV_NONE ?
8956 el_act2img(effective_element, effective_action) :
8957 el_act_dir2img(effective_element, effective_action,
8959 struct GraphicInfo *g = &graphic_info[graphic];
8960 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8966 Bitmap *debug_bitmap = g_em->bitmap;
8967 int debug_src_x = g_em->src_x;
8968 int debug_src_y = g_em->src_y;
8971 int frame = getAnimationFrame(g->anim_frames,
8974 g->anim_start_frame,
8977 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8979 g_em->bitmap = src_bitmap;
8980 g_em->src_x = src_x;
8981 g_em->src_y = src_y;
8982 g_em->src_offset_x = 0;
8983 g_em->src_offset_y = 0;
8984 g_em->dst_offset_x = 0;
8985 g_em->dst_offset_y = 0;
8986 g_em->width = TILEX;
8987 g_em->height = TILEY;
8991 // skip check for EMC elements not contained in original EMC artwork
8992 if (element == EL_PLAYER_3 ||
8993 element == EL_PLAYER_4)
8996 if (g_em->bitmap != debug_bitmap ||
8997 g_em->src_x != debug_src_x ||
8998 g_em->src_y != debug_src_y)
9000 static int last_i = -1;
9008 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9009 p, i, element, element_info[element].token_name,
9010 element_action_info[effective_action].suffix, direction);
9012 if (element != effective_element)
9013 printf(" [%d ('%s')]",
9015 element_info[effective_element].token_name);
9019 if (g_em->bitmap != debug_bitmap)
9020 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9021 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9023 if (g_em->src_x != debug_src_x ||
9024 g_em->src_y != debug_src_y)
9025 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9027 g_em->src_x, g_em->src_y,
9028 g_em->src_x / 32, g_em->src_y / 32,
9029 debug_src_x, debug_src_y,
9030 debug_src_x / 32, debug_src_y / 32);
9032 num_em_gfx_errors++;
9042 printf("::: [%d errors found]\n", num_em_gfx_errors);
9048 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9049 boolean any_player_moving,
9050 boolean any_player_snapping,
9051 boolean any_player_dropping)
9053 if (frame == 0 && !any_player_dropping)
9055 if (!local_player->was_waiting)
9057 if (!CheckSaveEngineSnapshotToList())
9060 local_player->was_waiting = TRUE;
9063 else if (any_player_moving || any_player_snapping || any_player_dropping)
9065 local_player->was_waiting = FALSE;
9069 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9070 boolean murphy_is_dropping)
9072 if (murphy_is_waiting)
9074 if (!local_player->was_waiting)
9076 if (!CheckSaveEngineSnapshotToList())
9079 local_player->was_waiting = TRUE;
9084 local_player->was_waiting = FALSE;
9088 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9089 boolean button_released)
9091 if (button_released)
9093 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9094 CheckSaveEngineSnapshotToList();
9096 else if (element_clicked)
9098 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9099 CheckSaveEngineSnapshotToList();
9101 game.snapshot.changed_action = TRUE;
9105 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9106 boolean any_player_moving,
9107 boolean any_player_snapping,
9108 boolean any_player_dropping)
9110 if (tape.single_step && tape.recording && !tape.pausing)
9111 if (frame == 0 && !any_player_dropping)
9112 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9114 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9115 any_player_snapping, any_player_dropping);
9118 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9119 boolean murphy_is_dropping)
9121 boolean murphy_starts_dropping = FALSE;
9124 for (i = 0; i < MAX_PLAYERS; i++)
9125 if (stored_player[i].force_dropping)
9126 murphy_starts_dropping = TRUE;
9128 if (tape.single_step && tape.recording && !tape.pausing)
9129 if (murphy_is_waiting && !murphy_starts_dropping)
9130 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9132 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9135 void CheckSingleStepMode_MM(boolean element_clicked,
9136 boolean button_released)
9138 if (tape.single_step && tape.recording && !tape.pausing)
9139 if (button_released)
9140 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9142 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9145 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9146 int graphic, int sync_frame, int x, int y)
9148 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9150 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9153 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9155 return (IS_NEXT_FRAME(sync_frame, graphic));
9158 int getGraphicInfo_Delay(int graphic)
9160 return graphic_info[graphic].anim_delay;
9163 void PlayMenuSoundExt(int sound)
9165 if (sound == SND_UNDEFINED)
9168 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9169 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9172 if (IS_LOOP_SOUND(sound))
9173 PlaySoundLoop(sound);
9178 void PlayMenuSound(void)
9180 PlayMenuSoundExt(menu.sound[game_status]);
9183 void PlayMenuSoundStereo(int sound, int stereo_position)
9185 if (sound == SND_UNDEFINED)
9188 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9189 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9192 if (IS_LOOP_SOUND(sound))
9193 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9195 PlaySoundStereo(sound, stereo_position);
9198 void PlayMenuSoundIfLoopExt(int sound)
9200 if (sound == SND_UNDEFINED)
9203 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9204 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9207 if (IS_LOOP_SOUND(sound))
9208 PlaySoundLoop(sound);
9211 void PlayMenuSoundIfLoop(void)
9213 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9216 void PlayMenuMusicExt(int music)
9218 if (music == MUS_UNDEFINED)
9221 if (!setup.sound_music)
9224 if (IS_LOOP_MUSIC(music))
9225 PlayMusicLoop(music);
9230 void PlayMenuMusic(void)
9232 char *curr_music = getCurrentlyPlayingMusicFilename();
9233 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9235 if (!strEqual(curr_music, next_music))
9236 PlayMenuMusicExt(menu.music[game_status]);
9239 void PlayMenuSoundsAndMusic(void)
9245 static void FadeMenuSounds(void)
9250 static void FadeMenuMusic(void)
9252 char *curr_music = getCurrentlyPlayingMusicFilename();
9253 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9255 if (!strEqual(curr_music, next_music))
9259 void FadeMenuSoundsAndMusic(void)
9265 void PlaySoundActivating(void)
9268 PlaySound(SND_MENU_ITEM_ACTIVATING);
9272 void PlaySoundSelecting(void)
9275 PlaySound(SND_MENU_ITEM_SELECTING);
9279 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9281 boolean change_fullscreen = (setup.fullscreen !=
9282 video.fullscreen_enabled);
9283 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9284 setup.window_scaling_percent !=
9285 video.window_scaling_percent);
9287 if (change_window_scaling_percent && video.fullscreen_enabled)
9290 if (!change_window_scaling_percent && !video.fullscreen_available)
9293 if (change_window_scaling_percent)
9295 SDLSetWindowScaling(setup.window_scaling_percent);
9299 else if (change_fullscreen)
9301 SDLSetWindowFullscreen(setup.fullscreen);
9303 // set setup value according to successfully changed fullscreen mode
9304 setup.fullscreen = video.fullscreen_enabled;
9309 if (change_fullscreen ||
9310 change_window_scaling_percent)
9312 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9314 // save backbuffer content which gets lost when toggling fullscreen mode
9315 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9317 if (change_window_scaling_percent)
9319 // keep window mode, but change window scaling
9320 video.fullscreen_enabled = TRUE; // force new window scaling
9323 // toggle fullscreen
9324 ChangeVideoModeIfNeeded(setup.fullscreen);
9326 // set setup value according to successfully changed fullscreen mode
9327 setup.fullscreen = video.fullscreen_enabled;
9329 // restore backbuffer content from temporary backbuffer backup bitmap
9330 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9332 FreeBitmap(tmp_backbuffer);
9334 // update visible window/screen
9335 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9339 static void JoinRectangles(int *x, int *y, int *width, int *height,
9340 int x2, int y2, int width2, int height2)
9342 // do not join with "off-screen" rectangle
9343 if (x2 == -1 || y2 == -1)
9348 *width = MAX(*width, width2);
9349 *height = MAX(*height, height2);
9352 void SetAnimStatus(int anim_status_new)
9354 if (anim_status_new == GAME_MODE_MAIN)
9355 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9356 else if (anim_status_new == GAME_MODE_SCORES)
9357 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9359 global.anim_status_next = anim_status_new;
9361 // directly set screen modes that are entered without fading
9362 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9363 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9364 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9365 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9366 global.anim_status = global.anim_status_next;
9369 void SetGameStatus(int game_status_new)
9371 if (game_status_new != game_status)
9372 game_status_last_screen = game_status;
9374 game_status = game_status_new;
9376 SetAnimStatus(game_status_new);
9379 void SetFontStatus(int game_status_new)
9381 static int last_game_status = -1;
9383 if (game_status_new != -1)
9385 // set game status for font use after storing last game status
9386 last_game_status = game_status;
9387 game_status = game_status_new;
9391 // reset game status after font use from last stored game status
9392 game_status = last_game_status;
9396 void ResetFontStatus(void)
9401 void SetLevelSetInfo(char *identifier, int level_nr)
9403 setString(&levelset.identifier, identifier);
9405 levelset.level_nr = level_nr;
9408 boolean CheckIfAllViewportsHaveChanged(void)
9410 // if game status has not changed, viewports have not changed either
9411 if (game_status == game_status_last)
9414 // check if all viewports have changed with current game status
9416 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9417 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9418 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9419 int new_real_sx = vp_playfield->x;
9420 int new_real_sy = vp_playfield->y;
9421 int new_full_sxsize = vp_playfield->width;
9422 int new_full_sysize = vp_playfield->height;
9423 int new_dx = vp_door_1->x;
9424 int new_dy = vp_door_1->y;
9425 int new_dxsize = vp_door_1->width;
9426 int new_dysize = vp_door_1->height;
9427 int new_vx = vp_door_2->x;
9428 int new_vy = vp_door_2->y;
9429 int new_vxsize = vp_door_2->width;
9430 int new_vysize = vp_door_2->height;
9432 boolean playfield_viewport_has_changed =
9433 (new_real_sx != REAL_SX ||
9434 new_real_sy != REAL_SY ||
9435 new_full_sxsize != FULL_SXSIZE ||
9436 new_full_sysize != FULL_SYSIZE);
9438 boolean door_1_viewport_has_changed =
9441 new_dxsize != DXSIZE ||
9442 new_dysize != DYSIZE);
9444 boolean door_2_viewport_has_changed =
9447 new_vxsize != VXSIZE ||
9448 new_vysize != VYSIZE ||
9449 game_status_last == GAME_MODE_EDITOR);
9451 return (playfield_viewport_has_changed &&
9452 door_1_viewport_has_changed &&
9453 door_2_viewport_has_changed);
9456 boolean CheckFadeAll(void)
9458 return (CheckIfGlobalBorderHasChanged() ||
9459 CheckIfAllViewportsHaveChanged());
9462 void ChangeViewportPropertiesIfNeeded(void)
9464 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9465 FALSE : setup.small_game_graphics);
9466 int gfx_game_mode = game_status;
9467 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9469 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9470 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9471 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9472 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9473 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9474 int new_win_xsize = vp_window->width;
9475 int new_win_ysize = vp_window->height;
9476 int border_left = vp_playfield->border_left;
9477 int border_right = vp_playfield->border_right;
9478 int border_top = vp_playfield->border_top;
9479 int border_bottom = vp_playfield->border_bottom;
9480 int new_sx = vp_playfield->x + border_left;
9481 int new_sy = vp_playfield->y + border_top;
9482 int new_sxsize = vp_playfield->width - border_left - border_right;
9483 int new_sysize = vp_playfield->height - border_top - border_bottom;
9484 int new_real_sx = vp_playfield->x;
9485 int new_real_sy = vp_playfield->y;
9486 int new_full_sxsize = vp_playfield->width;
9487 int new_full_sysize = vp_playfield->height;
9488 int new_dx = vp_door_1->x;
9489 int new_dy = vp_door_1->y;
9490 int new_dxsize = vp_door_1->width;
9491 int new_dysize = vp_door_1->height;
9492 int new_vx = vp_door_2->x;
9493 int new_vy = vp_door_2->y;
9494 int new_vxsize = vp_door_2->width;
9495 int new_vysize = vp_door_2->height;
9496 int new_ex = vp_door_3->x;
9497 int new_ey = vp_door_3->y;
9498 int new_exsize = vp_door_3->width;
9499 int new_eysize = vp_door_3->height;
9500 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9501 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9502 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9503 int new_scr_fieldx = new_sxsize / tilesize;
9504 int new_scr_fieldy = new_sysize / tilesize;
9505 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9506 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9507 boolean init_gfx_buffers = FALSE;
9508 boolean init_video_buffer = FALSE;
9509 boolean init_gadgets_and_anims = FALSE;
9510 boolean init_em_graphics = FALSE;
9512 if (new_win_xsize != WIN_XSIZE ||
9513 new_win_ysize != WIN_YSIZE)
9515 WIN_XSIZE = new_win_xsize;
9516 WIN_YSIZE = new_win_ysize;
9518 init_video_buffer = TRUE;
9519 init_gfx_buffers = TRUE;
9520 init_gadgets_and_anims = TRUE;
9522 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9525 if (new_scr_fieldx != SCR_FIELDX ||
9526 new_scr_fieldy != SCR_FIELDY)
9528 // this always toggles between MAIN and GAME when using small tile size
9530 SCR_FIELDX = new_scr_fieldx;
9531 SCR_FIELDY = new_scr_fieldy;
9533 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9544 new_sxsize != SXSIZE ||
9545 new_sysize != SYSIZE ||
9546 new_dxsize != DXSIZE ||
9547 new_dysize != DYSIZE ||
9548 new_vxsize != VXSIZE ||
9549 new_vysize != VYSIZE ||
9550 new_exsize != EXSIZE ||
9551 new_eysize != EYSIZE ||
9552 new_real_sx != REAL_SX ||
9553 new_real_sy != REAL_SY ||
9554 new_full_sxsize != FULL_SXSIZE ||
9555 new_full_sysize != FULL_SYSIZE ||
9556 new_tilesize_var != TILESIZE_VAR
9559 // ------------------------------------------------------------------------
9560 // determine next fading area for changed viewport definitions
9561 // ------------------------------------------------------------------------
9563 // start with current playfield area (default fading area)
9566 FADE_SXSIZE = FULL_SXSIZE;
9567 FADE_SYSIZE = FULL_SYSIZE;
9569 // add new playfield area if position or size has changed
9570 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9571 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9573 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9574 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9577 // add current and new door 1 area if position or size has changed
9578 if (new_dx != DX || new_dy != DY ||
9579 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9581 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9582 DX, DY, DXSIZE, DYSIZE);
9583 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9584 new_dx, new_dy, new_dxsize, new_dysize);
9587 // add current and new door 2 area if position or size has changed
9588 if (new_vx != VX || new_vy != VY ||
9589 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9591 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9592 VX, VY, VXSIZE, VYSIZE);
9593 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9594 new_vx, new_vy, new_vxsize, new_vysize);
9597 // ------------------------------------------------------------------------
9598 // handle changed tile size
9599 // ------------------------------------------------------------------------
9601 if (new_tilesize_var != TILESIZE_VAR)
9603 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9605 // changing tile size invalidates scroll values of engine snapshots
9606 FreeEngineSnapshotSingle();
9608 // changing tile size requires update of graphic mapping for EM engine
9609 init_em_graphics = TRUE;
9620 SXSIZE = new_sxsize;
9621 SYSIZE = new_sysize;
9622 DXSIZE = new_dxsize;
9623 DYSIZE = new_dysize;
9624 VXSIZE = new_vxsize;
9625 VYSIZE = new_vysize;
9626 EXSIZE = new_exsize;
9627 EYSIZE = new_eysize;
9628 REAL_SX = new_real_sx;
9629 REAL_SY = new_real_sy;
9630 FULL_SXSIZE = new_full_sxsize;
9631 FULL_SYSIZE = new_full_sysize;
9632 TILESIZE_VAR = new_tilesize_var;
9634 init_gfx_buffers = TRUE;
9635 init_gadgets_and_anims = TRUE;
9637 // printf("::: viewports: init_gfx_buffers\n");
9638 // printf("::: viewports: init_gadgets_and_anims\n");
9641 if (init_gfx_buffers)
9643 // printf("::: init_gfx_buffers\n");
9645 SCR_FIELDX = new_scr_fieldx_buffers;
9646 SCR_FIELDY = new_scr_fieldy_buffers;
9650 SCR_FIELDX = new_scr_fieldx;
9651 SCR_FIELDY = new_scr_fieldy;
9653 SetDrawDeactivationMask(REDRAW_NONE);
9654 SetDrawBackgroundMask(REDRAW_FIELD);
9657 if (init_video_buffer)
9659 // printf("::: init_video_buffer\n");
9661 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9662 InitImageTextures();
9665 if (init_gadgets_and_anims)
9667 // printf("::: init_gadgets_and_anims\n");
9670 InitGlobalAnimations();
9673 if (init_em_graphics)
9675 InitGraphicInfo_EM();