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 if (graphic == IMG_UNDEFINED)
1156 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1158 return (graphic_info[graphic].bitmap != NULL || redefined ?
1159 graphic_info[graphic].bitmap :
1160 graphic_info[default_graphic].bitmap);
1163 static Bitmap *getBackgroundBitmap(int graphic)
1165 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1168 static Bitmap *getGlobalBorderBitmap(int graphic)
1170 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1173 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1176 (status == GAME_MODE_MAIN ||
1177 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1178 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1179 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1180 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1183 return getGlobalBorderBitmap(graphic);
1186 void SetWindowBackgroundImageIfDefined(int graphic)
1188 if (graphic_info[graphic].bitmap)
1189 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1192 void SetMainBackgroundImageIfDefined(int graphic)
1194 if (graphic_info[graphic].bitmap)
1195 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1198 void SetDoorBackgroundImageIfDefined(int graphic)
1200 if (graphic_info[graphic].bitmap)
1201 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1204 void SetWindowBackgroundImage(int graphic)
1206 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1209 void SetMainBackgroundImage(int graphic)
1211 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1214 void SetDoorBackgroundImage(int graphic)
1216 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1219 void SetPanelBackground(void)
1221 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1223 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1224 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1226 SetDoorBackgroundBitmap(bitmap_db_panel);
1229 void DrawBackground(int x, int y, int width, int height)
1231 // "drawto" might still point to playfield buffer here (hall of fame)
1232 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1234 if (IN_GFX_FIELD_FULL(x, y))
1235 redraw_mask |= REDRAW_FIELD;
1236 else if (IN_GFX_DOOR_1(x, y))
1237 redraw_mask |= REDRAW_DOOR_1;
1238 else if (IN_GFX_DOOR_2(x, y))
1239 redraw_mask |= REDRAW_DOOR_2;
1240 else if (IN_GFX_DOOR_3(x, y))
1241 redraw_mask |= REDRAW_DOOR_3;
1244 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1246 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1248 if (font->bitmap == NULL)
1251 DrawBackground(x, y, width, height);
1254 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1256 struct GraphicInfo *g = &graphic_info[graphic];
1258 if (g->bitmap == NULL)
1261 DrawBackground(x, y, width, height);
1264 static int game_status_last = -1;
1265 static Bitmap *global_border_bitmap_last = NULL;
1266 static Bitmap *global_border_bitmap = NULL;
1267 static int real_sx_last = -1, real_sy_last = -1;
1268 static int full_sxsize_last = -1, full_sysize_last = -1;
1269 static int dx_last = -1, dy_last = -1;
1270 static int dxsize_last = -1, dysize_last = -1;
1271 static int vx_last = -1, vy_last = -1;
1272 static int vxsize_last = -1, vysize_last = -1;
1273 static int ex_last = -1, ey_last = -1;
1274 static int exsize_last = -1, eysize_last = -1;
1276 boolean CheckIfGlobalBorderHasChanged(void)
1278 // if game status has not changed, global border has not changed either
1279 if (game_status == game_status_last)
1282 // determine and store new global border bitmap for current game status
1283 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1285 return (global_border_bitmap_last != global_border_bitmap);
1288 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1290 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1291 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1293 // if game status has not changed, nothing has to be redrawn
1294 if (game_status == game_status_last)
1297 // redraw if last screen was title screen
1298 if (game_status_last == GAME_MODE_TITLE)
1301 // redraw if global screen border has changed
1302 if (CheckIfGlobalBorderHasChanged())
1305 // redraw if position or size of playfield area has changed
1306 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1307 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1310 // redraw if position or size of door area has changed
1311 if (dx_last != DX || dy_last != DY ||
1312 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1315 // redraw if position or size of tape area has changed
1316 if (vx_last != VX || vy_last != VY ||
1317 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1320 // redraw if position or size of editor area has changed
1321 if (ex_last != EX || ey_last != EY ||
1322 exsize_last != EXSIZE || eysize_last != EYSIZE)
1329 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1332 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1334 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1337 void RedrawGlobalBorder(void)
1339 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1341 RedrawGlobalBorderFromBitmap(bitmap);
1343 redraw_mask = REDRAW_ALL;
1346 static void RedrawGlobalBorderIfNeeded(void)
1348 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1349 if (game_status == game_status_last)
1353 // copy current draw buffer to later copy back areas that have not changed
1354 if (game_status_last != GAME_MODE_TITLE)
1355 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1357 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1358 if (CheckIfGlobalBorderRedrawIsNeeded())
1361 // redraw global screen border (or clear, if defined to be empty)
1362 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1364 if (game_status == GAME_MODE_EDITOR)
1365 DrawSpecialEditorDoor();
1367 // copy previous playfield and door areas, if they are defined on both
1368 // previous and current screen and if they still have the same size
1370 if (real_sx_last != -1 && real_sy_last != -1 &&
1371 REAL_SX != -1 && REAL_SY != -1 &&
1372 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1373 BlitBitmap(bitmap_db_store_1, backbuffer,
1374 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1377 if (dx_last != -1 && dy_last != -1 &&
1378 DX != -1 && DY != -1 &&
1379 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1380 BlitBitmap(bitmap_db_store_1, backbuffer,
1381 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1383 if (game_status != GAME_MODE_EDITOR)
1385 if (vx_last != -1 && vy_last != -1 &&
1386 VX != -1 && VY != -1 &&
1387 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1388 BlitBitmap(bitmap_db_store_1, backbuffer,
1389 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1393 if (ex_last != -1 && ey_last != -1 &&
1394 EX != -1 && EY != -1 &&
1395 exsize_last == EXSIZE && eysize_last == EYSIZE)
1396 BlitBitmap(bitmap_db_store_1, backbuffer,
1397 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1400 redraw_mask = REDRAW_ALL;
1403 game_status_last = game_status;
1405 global_border_bitmap_last = global_border_bitmap;
1407 real_sx_last = REAL_SX;
1408 real_sy_last = REAL_SY;
1409 full_sxsize_last = FULL_SXSIZE;
1410 full_sysize_last = FULL_SYSIZE;
1413 dxsize_last = DXSIZE;
1414 dysize_last = DYSIZE;
1417 vxsize_last = VXSIZE;
1418 vysize_last = VYSIZE;
1421 exsize_last = EXSIZE;
1422 eysize_last = EYSIZE;
1425 void ClearField(void)
1427 RedrawGlobalBorderIfNeeded();
1429 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1430 // (when entering hall of fame after playing)
1431 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1433 // !!! maybe this should be done before clearing the background !!!
1434 if (game_status == GAME_MODE_PLAYING)
1436 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1437 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1441 SetDrawtoField(DRAW_TO_BACKBUFFER);
1445 void MarkTileDirty(int x, int y)
1447 redraw_mask |= REDRAW_FIELD;
1450 void SetBorderElement(void)
1454 BorderElement = EL_EMPTY;
1456 // the MM game engine does not use a visible border element
1457 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1460 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1462 for (x = 0; x < lev_fieldx; x++)
1464 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1465 BorderElement = EL_STEELWALL;
1467 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1473 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1474 int max_array_fieldx, int max_array_fieldy,
1475 short field[max_array_fieldx][max_array_fieldy],
1476 int max_fieldx, int max_fieldy)
1480 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1481 static int safety = 0;
1483 // check if starting field still has the desired content
1484 if (field[from_x][from_y] == fill_element)
1489 if (safety > max_fieldx * max_fieldy)
1490 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1492 old_element = field[from_x][from_y];
1493 field[from_x][from_y] = fill_element;
1495 for (i = 0; i < 4; i++)
1497 x = from_x + check[i][0];
1498 y = from_y + check[i][1];
1500 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1501 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1502 field, max_fieldx, max_fieldy);
1508 void FloodFillLevel(int from_x, int from_y, int fill_element,
1509 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1510 int max_fieldx, int max_fieldy)
1512 FloodFillLevelExt(from_x, from_y, fill_element,
1513 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1514 max_fieldx, max_fieldy);
1517 void SetRandomAnimationValue(int x, int y)
1519 gfx.anim_random_frame = GfxRandom[x][y];
1522 int getGraphicAnimationFrame(int graphic, int sync_frame)
1524 // animation synchronized with global frame counter, not move position
1525 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1526 sync_frame = FrameCounter;
1528 return getAnimationFrame(graphic_info[graphic].anim_frames,
1529 graphic_info[graphic].anim_delay,
1530 graphic_info[graphic].anim_mode,
1531 graphic_info[graphic].anim_start_frame,
1535 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1537 struct GraphicInfo *g = &graphic_info[graphic];
1538 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1540 if (tilesize == gfx.standard_tile_size)
1541 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1542 else if (tilesize == game.tile_size)
1543 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1545 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1548 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1549 boolean get_backside)
1551 struct GraphicInfo *g = &graphic_info[graphic];
1552 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1553 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1555 if (g->offset_y == 0) // frames are ordered horizontally
1557 int max_width = g->anim_frames_per_line * g->width;
1558 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1560 *x = pos % max_width;
1561 *y = src_y % g->height + pos / max_width * g->height;
1563 else if (g->offset_x == 0) // frames are ordered vertically
1565 int max_height = g->anim_frames_per_line * g->height;
1566 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1568 *x = src_x % g->width + pos / max_height * g->width;
1569 *y = pos % max_height;
1571 else // frames are ordered diagonally
1573 *x = src_x + frame * g->offset_x;
1574 *y = src_y + frame * g->offset_y;
1578 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1579 Bitmap **bitmap, int *x, int *y,
1580 boolean get_backside)
1582 struct GraphicInfo *g = &graphic_info[graphic];
1584 // if no graphics defined at all, use fallback graphics
1585 if (g->bitmaps == NULL)
1586 *g = graphic_info[IMG_CHAR_EXCLAM];
1588 // if no in-game graphics defined, always use standard graphic size
1589 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1590 tilesize = TILESIZE;
1592 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1593 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1595 *x = *x * tilesize / g->tile_size;
1596 *y = *y * tilesize / g->tile_size;
1599 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1600 Bitmap **bitmap, int *x, int *y)
1602 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1605 void getFixedGraphicSource(int graphic, int frame,
1606 Bitmap **bitmap, int *x, int *y)
1608 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1611 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1613 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1616 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1617 int *x, int *y, boolean get_backside)
1619 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1623 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1625 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1628 void DrawGraphic(int x, int y, int graphic, int frame)
1631 if (!IN_SCR_FIELD(x, y))
1633 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1634 printf("DrawGraphic(): This should never happen!\n");
1639 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1642 MarkTileDirty(x, y);
1645 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1648 if (!IN_SCR_FIELD(x, y))
1650 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1651 printf("DrawGraphic(): This should never happen!\n");
1656 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1658 MarkTileDirty(x, y);
1661 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1667 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1669 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1672 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1678 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1679 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1682 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1685 if (!IN_SCR_FIELD(x, y))
1687 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1688 printf("DrawGraphicThruMask(): This should never happen!\n");
1693 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1696 MarkTileDirty(x, y);
1699 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1702 if (!IN_SCR_FIELD(x, y))
1704 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1705 printf("DrawGraphicThruMask(): This should never happen!\n");
1710 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1712 MarkTileDirty(x, y);
1715 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1721 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1723 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1727 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1728 int graphic, int frame)
1733 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1735 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1739 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1741 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1743 MarkTileDirty(x / tilesize, y / tilesize);
1746 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1749 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1750 graphic, frame, tilesize);
1751 MarkTileDirty(x / tilesize, y / tilesize);
1754 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1760 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1761 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1764 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1765 int frame, int tilesize)
1770 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1771 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1774 void DrawMiniGraphic(int x, int y, int graphic)
1776 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1777 MarkTileDirty(x / 2, y / 2);
1780 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1785 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1786 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1789 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1790 int graphic, int frame,
1791 int cut_mode, int mask_mode)
1796 int width = TILEX, height = TILEY;
1799 if (dx || dy) // shifted graphic
1801 if (x < BX1) // object enters playfield from the left
1808 else if (x > BX2) // object enters playfield from the right
1814 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1820 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1822 else if (dx) // general horizontal movement
1823 MarkTileDirty(x + SIGN(dx), y);
1825 if (y < BY1) // object enters playfield from the top
1827 if (cut_mode == CUT_BELOW) // object completely above top border
1835 else if (y > BY2) // object enters playfield from the bottom
1841 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1847 else if (dy > 0 && cut_mode == CUT_ABOVE)
1849 if (y == BY2) // object completely above bottom border
1855 MarkTileDirty(x, y + 1);
1856 } // object leaves playfield to the bottom
1857 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1859 else if (dy) // general vertical movement
1860 MarkTileDirty(x, y + SIGN(dy));
1864 if (!IN_SCR_FIELD(x, y))
1866 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1867 printf("DrawGraphicShifted(): This should never happen!\n");
1872 width = width * TILESIZE_VAR / TILESIZE;
1873 height = height * TILESIZE_VAR / TILESIZE;
1874 cx = cx * TILESIZE_VAR / TILESIZE;
1875 cy = cy * TILESIZE_VAR / TILESIZE;
1876 dx = dx * TILESIZE_VAR / TILESIZE;
1877 dy = dy * TILESIZE_VAR / TILESIZE;
1879 if (width > 0 && height > 0)
1881 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1886 dst_x = FX + x * TILEX_VAR + dx;
1887 dst_y = FY + y * TILEY_VAR + dy;
1889 if (mask_mode == USE_MASKING)
1890 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1893 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1896 MarkTileDirty(x, y);
1900 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1901 int graphic, int frame,
1902 int cut_mode, int mask_mode)
1907 int width = TILEX_VAR, height = TILEY_VAR;
1910 int x2 = x + SIGN(dx);
1911 int y2 = y + SIGN(dy);
1913 // movement with two-tile animations must be sync'ed with movement position,
1914 // not with current GfxFrame (which can be higher when using slow movement)
1915 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1916 int anim_frames = graphic_info[graphic].anim_frames;
1918 // (we also need anim_delay here for movement animations with less frames)
1919 int anim_delay = graphic_info[graphic].anim_delay;
1920 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1922 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1923 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1925 // re-calculate animation frame for two-tile movement animation
1926 frame = getGraphicAnimationFrame(graphic, sync_frame);
1928 // check if movement start graphic inside screen area and should be drawn
1929 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1931 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1933 dst_x = FX + x1 * TILEX_VAR;
1934 dst_y = FY + y1 * TILEY_VAR;
1936 if (mask_mode == USE_MASKING)
1937 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1940 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1943 MarkTileDirty(x1, y1);
1946 // check if movement end graphic inside screen area and should be drawn
1947 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1949 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1951 dst_x = FX + x2 * TILEX_VAR;
1952 dst_y = FY + y2 * TILEY_VAR;
1954 if (mask_mode == USE_MASKING)
1955 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1958 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1961 MarkTileDirty(x2, y2);
1965 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1966 int graphic, int frame,
1967 int cut_mode, int mask_mode)
1971 DrawGraphic(x, y, graphic, frame);
1976 if (graphic_info[graphic].double_movement) // EM style movement images
1977 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1979 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1982 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1983 int graphic, int frame, int cut_mode)
1985 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1988 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1989 int cut_mode, int mask_mode)
1991 int lx = LEVELX(x), ly = LEVELY(y);
1995 if (IN_LEV_FIELD(lx, ly))
1997 SetRandomAnimationValue(lx, ly);
1999 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2000 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2002 // do not use double (EM style) movement graphic when not moving
2003 if (graphic_info[graphic].double_movement && !dx && !dy)
2005 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2006 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2009 else // border element
2011 graphic = el2img(element);
2012 frame = getGraphicAnimationFrame(graphic, -1);
2015 if (element == EL_EXPANDABLE_WALL)
2017 boolean left_stopped = FALSE, right_stopped = FALSE;
2019 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2020 left_stopped = TRUE;
2021 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2022 right_stopped = TRUE;
2024 if (left_stopped && right_stopped)
2026 else if (left_stopped)
2028 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2029 frame = graphic_info[graphic].anim_frames - 1;
2031 else if (right_stopped)
2033 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2034 frame = graphic_info[graphic].anim_frames - 1;
2039 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2040 else if (mask_mode == USE_MASKING)
2041 DrawGraphicThruMask(x, y, graphic, frame);
2043 DrawGraphic(x, y, graphic, frame);
2046 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2047 int cut_mode, int mask_mode)
2049 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2050 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2051 cut_mode, mask_mode);
2054 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2057 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2060 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2063 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2066 void DrawLevelElementThruMask(int x, int y, int element)
2068 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2071 void DrawLevelFieldThruMask(int x, int y)
2073 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2076 // !!! implementation of quicksand is totally broken !!!
2077 #define IS_CRUMBLED_TILE(x, y, e) \
2078 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2079 !IS_MOVING(x, y) || \
2080 (e) == EL_QUICKSAND_EMPTYING || \
2081 (e) == EL_QUICKSAND_FAST_EMPTYING))
2083 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2088 int width, height, cx, cy;
2089 int sx = SCREENX(x), sy = SCREENY(y);
2090 int crumbled_border_size = graphic_info[graphic].border_size;
2091 int crumbled_tile_size = graphic_info[graphic].tile_size;
2092 int crumbled_border_size_var =
2093 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2096 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2098 for (i = 1; i < 4; i++)
2100 int dxx = (i & 1 ? dx : 0);
2101 int dyy = (i & 2 ? dy : 0);
2104 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2107 // check if neighbour field is of same crumble type
2108 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2109 graphic_info[graphic].class ==
2110 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2112 // return if check prevents inner corner
2113 if (same == (dxx == dx && dyy == dy))
2117 // if we reach this point, we have an inner corner
2119 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2121 width = crumbled_border_size_var;
2122 height = crumbled_border_size_var;
2123 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2124 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2126 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2127 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2130 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2135 int width, height, bx, by, cx, cy;
2136 int sx = SCREENX(x), sy = SCREENY(y);
2137 int crumbled_border_size = graphic_info[graphic].border_size;
2138 int crumbled_tile_size = graphic_info[graphic].tile_size;
2139 int crumbled_border_size_var =
2140 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2141 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2144 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2146 // draw simple, sloppy, non-corner-accurate crumbled border
2148 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2149 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2150 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2151 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2153 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2154 FX + sx * TILEX_VAR + cx,
2155 FY + sy * TILEY_VAR + cy);
2157 // (remaining middle border part must be at least as big as corner part)
2158 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2159 crumbled_border_size_var >= TILESIZE_VAR / 3)
2162 // correct corners of crumbled border, if needed
2164 for (i = -1; i <= 1; i += 2)
2166 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2167 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2168 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2171 // check if neighbour field is of same crumble type
2172 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2173 graphic_info[graphic].class ==
2174 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2176 // no crumbled corner, but continued crumbled border
2178 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2179 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2180 int b1 = (i == 1 ? crumbled_border_size_var :
2181 TILESIZE_VAR - 2 * crumbled_border_size_var);
2183 width = crumbled_border_size_var;
2184 height = crumbled_border_size_var;
2186 if (dir == 1 || dir == 2)
2201 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2203 FX + sx * TILEX_VAR + cx,
2204 FY + sy * TILEY_VAR + cy);
2209 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2211 int sx = SCREENX(x), sy = SCREENY(y);
2214 static int xy[4][2] =
2222 if (!IN_LEV_FIELD(x, y))
2225 element = TILE_GFX_ELEMENT(x, y);
2227 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2229 if (!IN_SCR_FIELD(sx, sy))
2232 // crumble field borders towards direct neighbour fields
2233 for (i = 0; i < 4; i++)
2235 int xx = x + xy[i][0];
2236 int yy = y + xy[i][1];
2238 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2241 // check if neighbour field is of same crumble type
2242 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2243 graphic_info[graphic].class ==
2244 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2247 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2250 // crumble inner field corners towards corner neighbour fields
2251 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2252 graphic_info[graphic].anim_frames == 2)
2254 for (i = 0; i < 4; i++)
2256 int dx = (i & 1 ? +1 : -1);
2257 int dy = (i & 2 ? +1 : -1);
2259 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2263 MarkTileDirty(sx, sy);
2265 else // center field is not crumbled -- crumble neighbour fields
2267 // crumble field borders of direct neighbour fields
2268 for (i = 0; i < 4; i++)
2270 int xx = x + xy[i][0];
2271 int yy = y + xy[i][1];
2272 int sxx = sx + xy[i][0];
2273 int syy = sy + xy[i][1];
2275 if (!IN_LEV_FIELD(xx, yy) ||
2276 !IN_SCR_FIELD(sxx, syy))
2279 // do not crumble fields that are being digged or snapped
2280 if (Feld[xx][yy] == EL_EMPTY ||
2281 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2284 element = TILE_GFX_ELEMENT(xx, yy);
2286 if (!IS_CRUMBLED_TILE(xx, yy, element))
2289 graphic = el_act2crm(element, ACTION_DEFAULT);
2291 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2293 MarkTileDirty(sxx, syy);
2296 // crumble inner field corners of corner neighbour fields
2297 for (i = 0; i < 4; i++)
2299 int dx = (i & 1 ? +1 : -1);
2300 int dy = (i & 2 ? +1 : -1);
2306 if (!IN_LEV_FIELD(xx, yy) ||
2307 !IN_SCR_FIELD(sxx, syy))
2310 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2313 element = TILE_GFX_ELEMENT(xx, yy);
2315 if (!IS_CRUMBLED_TILE(xx, yy, element))
2318 graphic = el_act2crm(element, ACTION_DEFAULT);
2320 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2321 graphic_info[graphic].anim_frames == 2)
2322 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2324 MarkTileDirty(sxx, syy);
2329 void DrawLevelFieldCrumbled(int x, int y)
2333 if (!IN_LEV_FIELD(x, y))
2336 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2337 GfxElement[x][y] != EL_UNDEFINED &&
2338 GFX_CRUMBLED(GfxElement[x][y]))
2340 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2345 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2347 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2350 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2353 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2354 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2355 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2356 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2357 int sx = SCREENX(x), sy = SCREENY(y);
2359 DrawGraphic(sx, sy, graphic1, frame1);
2360 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2363 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2365 int sx = SCREENX(x), sy = SCREENY(y);
2366 static int xy[4][2] =
2375 // crumble direct neighbour fields (required for field borders)
2376 for (i = 0; i < 4; i++)
2378 int xx = x + xy[i][0];
2379 int yy = y + xy[i][1];
2380 int sxx = sx + xy[i][0];
2381 int syy = sy + xy[i][1];
2383 if (!IN_LEV_FIELD(xx, yy) ||
2384 !IN_SCR_FIELD(sxx, syy) ||
2385 !GFX_CRUMBLED(Feld[xx][yy]) ||
2389 DrawLevelField(xx, yy);
2392 // crumble corner neighbour fields (required for inner field corners)
2393 for (i = 0; i < 4; i++)
2395 int dx = (i & 1 ? +1 : -1);
2396 int dy = (i & 2 ? +1 : -1);
2402 if (!IN_LEV_FIELD(xx, yy) ||
2403 !IN_SCR_FIELD(sxx, syy) ||
2404 !GFX_CRUMBLED(Feld[xx][yy]) ||
2408 int element = TILE_GFX_ELEMENT(xx, yy);
2409 int graphic = el_act2crm(element, ACTION_DEFAULT);
2411 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2412 graphic_info[graphic].anim_frames == 2)
2413 DrawLevelField(xx, yy);
2417 static int getBorderElement(int x, int y)
2421 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2422 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2423 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2424 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2425 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2426 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2427 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2429 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2430 int steel_position = (x == -1 && y == -1 ? 0 :
2431 x == lev_fieldx && y == -1 ? 1 :
2432 x == -1 && y == lev_fieldy ? 2 :
2433 x == lev_fieldx && y == lev_fieldy ? 3 :
2434 x == -1 || x == lev_fieldx ? 4 :
2435 y == -1 || y == lev_fieldy ? 5 : 6);
2437 return border[steel_position][steel_type];
2440 void DrawScreenElement(int x, int y, int element)
2442 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2443 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2446 void DrawLevelElement(int x, int y, int element)
2448 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2449 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2452 void DrawScreenField(int x, int y)
2454 int lx = LEVELX(x), ly = LEVELY(y);
2455 int element, content;
2457 if (!IN_LEV_FIELD(lx, ly))
2459 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2462 element = getBorderElement(lx, ly);
2464 DrawScreenElement(x, y, element);
2469 element = Feld[lx][ly];
2470 content = Store[lx][ly];
2472 if (IS_MOVING(lx, ly))
2474 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2475 boolean cut_mode = NO_CUTTING;
2477 if (element == EL_QUICKSAND_EMPTYING ||
2478 element == EL_QUICKSAND_FAST_EMPTYING ||
2479 element == EL_MAGIC_WALL_EMPTYING ||
2480 element == EL_BD_MAGIC_WALL_EMPTYING ||
2481 element == EL_DC_MAGIC_WALL_EMPTYING ||
2482 element == EL_AMOEBA_DROPPING)
2483 cut_mode = CUT_ABOVE;
2484 else if (element == EL_QUICKSAND_FILLING ||
2485 element == EL_QUICKSAND_FAST_FILLING ||
2486 element == EL_MAGIC_WALL_FILLING ||
2487 element == EL_BD_MAGIC_WALL_FILLING ||
2488 element == EL_DC_MAGIC_WALL_FILLING)
2489 cut_mode = CUT_BELOW;
2491 if (cut_mode == CUT_ABOVE)
2492 DrawScreenElement(x, y, element);
2494 DrawScreenElement(x, y, EL_EMPTY);
2497 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2498 else if (cut_mode == NO_CUTTING)
2499 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2502 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2504 if (cut_mode == CUT_BELOW &&
2505 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2506 DrawLevelElement(lx, ly + 1, element);
2509 if (content == EL_ACID)
2511 int dir = MovDir[lx][ly];
2512 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2513 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2515 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2517 // prevent target field from being drawn again (but without masking)
2518 // (this would happen if target field is scanned after moving element)
2519 Stop[newlx][newly] = TRUE;
2522 else if (IS_BLOCKED(lx, ly))
2527 boolean cut_mode = NO_CUTTING;
2528 int element_old, content_old;
2530 Blocked2Moving(lx, ly, &oldx, &oldy);
2533 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2534 MovDir[oldx][oldy] == MV_RIGHT);
2536 element_old = Feld[oldx][oldy];
2537 content_old = Store[oldx][oldy];
2539 if (element_old == EL_QUICKSAND_EMPTYING ||
2540 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2541 element_old == EL_MAGIC_WALL_EMPTYING ||
2542 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2543 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2544 element_old == EL_AMOEBA_DROPPING)
2545 cut_mode = CUT_ABOVE;
2547 DrawScreenElement(x, y, EL_EMPTY);
2550 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2552 else if (cut_mode == NO_CUTTING)
2553 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2556 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2559 else if (IS_DRAWABLE(element))
2560 DrawScreenElement(x, y, element);
2562 DrawScreenElement(x, y, EL_EMPTY);
2565 void DrawLevelField(int x, int y)
2567 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2568 DrawScreenField(SCREENX(x), SCREENY(y));
2569 else if (IS_MOVING(x, y))
2573 Moving2Blocked(x, y, &newx, &newy);
2574 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2575 DrawScreenField(SCREENX(newx), SCREENY(newy));
2577 else if (IS_BLOCKED(x, y))
2581 Blocked2Moving(x, y, &oldx, &oldy);
2582 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2583 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2587 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2588 int (*el2img_function)(int), boolean masked,
2589 int element_bits_draw)
2591 int element_base = map_mm_wall_element(element);
2592 int element_bits = (IS_DF_WALL(element) ?
2593 element - EL_DF_WALL_START :
2594 IS_MM_WALL(element) ?
2595 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2596 int graphic = el2img_function(element_base);
2597 int tilesize_draw = tilesize / 2;
2602 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2604 for (i = 0; i < 4; i++)
2606 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2607 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2609 if (!(element_bits_draw & (1 << i)))
2612 if (element_bits & (1 << i))
2615 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2616 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2618 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2619 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2624 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2625 tilesize_draw, tilesize_draw);
2630 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2631 boolean masked, int element_bits_draw)
2633 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2634 element, tilesize, el2edimg, masked, element_bits_draw);
2637 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2638 int (*el2img_function)(int))
2640 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2644 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2647 if (IS_MM_WALL(element))
2649 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2650 element, tilesize, el2edimg, masked, 0x000f);
2654 int graphic = el2edimg(element);
2657 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2659 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2663 void DrawSizedElement(int x, int y, int element, int tilesize)
2665 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2668 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2670 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2673 void DrawMiniElement(int x, int y, int element)
2677 graphic = el2edimg(element);
2678 DrawMiniGraphic(x, y, graphic);
2681 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2684 int x = sx + scroll_x, y = sy + scroll_y;
2686 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2687 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2688 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2689 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2691 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2694 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2696 int x = sx + scroll_x, y = sy + scroll_y;
2698 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2699 DrawMiniElement(sx, sy, EL_EMPTY);
2700 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2701 DrawMiniElement(sx, sy, Feld[x][y]);
2703 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2706 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2707 int x, int y, int xsize, int ysize,
2708 int tile_width, int tile_height)
2712 int dst_x = startx + x * tile_width;
2713 int dst_y = starty + y * tile_height;
2714 int width = graphic_info[graphic].width;
2715 int height = graphic_info[graphic].height;
2716 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2717 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2718 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2719 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2720 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2721 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2722 boolean draw_masked = graphic_info[graphic].draw_masked;
2724 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2726 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2728 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2732 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2733 inner_sx + (x - 1) * tile_width % inner_width);
2734 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2735 inner_sy + (y - 1) * tile_height % inner_height);
2738 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2741 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2745 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2746 int x, int y, int xsize, int ysize,
2749 int font_width = getFontWidth(font_nr);
2750 int font_height = getFontHeight(font_nr);
2752 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2753 font_width, font_height);
2756 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2758 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2759 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2760 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2761 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2762 boolean no_delay = (tape.warp_forward);
2763 unsigned int anim_delay = 0;
2764 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2765 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2766 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2767 int font_width = getFontWidth(font_nr);
2768 int font_height = getFontHeight(font_nr);
2769 int max_xsize = level.envelope[envelope_nr].xsize;
2770 int max_ysize = level.envelope[envelope_nr].ysize;
2771 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2772 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2773 int xend = max_xsize;
2774 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2775 int xstep = (xstart < xend ? 1 : 0);
2776 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2778 int end = MAX(xend - xstart, yend - ystart);
2781 for (i = start; i <= end; i++)
2783 int last_frame = end; // last frame of this "for" loop
2784 int x = xstart + i * xstep;
2785 int y = ystart + i * ystep;
2786 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2787 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2788 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2789 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2792 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2794 BlitScreenToBitmap(backbuffer);
2796 SetDrawtoField(DRAW_TO_BACKBUFFER);
2798 for (yy = 0; yy < ysize; yy++)
2799 for (xx = 0; xx < xsize; xx++)
2800 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2802 DrawTextBuffer(sx + font_width, sy + font_height,
2803 level.envelope[envelope_nr].text, font_nr, max_xsize,
2804 xsize - 2, ysize - 2, 0, mask_mode,
2805 level.envelope[envelope_nr].autowrap,
2806 level.envelope[envelope_nr].centered, FALSE);
2808 redraw_mask |= REDRAW_FIELD;
2811 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2814 ClearAutoRepeatKeyEvents();
2817 void ShowEnvelope(int envelope_nr)
2819 int element = EL_ENVELOPE_1 + envelope_nr;
2820 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2821 int sound_opening = element_info[element].sound[ACTION_OPENING];
2822 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2823 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2824 boolean no_delay = (tape.warp_forward);
2825 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2826 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2827 int anim_mode = graphic_info[graphic].anim_mode;
2828 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2829 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2831 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2833 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2835 if (anim_mode == ANIM_DEFAULT)
2836 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2838 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2841 Delay(wait_delay_value);
2843 WaitForEventToContinue();
2845 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2847 if (anim_mode != ANIM_NONE)
2848 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2850 if (anim_mode == ANIM_DEFAULT)
2851 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2853 game.envelope_active = FALSE;
2855 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2857 redraw_mask |= REDRAW_FIELD;
2861 static void setRequestBasePosition(int *x, int *y)
2863 int sx_base, sy_base;
2865 if (request.x != -1)
2866 sx_base = request.x;
2867 else if (request.align == ALIGN_LEFT)
2869 else if (request.align == ALIGN_RIGHT)
2870 sx_base = SX + SXSIZE;
2872 sx_base = SX + SXSIZE / 2;
2874 if (request.y != -1)
2875 sy_base = request.y;
2876 else if (request.valign == VALIGN_TOP)
2878 else if (request.valign == VALIGN_BOTTOM)
2879 sy_base = SY + SYSIZE;
2881 sy_base = SY + SYSIZE / 2;
2887 static void setRequestPositionExt(int *x, int *y, int width, int height,
2888 boolean add_border_size)
2890 int border_size = request.border_size;
2891 int sx_base, sy_base;
2894 setRequestBasePosition(&sx_base, &sy_base);
2896 if (request.align == ALIGN_LEFT)
2898 else if (request.align == ALIGN_RIGHT)
2899 sx = sx_base - width;
2901 sx = sx_base - width / 2;
2903 if (request.valign == VALIGN_TOP)
2905 else if (request.valign == VALIGN_BOTTOM)
2906 sy = sy_base - height;
2908 sy = sy_base - height / 2;
2910 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2911 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2913 if (add_border_size)
2923 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2925 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2928 static void DrawEnvelopeRequest(char *text)
2930 char *text_final = text;
2931 char *text_door_style = NULL;
2932 int graphic = IMG_BACKGROUND_REQUEST;
2933 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2934 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2935 int font_nr = FONT_REQUEST;
2936 int font_width = getFontWidth(font_nr);
2937 int font_height = getFontHeight(font_nr);
2938 int border_size = request.border_size;
2939 int line_spacing = request.line_spacing;
2940 int line_height = font_height + line_spacing;
2941 int max_text_width = request.width - 2 * border_size;
2942 int max_text_height = request.height - 2 * border_size;
2943 int line_length = max_text_width / font_width;
2944 int max_lines = max_text_height / line_height;
2945 int text_width = line_length * font_width;
2946 int width = request.width;
2947 int height = request.height;
2948 int tile_size = MAX(request.step_offset, 1);
2949 int x_steps = width / tile_size;
2950 int y_steps = height / tile_size;
2951 int sx_offset = border_size;
2952 int sy_offset = border_size;
2956 if (request.centered)
2957 sx_offset = (request.width - text_width) / 2;
2959 if (request.wrap_single_words && !request.autowrap)
2961 char *src_text_ptr, *dst_text_ptr;
2963 text_door_style = checked_malloc(2 * strlen(text) + 1);
2965 src_text_ptr = text;
2966 dst_text_ptr = text_door_style;
2968 while (*src_text_ptr)
2970 if (*src_text_ptr == ' ' ||
2971 *src_text_ptr == '?' ||
2972 *src_text_ptr == '!')
2973 *dst_text_ptr++ = '\n';
2975 if (*src_text_ptr != ' ')
2976 *dst_text_ptr++ = *src_text_ptr;
2981 *dst_text_ptr = '\0';
2983 text_final = text_door_style;
2986 setRequestPosition(&sx, &sy, FALSE);
2988 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2990 for (y = 0; y < y_steps; y++)
2991 for (x = 0; x < x_steps; x++)
2992 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2993 x, y, x_steps, y_steps,
2994 tile_size, tile_size);
2996 // force DOOR font inside door area
2997 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2999 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3000 line_length, -1, max_lines, line_spacing, mask_mode,
3001 request.autowrap, request.centered, FALSE);
3005 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3006 RedrawGadget(tool_gadget[i]);
3008 // store readily prepared envelope request for later use when animating
3009 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3011 if (text_door_style)
3012 free(text_door_style);
3015 static void AnimateEnvelopeRequest(int anim_mode, int action)
3017 int graphic = IMG_BACKGROUND_REQUEST;
3018 boolean draw_masked = graphic_info[graphic].draw_masked;
3019 int delay_value_normal = request.step_delay;
3020 int delay_value_fast = delay_value_normal / 2;
3021 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3022 boolean no_delay = (tape.warp_forward);
3023 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3024 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3025 unsigned int anim_delay = 0;
3027 int tile_size = MAX(request.step_offset, 1);
3028 int max_xsize = request.width / tile_size;
3029 int max_ysize = request.height / tile_size;
3030 int max_xsize_inner = max_xsize - 2;
3031 int max_ysize_inner = max_ysize - 2;
3033 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3034 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3035 int xend = max_xsize_inner;
3036 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3037 int xstep = (xstart < xend ? 1 : 0);
3038 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3040 int end = MAX(xend - xstart, yend - ystart);
3043 if (setup.quick_doors)
3050 for (i = start; i <= end; i++)
3052 int last_frame = end; // last frame of this "for" loop
3053 int x = xstart + i * xstep;
3054 int y = ystart + i * ystep;
3055 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3056 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3057 int xsize_size_left = (xsize - 1) * tile_size;
3058 int ysize_size_top = (ysize - 1) * tile_size;
3059 int max_xsize_pos = (max_xsize - 1) * tile_size;
3060 int max_ysize_pos = (max_ysize - 1) * tile_size;
3061 int width = xsize * tile_size;
3062 int height = ysize * tile_size;
3067 setRequestPosition(&src_x, &src_y, FALSE);
3068 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3070 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3072 for (yy = 0; yy < 2; yy++)
3074 for (xx = 0; xx < 2; xx++)
3076 int src_xx = src_x + xx * max_xsize_pos;
3077 int src_yy = src_y + yy * max_ysize_pos;
3078 int dst_xx = dst_x + xx * xsize_size_left;
3079 int dst_yy = dst_y + yy * ysize_size_top;
3080 int xx_size = (xx ? tile_size : xsize_size_left);
3081 int yy_size = (yy ? tile_size : ysize_size_top);
3084 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3085 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3087 BlitBitmap(bitmap_db_store_2, backbuffer,
3088 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3092 redraw_mask |= REDRAW_FIELD;
3096 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3099 ClearAutoRepeatKeyEvents();
3102 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3104 int graphic = IMG_BACKGROUND_REQUEST;
3105 int sound_opening = SND_REQUEST_OPENING;
3106 int sound_closing = SND_REQUEST_CLOSING;
3107 int anim_mode_1 = request.anim_mode; // (higher priority)
3108 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3109 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3110 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3111 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3113 if (game_status == GAME_MODE_PLAYING)
3114 BlitScreenToBitmap(backbuffer);
3116 SetDrawtoField(DRAW_TO_BACKBUFFER);
3118 // SetDrawBackgroundMask(REDRAW_NONE);
3120 if (action == ACTION_OPENING)
3122 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3124 if (req_state & REQ_ASK)
3126 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3127 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3129 else if (req_state & REQ_CONFIRM)
3131 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3133 else if (req_state & REQ_PLAYER)
3135 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3136 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3137 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3138 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3141 DrawEnvelopeRequest(text);
3144 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3146 if (action == ACTION_OPENING)
3148 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3150 if (anim_mode == ANIM_DEFAULT)
3151 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3153 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3157 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3159 if (anim_mode != ANIM_NONE)
3160 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3162 if (anim_mode == ANIM_DEFAULT)
3163 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3166 game.envelope_active = FALSE;
3168 if (action == ACTION_CLOSING)
3169 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3171 // SetDrawBackgroundMask(last_draw_background_mask);
3173 redraw_mask |= REDRAW_FIELD;
3177 if (action == ACTION_CLOSING &&
3178 game_status == GAME_MODE_PLAYING &&
3179 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3180 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3183 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3185 if (IS_MM_WALL(element))
3187 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3193 int graphic = el2preimg(element);
3195 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3196 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3201 void DrawLevel(int draw_background_mask)
3205 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3206 SetDrawBackgroundMask(draw_background_mask);
3210 for (x = BX1; x <= BX2; x++)
3211 for (y = BY1; y <= BY2; y++)
3212 DrawScreenField(x, y);
3214 redraw_mask |= REDRAW_FIELD;
3217 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3222 for (x = 0; x < size_x; x++)
3223 for (y = 0; y < size_y; y++)
3224 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3226 redraw_mask |= REDRAW_FIELD;
3229 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3233 for (x = 0; x < size_x; x++)
3234 for (y = 0; y < size_y; y++)
3235 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3237 redraw_mask |= REDRAW_FIELD;
3240 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3242 boolean show_level_border = (BorderElement != EL_EMPTY);
3243 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3244 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3245 int tile_size = preview.tile_size;
3246 int preview_width = preview.xsize * tile_size;
3247 int preview_height = preview.ysize * tile_size;
3248 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3249 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3250 int real_preview_width = real_preview_xsize * tile_size;
3251 int real_preview_height = real_preview_ysize * tile_size;
3252 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3253 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3256 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3259 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3261 dst_x += (preview_width - real_preview_width) / 2;
3262 dst_y += (preview_height - real_preview_height) / 2;
3264 for (x = 0; x < real_preview_xsize; x++)
3266 for (y = 0; y < real_preview_ysize; y++)
3268 int lx = from_x + x + (show_level_border ? -1 : 0);
3269 int ly = from_y + y + (show_level_border ? -1 : 0);
3270 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3271 getBorderElement(lx, ly));
3273 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3274 element, tile_size);
3278 redraw_mask |= REDRAW_FIELD;
3281 #define MICROLABEL_EMPTY 0
3282 #define MICROLABEL_LEVEL_NAME 1
3283 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3284 #define MICROLABEL_LEVEL_AUTHOR 3
3285 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3286 #define MICROLABEL_IMPORTED_FROM 5
3287 #define MICROLABEL_IMPORTED_BY_HEAD 6
3288 #define MICROLABEL_IMPORTED_BY 7
3290 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3292 int max_text_width = SXSIZE;
3293 int font_width = getFontWidth(font_nr);
3295 if (pos->align == ALIGN_CENTER)
3296 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3297 else if (pos->align == ALIGN_RIGHT)
3298 max_text_width = pos->x;
3300 max_text_width = SXSIZE - pos->x;
3302 return max_text_width / font_width;
3305 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3307 char label_text[MAX_OUTPUT_LINESIZE + 1];
3308 int max_len_label_text;
3309 int font_nr = pos->font;
3312 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3315 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3316 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3317 mode == MICROLABEL_IMPORTED_BY_HEAD)
3318 font_nr = pos->font_alt;
3320 max_len_label_text = getMaxTextLength(pos, font_nr);
3322 if (pos->size != -1)
3323 max_len_label_text = pos->size;
3325 for (i = 0; i < max_len_label_text; i++)
3326 label_text[i] = ' ';
3327 label_text[max_len_label_text] = '\0';
3329 if (strlen(label_text) > 0)
3330 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3333 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3334 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3335 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3336 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3337 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3338 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3339 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3340 max_len_label_text);
3341 label_text[max_len_label_text] = '\0';
3343 if (strlen(label_text) > 0)
3344 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3346 redraw_mask |= REDRAW_FIELD;
3349 static void DrawPreviewLevelLabel(int mode)
3351 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3354 static void DrawPreviewLevelInfo(int mode)
3356 if (mode == MICROLABEL_LEVEL_NAME)
3357 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3358 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3359 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3362 static void DrawPreviewLevelExt(boolean restart)
3364 static unsigned int scroll_delay = 0;
3365 static unsigned int label_delay = 0;
3366 static int from_x, from_y, scroll_direction;
3367 static int label_state, label_counter;
3368 unsigned int scroll_delay_value = preview.step_delay;
3369 boolean show_level_border = (BorderElement != EL_EMPTY);
3370 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3371 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3378 if (preview.anim_mode == ANIM_CENTERED)
3380 if (level_xsize > preview.xsize)
3381 from_x = (level_xsize - preview.xsize) / 2;
3382 if (level_ysize > preview.ysize)
3383 from_y = (level_ysize - preview.ysize) / 2;
3386 from_x += preview.xoffset;
3387 from_y += preview.yoffset;
3389 scroll_direction = MV_RIGHT;
3393 DrawPreviewLevelPlayfield(from_x, from_y);
3394 DrawPreviewLevelLabel(label_state);
3396 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3397 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3399 // initialize delay counters
3400 DelayReached(&scroll_delay, 0);
3401 DelayReached(&label_delay, 0);
3403 if (leveldir_current->name)
3405 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3406 char label_text[MAX_OUTPUT_LINESIZE + 1];
3407 int font_nr = pos->font;
3408 int max_len_label_text = getMaxTextLength(pos, font_nr);
3410 if (pos->size != -1)
3411 max_len_label_text = pos->size;
3413 strncpy(label_text, leveldir_current->name, max_len_label_text);
3414 label_text[max_len_label_text] = '\0';
3416 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3417 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3423 // scroll preview level, if needed
3424 if (preview.anim_mode != ANIM_NONE &&
3425 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3426 DelayReached(&scroll_delay, scroll_delay_value))
3428 switch (scroll_direction)
3433 from_x -= preview.step_offset;
3434 from_x = (from_x < 0 ? 0 : from_x);
3437 scroll_direction = MV_UP;
3441 if (from_x < level_xsize - preview.xsize)
3443 from_x += preview.step_offset;
3444 from_x = (from_x > level_xsize - preview.xsize ?
3445 level_xsize - preview.xsize : from_x);
3448 scroll_direction = MV_DOWN;
3454 from_y -= preview.step_offset;
3455 from_y = (from_y < 0 ? 0 : from_y);
3458 scroll_direction = MV_RIGHT;
3462 if (from_y < level_ysize - preview.ysize)
3464 from_y += preview.step_offset;
3465 from_y = (from_y > level_ysize - preview.ysize ?
3466 level_ysize - preview.ysize : from_y);
3469 scroll_direction = MV_LEFT;
3476 DrawPreviewLevelPlayfield(from_x, from_y);
3479 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3480 // redraw micro level label, if needed
3481 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3482 !strEqual(level.author, ANONYMOUS_NAME) &&
3483 !strEqual(level.author, leveldir_current->name) &&
3484 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3486 int max_label_counter = 23;
3488 if (leveldir_current->imported_from != NULL &&
3489 strlen(leveldir_current->imported_from) > 0)
3490 max_label_counter += 14;
3491 if (leveldir_current->imported_by != NULL &&
3492 strlen(leveldir_current->imported_by) > 0)
3493 max_label_counter += 14;
3495 label_counter = (label_counter + 1) % max_label_counter;
3496 label_state = (label_counter >= 0 && label_counter <= 7 ?
3497 MICROLABEL_LEVEL_NAME :
3498 label_counter >= 9 && label_counter <= 12 ?
3499 MICROLABEL_LEVEL_AUTHOR_HEAD :
3500 label_counter >= 14 && label_counter <= 21 ?
3501 MICROLABEL_LEVEL_AUTHOR :
3502 label_counter >= 23 && label_counter <= 26 ?
3503 MICROLABEL_IMPORTED_FROM_HEAD :
3504 label_counter >= 28 && label_counter <= 35 ?
3505 MICROLABEL_IMPORTED_FROM :
3506 label_counter >= 37 && label_counter <= 40 ?
3507 MICROLABEL_IMPORTED_BY_HEAD :
3508 label_counter >= 42 && label_counter <= 49 ?
3509 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3511 if (leveldir_current->imported_from == NULL &&
3512 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3513 label_state == MICROLABEL_IMPORTED_FROM))
3514 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3515 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3517 DrawPreviewLevelLabel(label_state);
3521 void DrawPreviewPlayers(void)
3523 if (game_status != GAME_MODE_MAIN)
3526 boolean player_found[MAX_PLAYERS];
3527 int num_players = 0;
3530 for (i = 0; i < MAX_PLAYERS; i++)
3531 player_found[i] = FALSE;
3533 // check which players can be found in the level (simple approach)
3534 for (x = 0; x < lev_fieldx; x++)
3536 for (y = 0; y < lev_fieldy; y++)
3538 int element = level.field[x][y];
3540 if (ELEM_IS_PLAYER(element))
3542 int player_nr = GET_PLAYER_NR(element);
3544 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3546 if (!player_found[player_nr])
3549 player_found[player_nr] = TRUE;
3554 struct TextPosInfo *pos = &menu.main.preview_players;
3555 int tile_size = pos->tile_size;
3556 int border_size = pos->border_size;
3557 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3558 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3559 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3560 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3561 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3562 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3563 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3564 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3565 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3566 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3567 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3568 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3570 // clear area in which the players will be drawn
3571 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3572 max_players_width, max_players_height);
3574 if (!network.enabled && !setup.team_mode)
3577 // only draw players if level is suited for team mode
3578 if (num_players < 2)
3581 // draw all players that were found in the level
3582 for (i = 0; i < MAX_PLAYERS; i++)
3584 if (player_found[i])
3586 int graphic = el2img(EL_PLAYER_1 + i);
3588 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3590 xpos += player_xoffset;
3591 ypos += player_yoffset;
3596 void DrawPreviewLevelInitial(void)
3598 DrawPreviewLevelExt(TRUE);
3599 DrawPreviewPlayers();
3602 void DrawPreviewLevelAnimation(void)
3604 DrawPreviewLevelExt(FALSE);
3607 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3608 int border_size, int font_nr)
3610 int graphic = el2img(EL_PLAYER_1 + player_nr);
3611 int font_height = getFontHeight(font_nr);
3612 int player_height = MAX(tile_size, font_height);
3613 int xoffset_text = tile_size + border_size;
3614 int yoffset_text = (player_height - font_height) / 2;
3615 int yoffset_graphic = (player_height - tile_size) / 2;
3616 char *player_name = getNetworkPlayerName(player_nr + 1);
3618 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3620 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3623 static void DrawNetworkPlayersExt(boolean force)
3625 if (game_status != GAME_MODE_MAIN)
3628 if (!network.connected && !force)
3631 int num_players = 0;
3634 for (i = 0; i < MAX_PLAYERS; i++)
3635 if (stored_player[i].connected_network)
3638 struct TextPosInfo *pos = &menu.main.network_players;
3639 int tile_size = pos->tile_size;
3640 int border_size = pos->border_size;
3641 int xoffset_text = tile_size + border_size;
3642 int font_nr = pos->font;
3643 int font_width = getFontWidth(font_nr);
3644 int font_height = getFontHeight(font_nr);
3645 int player_height = MAX(tile_size, font_height);
3646 int player_yoffset = player_height + border_size;
3647 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3648 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3649 int all_players_height = num_players * player_yoffset - border_size;
3650 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3651 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3652 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3654 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3655 max_players_width, max_players_height);
3657 // first draw local network player ...
3658 for (i = 0; i < MAX_PLAYERS; i++)
3660 if (stored_player[i].connected_network &&
3661 stored_player[i].connected_locally)
3663 char *player_name = getNetworkPlayerName(i + 1);
3664 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3665 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3667 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3669 ypos += player_yoffset;
3673 // ... then draw all other network players
3674 for (i = 0; i < MAX_PLAYERS; i++)
3676 if (stored_player[i].connected_network &&
3677 !stored_player[i].connected_locally)
3679 char *player_name = getNetworkPlayerName(i + 1);
3680 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3681 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3683 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3685 ypos += player_yoffset;
3690 void DrawNetworkPlayers(void)
3692 DrawNetworkPlayersExt(FALSE);
3695 void ClearNetworkPlayers(void)
3697 DrawNetworkPlayersExt(TRUE);
3700 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3701 int graphic, int sync_frame,
3704 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3706 if (mask_mode == USE_MASKING)
3707 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3709 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3712 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3713 int graphic, int sync_frame, int mask_mode)
3715 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3717 if (mask_mode == USE_MASKING)
3718 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3720 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3723 static void DrawGraphicAnimation(int x, int y, int graphic)
3725 int lx = LEVELX(x), ly = LEVELY(y);
3727 if (!IN_SCR_FIELD(x, y))
3730 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3731 graphic, GfxFrame[lx][ly], NO_MASKING);
3733 MarkTileDirty(x, y);
3736 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3738 int lx = LEVELX(x), ly = LEVELY(y);
3740 if (!IN_SCR_FIELD(x, y))
3743 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3744 graphic, GfxFrame[lx][ly], NO_MASKING);
3745 MarkTileDirty(x, y);
3748 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3750 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3753 void DrawLevelElementAnimation(int x, int y, int element)
3755 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3757 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3760 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3762 int sx = SCREENX(x), sy = SCREENY(y);
3764 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3767 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3770 DrawGraphicAnimation(sx, sy, graphic);
3773 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3774 DrawLevelFieldCrumbled(x, y);
3776 if (GFX_CRUMBLED(Feld[x][y]))
3777 DrawLevelFieldCrumbled(x, y);
3781 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3783 int sx = SCREENX(x), sy = SCREENY(y);
3786 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3789 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3791 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3794 DrawGraphicAnimation(sx, sy, graphic);
3796 if (GFX_CRUMBLED(element))
3797 DrawLevelFieldCrumbled(x, y);
3800 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3802 if (player->use_murphy)
3804 // this works only because currently only one player can be "murphy" ...
3805 static int last_horizontal_dir = MV_LEFT;
3806 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3808 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3809 last_horizontal_dir = move_dir;
3811 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3813 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3815 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3821 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3824 static boolean equalGraphics(int graphic1, int graphic2)
3826 struct GraphicInfo *g1 = &graphic_info[graphic1];
3827 struct GraphicInfo *g2 = &graphic_info[graphic2];
3829 return (g1->bitmap == g2->bitmap &&
3830 g1->src_x == g2->src_x &&
3831 g1->src_y == g2->src_y &&
3832 g1->anim_frames == g2->anim_frames &&
3833 g1->anim_delay == g2->anim_delay &&
3834 g1->anim_mode == g2->anim_mode);
3837 void DrawAllPlayers(void)
3841 for (i = 0; i < MAX_PLAYERS; i++)
3842 if (stored_player[i].active)
3843 DrawPlayer(&stored_player[i]);
3846 void DrawPlayerField(int x, int y)
3848 if (!IS_PLAYER(x, y))
3851 DrawPlayer(PLAYERINFO(x, y));
3854 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3856 void DrawPlayer(struct PlayerInfo *player)
3858 int jx = player->jx;
3859 int jy = player->jy;
3860 int move_dir = player->MovDir;
3861 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3862 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3863 int last_jx = (player->is_moving ? jx - dx : jx);
3864 int last_jy = (player->is_moving ? jy - dy : jy);
3865 int next_jx = jx + dx;
3866 int next_jy = jy + dy;
3867 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3868 boolean player_is_opaque = FALSE;
3869 int sx = SCREENX(jx), sy = SCREENY(jy);
3870 int sxx = 0, syy = 0;
3871 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3873 int action = ACTION_DEFAULT;
3874 int last_player_graphic = getPlayerGraphic(player, move_dir);
3875 int last_player_frame = player->Frame;
3878 // GfxElement[][] is set to the element the player is digging or collecting;
3879 // remove also for off-screen player if the player is not moving anymore
3880 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3881 GfxElement[jx][jy] = EL_UNDEFINED;
3883 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3887 if (!IN_LEV_FIELD(jx, jy))
3889 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3890 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3891 printf("DrawPlayerField(): This should never happen!\n");
3896 if (element == EL_EXPLOSION)
3899 action = (player->is_pushing ? ACTION_PUSHING :
3900 player->is_digging ? ACTION_DIGGING :
3901 player->is_collecting ? ACTION_COLLECTING :
3902 player->is_moving ? ACTION_MOVING :
3903 player->is_snapping ? ACTION_SNAPPING :
3904 player->is_dropping ? ACTION_DROPPING :
3905 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3907 if (player->is_waiting)
3908 move_dir = player->dir_waiting;
3910 InitPlayerGfxAnimation(player, action, move_dir);
3912 // --------------------------------------------------------------------------
3913 // draw things in the field the player is leaving, if needed
3914 // --------------------------------------------------------------------------
3916 if (player->is_moving)
3918 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3920 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3922 if (last_element == EL_DYNAMITE_ACTIVE ||
3923 last_element == EL_EM_DYNAMITE_ACTIVE ||
3924 last_element == EL_SP_DISK_RED_ACTIVE)
3925 DrawDynamite(last_jx, last_jy);
3927 DrawLevelFieldThruMask(last_jx, last_jy);
3929 else if (last_element == EL_DYNAMITE_ACTIVE ||
3930 last_element == EL_EM_DYNAMITE_ACTIVE ||
3931 last_element == EL_SP_DISK_RED_ACTIVE)
3932 DrawDynamite(last_jx, last_jy);
3934 /* !!! this is not enough to prevent flickering of players which are
3935 moving next to each others without a free tile between them -- this
3936 can only be solved by drawing all players layer by layer (first the
3937 background, then the foreground etc.) !!! => TODO */
3938 else if (!IS_PLAYER(last_jx, last_jy))
3939 DrawLevelField(last_jx, last_jy);
3942 DrawLevelField(last_jx, last_jy);
3945 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3946 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3949 if (!IN_SCR_FIELD(sx, sy))
3952 // --------------------------------------------------------------------------
3953 // draw things behind the player, if needed
3954 // --------------------------------------------------------------------------
3957 DrawLevelElement(jx, jy, Back[jx][jy]);
3958 else if (IS_ACTIVE_BOMB(element))
3959 DrawLevelElement(jx, jy, EL_EMPTY);
3962 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3964 int old_element = GfxElement[jx][jy];
3965 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3966 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3968 if (GFX_CRUMBLED(old_element))
3969 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3971 DrawGraphic(sx, sy, old_graphic, frame);
3973 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3974 player_is_opaque = TRUE;
3978 GfxElement[jx][jy] = EL_UNDEFINED;
3980 // make sure that pushed elements are drawn with correct frame rate
3981 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3983 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3984 GfxFrame[jx][jy] = player->StepFrame;
3986 DrawLevelField(jx, jy);
3990 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3991 // -----------------------------------------------------------------------
3992 // draw player himself
3993 // -----------------------------------------------------------------------
3995 graphic = getPlayerGraphic(player, move_dir);
3997 // in the case of changed player action or direction, prevent the current
3998 // animation frame from being restarted for identical animations
3999 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4000 player->Frame = last_player_frame;
4002 frame = getGraphicAnimationFrame(graphic, player->Frame);
4006 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4007 sxx = player->GfxPos;
4009 syy = player->GfxPos;
4012 if (player_is_opaque)
4013 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4015 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4017 if (SHIELD_ON(player))
4019 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4020 IMG_SHIELD_NORMAL_ACTIVE);
4021 int frame = getGraphicAnimationFrame(graphic, -1);
4023 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4027 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4030 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4031 sxx = player->GfxPos;
4033 syy = player->GfxPos;
4037 // --------------------------------------------------------------------------
4038 // draw things the player is pushing, if needed
4039 // --------------------------------------------------------------------------
4041 if (player->is_pushing && player->is_moving)
4043 int px = SCREENX(jx), py = SCREENY(jy);
4044 int pxx = (TILEX - ABS(sxx)) * dx;
4045 int pyy = (TILEY - ABS(syy)) * dy;
4046 int gfx_frame = GfxFrame[jx][jy];
4052 if (!IS_MOVING(jx, jy)) // push movement already finished
4054 element = Feld[next_jx][next_jy];
4055 gfx_frame = GfxFrame[next_jx][next_jy];
4058 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4060 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4061 frame = getGraphicAnimationFrame(graphic, sync_frame);
4063 // draw background element under pushed element (like the Sokoban field)
4064 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4066 // this allows transparent pushing animation over non-black background
4069 DrawLevelElement(jx, jy, Back[jx][jy]);
4071 DrawLevelElement(jx, jy, EL_EMPTY);
4073 if (Back[next_jx][next_jy])
4074 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4076 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4078 else if (Back[next_jx][next_jy])
4079 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4082 // do not draw (EM style) pushing animation when pushing is finished
4083 // (two-tile animations usually do not contain start and end frame)
4084 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4085 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4087 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4089 // masked drawing is needed for EMC style (double) movement graphics
4090 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4091 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4095 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4096 // -----------------------------------------------------------------------
4097 // draw player himself
4098 // -----------------------------------------------------------------------
4100 graphic = getPlayerGraphic(player, move_dir);
4102 // in the case of changed player action or direction, prevent the current
4103 // animation frame from being restarted for identical animations
4104 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4105 player->Frame = last_player_frame;
4107 frame = getGraphicAnimationFrame(graphic, player->Frame);
4111 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4112 sxx = player->GfxPos;
4114 syy = player->GfxPos;
4117 if (player_is_opaque)
4118 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4120 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4122 if (SHIELD_ON(player))
4124 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4125 IMG_SHIELD_NORMAL_ACTIVE);
4126 int frame = getGraphicAnimationFrame(graphic, -1);
4128 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4132 // --------------------------------------------------------------------------
4133 // draw things in front of player (active dynamite or dynabombs)
4134 // --------------------------------------------------------------------------
4136 if (IS_ACTIVE_BOMB(element))
4138 graphic = el2img(element);
4139 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4141 if (game.emulation == EMU_SUPAPLEX)
4142 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4144 DrawGraphicThruMask(sx, sy, graphic, frame);
4147 if (player_is_moving && last_element == EL_EXPLOSION)
4149 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4150 GfxElement[last_jx][last_jy] : EL_EMPTY);
4151 int graphic = el_act2img(element, ACTION_EXPLODING);
4152 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4153 int phase = ExplodePhase[last_jx][last_jy] - 1;
4154 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4157 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4160 // --------------------------------------------------------------------------
4161 // draw elements the player is just walking/passing through/under
4162 // --------------------------------------------------------------------------
4164 if (player_is_moving)
4166 // handle the field the player is leaving ...
4167 if (IS_ACCESSIBLE_INSIDE(last_element))
4168 DrawLevelField(last_jx, last_jy);
4169 else if (IS_ACCESSIBLE_UNDER(last_element))
4170 DrawLevelFieldThruMask(last_jx, last_jy);
4173 // do not redraw accessible elements if the player is just pushing them
4174 if (!player_is_moving || !player->is_pushing)
4176 // ... and the field the player is entering
4177 if (IS_ACCESSIBLE_INSIDE(element))
4178 DrawLevelField(jx, jy);
4179 else if (IS_ACCESSIBLE_UNDER(element))
4180 DrawLevelFieldThruMask(jx, jy);
4183 MarkTileDirty(sx, sy);
4186 // ----------------------------------------------------------------------------
4188 void WaitForEventToContinue(void)
4190 boolean still_wait = TRUE;
4192 if (program.headless)
4195 // simulate releasing mouse button over last gadget, if still pressed
4197 HandleGadgets(-1, -1, 0);
4199 button_status = MB_RELEASED;
4207 if (NextValidEvent(&event))
4211 case EVENT_BUTTONRELEASE:
4212 case EVENT_KEYPRESS:
4213 case SDL_CONTROLLERBUTTONDOWN:
4214 case SDL_JOYBUTTONDOWN:
4218 case EVENT_KEYRELEASE:
4219 ClearPlayerAction();
4223 HandleOtherEvents(&event);
4227 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4236 #define MAX_REQUEST_LINES 13
4237 #define MAX_REQUEST_LINE_FONT1_LEN 7
4238 #define MAX_REQUEST_LINE_FONT2_LEN 10
4240 static int RequestHandleEvents(unsigned int req_state)
4242 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4244 int width = request.width;
4245 int height = request.height;
4249 // when showing request dialog after game ended, deactivate game panel
4250 if (game_just_ended)
4251 game.panel.active = FALSE;
4253 game.request_active = TRUE;
4255 setRequestPosition(&sx, &sy, FALSE);
4257 button_status = MB_RELEASED;
4259 request_gadget_id = -1;
4264 if (game_just_ended)
4266 // the MM game engine does not use a special (scrollable) field buffer
4267 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4268 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4270 HandleGameActions();
4272 SetDrawtoField(DRAW_TO_BACKBUFFER);
4274 if (global.use_envelope_request)
4276 // copy current state of request area to middle of playfield area
4277 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4285 while (NextValidEvent(&event))
4289 case EVENT_BUTTONPRESS:
4290 case EVENT_BUTTONRELEASE:
4291 case EVENT_MOTIONNOTIFY:
4295 if (event.type == EVENT_MOTIONNOTIFY)
4300 motion_status = TRUE;
4301 mx = ((MotionEvent *) &event)->x;
4302 my = ((MotionEvent *) &event)->y;
4306 motion_status = FALSE;
4307 mx = ((ButtonEvent *) &event)->x;
4308 my = ((ButtonEvent *) &event)->y;
4309 if (event.type == EVENT_BUTTONPRESS)
4310 button_status = ((ButtonEvent *) &event)->button;
4312 button_status = MB_RELEASED;
4315 // this sets 'request_gadget_id'
4316 HandleGadgets(mx, my, button_status);
4318 switch (request_gadget_id)
4320 case TOOL_CTRL_ID_YES:
4323 case TOOL_CTRL_ID_NO:
4326 case TOOL_CTRL_ID_CONFIRM:
4327 result = TRUE | FALSE;
4330 case TOOL_CTRL_ID_PLAYER_1:
4333 case TOOL_CTRL_ID_PLAYER_2:
4336 case TOOL_CTRL_ID_PLAYER_3:
4339 case TOOL_CTRL_ID_PLAYER_4:
4350 case SDL_WINDOWEVENT:
4351 HandleWindowEvent((WindowEvent *) &event);
4354 case SDL_APP_WILLENTERBACKGROUND:
4355 case SDL_APP_DIDENTERBACKGROUND:
4356 case SDL_APP_WILLENTERFOREGROUND:
4357 case SDL_APP_DIDENTERFOREGROUND:
4358 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4361 case EVENT_KEYPRESS:
4363 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4368 if (req_state & REQ_CONFIRM)
4377 #if defined(KSYM_Rewind)
4378 case KSYM_Rewind: // for Amazon Fire TV remote
4387 #if defined(KSYM_FastForward)
4388 case KSYM_FastForward: // for Amazon Fire TV remote
4394 HandleKeysDebug(key, KEY_PRESSED);
4398 if (req_state & REQ_PLAYER)
4400 int old_player_nr = setup.network_player_nr;
4403 result = old_player_nr + 1;
4408 result = old_player_nr + 1;
4439 case EVENT_KEYRELEASE:
4440 ClearPlayerAction();
4443 case SDL_CONTROLLERBUTTONDOWN:
4444 switch (event.cbutton.button)
4446 case SDL_CONTROLLER_BUTTON_A:
4447 case SDL_CONTROLLER_BUTTON_X:
4448 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4449 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4453 case SDL_CONTROLLER_BUTTON_B:
4454 case SDL_CONTROLLER_BUTTON_Y:
4455 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4456 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4457 case SDL_CONTROLLER_BUTTON_BACK:
4462 if (req_state & REQ_PLAYER)
4464 int old_player_nr = setup.network_player_nr;
4467 result = old_player_nr + 1;
4469 switch (event.cbutton.button)
4471 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4472 case SDL_CONTROLLER_BUTTON_Y:
4476 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4477 case SDL_CONTROLLER_BUTTON_B:
4481 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4482 case SDL_CONTROLLER_BUTTON_A:
4486 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4487 case SDL_CONTROLLER_BUTTON_X:
4498 case SDL_CONTROLLERBUTTONUP:
4499 HandleJoystickEvent(&event);
4500 ClearPlayerAction();
4504 HandleOtherEvents(&event);
4509 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4511 int joy = AnyJoystick();
4513 if (joy & JOY_BUTTON_1)
4515 else if (joy & JOY_BUTTON_2)
4518 else if (AnyJoystick())
4520 int joy = AnyJoystick();
4522 if (req_state & REQ_PLAYER)
4526 else if (joy & JOY_RIGHT)
4528 else if (joy & JOY_DOWN)
4530 else if (joy & JOY_LEFT)
4535 if (game_just_ended)
4537 if (global.use_envelope_request)
4539 // copy back current state of pressed buttons inside request area
4540 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4547 game.request_active = FALSE;
4552 static boolean RequestDoor(char *text, unsigned int req_state)
4554 unsigned int old_door_state;
4555 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4556 int font_nr = FONT_TEXT_2;
4561 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4563 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4564 font_nr = FONT_TEXT_1;
4567 if (game_status == GAME_MODE_PLAYING)
4568 BlitScreenToBitmap(backbuffer);
4570 // disable deactivated drawing when quick-loading level tape recording
4571 if (tape.playing && tape.deactivate_display)
4572 TapeDeactivateDisplayOff(TRUE);
4574 SetMouseCursor(CURSOR_DEFAULT);
4576 // pause network game while waiting for request to answer
4577 if (network.enabled &&
4578 game_status == GAME_MODE_PLAYING &&
4579 !game.all_players_gone &&
4580 req_state & REQUEST_WAIT_FOR_INPUT)
4581 SendToServer_PausePlaying();
4583 old_door_state = GetDoorState();
4585 // simulate releasing mouse button over last gadget, if still pressed
4587 HandleGadgets(-1, -1, 0);
4591 // draw released gadget before proceeding
4594 if (old_door_state & DOOR_OPEN_1)
4596 CloseDoor(DOOR_CLOSE_1);
4598 // save old door content
4599 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4600 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4603 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4604 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4606 // clear door drawing field
4607 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4609 // force DOOR font inside door area
4610 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4612 // write text for request
4613 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4615 char text_line[max_request_line_len + 1];
4621 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4623 tc = *(text_ptr + tx);
4624 // if (!tc || tc == ' ')
4625 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4629 if ((tc == '?' || tc == '!') && tl == 0)
4639 strncpy(text_line, text_ptr, tl);
4642 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4643 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4644 text_line, font_nr);
4646 text_ptr += tl + (tc == ' ' ? 1 : 0);
4647 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4652 if (req_state & REQ_ASK)
4654 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4655 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4657 else if (req_state & REQ_CONFIRM)
4659 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4661 else if (req_state & REQ_PLAYER)
4663 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4664 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4665 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4666 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4669 // copy request gadgets to door backbuffer
4670 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4672 OpenDoor(DOOR_OPEN_1);
4674 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4676 if (game_status == GAME_MODE_PLAYING)
4678 SetPanelBackground();
4679 SetDrawBackgroundMask(REDRAW_DOOR_1);
4683 SetDrawBackgroundMask(REDRAW_FIELD);
4689 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4691 // ---------- handle request buttons ----------
4692 result = RequestHandleEvents(req_state);
4696 if (!(req_state & REQ_STAY_OPEN))
4698 CloseDoor(DOOR_CLOSE_1);
4700 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4701 (req_state & REQ_REOPEN))
4702 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4707 if (game_status == GAME_MODE_PLAYING)
4709 SetPanelBackground();
4710 SetDrawBackgroundMask(REDRAW_DOOR_1);
4714 SetDrawBackgroundMask(REDRAW_FIELD);
4717 // continue network game after request
4718 if (network.enabled &&
4719 game_status == GAME_MODE_PLAYING &&
4720 !game.all_players_gone &&
4721 req_state & REQUEST_WAIT_FOR_INPUT)
4722 SendToServer_ContinuePlaying();
4724 // restore deactivated drawing when quick-loading level tape recording
4725 if (tape.playing && tape.deactivate_display)
4726 TapeDeactivateDisplayOn();
4731 static boolean RequestEnvelope(char *text, unsigned int req_state)
4735 if (game_status == GAME_MODE_PLAYING)
4736 BlitScreenToBitmap(backbuffer);
4738 // disable deactivated drawing when quick-loading level tape recording
4739 if (tape.playing && tape.deactivate_display)
4740 TapeDeactivateDisplayOff(TRUE);
4742 SetMouseCursor(CURSOR_DEFAULT);
4744 // pause network game while waiting for request to answer
4745 if (network.enabled &&
4746 game_status == GAME_MODE_PLAYING &&
4747 !game.all_players_gone &&
4748 req_state & REQUEST_WAIT_FOR_INPUT)
4749 SendToServer_PausePlaying();
4751 // simulate releasing mouse button over last gadget, if still pressed
4753 HandleGadgets(-1, -1, 0);
4757 // (replace with setting corresponding request background)
4758 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4759 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4761 // clear door drawing field
4762 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4764 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4766 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4768 if (game_status == GAME_MODE_PLAYING)
4770 SetPanelBackground();
4771 SetDrawBackgroundMask(REDRAW_DOOR_1);
4775 SetDrawBackgroundMask(REDRAW_FIELD);
4781 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4783 // ---------- handle request buttons ----------
4784 result = RequestHandleEvents(req_state);
4788 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4792 if (game_status == GAME_MODE_PLAYING)
4794 SetPanelBackground();
4795 SetDrawBackgroundMask(REDRAW_DOOR_1);
4799 SetDrawBackgroundMask(REDRAW_FIELD);
4802 // continue network game after request
4803 if (network.enabled &&
4804 game_status == GAME_MODE_PLAYING &&
4805 !game.all_players_gone &&
4806 req_state & REQUEST_WAIT_FOR_INPUT)
4807 SendToServer_ContinuePlaying();
4809 // restore deactivated drawing when quick-loading level tape recording
4810 if (tape.playing && tape.deactivate_display)
4811 TapeDeactivateDisplayOn();
4816 boolean Request(char *text, unsigned int req_state)
4818 boolean overlay_enabled = GetOverlayEnabled();
4821 SetOverlayEnabled(FALSE);
4823 if (global.use_envelope_request)
4824 result = RequestEnvelope(text, req_state);
4826 result = RequestDoor(text, req_state);
4828 SetOverlayEnabled(overlay_enabled);
4833 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4835 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4836 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4839 if (dpo1->sort_priority != dpo2->sort_priority)
4840 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4842 compare_result = dpo1->nr - dpo2->nr;
4844 return compare_result;
4847 void InitGraphicCompatibilityInfo_Doors(void)
4853 struct DoorInfo *door;
4857 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4858 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4860 { -1, -1, -1, NULL }
4862 struct Rect door_rect_list[] =
4864 { DX, DY, DXSIZE, DYSIZE },
4865 { VX, VY, VXSIZE, VYSIZE }
4869 for (i = 0; doors[i].door_token != -1; i++)
4871 int door_token = doors[i].door_token;
4872 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4873 int part_1 = doors[i].part_1;
4874 int part_8 = doors[i].part_8;
4875 int part_2 = part_1 + 1;
4876 int part_3 = part_1 + 2;
4877 struct DoorInfo *door = doors[i].door;
4878 struct Rect *door_rect = &door_rect_list[door_index];
4879 boolean door_gfx_redefined = FALSE;
4881 // check if any door part graphic definitions have been redefined
4883 for (j = 0; door_part_controls[j].door_token != -1; j++)
4885 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4886 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4888 if (dpc->door_token == door_token && fi->redefined)
4889 door_gfx_redefined = TRUE;
4892 // check for old-style door graphic/animation modifications
4894 if (!door_gfx_redefined)
4896 if (door->anim_mode & ANIM_STATIC_PANEL)
4898 door->panel.step_xoffset = 0;
4899 door->panel.step_yoffset = 0;
4902 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4904 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4905 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4906 int num_door_steps, num_panel_steps;
4908 // remove door part graphics other than the two default wings
4910 for (j = 0; door_part_controls[j].door_token != -1; j++)
4912 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4913 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4915 if (dpc->graphic >= part_3 &&
4916 dpc->graphic <= part_8)
4920 // set graphics and screen positions of the default wings
4922 g_part_1->width = door_rect->width;
4923 g_part_1->height = door_rect->height;
4924 g_part_2->width = door_rect->width;
4925 g_part_2->height = door_rect->height;
4926 g_part_2->src_x = door_rect->width;
4927 g_part_2->src_y = g_part_1->src_y;
4929 door->part_2.x = door->part_1.x;
4930 door->part_2.y = door->part_1.y;
4932 if (door->width != -1)
4934 g_part_1->width = door->width;
4935 g_part_2->width = door->width;
4937 // special treatment for graphics and screen position of right wing
4938 g_part_2->src_x += door_rect->width - door->width;
4939 door->part_2.x += door_rect->width - door->width;
4942 if (door->height != -1)
4944 g_part_1->height = door->height;
4945 g_part_2->height = door->height;
4947 // special treatment for graphics and screen position of bottom wing
4948 g_part_2->src_y += door_rect->height - door->height;
4949 door->part_2.y += door_rect->height - door->height;
4952 // set animation delays for the default wings and panels
4954 door->part_1.step_delay = door->step_delay;
4955 door->part_2.step_delay = door->step_delay;
4956 door->panel.step_delay = door->step_delay;
4958 // set animation draw order for the default wings
4960 door->part_1.sort_priority = 2; // draw left wing over ...
4961 door->part_2.sort_priority = 1; // ... right wing
4963 // set animation draw offset for the default wings
4965 if (door->anim_mode & ANIM_HORIZONTAL)
4967 door->part_1.step_xoffset = door->step_offset;
4968 door->part_1.step_yoffset = 0;
4969 door->part_2.step_xoffset = door->step_offset * -1;
4970 door->part_2.step_yoffset = 0;
4972 num_door_steps = g_part_1->width / door->step_offset;
4974 else // ANIM_VERTICAL
4976 door->part_1.step_xoffset = 0;
4977 door->part_1.step_yoffset = door->step_offset;
4978 door->part_2.step_xoffset = 0;
4979 door->part_2.step_yoffset = door->step_offset * -1;
4981 num_door_steps = g_part_1->height / door->step_offset;
4984 // set animation draw offset for the default panels
4986 if (door->step_offset > 1)
4988 num_panel_steps = 2 * door_rect->height / door->step_offset;
4989 door->panel.start_step = num_panel_steps - num_door_steps;
4990 door->panel.start_step_closing = door->panel.start_step;
4994 num_panel_steps = door_rect->height / door->step_offset;
4995 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4996 door->panel.start_step_closing = door->panel.start_step;
4997 door->panel.step_delay *= 2;
5004 void InitDoors(void)
5008 for (i = 0; door_part_controls[i].door_token != -1; i++)
5010 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5011 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5013 // initialize "start_step_opening" and "start_step_closing", if needed
5014 if (dpc->pos->start_step_opening == 0 &&
5015 dpc->pos->start_step_closing == 0)
5017 // dpc->pos->start_step_opening = dpc->pos->start_step;
5018 dpc->pos->start_step_closing = dpc->pos->start_step;
5021 // fill structure for door part draw order (sorted below)
5023 dpo->sort_priority = dpc->pos->sort_priority;
5026 // sort door part controls according to sort_priority and graphic number
5027 qsort(door_part_order, MAX_DOOR_PARTS,
5028 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5031 unsigned int OpenDoor(unsigned int door_state)
5033 if (door_state & DOOR_COPY_BACK)
5035 if (door_state & DOOR_OPEN_1)
5036 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5037 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5039 if (door_state & DOOR_OPEN_2)
5040 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5041 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5043 door_state &= ~DOOR_COPY_BACK;
5046 return MoveDoor(door_state);
5049 unsigned int CloseDoor(unsigned int door_state)
5051 unsigned int old_door_state = GetDoorState();
5053 if (!(door_state & DOOR_NO_COPY_BACK))
5055 if (old_door_state & DOOR_OPEN_1)
5056 BlitBitmap(backbuffer, bitmap_db_door_1,
5057 DX, DY, DXSIZE, DYSIZE, 0, 0);
5059 if (old_door_state & DOOR_OPEN_2)
5060 BlitBitmap(backbuffer, bitmap_db_door_2,
5061 VX, VY, VXSIZE, VYSIZE, 0, 0);
5063 door_state &= ~DOOR_NO_COPY_BACK;
5066 return MoveDoor(door_state);
5069 unsigned int GetDoorState(void)
5071 return MoveDoor(DOOR_GET_STATE);
5074 unsigned int SetDoorState(unsigned int door_state)
5076 return MoveDoor(door_state | DOOR_SET_STATE);
5079 static int euclid(int a, int b)
5081 return (b ? euclid(b, a % b) : a);
5084 unsigned int MoveDoor(unsigned int door_state)
5086 struct Rect door_rect_list[] =
5088 { DX, DY, DXSIZE, DYSIZE },
5089 { VX, VY, VXSIZE, VYSIZE }
5091 static int door1 = DOOR_CLOSE_1;
5092 static int door2 = DOOR_CLOSE_2;
5093 unsigned int door_delay = 0;
5094 unsigned int door_delay_value;
5097 if (door_state == DOOR_GET_STATE)
5098 return (door1 | door2);
5100 if (door_state & DOOR_SET_STATE)
5102 if (door_state & DOOR_ACTION_1)
5103 door1 = door_state & DOOR_ACTION_1;
5104 if (door_state & DOOR_ACTION_2)
5105 door2 = door_state & DOOR_ACTION_2;
5107 return (door1 | door2);
5110 if (!(door_state & DOOR_FORCE_REDRAW))
5112 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5113 door_state &= ~DOOR_OPEN_1;
5114 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5115 door_state &= ~DOOR_CLOSE_1;
5116 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5117 door_state &= ~DOOR_OPEN_2;
5118 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5119 door_state &= ~DOOR_CLOSE_2;
5122 if (global.autoplay_leveldir)
5124 door_state |= DOOR_NO_DELAY;
5125 door_state &= ~DOOR_CLOSE_ALL;
5128 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5129 door_state |= DOOR_NO_DELAY;
5131 if (door_state & DOOR_ACTION)
5133 boolean door_panel_drawn[NUM_DOORS];
5134 boolean panel_has_doors[NUM_DOORS];
5135 boolean door_part_skip[MAX_DOOR_PARTS];
5136 boolean door_part_done[MAX_DOOR_PARTS];
5137 boolean door_part_done_all;
5138 int num_steps[MAX_DOOR_PARTS];
5139 int max_move_delay = 0; // delay for complete animations of all doors
5140 int max_step_delay = 0; // delay (ms) between two animation frames
5141 int num_move_steps = 0; // number of animation steps for all doors
5142 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5143 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5144 int current_move_delay = 0;
5148 for (i = 0; i < NUM_DOORS; i++)
5149 panel_has_doors[i] = FALSE;
5151 for (i = 0; i < MAX_DOOR_PARTS; i++)
5153 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5154 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5155 int door_token = dpc->door_token;
5157 door_part_done[i] = FALSE;
5158 door_part_skip[i] = (!(door_state & door_token) ||
5162 for (i = 0; i < MAX_DOOR_PARTS; i++)
5164 int nr = door_part_order[i].nr;
5165 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5166 struct DoorPartPosInfo *pos = dpc->pos;
5167 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5168 int door_token = dpc->door_token;
5169 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5170 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5171 int step_xoffset = ABS(pos->step_xoffset);
5172 int step_yoffset = ABS(pos->step_yoffset);
5173 int step_delay = pos->step_delay;
5174 int current_door_state = door_state & door_token;
5175 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5176 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5177 boolean part_opening = (is_panel ? door_closing : door_opening);
5178 int start_step = (part_opening ? pos->start_step_opening :
5179 pos->start_step_closing);
5180 float move_xsize = (step_xoffset ? g->width : 0);
5181 float move_ysize = (step_yoffset ? g->height : 0);
5182 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5183 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5184 int move_steps = (move_xsteps && move_ysteps ?
5185 MIN(move_xsteps, move_ysteps) :
5186 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5187 int move_delay = move_steps * step_delay;
5189 if (door_part_skip[nr])
5192 max_move_delay = MAX(max_move_delay, move_delay);
5193 max_step_delay = (max_step_delay == 0 ? step_delay :
5194 euclid(max_step_delay, step_delay));
5195 num_steps[nr] = move_steps;
5199 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5201 panel_has_doors[door_index] = TRUE;
5205 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5207 num_move_steps = max_move_delay / max_step_delay;
5208 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5210 door_delay_value = max_step_delay;
5212 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5214 start = num_move_steps - 1;
5218 // opening door sound has priority over simultaneously closing door
5219 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5221 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5223 if (door_state & DOOR_OPEN_1)
5224 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5225 if (door_state & DOOR_OPEN_2)
5226 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5228 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5230 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5232 if (door_state & DOOR_CLOSE_1)
5233 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5234 if (door_state & DOOR_CLOSE_2)
5235 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5239 for (k = start; k < num_move_steps; k++)
5241 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5243 door_part_done_all = TRUE;
5245 for (i = 0; i < NUM_DOORS; i++)
5246 door_panel_drawn[i] = FALSE;
5248 for (i = 0; i < MAX_DOOR_PARTS; i++)
5250 int nr = door_part_order[i].nr;
5251 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5252 struct DoorPartPosInfo *pos = dpc->pos;
5253 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5254 int door_token = dpc->door_token;
5255 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5256 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5257 boolean is_panel_and_door_has_closed = FALSE;
5258 struct Rect *door_rect = &door_rect_list[door_index];
5259 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5261 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5262 int current_door_state = door_state & door_token;
5263 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5264 boolean door_closing = !door_opening;
5265 boolean part_opening = (is_panel ? door_closing : door_opening);
5266 boolean part_closing = !part_opening;
5267 int start_step = (part_opening ? pos->start_step_opening :
5268 pos->start_step_closing);
5269 int step_delay = pos->step_delay;
5270 int step_factor = step_delay / max_step_delay;
5271 int k1 = (step_factor ? k / step_factor + 1 : k);
5272 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5273 int kk = MAX(0, k2);
5276 int src_x, src_y, src_xx, src_yy;
5277 int dst_x, dst_y, dst_xx, dst_yy;
5280 if (door_part_skip[nr])
5283 if (!(door_state & door_token))
5291 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5292 int kk_door = MAX(0, k2_door);
5293 int sync_frame = kk_door * door_delay_value;
5294 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5296 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5297 &g_src_x, &g_src_y);
5302 if (!door_panel_drawn[door_index])
5304 ClearRectangle(drawto, door_rect->x, door_rect->y,
5305 door_rect->width, door_rect->height);
5307 door_panel_drawn[door_index] = TRUE;
5310 // draw opening or closing door parts
5312 if (pos->step_xoffset < 0) // door part on right side
5315 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5318 if (dst_xx + width > door_rect->width)
5319 width = door_rect->width - dst_xx;
5321 else // door part on left side
5324 dst_xx = pos->x - kk * pos->step_xoffset;
5328 src_xx = ABS(dst_xx);
5332 width = g->width - src_xx;
5334 if (width > door_rect->width)
5335 width = door_rect->width;
5337 // printf("::: k == %d [%d] \n", k, start_step);
5340 if (pos->step_yoffset < 0) // door part on bottom side
5343 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5346 if (dst_yy + height > door_rect->height)
5347 height = door_rect->height - dst_yy;
5349 else // door part on top side
5352 dst_yy = pos->y - kk * pos->step_yoffset;
5356 src_yy = ABS(dst_yy);
5360 height = g->height - src_yy;
5363 src_x = g_src_x + src_xx;
5364 src_y = g_src_y + src_yy;
5366 dst_x = door_rect->x + dst_xx;
5367 dst_y = door_rect->y + dst_yy;
5369 is_panel_and_door_has_closed =
5372 panel_has_doors[door_index] &&
5373 k >= num_move_steps_doors_only - 1);
5375 if (width >= 0 && width <= g->width &&
5376 height >= 0 && height <= g->height &&
5377 !is_panel_and_door_has_closed)
5379 if (is_panel || !pos->draw_masked)
5380 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5383 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5387 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5389 if ((part_opening && (width < 0 || height < 0)) ||
5390 (part_closing && (width >= g->width && height >= g->height)))
5391 door_part_done[nr] = TRUE;
5393 // continue door part animations, but not panel after door has closed
5394 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5395 door_part_done_all = FALSE;
5398 if (!(door_state & DOOR_NO_DELAY))
5402 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5404 current_move_delay += max_step_delay;
5406 // prevent OS (Windows) from complaining about program not responding
5410 if (door_part_done_all)
5414 if (!(door_state & DOOR_NO_DELAY))
5416 // wait for specified door action post delay
5417 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5418 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5419 else if (door_state & DOOR_ACTION_1)
5420 door_delay_value = door_1.post_delay;
5421 else if (door_state & DOOR_ACTION_2)
5422 door_delay_value = door_2.post_delay;
5424 while (!DelayReached(&door_delay, door_delay_value))
5429 if (door_state & DOOR_ACTION_1)
5430 door1 = door_state & DOOR_ACTION_1;
5431 if (door_state & DOOR_ACTION_2)
5432 door2 = door_state & DOOR_ACTION_2;
5434 // draw masked border over door area
5435 DrawMaskedBorder(REDRAW_DOOR_1);
5436 DrawMaskedBorder(REDRAW_DOOR_2);
5438 ClearAutoRepeatKeyEvents();
5440 return (door1 | door2);
5443 static boolean useSpecialEditorDoor(void)
5445 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5446 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5448 // do not draw special editor door if editor border defined or redefined
5449 if (graphic_info[graphic].bitmap != NULL || redefined)
5452 // do not draw special editor door if global border defined to be empty
5453 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5456 // do not draw special editor door if viewport definitions do not match
5460 EY + EYSIZE != VY + VYSIZE)
5466 void DrawSpecialEditorDoor(void)
5468 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5469 int top_border_width = gfx1->width;
5470 int top_border_height = gfx1->height;
5471 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5472 int ex = EX - outer_border;
5473 int ey = EY - outer_border;
5474 int vy = VY - outer_border;
5475 int exsize = EXSIZE + 2 * outer_border;
5477 if (!useSpecialEditorDoor())
5480 // draw bigger level editor toolbox window
5481 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5482 top_border_width, top_border_height, ex, ey - top_border_height);
5483 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5484 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5486 redraw_mask |= REDRAW_ALL;
5489 void UndrawSpecialEditorDoor(void)
5491 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5492 int top_border_width = gfx1->width;
5493 int top_border_height = gfx1->height;
5494 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5495 int ex = EX - outer_border;
5496 int ey = EY - outer_border;
5497 int ey_top = ey - top_border_height;
5498 int exsize = EXSIZE + 2 * outer_border;
5499 int eysize = EYSIZE + 2 * outer_border;
5501 if (!useSpecialEditorDoor())
5504 // draw normal tape recorder window
5505 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5507 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5508 ex, ey_top, top_border_width, top_border_height,
5510 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5511 ex, ey, exsize, eysize, ex, ey);
5515 // if screen background is set to "[NONE]", clear editor toolbox window
5516 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5517 ClearRectangle(drawto, ex, ey, exsize, eysize);
5520 redraw_mask |= REDRAW_ALL;
5524 // ---------- new tool button stuff -------------------------------------------
5529 struct TextPosInfo *pos;
5532 } toolbutton_info[NUM_TOOL_BUTTONS] =
5535 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5536 TOOL_CTRL_ID_YES, "yes"
5539 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5540 TOOL_CTRL_ID_NO, "no"
5543 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5544 TOOL_CTRL_ID_CONFIRM, "confirm"
5547 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5548 TOOL_CTRL_ID_PLAYER_1, "player 1"
5551 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5552 TOOL_CTRL_ID_PLAYER_2, "player 2"
5555 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5556 TOOL_CTRL_ID_PLAYER_3, "player 3"
5559 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5560 TOOL_CTRL_ID_PLAYER_4, "player 4"
5564 void CreateToolButtons(void)
5568 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5570 int graphic = toolbutton_info[i].graphic;
5571 struct GraphicInfo *gfx = &graphic_info[graphic];
5572 struct TextPosInfo *pos = toolbutton_info[i].pos;
5573 struct GadgetInfo *gi;
5574 Bitmap *deco_bitmap = None;
5575 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5576 unsigned int event_mask = GD_EVENT_RELEASED;
5579 int gd_x = gfx->src_x;
5580 int gd_y = gfx->src_y;
5581 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5582 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5587 if (global.use_envelope_request)
5589 setRequestPosition(&dx, &dy, TRUE);
5591 // check if request buttons are outside of envelope and fix, if needed
5592 if (x < 0 || x + gfx->width > request.width ||
5593 y < 0 || y + gfx->height > request.height)
5595 if (id == TOOL_CTRL_ID_YES)
5598 y = request.height - 2 * request.border_size - gfx->height;
5600 else if (id == TOOL_CTRL_ID_NO)
5602 x = request.width - 2 * request.border_size - gfx->width;
5603 y = request.height - 2 * request.border_size - gfx->height;
5605 else if (id == TOOL_CTRL_ID_CONFIRM)
5607 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5608 y = request.height - 2 * request.border_size - gfx->height;
5610 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5612 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5614 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5615 y = request.height - 2 * request.border_size - gfx->height * 2;
5617 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5618 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5623 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5625 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5627 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5628 pos->size, &deco_bitmap, &deco_x, &deco_y);
5629 deco_xpos = (gfx->width - pos->size) / 2;
5630 deco_ypos = (gfx->height - pos->size) / 2;
5633 gi = CreateGadget(GDI_CUSTOM_ID, id,
5634 GDI_IMAGE_ID, graphic,
5635 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5638 GDI_WIDTH, gfx->width,
5639 GDI_HEIGHT, gfx->height,
5640 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5641 GDI_STATE, GD_BUTTON_UNPRESSED,
5642 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5643 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5644 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5645 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5646 GDI_DECORATION_SIZE, pos->size, pos->size,
5647 GDI_DECORATION_SHIFTING, 1, 1,
5648 GDI_DIRECT_DRAW, FALSE,
5649 GDI_EVENT_MASK, event_mask,
5650 GDI_CALLBACK_ACTION, HandleToolButtons,
5654 Error(ERR_EXIT, "cannot create gadget");
5656 tool_gadget[id] = gi;
5660 void FreeToolButtons(void)
5664 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5665 FreeGadget(tool_gadget[i]);
5668 static void UnmapToolButtons(void)
5672 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5673 UnmapGadget(tool_gadget[i]);
5676 static void HandleToolButtons(struct GadgetInfo *gi)
5678 request_gadget_id = gi->custom_id;
5681 static struct Mapping_EM_to_RND_object
5684 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5685 boolean is_backside; // backside of moving element
5691 em_object_mapping_list[] =
5694 Xblank, TRUE, FALSE,
5698 Yacid_splash_eB, FALSE, FALSE,
5699 EL_ACID_SPLASH_RIGHT, -1, -1
5702 Yacid_splash_wB, FALSE, FALSE,
5703 EL_ACID_SPLASH_LEFT, -1, -1
5706 #ifdef EM_ENGINE_BAD_ROLL
5708 Xstone_force_e, FALSE, FALSE,
5709 EL_ROCK, -1, MV_BIT_RIGHT
5712 Xstone_force_w, FALSE, FALSE,
5713 EL_ROCK, -1, MV_BIT_LEFT
5716 Xnut_force_e, FALSE, FALSE,
5717 EL_NUT, -1, MV_BIT_RIGHT
5720 Xnut_force_w, FALSE, FALSE,
5721 EL_NUT, -1, MV_BIT_LEFT
5724 Xspring_force_e, FALSE, FALSE,
5725 EL_SPRING, -1, MV_BIT_RIGHT
5728 Xspring_force_w, FALSE, FALSE,
5729 EL_SPRING, -1, MV_BIT_LEFT
5732 Xemerald_force_e, FALSE, FALSE,
5733 EL_EMERALD, -1, MV_BIT_RIGHT
5736 Xemerald_force_w, FALSE, FALSE,
5737 EL_EMERALD, -1, MV_BIT_LEFT
5740 Xdiamond_force_e, FALSE, FALSE,
5741 EL_DIAMOND, -1, MV_BIT_RIGHT
5744 Xdiamond_force_w, FALSE, FALSE,
5745 EL_DIAMOND, -1, MV_BIT_LEFT
5748 Xbomb_force_e, FALSE, FALSE,
5749 EL_BOMB, -1, MV_BIT_RIGHT
5752 Xbomb_force_w, FALSE, FALSE,
5753 EL_BOMB, -1, MV_BIT_LEFT
5755 #endif // EM_ENGINE_BAD_ROLL
5758 Xstone, TRUE, FALSE,
5762 Xstone_pause, FALSE, FALSE,
5766 Xstone_fall, FALSE, FALSE,
5770 Ystone_s, FALSE, FALSE,
5771 EL_ROCK, ACTION_FALLING, -1
5774 Ystone_sB, FALSE, TRUE,
5775 EL_ROCK, ACTION_FALLING, -1
5778 Ystone_e, FALSE, FALSE,
5779 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5782 Ystone_eB, FALSE, TRUE,
5783 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5786 Ystone_w, FALSE, FALSE,
5787 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5790 Ystone_wB, FALSE, TRUE,
5791 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5798 Xnut_pause, FALSE, FALSE,
5802 Xnut_fall, FALSE, FALSE,
5806 Ynut_s, FALSE, FALSE,
5807 EL_NUT, ACTION_FALLING, -1
5810 Ynut_sB, FALSE, TRUE,
5811 EL_NUT, ACTION_FALLING, -1
5814 Ynut_e, FALSE, FALSE,
5815 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5818 Ynut_eB, FALSE, TRUE,
5819 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5822 Ynut_w, FALSE, FALSE,
5823 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5826 Ynut_wB, FALSE, TRUE,
5827 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5830 Xbug_n, TRUE, FALSE,
5834 Xbug_e, TRUE, FALSE,
5835 EL_BUG_RIGHT, -1, -1
5838 Xbug_s, TRUE, FALSE,
5842 Xbug_w, TRUE, FALSE,
5846 Xbug_gon, FALSE, FALSE,
5850 Xbug_goe, FALSE, FALSE,
5851 EL_BUG_RIGHT, -1, -1
5854 Xbug_gos, FALSE, FALSE,
5858 Xbug_gow, FALSE, FALSE,
5862 Ybug_n, FALSE, FALSE,
5863 EL_BUG, ACTION_MOVING, MV_BIT_UP
5866 Ybug_nB, FALSE, TRUE,
5867 EL_BUG, ACTION_MOVING, MV_BIT_UP
5870 Ybug_e, FALSE, FALSE,
5871 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5874 Ybug_eB, FALSE, TRUE,
5875 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5878 Ybug_s, FALSE, FALSE,
5879 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5882 Ybug_sB, FALSE, TRUE,
5883 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5886 Ybug_w, FALSE, FALSE,
5887 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5890 Ybug_wB, FALSE, TRUE,
5891 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5894 Ybug_w_n, FALSE, FALSE,
5895 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5898 Ybug_n_e, FALSE, FALSE,
5899 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5902 Ybug_e_s, FALSE, FALSE,
5903 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5906 Ybug_s_w, FALSE, FALSE,
5907 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5910 Ybug_e_n, FALSE, FALSE,
5911 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5914 Ybug_s_e, FALSE, FALSE,
5915 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5918 Ybug_w_s, FALSE, FALSE,
5919 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5922 Ybug_n_w, FALSE, FALSE,
5923 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5926 Ybug_stone, FALSE, FALSE,
5927 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5930 Ybug_spring, FALSE, FALSE,
5931 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5934 Xtank_n, TRUE, FALSE,
5935 EL_SPACESHIP_UP, -1, -1
5938 Xtank_e, TRUE, FALSE,
5939 EL_SPACESHIP_RIGHT, -1, -1
5942 Xtank_s, TRUE, FALSE,
5943 EL_SPACESHIP_DOWN, -1, -1
5946 Xtank_w, TRUE, FALSE,
5947 EL_SPACESHIP_LEFT, -1, -1
5950 Xtank_gon, FALSE, FALSE,
5951 EL_SPACESHIP_UP, -1, -1
5954 Xtank_goe, FALSE, FALSE,
5955 EL_SPACESHIP_RIGHT, -1, -1
5958 Xtank_gos, FALSE, FALSE,
5959 EL_SPACESHIP_DOWN, -1, -1
5962 Xtank_gow, FALSE, FALSE,
5963 EL_SPACESHIP_LEFT, -1, -1
5966 Ytank_n, FALSE, FALSE,
5967 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5970 Ytank_nB, FALSE, TRUE,
5971 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5974 Ytank_e, FALSE, FALSE,
5975 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5978 Ytank_eB, FALSE, TRUE,
5979 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5982 Ytank_s, FALSE, FALSE,
5983 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5986 Ytank_sB, FALSE, TRUE,
5987 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5990 Ytank_w, FALSE, FALSE,
5991 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5994 Ytank_wB, FALSE, TRUE,
5995 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5998 Ytank_w_n, FALSE, FALSE,
5999 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6002 Ytank_n_e, FALSE, FALSE,
6003 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6006 Ytank_e_s, FALSE, FALSE,
6007 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6010 Ytank_s_w, FALSE, FALSE,
6011 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6014 Ytank_e_n, FALSE, FALSE,
6015 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6018 Ytank_s_e, FALSE, FALSE,
6019 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6022 Ytank_w_s, FALSE, FALSE,
6023 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6026 Ytank_n_w, FALSE, FALSE,
6027 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6030 Ytank_stone, FALSE, FALSE,
6031 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6034 Ytank_spring, FALSE, FALSE,
6035 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6038 Xandroid, TRUE, FALSE,
6039 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6042 Xandroid_1_n, FALSE, FALSE,
6043 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6046 Xandroid_2_n, FALSE, FALSE,
6047 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6050 Xandroid_1_e, FALSE, FALSE,
6051 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6054 Xandroid_2_e, FALSE, FALSE,
6055 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6058 Xandroid_1_w, FALSE, FALSE,
6059 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6062 Xandroid_2_w, FALSE, FALSE,
6063 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6066 Xandroid_1_s, FALSE, FALSE,
6067 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6070 Xandroid_2_s, FALSE, FALSE,
6071 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6074 Yandroid_n, FALSE, FALSE,
6075 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6078 Yandroid_nB, FALSE, TRUE,
6079 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6082 Yandroid_ne, FALSE, FALSE,
6083 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6086 Yandroid_neB, FALSE, TRUE,
6087 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6090 Yandroid_e, FALSE, FALSE,
6091 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6094 Yandroid_eB, FALSE, TRUE,
6095 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6098 Yandroid_se, FALSE, FALSE,
6099 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6102 Yandroid_seB, FALSE, TRUE,
6103 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6106 Yandroid_s, FALSE, FALSE,
6107 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6110 Yandroid_sB, FALSE, TRUE,
6111 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6114 Yandroid_sw, FALSE, FALSE,
6115 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6118 Yandroid_swB, FALSE, TRUE,
6119 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6122 Yandroid_w, FALSE, FALSE,
6123 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6126 Yandroid_wB, FALSE, TRUE,
6127 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6130 Yandroid_nw, FALSE, FALSE,
6131 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6134 Yandroid_nwB, FALSE, TRUE,
6135 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6138 Xspring, TRUE, FALSE,
6142 Xspring_pause, FALSE, FALSE,
6146 Xspring_e, FALSE, FALSE,
6150 Xspring_w, FALSE, FALSE,
6154 Xspring_fall, FALSE, FALSE,
6158 Yspring_s, FALSE, FALSE,
6159 EL_SPRING, ACTION_FALLING, -1
6162 Yspring_sB, FALSE, TRUE,
6163 EL_SPRING, ACTION_FALLING, -1
6166 Yspring_e, FALSE, FALSE,
6167 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6170 Yspring_eB, FALSE, TRUE,
6171 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6174 Yspring_w, FALSE, FALSE,
6175 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6178 Yspring_wB, FALSE, TRUE,
6179 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6182 Yspring_kill_e, FALSE, FALSE,
6183 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6186 Yspring_kill_eB, FALSE, TRUE,
6187 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6190 Yspring_kill_w, FALSE, FALSE,
6191 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6194 Yspring_kill_wB, FALSE, TRUE,
6195 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6198 Xeater_n, TRUE, FALSE,
6199 EL_YAMYAM_UP, -1, -1
6202 Xeater_e, TRUE, FALSE,
6203 EL_YAMYAM_RIGHT, -1, -1
6206 Xeater_w, TRUE, FALSE,
6207 EL_YAMYAM_LEFT, -1, -1
6210 Xeater_s, TRUE, FALSE,
6211 EL_YAMYAM_DOWN, -1, -1
6214 Yeater_n, FALSE, FALSE,
6215 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6218 Yeater_nB, FALSE, TRUE,
6219 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6222 Yeater_e, FALSE, FALSE,
6223 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6226 Yeater_eB, FALSE, TRUE,
6227 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6230 Yeater_s, FALSE, FALSE,
6231 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6234 Yeater_sB, FALSE, TRUE,
6235 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6238 Yeater_w, FALSE, FALSE,
6239 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6242 Yeater_wB, FALSE, TRUE,
6243 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6246 Yeater_stone, FALSE, FALSE,
6247 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6250 Yeater_spring, FALSE, FALSE,
6251 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6254 Xalien, TRUE, FALSE,
6258 Xalien_pause, FALSE, FALSE,
6262 Yalien_n, FALSE, FALSE,
6263 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6266 Yalien_nB, FALSE, TRUE,
6267 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6270 Yalien_e, FALSE, FALSE,
6271 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6274 Yalien_eB, FALSE, TRUE,
6275 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6278 Yalien_s, FALSE, FALSE,
6279 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6282 Yalien_sB, FALSE, TRUE,
6283 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6286 Yalien_w, FALSE, FALSE,
6287 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6290 Yalien_wB, FALSE, TRUE,
6291 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6294 Yalien_stone, FALSE, FALSE,
6295 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6298 Yalien_spring, FALSE, FALSE,
6299 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6302 Xemerald, TRUE, FALSE,
6306 Xemerald_pause, FALSE, FALSE,
6310 Xemerald_fall, FALSE, FALSE,
6314 Xemerald_shine, FALSE, FALSE,
6315 EL_EMERALD, ACTION_TWINKLING, -1
6318 Yemerald_s, FALSE, FALSE,
6319 EL_EMERALD, ACTION_FALLING, -1
6322 Yemerald_sB, FALSE, TRUE,
6323 EL_EMERALD, ACTION_FALLING, -1
6326 Yemerald_e, FALSE, FALSE,
6327 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6330 Yemerald_eB, FALSE, TRUE,
6331 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6334 Yemerald_w, FALSE, FALSE,
6335 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6338 Yemerald_wB, FALSE, TRUE,
6339 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6342 Yemerald_eat, FALSE, FALSE,
6343 EL_EMERALD, ACTION_COLLECTING, -1
6346 Yemerald_stone, FALSE, FALSE,
6347 EL_NUT, ACTION_BREAKING, -1
6350 Xdiamond, TRUE, FALSE,
6354 Xdiamond_pause, FALSE, FALSE,
6358 Xdiamond_fall, FALSE, FALSE,
6362 Xdiamond_shine, FALSE, FALSE,
6363 EL_DIAMOND, ACTION_TWINKLING, -1
6366 Ydiamond_s, FALSE, FALSE,
6367 EL_DIAMOND, ACTION_FALLING, -1
6370 Ydiamond_sB, FALSE, TRUE,
6371 EL_DIAMOND, ACTION_FALLING, -1
6374 Ydiamond_e, FALSE, FALSE,
6375 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6378 Ydiamond_eB, FALSE, TRUE,
6379 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6382 Ydiamond_w, FALSE, FALSE,
6383 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6386 Ydiamond_wB, FALSE, TRUE,
6387 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6390 Ydiamond_eat, FALSE, FALSE,
6391 EL_DIAMOND, ACTION_COLLECTING, -1
6394 Ydiamond_stone, FALSE, FALSE,
6395 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6398 Xdrip_fall, TRUE, FALSE,
6399 EL_AMOEBA_DROP, -1, -1
6402 Xdrip_stretch, FALSE, FALSE,
6403 EL_AMOEBA_DROP, ACTION_FALLING, -1
6406 Xdrip_stretchB, FALSE, TRUE,
6407 EL_AMOEBA_DROP, ACTION_FALLING, -1
6410 Xdrip_eat, FALSE, FALSE,
6411 EL_AMOEBA_DROP, ACTION_GROWING, -1
6414 Ydrip_s1, FALSE, FALSE,
6415 EL_AMOEBA_DROP, ACTION_FALLING, -1
6418 Ydrip_s1B, FALSE, TRUE,
6419 EL_AMOEBA_DROP, ACTION_FALLING, -1
6422 Ydrip_s2, FALSE, FALSE,
6423 EL_AMOEBA_DROP, ACTION_FALLING, -1
6426 Ydrip_s2B, FALSE, TRUE,
6427 EL_AMOEBA_DROP, ACTION_FALLING, -1
6434 Xbomb_pause, FALSE, FALSE,
6438 Xbomb_fall, FALSE, FALSE,
6442 Ybomb_s, FALSE, FALSE,
6443 EL_BOMB, ACTION_FALLING, -1
6446 Ybomb_sB, FALSE, TRUE,
6447 EL_BOMB, ACTION_FALLING, -1
6450 Ybomb_e, FALSE, FALSE,
6451 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6454 Ybomb_eB, FALSE, TRUE,
6455 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6458 Ybomb_w, FALSE, FALSE,
6459 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6462 Ybomb_wB, FALSE, TRUE,
6463 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6466 Ybomb_eat, FALSE, FALSE,
6467 EL_BOMB, ACTION_ACTIVATING, -1
6470 Xballoon, TRUE, FALSE,
6474 Yballoon_n, FALSE, FALSE,
6475 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6478 Yballoon_nB, FALSE, TRUE,
6479 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6482 Yballoon_e, FALSE, FALSE,
6483 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6486 Yballoon_eB, FALSE, TRUE,
6487 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6490 Yballoon_s, FALSE, FALSE,
6491 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6494 Yballoon_sB, FALSE, TRUE,
6495 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6498 Yballoon_w, FALSE, FALSE,
6499 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6502 Yballoon_wB, FALSE, TRUE,
6503 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6506 Xgrass, TRUE, FALSE,
6507 EL_EMC_GRASS, -1, -1
6510 Ygrass_nB, FALSE, FALSE,
6511 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6514 Ygrass_eB, FALSE, FALSE,
6515 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6518 Ygrass_sB, FALSE, FALSE,
6519 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6522 Ygrass_wB, FALSE, FALSE,
6523 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6530 Ydirt_nB, FALSE, FALSE,
6531 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6534 Ydirt_eB, FALSE, FALSE,
6535 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6538 Ydirt_sB, FALSE, FALSE,
6539 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6542 Ydirt_wB, FALSE, FALSE,
6543 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6546 Xacid_ne, TRUE, FALSE,
6547 EL_ACID_POOL_TOPRIGHT, -1, -1
6550 Xacid_se, TRUE, FALSE,
6551 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6554 Xacid_s, TRUE, FALSE,
6555 EL_ACID_POOL_BOTTOM, -1, -1
6558 Xacid_sw, TRUE, FALSE,
6559 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6562 Xacid_nw, TRUE, FALSE,
6563 EL_ACID_POOL_TOPLEFT, -1, -1
6566 Xacid_1, TRUE, FALSE,
6570 Xacid_2, FALSE, FALSE,
6574 Xacid_3, FALSE, FALSE,
6578 Xacid_4, FALSE, FALSE,
6582 Xacid_5, FALSE, FALSE,
6586 Xacid_6, FALSE, FALSE,
6590 Xacid_7, FALSE, FALSE,
6594 Xacid_8, FALSE, FALSE,
6598 Xball_1, TRUE, FALSE,
6599 EL_EMC_MAGIC_BALL, -1, -1
6602 Xball_1B, FALSE, FALSE,
6603 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6606 Xball_2, FALSE, FALSE,
6607 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6610 Xball_2B, FALSE, FALSE,
6611 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6614 Yball_eat, FALSE, FALSE,
6615 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6618 Ykey_1_eat, FALSE, FALSE,
6619 EL_EM_KEY_1, ACTION_COLLECTING, -1
6622 Ykey_2_eat, FALSE, FALSE,
6623 EL_EM_KEY_2, ACTION_COLLECTING, -1
6626 Ykey_3_eat, FALSE, FALSE,
6627 EL_EM_KEY_3, ACTION_COLLECTING, -1
6630 Ykey_4_eat, FALSE, FALSE,
6631 EL_EM_KEY_4, ACTION_COLLECTING, -1
6634 Ykey_5_eat, FALSE, FALSE,
6635 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6638 Ykey_6_eat, FALSE, FALSE,
6639 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6642 Ykey_7_eat, FALSE, FALSE,
6643 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6646 Ykey_8_eat, FALSE, FALSE,
6647 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6650 Ylenses_eat, FALSE, FALSE,
6651 EL_EMC_LENSES, ACTION_COLLECTING, -1
6654 Ymagnify_eat, FALSE, FALSE,
6655 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6658 Ygrass_eat, FALSE, FALSE,
6659 EL_EMC_GRASS, ACTION_SNAPPING, -1
6662 Ydirt_eat, FALSE, FALSE,
6663 EL_SAND, ACTION_SNAPPING, -1
6666 Xgrow_ns, TRUE, FALSE,
6667 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6670 Ygrow_ns_eat, FALSE, FALSE,
6671 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6674 Xgrow_ew, TRUE, FALSE,
6675 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6678 Ygrow_ew_eat, FALSE, FALSE,
6679 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6682 Xwonderwall, TRUE, FALSE,
6683 EL_MAGIC_WALL, -1, -1
6686 XwonderwallB, FALSE, FALSE,
6687 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6690 Xamoeba_1, TRUE, FALSE,
6691 EL_AMOEBA_DRY, ACTION_OTHER, -1
6694 Xamoeba_2, FALSE, FALSE,
6695 EL_AMOEBA_DRY, ACTION_OTHER, -1
6698 Xamoeba_3, FALSE, FALSE,
6699 EL_AMOEBA_DRY, ACTION_OTHER, -1
6702 Xamoeba_4, FALSE, FALSE,
6703 EL_AMOEBA_DRY, ACTION_OTHER, -1
6706 Xamoeba_5, TRUE, FALSE,
6707 EL_AMOEBA_WET, ACTION_OTHER, -1
6710 Xamoeba_6, FALSE, FALSE,
6711 EL_AMOEBA_WET, ACTION_OTHER, -1
6714 Xamoeba_7, FALSE, FALSE,
6715 EL_AMOEBA_WET, ACTION_OTHER, -1
6718 Xamoeba_8, FALSE, FALSE,
6719 EL_AMOEBA_WET, ACTION_OTHER, -1
6722 Xdoor_1, TRUE, FALSE,
6723 EL_EM_GATE_1, -1, -1
6726 Xdoor_2, TRUE, FALSE,
6727 EL_EM_GATE_2, -1, -1
6730 Xdoor_3, TRUE, FALSE,
6731 EL_EM_GATE_3, -1, -1
6734 Xdoor_4, TRUE, FALSE,
6735 EL_EM_GATE_4, -1, -1
6738 Xdoor_5, TRUE, FALSE,
6739 EL_EMC_GATE_5, -1, -1
6742 Xdoor_6, TRUE, FALSE,
6743 EL_EMC_GATE_6, -1, -1
6746 Xdoor_7, TRUE, FALSE,
6747 EL_EMC_GATE_7, -1, -1
6750 Xdoor_8, TRUE, FALSE,
6751 EL_EMC_GATE_8, -1, -1
6754 Xkey_1, TRUE, FALSE,
6758 Xkey_2, TRUE, FALSE,
6762 Xkey_3, TRUE, FALSE,
6766 Xkey_4, TRUE, FALSE,
6770 Xkey_5, TRUE, FALSE,
6771 EL_EMC_KEY_5, -1, -1
6774 Xkey_6, TRUE, FALSE,
6775 EL_EMC_KEY_6, -1, -1
6778 Xkey_7, TRUE, FALSE,
6779 EL_EMC_KEY_7, -1, -1
6782 Xkey_8, TRUE, FALSE,
6783 EL_EMC_KEY_8, -1, -1
6786 Xwind_n, TRUE, FALSE,
6787 EL_BALLOON_SWITCH_UP, -1, -1
6790 Xwind_e, TRUE, FALSE,
6791 EL_BALLOON_SWITCH_RIGHT, -1, -1
6794 Xwind_s, TRUE, FALSE,
6795 EL_BALLOON_SWITCH_DOWN, -1, -1
6798 Xwind_w, TRUE, FALSE,
6799 EL_BALLOON_SWITCH_LEFT, -1, -1
6802 Xwind_nesw, TRUE, FALSE,
6803 EL_BALLOON_SWITCH_ANY, -1, -1
6806 Xwind_stop, TRUE, FALSE,
6807 EL_BALLOON_SWITCH_NONE, -1, -1
6811 EL_EM_EXIT_CLOSED, -1, -1
6814 Xexit_1, TRUE, FALSE,
6815 EL_EM_EXIT_OPEN, -1, -1
6818 Xexit_2, FALSE, FALSE,
6819 EL_EM_EXIT_OPEN, -1, -1
6822 Xexit_3, FALSE, FALSE,
6823 EL_EM_EXIT_OPEN, -1, -1
6826 Xdynamite, TRUE, FALSE,
6827 EL_EM_DYNAMITE, -1, -1
6830 Ydynamite_eat, FALSE, FALSE,
6831 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6834 Xdynamite_1, TRUE, FALSE,
6835 EL_EM_DYNAMITE_ACTIVE, -1, -1
6838 Xdynamite_2, FALSE, FALSE,
6839 EL_EM_DYNAMITE_ACTIVE, -1, -1
6842 Xdynamite_3, FALSE, FALSE,
6843 EL_EM_DYNAMITE_ACTIVE, -1, -1
6846 Xdynamite_4, FALSE, FALSE,
6847 EL_EM_DYNAMITE_ACTIVE, -1, -1
6850 Xbumper, TRUE, FALSE,
6851 EL_EMC_SPRING_BUMPER, -1, -1
6854 XbumperB, FALSE, FALSE,
6855 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6858 Xwheel, TRUE, FALSE,
6859 EL_ROBOT_WHEEL, -1, -1
6862 XwheelB, FALSE, FALSE,
6863 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6866 Xswitch, TRUE, FALSE,
6867 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6870 XswitchB, FALSE, FALSE,
6871 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6875 EL_QUICKSAND_EMPTY, -1, -1
6878 Xsand_stone, TRUE, FALSE,
6879 EL_QUICKSAND_FULL, -1, -1
6882 Xsand_stonein_1, FALSE, TRUE,
6883 EL_ROCK, ACTION_FILLING, -1
6886 Xsand_stonein_2, FALSE, TRUE,
6887 EL_ROCK, ACTION_FILLING, -1
6890 Xsand_stonein_3, FALSE, TRUE,
6891 EL_ROCK, ACTION_FILLING, -1
6894 Xsand_stonein_4, FALSE, TRUE,
6895 EL_ROCK, ACTION_FILLING, -1
6898 Xsand_stonesand_1, FALSE, FALSE,
6899 EL_QUICKSAND_EMPTYING, -1, -1
6902 Xsand_stonesand_2, FALSE, FALSE,
6903 EL_QUICKSAND_EMPTYING, -1, -1
6906 Xsand_stonesand_3, FALSE, FALSE,
6907 EL_QUICKSAND_EMPTYING, -1, -1
6910 Xsand_stonesand_4, FALSE, FALSE,
6911 EL_QUICKSAND_EMPTYING, -1, -1
6914 Xsand_stonesand_quickout_1, FALSE, FALSE,
6915 EL_QUICKSAND_EMPTYING, -1, -1
6918 Xsand_stonesand_quickout_2, FALSE, FALSE,
6919 EL_QUICKSAND_EMPTYING, -1, -1
6922 Xsand_stoneout_1, FALSE, FALSE,
6923 EL_ROCK, ACTION_EMPTYING, -1
6926 Xsand_stoneout_2, FALSE, FALSE,
6927 EL_ROCK, ACTION_EMPTYING, -1
6930 Xsand_sandstone_1, FALSE, FALSE,
6931 EL_QUICKSAND_FILLING, -1, -1
6934 Xsand_sandstone_2, FALSE, FALSE,
6935 EL_QUICKSAND_FILLING, -1, -1
6938 Xsand_sandstone_3, FALSE, FALSE,
6939 EL_QUICKSAND_FILLING, -1, -1
6942 Xsand_sandstone_4, FALSE, FALSE,
6943 EL_QUICKSAND_FILLING, -1, -1
6946 Xplant, TRUE, FALSE,
6947 EL_EMC_PLANT, -1, -1
6950 Yplant, FALSE, FALSE,
6951 EL_EMC_PLANT, -1, -1
6954 Xlenses, TRUE, FALSE,
6955 EL_EMC_LENSES, -1, -1
6958 Xmagnify, TRUE, FALSE,
6959 EL_EMC_MAGNIFIER, -1, -1
6962 Xdripper, TRUE, FALSE,
6963 EL_EMC_DRIPPER, -1, -1
6966 XdripperB, FALSE, FALSE,
6967 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6970 Xfake_blank, TRUE, FALSE,
6971 EL_INVISIBLE_WALL, -1, -1
6974 Xfake_blankB, FALSE, FALSE,
6975 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6978 Xfake_grass, TRUE, FALSE,
6979 EL_EMC_FAKE_GRASS, -1, -1
6982 Xfake_grassB, FALSE, FALSE,
6983 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6986 Xfake_door_1, TRUE, FALSE,
6987 EL_EM_GATE_1_GRAY, -1, -1
6990 Xfake_door_2, TRUE, FALSE,
6991 EL_EM_GATE_2_GRAY, -1, -1
6994 Xfake_door_3, TRUE, FALSE,
6995 EL_EM_GATE_3_GRAY, -1, -1
6998 Xfake_door_4, TRUE, FALSE,
6999 EL_EM_GATE_4_GRAY, -1, -1
7002 Xfake_door_5, TRUE, FALSE,
7003 EL_EMC_GATE_5_GRAY, -1, -1
7006 Xfake_door_6, TRUE, FALSE,
7007 EL_EMC_GATE_6_GRAY, -1, -1
7010 Xfake_door_7, TRUE, FALSE,
7011 EL_EMC_GATE_7_GRAY, -1, -1
7014 Xfake_door_8, TRUE, FALSE,
7015 EL_EMC_GATE_8_GRAY, -1, -1
7018 Xfake_acid_1, TRUE, FALSE,
7019 EL_EMC_FAKE_ACID, -1, -1
7022 Xfake_acid_2, FALSE, FALSE,
7023 EL_EMC_FAKE_ACID, -1, -1
7026 Xfake_acid_3, FALSE, FALSE,
7027 EL_EMC_FAKE_ACID, -1, -1
7030 Xfake_acid_4, FALSE, FALSE,
7031 EL_EMC_FAKE_ACID, -1, -1
7034 Xfake_acid_5, FALSE, FALSE,
7035 EL_EMC_FAKE_ACID, -1, -1
7038 Xfake_acid_6, FALSE, FALSE,
7039 EL_EMC_FAKE_ACID, -1, -1
7042 Xfake_acid_7, FALSE, FALSE,
7043 EL_EMC_FAKE_ACID, -1, -1
7046 Xfake_acid_8, FALSE, FALSE,
7047 EL_EMC_FAKE_ACID, -1, -1
7050 Xsteel_1, TRUE, FALSE,
7051 EL_STEELWALL, -1, -1
7054 Xsteel_2, TRUE, FALSE,
7055 EL_EMC_STEELWALL_2, -1, -1
7058 Xsteel_3, TRUE, FALSE,
7059 EL_EMC_STEELWALL_3, -1, -1
7062 Xsteel_4, TRUE, FALSE,
7063 EL_EMC_STEELWALL_4, -1, -1
7066 Xwall_1, TRUE, FALSE,
7070 Xwall_2, TRUE, FALSE,
7071 EL_EMC_WALL_14, -1, -1
7074 Xwall_3, TRUE, FALSE,
7075 EL_EMC_WALL_15, -1, -1
7078 Xwall_4, TRUE, FALSE,
7079 EL_EMC_WALL_16, -1, -1
7082 Xround_wall_1, TRUE, FALSE,
7083 EL_WALL_SLIPPERY, -1, -1
7086 Xround_wall_2, TRUE, FALSE,
7087 EL_EMC_WALL_SLIPPERY_2, -1, -1
7090 Xround_wall_3, TRUE, FALSE,
7091 EL_EMC_WALL_SLIPPERY_3, -1, -1
7094 Xround_wall_4, TRUE, FALSE,
7095 EL_EMC_WALL_SLIPPERY_4, -1, -1
7098 Xdecor_1, TRUE, FALSE,
7099 EL_EMC_WALL_8, -1, -1
7102 Xdecor_2, TRUE, FALSE,
7103 EL_EMC_WALL_6, -1, -1
7106 Xdecor_3, TRUE, FALSE,
7107 EL_EMC_WALL_4, -1, -1
7110 Xdecor_4, TRUE, FALSE,
7111 EL_EMC_WALL_7, -1, -1
7114 Xdecor_5, TRUE, FALSE,
7115 EL_EMC_WALL_5, -1, -1
7118 Xdecor_6, TRUE, FALSE,
7119 EL_EMC_WALL_9, -1, -1
7122 Xdecor_7, TRUE, FALSE,
7123 EL_EMC_WALL_10, -1, -1
7126 Xdecor_8, TRUE, FALSE,
7127 EL_EMC_WALL_1, -1, -1
7130 Xdecor_9, TRUE, FALSE,
7131 EL_EMC_WALL_2, -1, -1
7134 Xdecor_10, TRUE, FALSE,
7135 EL_EMC_WALL_3, -1, -1
7138 Xdecor_11, TRUE, FALSE,
7139 EL_EMC_WALL_11, -1, -1
7142 Xdecor_12, TRUE, FALSE,
7143 EL_EMC_WALL_12, -1, -1
7146 Xalpha_0, TRUE, FALSE,
7147 EL_CHAR('0'), -1, -1
7150 Xalpha_1, TRUE, FALSE,
7151 EL_CHAR('1'), -1, -1
7154 Xalpha_2, TRUE, FALSE,
7155 EL_CHAR('2'), -1, -1
7158 Xalpha_3, TRUE, FALSE,
7159 EL_CHAR('3'), -1, -1
7162 Xalpha_4, TRUE, FALSE,
7163 EL_CHAR('4'), -1, -1
7166 Xalpha_5, TRUE, FALSE,
7167 EL_CHAR('5'), -1, -1
7170 Xalpha_6, TRUE, FALSE,
7171 EL_CHAR('6'), -1, -1
7174 Xalpha_7, TRUE, FALSE,
7175 EL_CHAR('7'), -1, -1
7178 Xalpha_8, TRUE, FALSE,
7179 EL_CHAR('8'), -1, -1
7182 Xalpha_9, TRUE, FALSE,
7183 EL_CHAR('9'), -1, -1
7186 Xalpha_excla, TRUE, FALSE,
7187 EL_CHAR('!'), -1, -1
7190 Xalpha_quote, TRUE, FALSE,
7191 EL_CHAR('"'), -1, -1
7194 Xalpha_comma, TRUE, FALSE,
7195 EL_CHAR(','), -1, -1
7198 Xalpha_minus, TRUE, FALSE,
7199 EL_CHAR('-'), -1, -1
7202 Xalpha_perio, TRUE, FALSE,
7203 EL_CHAR('.'), -1, -1
7206 Xalpha_colon, TRUE, FALSE,
7207 EL_CHAR(':'), -1, -1
7210 Xalpha_quest, TRUE, FALSE,
7211 EL_CHAR('?'), -1, -1
7214 Xalpha_a, TRUE, FALSE,
7215 EL_CHAR('A'), -1, -1
7218 Xalpha_b, TRUE, FALSE,
7219 EL_CHAR('B'), -1, -1
7222 Xalpha_c, TRUE, FALSE,
7223 EL_CHAR('C'), -1, -1
7226 Xalpha_d, TRUE, FALSE,
7227 EL_CHAR('D'), -1, -1
7230 Xalpha_e, TRUE, FALSE,
7231 EL_CHAR('E'), -1, -1
7234 Xalpha_f, TRUE, FALSE,
7235 EL_CHAR('F'), -1, -1
7238 Xalpha_g, TRUE, FALSE,
7239 EL_CHAR('G'), -1, -1
7242 Xalpha_h, TRUE, FALSE,
7243 EL_CHAR('H'), -1, -1
7246 Xalpha_i, TRUE, FALSE,
7247 EL_CHAR('I'), -1, -1
7250 Xalpha_j, TRUE, FALSE,
7251 EL_CHAR('J'), -1, -1
7254 Xalpha_k, TRUE, FALSE,
7255 EL_CHAR('K'), -1, -1
7258 Xalpha_l, TRUE, FALSE,
7259 EL_CHAR('L'), -1, -1
7262 Xalpha_m, TRUE, FALSE,
7263 EL_CHAR('M'), -1, -1
7266 Xalpha_n, TRUE, FALSE,
7267 EL_CHAR('N'), -1, -1
7270 Xalpha_o, TRUE, FALSE,
7271 EL_CHAR('O'), -1, -1
7274 Xalpha_p, TRUE, FALSE,
7275 EL_CHAR('P'), -1, -1
7278 Xalpha_q, TRUE, FALSE,
7279 EL_CHAR('Q'), -1, -1
7282 Xalpha_r, TRUE, FALSE,
7283 EL_CHAR('R'), -1, -1
7286 Xalpha_s, TRUE, FALSE,
7287 EL_CHAR('S'), -1, -1
7290 Xalpha_t, TRUE, FALSE,
7291 EL_CHAR('T'), -1, -1
7294 Xalpha_u, TRUE, FALSE,
7295 EL_CHAR('U'), -1, -1
7298 Xalpha_v, TRUE, FALSE,
7299 EL_CHAR('V'), -1, -1
7302 Xalpha_w, TRUE, FALSE,
7303 EL_CHAR('W'), -1, -1
7306 Xalpha_x, TRUE, FALSE,
7307 EL_CHAR('X'), -1, -1
7310 Xalpha_y, TRUE, FALSE,
7311 EL_CHAR('Y'), -1, -1
7314 Xalpha_z, TRUE, FALSE,
7315 EL_CHAR('Z'), -1, -1
7318 Xalpha_arrow_e, TRUE, FALSE,
7319 EL_CHAR('>'), -1, -1
7322 Xalpha_arrow_w, TRUE, FALSE,
7323 EL_CHAR('<'), -1, -1
7326 Xalpha_copyr, TRUE, FALSE,
7327 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7331 Xboom_bug, FALSE, FALSE,
7332 EL_BUG, ACTION_EXPLODING, -1
7335 Xboom_bomb, FALSE, FALSE,
7336 EL_BOMB, ACTION_EXPLODING, -1
7339 Xboom_android, FALSE, FALSE,
7340 EL_EMC_ANDROID, ACTION_OTHER, -1
7343 Xboom_1, FALSE, FALSE,
7344 EL_DEFAULT, ACTION_EXPLODING, -1
7347 Xboom_2, FALSE, FALSE,
7348 EL_DEFAULT, ACTION_EXPLODING, -1
7351 Znormal, FALSE, FALSE,
7355 Zdynamite, FALSE, FALSE,
7359 Zplayer, FALSE, FALSE,
7363 ZBORDER, FALSE, FALSE,
7373 static struct Mapping_EM_to_RND_player
7382 em_player_mapping_list[] =
7386 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7390 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7394 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7398 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7402 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7406 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7410 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7414 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7418 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7422 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7426 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7430 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7434 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7438 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7442 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7446 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7450 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7454 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7458 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7462 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7466 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7470 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7474 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7478 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7482 EL_PLAYER_1, ACTION_DEFAULT, -1,
7486 EL_PLAYER_2, ACTION_DEFAULT, -1,
7490 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7494 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7498 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7502 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7506 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7510 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7514 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7518 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7522 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7526 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7530 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7534 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7538 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7542 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7546 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7550 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7554 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7558 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7562 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7566 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7570 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7574 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7578 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7582 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7586 EL_PLAYER_3, ACTION_DEFAULT, -1,
7590 EL_PLAYER_4, ACTION_DEFAULT, -1,
7599 int map_element_RND_to_EM(int element_rnd)
7601 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7602 static boolean mapping_initialized = FALSE;
7604 if (!mapping_initialized)
7608 // return "Xalpha_quest" for all undefined elements in mapping array
7609 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7610 mapping_RND_to_EM[i] = Xalpha_quest;
7612 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7613 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7614 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7615 em_object_mapping_list[i].element_em;
7617 mapping_initialized = TRUE;
7620 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7621 return mapping_RND_to_EM[element_rnd];
7623 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7628 int map_element_EM_to_RND(int element_em)
7630 static unsigned short mapping_EM_to_RND[TILE_MAX];
7631 static boolean mapping_initialized = FALSE;
7633 if (!mapping_initialized)
7637 // return "EL_UNKNOWN" for all undefined elements in mapping array
7638 for (i = 0; i < TILE_MAX; i++)
7639 mapping_EM_to_RND[i] = EL_UNKNOWN;
7641 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7642 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7643 em_object_mapping_list[i].element_rnd;
7645 mapping_initialized = TRUE;
7648 if (element_em >= 0 && element_em < TILE_MAX)
7649 return mapping_EM_to_RND[element_em];
7651 Error(ERR_WARN, "invalid EM level element %d", element_em);
7656 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7658 struct LevelInfo_EM *level_em = level->native_em_level;
7659 struct LEVEL *lev = level_em->lev;
7662 for (i = 0; i < TILE_MAX; i++)
7663 lev->android_array[i] = Xblank;
7665 for (i = 0; i < level->num_android_clone_elements; i++)
7667 int element_rnd = level->android_clone_element[i];
7668 int element_em = map_element_RND_to_EM(element_rnd);
7670 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7671 if (em_object_mapping_list[j].element_rnd == element_rnd)
7672 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7676 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7678 struct LevelInfo_EM *level_em = level->native_em_level;
7679 struct LEVEL *lev = level_em->lev;
7682 level->num_android_clone_elements = 0;
7684 for (i = 0; i < TILE_MAX; i++)
7686 int element_em = lev->android_array[i];
7688 boolean element_found = FALSE;
7690 if (element_em == Xblank)
7693 element_rnd = map_element_EM_to_RND(element_em);
7695 for (j = 0; j < level->num_android_clone_elements; j++)
7696 if (level->android_clone_element[j] == element_rnd)
7697 element_found = TRUE;
7701 level->android_clone_element[level->num_android_clone_elements++] =
7704 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7709 if (level->num_android_clone_elements == 0)
7711 level->num_android_clone_elements = 1;
7712 level->android_clone_element[0] = EL_EMPTY;
7716 int map_direction_RND_to_EM(int direction)
7718 return (direction == MV_UP ? 0 :
7719 direction == MV_RIGHT ? 1 :
7720 direction == MV_DOWN ? 2 :
7721 direction == MV_LEFT ? 3 :
7725 int map_direction_EM_to_RND(int direction)
7727 return (direction == 0 ? MV_UP :
7728 direction == 1 ? MV_RIGHT :
7729 direction == 2 ? MV_DOWN :
7730 direction == 3 ? MV_LEFT :
7734 int map_element_RND_to_SP(int element_rnd)
7736 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7738 if (element_rnd >= EL_SP_START &&
7739 element_rnd <= EL_SP_END)
7740 element_sp = element_rnd - EL_SP_START;
7741 else if (element_rnd == EL_EMPTY_SPACE)
7743 else if (element_rnd == EL_INVISIBLE_WALL)
7749 int map_element_SP_to_RND(int element_sp)
7751 int element_rnd = EL_UNKNOWN;
7753 if (element_sp >= 0x00 &&
7755 element_rnd = EL_SP_START + element_sp;
7756 else if (element_sp == 0x28)
7757 element_rnd = EL_INVISIBLE_WALL;
7762 int map_action_SP_to_RND(int action_sp)
7766 case actActive: return ACTION_ACTIVE;
7767 case actImpact: return ACTION_IMPACT;
7768 case actExploding: return ACTION_EXPLODING;
7769 case actDigging: return ACTION_DIGGING;
7770 case actSnapping: return ACTION_SNAPPING;
7771 case actCollecting: return ACTION_COLLECTING;
7772 case actPassing: return ACTION_PASSING;
7773 case actPushing: return ACTION_PUSHING;
7774 case actDropping: return ACTION_DROPPING;
7776 default: return ACTION_DEFAULT;
7780 int map_element_RND_to_MM(int element_rnd)
7782 return (element_rnd >= EL_MM_START_1 &&
7783 element_rnd <= EL_MM_END_1 ?
7784 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7786 element_rnd >= EL_MM_START_2 &&
7787 element_rnd <= EL_MM_END_2 ?
7788 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7790 element_rnd >= EL_CHAR_START &&
7791 element_rnd <= EL_CHAR_END ?
7792 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7794 element_rnd >= EL_MM_RUNTIME_START &&
7795 element_rnd <= EL_MM_RUNTIME_END ?
7796 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7798 element_rnd >= EL_MM_DUMMY_START &&
7799 element_rnd <= EL_MM_DUMMY_END ?
7800 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7802 EL_MM_EMPTY_NATIVE);
7805 int map_element_MM_to_RND(int element_mm)
7807 return (element_mm == EL_MM_EMPTY_NATIVE ||
7808 element_mm == EL_DF_EMPTY_NATIVE ?
7811 element_mm >= EL_MM_START_1_NATIVE &&
7812 element_mm <= EL_MM_END_1_NATIVE ?
7813 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7815 element_mm >= EL_MM_START_2_NATIVE &&
7816 element_mm <= EL_MM_END_2_NATIVE ?
7817 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7819 element_mm >= EL_MM_CHAR_START_NATIVE &&
7820 element_mm <= EL_MM_CHAR_END_NATIVE ?
7821 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7823 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7824 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7825 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7827 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7828 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7829 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7834 int map_action_MM_to_RND(int action_mm)
7836 // all MM actions are defined to exactly match their RND counterparts
7840 int map_sound_MM_to_RND(int sound_mm)
7844 case SND_MM_GAME_LEVELTIME_CHARGING:
7845 return SND_GAME_LEVELTIME_CHARGING;
7847 case SND_MM_GAME_HEALTH_CHARGING:
7848 return SND_GAME_HEALTH_CHARGING;
7851 return SND_UNDEFINED;
7855 int map_mm_wall_element(int element)
7857 return (element >= EL_MM_STEEL_WALL_START &&
7858 element <= EL_MM_STEEL_WALL_END ?
7861 element >= EL_MM_WOODEN_WALL_START &&
7862 element <= EL_MM_WOODEN_WALL_END ?
7865 element >= EL_MM_ICE_WALL_START &&
7866 element <= EL_MM_ICE_WALL_END ?
7869 element >= EL_MM_AMOEBA_WALL_START &&
7870 element <= EL_MM_AMOEBA_WALL_END ?
7873 element >= EL_DF_STEEL_WALL_START &&
7874 element <= EL_DF_STEEL_WALL_END ?
7877 element >= EL_DF_WOODEN_WALL_START &&
7878 element <= EL_DF_WOODEN_WALL_END ?
7884 int map_mm_wall_element_editor(int element)
7888 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7889 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7890 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7891 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7892 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7893 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7895 default: return element;
7899 int get_next_element(int element)
7903 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7904 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7905 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7906 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7907 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7908 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7909 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7910 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7911 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7912 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7913 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7915 default: return element;
7919 int el2img_mm(int element_mm)
7921 return el2img(map_element_MM_to_RND(element_mm));
7924 int el_act_dir2img(int element, int action, int direction)
7926 element = GFX_ELEMENT(element);
7927 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7929 // direction_graphic[][] == graphic[] for undefined direction graphics
7930 return element_info[element].direction_graphic[action][direction];
7933 static int el_act_dir2crm(int element, int action, int direction)
7935 element = GFX_ELEMENT(element);
7936 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7938 // direction_graphic[][] == graphic[] for undefined direction graphics
7939 return element_info[element].direction_crumbled[action][direction];
7942 int el_act2img(int element, int action)
7944 element = GFX_ELEMENT(element);
7946 return element_info[element].graphic[action];
7949 int el_act2crm(int element, int action)
7951 element = GFX_ELEMENT(element);
7953 return element_info[element].crumbled[action];
7956 int el_dir2img(int element, int direction)
7958 element = GFX_ELEMENT(element);
7960 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7963 int el2baseimg(int element)
7965 return element_info[element].graphic[ACTION_DEFAULT];
7968 int el2img(int element)
7970 element = GFX_ELEMENT(element);
7972 return element_info[element].graphic[ACTION_DEFAULT];
7975 int el2edimg(int element)
7977 element = GFX_ELEMENT(element);
7979 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7982 int el2preimg(int element)
7984 element = GFX_ELEMENT(element);
7986 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7989 int el2panelimg(int element)
7991 element = GFX_ELEMENT(element);
7993 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7996 int font2baseimg(int font_nr)
7998 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8001 int getBeltNrFromBeltElement(int element)
8003 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8004 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8005 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8008 int getBeltNrFromBeltActiveElement(int element)
8010 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8011 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8012 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8015 int getBeltNrFromBeltSwitchElement(int element)
8017 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8018 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8019 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8022 int getBeltDirNrFromBeltElement(int element)
8024 static int belt_base_element[4] =
8026 EL_CONVEYOR_BELT_1_LEFT,
8027 EL_CONVEYOR_BELT_2_LEFT,
8028 EL_CONVEYOR_BELT_3_LEFT,
8029 EL_CONVEYOR_BELT_4_LEFT
8032 int belt_nr = getBeltNrFromBeltElement(element);
8033 int belt_dir_nr = element - belt_base_element[belt_nr];
8035 return (belt_dir_nr % 3);
8038 int getBeltDirNrFromBeltSwitchElement(int element)
8040 static int belt_base_element[4] =
8042 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8043 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8044 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8045 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8048 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8049 int belt_dir_nr = element - belt_base_element[belt_nr];
8051 return (belt_dir_nr % 3);
8054 int getBeltDirFromBeltElement(int element)
8056 static int belt_move_dir[3] =
8063 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8065 return belt_move_dir[belt_dir_nr];
8068 int getBeltDirFromBeltSwitchElement(int element)
8070 static int belt_move_dir[3] =
8077 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8079 return belt_move_dir[belt_dir_nr];
8082 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8084 static int belt_base_element[4] =
8086 EL_CONVEYOR_BELT_1_LEFT,
8087 EL_CONVEYOR_BELT_2_LEFT,
8088 EL_CONVEYOR_BELT_3_LEFT,
8089 EL_CONVEYOR_BELT_4_LEFT
8092 return belt_base_element[belt_nr] + belt_dir_nr;
8095 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8097 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8099 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8102 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8104 static int belt_base_element[4] =
8106 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8107 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8108 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8109 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8112 return belt_base_element[belt_nr] + belt_dir_nr;
8115 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8117 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8119 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8122 boolean getTeamMode_EM(void)
8124 return game.team_mode || network_playing;
8127 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8129 int game_frame_delay_value;
8131 game_frame_delay_value =
8132 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8133 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8136 if (tape.playing && tape.warp_forward && !tape.pausing)
8137 game_frame_delay_value = 0;
8139 return game_frame_delay_value;
8142 unsigned int InitRND(int seed)
8144 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8145 return InitEngineRandom_EM(seed);
8146 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8147 return InitEngineRandom_SP(seed);
8148 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8149 return InitEngineRandom_MM(seed);
8151 return InitEngineRandom_RND(seed);
8154 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8155 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8157 static int get_effective_element_EM(int tile, int frame_em)
8159 int element = object_mapping[tile].element_rnd;
8160 int action = object_mapping[tile].action;
8161 boolean is_backside = object_mapping[tile].is_backside;
8162 boolean action_removing = (action == ACTION_DIGGING ||
8163 action == ACTION_SNAPPING ||
8164 action == ACTION_COLLECTING);
8170 case Yacid_splash_eB:
8171 case Yacid_splash_wB:
8172 return (frame_em > 5 ? EL_EMPTY : element);
8178 else // frame_em == 7
8182 case Yacid_splash_eB:
8183 case Yacid_splash_wB:
8186 case Yemerald_stone:
8189 case Ydiamond_stone:
8193 case Xdrip_stretchB:
8212 case Xsand_stonein_1:
8213 case Xsand_stonein_2:
8214 case Xsand_stonein_3:
8215 case Xsand_stonein_4:
8219 return (is_backside || action_removing ? EL_EMPTY : element);
8224 static boolean check_linear_animation_EM(int tile)
8228 case Xsand_stonesand_1:
8229 case Xsand_stonesand_quickout_1:
8230 case Xsand_sandstone_1:
8231 case Xsand_stonein_1:
8232 case Xsand_stoneout_1:
8251 case Yacid_splash_eB:
8252 case Yacid_splash_wB:
8253 case Yemerald_stone:
8260 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8261 boolean has_crumbled_graphics,
8262 int crumbled, int sync_frame)
8264 // if element can be crumbled, but certain action graphics are just empty
8265 // space (like instantly snapping sand to empty space in 1 frame), do not
8266 // treat these empty space graphics as crumbled graphics in EMC engine
8267 if (crumbled == IMG_EMPTY_SPACE)
8268 has_crumbled_graphics = FALSE;
8270 if (has_crumbled_graphics)
8272 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8273 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8274 g_crumbled->anim_delay,
8275 g_crumbled->anim_mode,
8276 g_crumbled->anim_start_frame,
8279 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8280 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8282 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8283 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8285 g_em->has_crumbled_graphics = TRUE;
8289 g_em->crumbled_bitmap = NULL;
8290 g_em->crumbled_src_x = 0;
8291 g_em->crumbled_src_y = 0;
8292 g_em->crumbled_border_size = 0;
8293 g_em->crumbled_tile_size = 0;
8295 g_em->has_crumbled_graphics = FALSE;
8300 void ResetGfxAnimation_EM(int x, int y, int tile)
8306 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8307 int tile, int frame_em, int x, int y)
8309 int action = object_mapping[tile].action;
8310 int direction = object_mapping[tile].direction;
8311 int effective_element = get_effective_element_EM(tile, frame_em);
8312 int graphic = (direction == MV_NONE ?
8313 el_act2img(effective_element, action) :
8314 el_act_dir2img(effective_element, action, direction));
8315 struct GraphicInfo *g = &graphic_info[graphic];
8317 boolean action_removing = (action == ACTION_DIGGING ||
8318 action == ACTION_SNAPPING ||
8319 action == ACTION_COLLECTING);
8320 boolean action_moving = (action == ACTION_FALLING ||
8321 action == ACTION_MOVING ||
8322 action == ACTION_PUSHING ||
8323 action == ACTION_EATING ||
8324 action == ACTION_FILLING ||
8325 action == ACTION_EMPTYING);
8326 boolean action_falling = (action == ACTION_FALLING ||
8327 action == ACTION_FILLING ||
8328 action == ACTION_EMPTYING);
8330 // special case: graphic uses "2nd movement tile" and has defined
8331 // 7 frames for movement animation (or less) => use default graphic
8332 // for last (8th) frame which ends the movement animation
8333 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8335 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8336 graphic = (direction == MV_NONE ?
8337 el_act2img(effective_element, action) :
8338 el_act_dir2img(effective_element, action, direction));
8340 g = &graphic_info[graphic];
8343 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8347 else if (action_moving)
8349 boolean is_backside = object_mapping[tile].is_backside;
8353 int direction = object_mapping[tile].direction;
8354 int move_dir = (action_falling ? MV_DOWN : direction);
8359 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8360 if (g->double_movement && frame_em == 0)
8364 if (move_dir == MV_LEFT)
8365 GfxFrame[x - 1][y] = GfxFrame[x][y];
8366 else if (move_dir == MV_RIGHT)
8367 GfxFrame[x + 1][y] = GfxFrame[x][y];
8368 else if (move_dir == MV_UP)
8369 GfxFrame[x][y - 1] = GfxFrame[x][y];
8370 else if (move_dir == MV_DOWN)
8371 GfxFrame[x][y + 1] = GfxFrame[x][y];
8378 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8379 if (tile == Xsand_stonesand_quickout_1 ||
8380 tile == Xsand_stonesand_quickout_2)
8384 if (graphic_info[graphic].anim_global_sync)
8385 sync_frame = FrameCounter;
8386 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8387 sync_frame = GfxFrame[x][y];
8389 sync_frame = 0; // playfield border (pseudo steel)
8391 SetRandomAnimationValue(x, y);
8393 int frame = getAnimationFrame(g->anim_frames,
8396 g->anim_start_frame,
8399 g_em->unique_identifier =
8400 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8403 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8404 int tile, int frame_em, int x, int y)
8406 int action = object_mapping[tile].action;
8407 int direction = object_mapping[tile].direction;
8408 boolean is_backside = object_mapping[tile].is_backside;
8409 int effective_element = get_effective_element_EM(tile, frame_em);
8410 int effective_action = action;
8411 int graphic = (direction == MV_NONE ?
8412 el_act2img(effective_element, effective_action) :
8413 el_act_dir2img(effective_element, effective_action,
8415 int crumbled = (direction == MV_NONE ?
8416 el_act2crm(effective_element, effective_action) :
8417 el_act_dir2crm(effective_element, effective_action,
8419 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8420 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8421 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8422 struct GraphicInfo *g = &graphic_info[graphic];
8425 // special case: graphic uses "2nd movement tile" and has defined
8426 // 7 frames for movement animation (or less) => use default graphic
8427 // for last (8th) frame which ends the movement animation
8428 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8430 effective_action = ACTION_DEFAULT;
8431 graphic = (direction == MV_NONE ?
8432 el_act2img(effective_element, effective_action) :
8433 el_act_dir2img(effective_element, effective_action,
8435 crumbled = (direction == MV_NONE ?
8436 el_act2crm(effective_element, effective_action) :
8437 el_act_dir2crm(effective_element, effective_action,
8440 g = &graphic_info[graphic];
8443 if (graphic_info[graphic].anim_global_sync)
8444 sync_frame = FrameCounter;
8445 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8446 sync_frame = GfxFrame[x][y];
8448 sync_frame = 0; // playfield border (pseudo steel)
8450 SetRandomAnimationValue(x, y);
8452 int frame = getAnimationFrame(g->anim_frames,
8455 g->anim_start_frame,
8458 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8459 g->double_movement && is_backside);
8461 // (updating the "crumbled" graphic definitions is probably not really needed,
8462 // as animations for crumbled graphics can't be longer than one EMC cycle)
8463 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8467 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8468 int player_nr, int anim, int frame_em)
8470 int element = player_mapping[player_nr][anim].element_rnd;
8471 int action = player_mapping[player_nr][anim].action;
8472 int direction = player_mapping[player_nr][anim].direction;
8473 int graphic = (direction == MV_NONE ?
8474 el_act2img(element, action) :
8475 el_act_dir2img(element, action, direction));
8476 struct GraphicInfo *g = &graphic_info[graphic];
8479 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8481 stored_player[player_nr].StepFrame = frame_em;
8483 sync_frame = stored_player[player_nr].Frame;
8485 int frame = getAnimationFrame(g->anim_frames,
8488 g->anim_start_frame,
8491 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8492 &g_em->src_x, &g_em->src_y, FALSE);
8495 void InitGraphicInfo_EM(void)
8500 int num_em_gfx_errors = 0;
8502 if (graphic_info_em_object[0][0].bitmap == NULL)
8504 // EM graphics not yet initialized in em_open_all()
8509 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8512 // always start with reliable default values
8513 for (i = 0; i < TILE_MAX; i++)
8515 object_mapping[i].element_rnd = EL_UNKNOWN;
8516 object_mapping[i].is_backside = FALSE;
8517 object_mapping[i].action = ACTION_DEFAULT;
8518 object_mapping[i].direction = MV_NONE;
8521 // always start with reliable default values
8522 for (p = 0; p < MAX_PLAYERS; p++)
8524 for (i = 0; i < SPR_MAX; i++)
8526 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8527 player_mapping[p][i].action = ACTION_DEFAULT;
8528 player_mapping[p][i].direction = MV_NONE;
8532 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8534 int e = em_object_mapping_list[i].element_em;
8536 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8537 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8539 if (em_object_mapping_list[i].action != -1)
8540 object_mapping[e].action = em_object_mapping_list[i].action;
8542 if (em_object_mapping_list[i].direction != -1)
8543 object_mapping[e].direction =
8544 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8547 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8549 int a = em_player_mapping_list[i].action_em;
8550 int p = em_player_mapping_list[i].player_nr;
8552 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8554 if (em_player_mapping_list[i].action != -1)
8555 player_mapping[p][a].action = em_player_mapping_list[i].action;
8557 if (em_player_mapping_list[i].direction != -1)
8558 player_mapping[p][a].direction =
8559 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8562 for (i = 0; i < TILE_MAX; i++)
8564 int element = object_mapping[i].element_rnd;
8565 int action = object_mapping[i].action;
8566 int direction = object_mapping[i].direction;
8567 boolean is_backside = object_mapping[i].is_backside;
8568 boolean action_exploding = ((action == ACTION_EXPLODING ||
8569 action == ACTION_SMASHED_BY_ROCK ||
8570 action == ACTION_SMASHED_BY_SPRING) &&
8571 element != EL_DIAMOND);
8572 boolean action_active = (action == ACTION_ACTIVE);
8573 boolean action_other = (action == ACTION_OTHER);
8575 for (j = 0; j < 8; j++)
8577 int effective_element = get_effective_element_EM(i, j);
8578 int effective_action = (j < 7 ? action :
8579 i == Xdrip_stretch ? action :
8580 i == Xdrip_stretchB ? action :
8581 i == Ydrip_s1 ? action :
8582 i == Ydrip_s1B ? action :
8583 i == Xball_1B ? action :
8584 i == Xball_2 ? action :
8585 i == Xball_2B ? action :
8586 i == Yball_eat ? action :
8587 i == Ykey_1_eat ? action :
8588 i == Ykey_2_eat ? action :
8589 i == Ykey_3_eat ? action :
8590 i == Ykey_4_eat ? action :
8591 i == Ykey_5_eat ? action :
8592 i == Ykey_6_eat ? action :
8593 i == Ykey_7_eat ? action :
8594 i == Ykey_8_eat ? action :
8595 i == Ylenses_eat ? action :
8596 i == Ymagnify_eat ? action :
8597 i == Ygrass_eat ? action :
8598 i == Ydirt_eat ? action :
8599 i == Xsand_stonein_1 ? action :
8600 i == Xsand_stonein_2 ? action :
8601 i == Xsand_stonein_3 ? action :
8602 i == Xsand_stonein_4 ? action :
8603 i == Xsand_stoneout_1 ? action :
8604 i == Xsand_stoneout_2 ? action :
8605 i == Xboom_android ? ACTION_EXPLODING :
8606 action_exploding ? ACTION_EXPLODING :
8607 action_active ? action :
8608 action_other ? action :
8610 int graphic = (el_act_dir2img(effective_element, effective_action,
8612 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8614 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8615 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8616 boolean has_action_graphics = (graphic != base_graphic);
8617 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8618 struct GraphicInfo *g = &graphic_info[graphic];
8619 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8622 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8623 boolean special_animation = (action != ACTION_DEFAULT &&
8624 g->anim_frames == 3 &&
8625 g->anim_delay == 2 &&
8626 g->anim_mode & ANIM_LINEAR);
8627 int sync_frame = (i == Xdrip_stretch ? 7 :
8628 i == Xdrip_stretchB ? 7 :
8629 i == Ydrip_s2 ? j + 8 :
8630 i == Ydrip_s2B ? j + 8 :
8639 i == Xfake_acid_1 ? 0 :
8640 i == Xfake_acid_2 ? 10 :
8641 i == Xfake_acid_3 ? 20 :
8642 i == Xfake_acid_4 ? 30 :
8643 i == Xfake_acid_5 ? 40 :
8644 i == Xfake_acid_6 ? 50 :
8645 i == Xfake_acid_7 ? 60 :
8646 i == Xfake_acid_8 ? 70 :
8648 i == Xball_2B ? j + 8 :
8649 i == Yball_eat ? j + 1 :
8650 i == Ykey_1_eat ? j + 1 :
8651 i == Ykey_2_eat ? j + 1 :
8652 i == Ykey_3_eat ? j + 1 :
8653 i == Ykey_4_eat ? j + 1 :
8654 i == Ykey_5_eat ? j + 1 :
8655 i == Ykey_6_eat ? j + 1 :
8656 i == Ykey_7_eat ? j + 1 :
8657 i == Ykey_8_eat ? j + 1 :
8658 i == Ylenses_eat ? j + 1 :
8659 i == Ymagnify_eat ? j + 1 :
8660 i == Ygrass_eat ? j + 1 :
8661 i == Ydirt_eat ? j + 1 :
8662 i == Xamoeba_1 ? 0 :
8663 i == Xamoeba_2 ? 1 :
8664 i == Xamoeba_3 ? 2 :
8665 i == Xamoeba_4 ? 3 :
8666 i == Xamoeba_5 ? 0 :
8667 i == Xamoeba_6 ? 1 :
8668 i == Xamoeba_7 ? 2 :
8669 i == Xamoeba_8 ? 3 :
8670 i == Xexit_2 ? j + 8 :
8671 i == Xexit_3 ? j + 16 :
8672 i == Xdynamite_1 ? 0 :
8673 i == Xdynamite_2 ? 8 :
8674 i == Xdynamite_3 ? 16 :
8675 i == Xdynamite_4 ? 24 :
8676 i == Xsand_stonein_1 ? j + 1 :
8677 i == Xsand_stonein_2 ? j + 9 :
8678 i == Xsand_stonein_3 ? j + 17 :
8679 i == Xsand_stonein_4 ? j + 25 :
8680 i == Xsand_stoneout_1 && j == 0 ? 0 :
8681 i == Xsand_stoneout_1 && j == 1 ? 0 :
8682 i == Xsand_stoneout_1 && j == 2 ? 1 :
8683 i == Xsand_stoneout_1 && j == 3 ? 2 :
8684 i == Xsand_stoneout_1 && j == 4 ? 2 :
8685 i == Xsand_stoneout_1 && j == 5 ? 3 :
8686 i == Xsand_stoneout_1 && j == 6 ? 4 :
8687 i == Xsand_stoneout_1 && j == 7 ? 4 :
8688 i == Xsand_stoneout_2 && j == 0 ? 5 :
8689 i == Xsand_stoneout_2 && j == 1 ? 6 :
8690 i == Xsand_stoneout_2 && j == 2 ? 7 :
8691 i == Xsand_stoneout_2 && j == 3 ? 8 :
8692 i == Xsand_stoneout_2 && j == 4 ? 9 :
8693 i == Xsand_stoneout_2 && j == 5 ? 11 :
8694 i == Xsand_stoneout_2 && j == 6 ? 13 :
8695 i == Xsand_stoneout_2 && j == 7 ? 15 :
8696 i == Xboom_bug && j == 1 ? 2 :
8697 i == Xboom_bug && j == 2 ? 2 :
8698 i == Xboom_bug && j == 3 ? 4 :
8699 i == Xboom_bug && j == 4 ? 4 :
8700 i == Xboom_bug && j == 5 ? 2 :
8701 i == Xboom_bug && j == 6 ? 2 :
8702 i == Xboom_bug && j == 7 ? 0 :
8703 i == Xboom_bomb && j == 1 ? 2 :
8704 i == Xboom_bomb && j == 2 ? 2 :
8705 i == Xboom_bomb && j == 3 ? 4 :
8706 i == Xboom_bomb && j == 4 ? 4 :
8707 i == Xboom_bomb && j == 5 ? 2 :
8708 i == Xboom_bomb && j == 6 ? 2 :
8709 i == Xboom_bomb && j == 7 ? 0 :
8710 i == Xboom_android && j == 7 ? 6 :
8711 i == Xboom_1 && j == 1 ? 2 :
8712 i == Xboom_1 && j == 2 ? 2 :
8713 i == Xboom_1 && j == 3 ? 4 :
8714 i == Xboom_1 && j == 4 ? 4 :
8715 i == Xboom_1 && j == 5 ? 6 :
8716 i == Xboom_1 && j == 6 ? 6 :
8717 i == Xboom_1 && j == 7 ? 8 :
8718 i == Xboom_2 && j == 0 ? 8 :
8719 i == Xboom_2 && j == 1 ? 8 :
8720 i == Xboom_2 && j == 2 ? 10 :
8721 i == Xboom_2 && j == 3 ? 10 :
8722 i == Xboom_2 && j == 4 ? 10 :
8723 i == Xboom_2 && j == 5 ? 12 :
8724 i == Xboom_2 && j == 6 ? 12 :
8725 i == Xboom_2 && j == 7 ? 12 :
8726 special_animation && j == 4 ? 3 :
8727 effective_action != action ? 0 :
8731 Bitmap *debug_bitmap = g_em->bitmap;
8732 int debug_src_x = g_em->src_x;
8733 int debug_src_y = g_em->src_y;
8736 int frame = getAnimationFrame(g->anim_frames,
8739 g->anim_start_frame,
8742 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8743 g->double_movement && is_backside);
8745 g_em->bitmap = src_bitmap;
8746 g_em->src_x = src_x;
8747 g_em->src_y = src_y;
8748 g_em->src_offset_x = 0;
8749 g_em->src_offset_y = 0;
8750 g_em->dst_offset_x = 0;
8751 g_em->dst_offset_y = 0;
8752 g_em->width = TILEX;
8753 g_em->height = TILEY;
8755 g_em->preserve_background = FALSE;
8757 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8760 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8761 effective_action == ACTION_MOVING ||
8762 effective_action == ACTION_PUSHING ||
8763 effective_action == ACTION_EATING)) ||
8764 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8765 effective_action == ACTION_EMPTYING)))
8768 (effective_action == ACTION_FALLING ||
8769 effective_action == ACTION_FILLING ||
8770 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8771 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8772 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8773 int num_steps = (i == Ydrip_s1 ? 16 :
8774 i == Ydrip_s1B ? 16 :
8775 i == Ydrip_s2 ? 16 :
8776 i == Ydrip_s2B ? 16 :
8777 i == Xsand_stonein_1 ? 32 :
8778 i == Xsand_stonein_2 ? 32 :
8779 i == Xsand_stonein_3 ? 32 :
8780 i == Xsand_stonein_4 ? 32 :
8781 i == Xsand_stoneout_1 ? 16 :
8782 i == Xsand_stoneout_2 ? 16 : 8);
8783 int cx = ABS(dx) * (TILEX / num_steps);
8784 int cy = ABS(dy) * (TILEY / num_steps);
8785 int step_frame = (i == Ydrip_s2 ? j + 8 :
8786 i == Ydrip_s2B ? j + 8 :
8787 i == Xsand_stonein_2 ? j + 8 :
8788 i == Xsand_stonein_3 ? j + 16 :
8789 i == Xsand_stonein_4 ? j + 24 :
8790 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8791 int step = (is_backside ? step_frame : num_steps - step_frame);
8793 if (is_backside) // tile where movement starts
8795 if (dx < 0 || dy < 0)
8797 g_em->src_offset_x = cx * step;
8798 g_em->src_offset_y = cy * step;
8802 g_em->dst_offset_x = cx * step;
8803 g_em->dst_offset_y = cy * step;
8806 else // tile where movement ends
8808 if (dx < 0 || dy < 0)
8810 g_em->dst_offset_x = cx * step;
8811 g_em->dst_offset_y = cy * step;
8815 g_em->src_offset_x = cx * step;
8816 g_em->src_offset_y = cy * step;
8820 g_em->width = TILEX - cx * step;
8821 g_em->height = TILEY - cy * step;
8824 // create unique graphic identifier to decide if tile must be redrawn
8825 /* bit 31 - 16 (16 bit): EM style graphic
8826 bit 15 - 12 ( 4 bit): EM style frame
8827 bit 11 - 6 ( 6 bit): graphic width
8828 bit 5 - 0 ( 6 bit): graphic height */
8829 g_em->unique_identifier =
8830 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8834 // skip check for EMC elements not contained in original EMC artwork
8835 if (element == EL_EMC_FAKE_ACID)
8838 if (g_em->bitmap != debug_bitmap ||
8839 g_em->src_x != debug_src_x ||
8840 g_em->src_y != debug_src_y ||
8841 g_em->src_offset_x != 0 ||
8842 g_em->src_offset_y != 0 ||
8843 g_em->dst_offset_x != 0 ||
8844 g_em->dst_offset_y != 0 ||
8845 g_em->width != TILEX ||
8846 g_em->height != TILEY)
8848 static int last_i = -1;
8856 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8857 i, element, element_info[element].token_name,
8858 element_action_info[effective_action].suffix, direction);
8860 if (element != effective_element)
8861 printf(" [%d ('%s')]",
8863 element_info[effective_element].token_name);
8867 if (g_em->bitmap != debug_bitmap)
8868 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8869 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8871 if (g_em->src_x != debug_src_x ||
8872 g_em->src_y != debug_src_y)
8873 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8874 j, (is_backside ? 'B' : 'F'),
8875 g_em->src_x, g_em->src_y,
8876 g_em->src_x / 32, g_em->src_y / 32,
8877 debug_src_x, debug_src_y,
8878 debug_src_x / 32, debug_src_y / 32);
8880 if (g_em->src_offset_x != 0 ||
8881 g_em->src_offset_y != 0 ||
8882 g_em->dst_offset_x != 0 ||
8883 g_em->dst_offset_y != 0)
8884 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8886 g_em->src_offset_x, g_em->src_offset_y,
8887 g_em->dst_offset_x, g_em->dst_offset_y);
8889 if (g_em->width != TILEX ||
8890 g_em->height != TILEY)
8891 printf(" %d (%d): size %d,%d should be %d,%d\n",
8893 g_em->width, g_em->height, TILEX, TILEY);
8895 num_em_gfx_errors++;
8902 for (i = 0; i < TILE_MAX; i++)
8904 for (j = 0; j < 8; j++)
8906 int element = object_mapping[i].element_rnd;
8907 int action = object_mapping[i].action;
8908 int direction = object_mapping[i].direction;
8909 boolean is_backside = object_mapping[i].is_backside;
8910 int graphic_action = el_act_dir2img(element, action, direction);
8911 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8913 if ((action == ACTION_SMASHED_BY_ROCK ||
8914 action == ACTION_SMASHED_BY_SPRING ||
8915 action == ACTION_EATING) &&
8916 graphic_action == graphic_default)
8918 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8919 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8920 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8921 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8924 // no separate animation for "smashed by rock" -- use rock instead
8925 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8926 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8928 g_em->bitmap = g_xx->bitmap;
8929 g_em->src_x = g_xx->src_x;
8930 g_em->src_y = g_xx->src_y;
8931 g_em->src_offset_x = g_xx->src_offset_x;
8932 g_em->src_offset_y = g_xx->src_offset_y;
8933 g_em->dst_offset_x = g_xx->dst_offset_x;
8934 g_em->dst_offset_y = g_xx->dst_offset_y;
8935 g_em->width = g_xx->width;
8936 g_em->height = g_xx->height;
8937 g_em->unique_identifier = g_xx->unique_identifier;
8940 g_em->preserve_background = TRUE;
8945 for (p = 0; p < MAX_PLAYERS; p++)
8947 for (i = 0; i < SPR_MAX; i++)
8949 int element = player_mapping[p][i].element_rnd;
8950 int action = player_mapping[p][i].action;
8951 int direction = player_mapping[p][i].direction;
8953 for (j = 0; j < 8; j++)
8955 int effective_element = element;
8956 int effective_action = action;
8957 int graphic = (direction == MV_NONE ?
8958 el_act2img(effective_element, effective_action) :
8959 el_act_dir2img(effective_element, effective_action,
8961 struct GraphicInfo *g = &graphic_info[graphic];
8962 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8968 Bitmap *debug_bitmap = g_em->bitmap;
8969 int debug_src_x = g_em->src_x;
8970 int debug_src_y = g_em->src_y;
8973 int frame = getAnimationFrame(g->anim_frames,
8976 g->anim_start_frame,
8979 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8981 g_em->bitmap = src_bitmap;
8982 g_em->src_x = src_x;
8983 g_em->src_y = src_y;
8984 g_em->src_offset_x = 0;
8985 g_em->src_offset_y = 0;
8986 g_em->dst_offset_x = 0;
8987 g_em->dst_offset_y = 0;
8988 g_em->width = TILEX;
8989 g_em->height = TILEY;
8993 // skip check for EMC elements not contained in original EMC artwork
8994 if (element == EL_PLAYER_3 ||
8995 element == EL_PLAYER_4)
8998 if (g_em->bitmap != debug_bitmap ||
8999 g_em->src_x != debug_src_x ||
9000 g_em->src_y != debug_src_y)
9002 static int last_i = -1;
9010 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9011 p, i, element, element_info[element].token_name,
9012 element_action_info[effective_action].suffix, direction);
9014 if (element != effective_element)
9015 printf(" [%d ('%s')]",
9017 element_info[effective_element].token_name);
9021 if (g_em->bitmap != debug_bitmap)
9022 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9023 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9025 if (g_em->src_x != debug_src_x ||
9026 g_em->src_y != debug_src_y)
9027 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9029 g_em->src_x, g_em->src_y,
9030 g_em->src_x / 32, g_em->src_y / 32,
9031 debug_src_x, debug_src_y,
9032 debug_src_x / 32, debug_src_y / 32);
9034 num_em_gfx_errors++;
9044 printf("::: [%d errors found]\n", num_em_gfx_errors);
9050 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9051 boolean any_player_moving,
9052 boolean any_player_snapping,
9053 boolean any_player_dropping)
9055 if (frame == 0 && !any_player_dropping)
9057 if (!local_player->was_waiting)
9059 if (!CheckSaveEngineSnapshotToList())
9062 local_player->was_waiting = TRUE;
9065 else if (any_player_moving || any_player_snapping || any_player_dropping)
9067 local_player->was_waiting = FALSE;
9071 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9072 boolean murphy_is_dropping)
9074 if (murphy_is_waiting)
9076 if (!local_player->was_waiting)
9078 if (!CheckSaveEngineSnapshotToList())
9081 local_player->was_waiting = TRUE;
9086 local_player->was_waiting = FALSE;
9090 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9091 boolean button_released)
9093 if (button_released)
9095 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9096 CheckSaveEngineSnapshotToList();
9098 else if (element_clicked)
9100 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9101 CheckSaveEngineSnapshotToList();
9103 game.snapshot.changed_action = TRUE;
9107 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9108 boolean any_player_moving,
9109 boolean any_player_snapping,
9110 boolean any_player_dropping)
9112 if (tape.single_step && tape.recording && !tape.pausing)
9113 if (frame == 0 && !any_player_dropping)
9114 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9116 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9117 any_player_snapping, any_player_dropping);
9120 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9121 boolean murphy_is_dropping)
9123 boolean murphy_starts_dropping = FALSE;
9126 for (i = 0; i < MAX_PLAYERS; i++)
9127 if (stored_player[i].force_dropping)
9128 murphy_starts_dropping = TRUE;
9130 if (tape.single_step && tape.recording && !tape.pausing)
9131 if (murphy_is_waiting && !murphy_starts_dropping)
9132 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9134 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9137 void CheckSingleStepMode_MM(boolean element_clicked,
9138 boolean button_released)
9140 if (tape.single_step && tape.recording && !tape.pausing)
9141 if (button_released)
9142 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9144 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9147 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9148 int graphic, int sync_frame, int x, int y)
9150 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9152 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9155 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9157 return (IS_NEXT_FRAME(sync_frame, graphic));
9160 int getGraphicInfo_Delay(int graphic)
9162 return graphic_info[graphic].anim_delay;
9165 void PlayMenuSoundExt(int sound)
9167 if (sound == SND_UNDEFINED)
9170 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9171 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9174 if (IS_LOOP_SOUND(sound))
9175 PlaySoundLoop(sound);
9180 void PlayMenuSound(void)
9182 PlayMenuSoundExt(menu.sound[game_status]);
9185 void PlayMenuSoundStereo(int sound, int stereo_position)
9187 if (sound == SND_UNDEFINED)
9190 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9191 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9194 if (IS_LOOP_SOUND(sound))
9195 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9197 PlaySoundStereo(sound, stereo_position);
9200 void PlayMenuSoundIfLoopExt(int sound)
9202 if (sound == SND_UNDEFINED)
9205 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9206 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9209 if (IS_LOOP_SOUND(sound))
9210 PlaySoundLoop(sound);
9213 void PlayMenuSoundIfLoop(void)
9215 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9218 void PlayMenuMusicExt(int music)
9220 if (music == MUS_UNDEFINED)
9223 if (!setup.sound_music)
9226 if (IS_LOOP_MUSIC(music))
9227 PlayMusicLoop(music);
9232 void PlayMenuMusic(void)
9234 char *curr_music = getCurrentlyPlayingMusicFilename();
9235 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9237 if (!strEqual(curr_music, next_music))
9238 PlayMenuMusicExt(menu.music[game_status]);
9241 void PlayMenuSoundsAndMusic(void)
9247 static void FadeMenuSounds(void)
9252 static void FadeMenuMusic(void)
9254 char *curr_music = getCurrentlyPlayingMusicFilename();
9255 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9257 if (!strEqual(curr_music, next_music))
9261 void FadeMenuSoundsAndMusic(void)
9267 void PlaySoundActivating(void)
9270 PlaySound(SND_MENU_ITEM_ACTIVATING);
9274 void PlaySoundSelecting(void)
9277 PlaySound(SND_MENU_ITEM_SELECTING);
9281 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9283 boolean change_fullscreen = (setup.fullscreen !=
9284 video.fullscreen_enabled);
9285 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9286 setup.window_scaling_percent !=
9287 video.window_scaling_percent);
9289 if (change_window_scaling_percent && video.fullscreen_enabled)
9292 if (!change_window_scaling_percent && !video.fullscreen_available)
9295 if (change_window_scaling_percent)
9297 SDLSetWindowScaling(setup.window_scaling_percent);
9301 else if (change_fullscreen)
9303 SDLSetWindowFullscreen(setup.fullscreen);
9305 // set setup value according to successfully changed fullscreen mode
9306 setup.fullscreen = video.fullscreen_enabled;
9311 if (change_fullscreen ||
9312 change_window_scaling_percent)
9314 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9316 // save backbuffer content which gets lost when toggling fullscreen mode
9317 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9319 if (change_window_scaling_percent)
9321 // keep window mode, but change window scaling
9322 video.fullscreen_enabled = TRUE; // force new window scaling
9325 // toggle fullscreen
9326 ChangeVideoModeIfNeeded(setup.fullscreen);
9328 // set setup value according to successfully changed fullscreen mode
9329 setup.fullscreen = video.fullscreen_enabled;
9331 // restore backbuffer content from temporary backbuffer backup bitmap
9332 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9334 FreeBitmap(tmp_backbuffer);
9336 // update visible window/screen
9337 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9341 static void JoinRectangles(int *x, int *y, int *width, int *height,
9342 int x2, int y2, int width2, int height2)
9344 // do not join with "off-screen" rectangle
9345 if (x2 == -1 || y2 == -1)
9350 *width = MAX(*width, width2);
9351 *height = MAX(*height, height2);
9354 void SetAnimStatus(int anim_status_new)
9356 if (anim_status_new == GAME_MODE_MAIN)
9357 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9358 else if (anim_status_new == GAME_MODE_SCORES)
9359 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9361 global.anim_status_next = anim_status_new;
9363 // directly set screen modes that are entered without fading
9364 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9365 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9366 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9367 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9368 global.anim_status = global.anim_status_next;
9371 void SetGameStatus(int game_status_new)
9373 if (game_status_new != game_status)
9374 game_status_last_screen = game_status;
9376 game_status = game_status_new;
9378 SetAnimStatus(game_status_new);
9381 void SetFontStatus(int game_status_new)
9383 static int last_game_status = -1;
9385 if (game_status_new != -1)
9387 // set game status for font use after storing last game status
9388 last_game_status = game_status;
9389 game_status = game_status_new;
9393 // reset game status after font use from last stored game status
9394 game_status = last_game_status;
9398 void ResetFontStatus(void)
9403 void SetLevelSetInfo(char *identifier, int level_nr)
9405 setString(&levelset.identifier, identifier);
9407 levelset.level_nr = level_nr;
9410 boolean CheckIfAllViewportsHaveChanged(void)
9412 // if game status has not changed, viewports have not changed either
9413 if (game_status == game_status_last)
9416 // check if all viewports have changed with current game status
9418 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9419 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9420 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9421 int new_real_sx = vp_playfield->x;
9422 int new_real_sy = vp_playfield->y;
9423 int new_full_sxsize = vp_playfield->width;
9424 int new_full_sysize = vp_playfield->height;
9425 int new_dx = vp_door_1->x;
9426 int new_dy = vp_door_1->y;
9427 int new_dxsize = vp_door_1->width;
9428 int new_dysize = vp_door_1->height;
9429 int new_vx = vp_door_2->x;
9430 int new_vy = vp_door_2->y;
9431 int new_vxsize = vp_door_2->width;
9432 int new_vysize = vp_door_2->height;
9434 boolean playfield_viewport_has_changed =
9435 (new_real_sx != REAL_SX ||
9436 new_real_sy != REAL_SY ||
9437 new_full_sxsize != FULL_SXSIZE ||
9438 new_full_sysize != FULL_SYSIZE);
9440 boolean door_1_viewport_has_changed =
9443 new_dxsize != DXSIZE ||
9444 new_dysize != DYSIZE);
9446 boolean door_2_viewport_has_changed =
9449 new_vxsize != VXSIZE ||
9450 new_vysize != VYSIZE ||
9451 game_status_last == GAME_MODE_EDITOR);
9453 return (playfield_viewport_has_changed &&
9454 door_1_viewport_has_changed &&
9455 door_2_viewport_has_changed);
9458 boolean CheckFadeAll(void)
9460 return (CheckIfGlobalBorderHasChanged() ||
9461 CheckIfAllViewportsHaveChanged());
9464 void ChangeViewportPropertiesIfNeeded(void)
9466 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9467 FALSE : setup.small_game_graphics);
9468 int gfx_game_mode = game_status;
9469 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9471 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9472 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9473 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9474 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9475 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9476 int new_win_xsize = vp_window->width;
9477 int new_win_ysize = vp_window->height;
9478 int border_left = vp_playfield->border_left;
9479 int border_right = vp_playfield->border_right;
9480 int border_top = vp_playfield->border_top;
9481 int border_bottom = vp_playfield->border_bottom;
9482 int new_sx = vp_playfield->x + border_left;
9483 int new_sy = vp_playfield->y + border_top;
9484 int new_sxsize = vp_playfield->width - border_left - border_right;
9485 int new_sysize = vp_playfield->height - border_top - border_bottom;
9486 int new_real_sx = vp_playfield->x;
9487 int new_real_sy = vp_playfield->y;
9488 int new_full_sxsize = vp_playfield->width;
9489 int new_full_sysize = vp_playfield->height;
9490 int new_dx = vp_door_1->x;
9491 int new_dy = vp_door_1->y;
9492 int new_dxsize = vp_door_1->width;
9493 int new_dysize = vp_door_1->height;
9494 int new_vx = vp_door_2->x;
9495 int new_vy = vp_door_2->y;
9496 int new_vxsize = vp_door_2->width;
9497 int new_vysize = vp_door_2->height;
9498 int new_ex = vp_door_3->x;
9499 int new_ey = vp_door_3->y;
9500 int new_exsize = vp_door_3->width;
9501 int new_eysize = vp_door_3->height;
9502 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9503 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9504 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9505 int new_scr_fieldx = new_sxsize / tilesize;
9506 int new_scr_fieldy = new_sysize / tilesize;
9507 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9508 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9509 boolean init_gfx_buffers = FALSE;
9510 boolean init_video_buffer = FALSE;
9511 boolean init_gadgets_and_anims = FALSE;
9512 boolean init_em_graphics = FALSE;
9514 if (new_win_xsize != WIN_XSIZE ||
9515 new_win_ysize != WIN_YSIZE)
9517 WIN_XSIZE = new_win_xsize;
9518 WIN_YSIZE = new_win_ysize;
9520 init_video_buffer = TRUE;
9521 init_gfx_buffers = TRUE;
9522 init_gadgets_and_anims = TRUE;
9524 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9527 if (new_scr_fieldx != SCR_FIELDX ||
9528 new_scr_fieldy != SCR_FIELDY)
9530 // this always toggles between MAIN and GAME when using small tile size
9532 SCR_FIELDX = new_scr_fieldx;
9533 SCR_FIELDY = new_scr_fieldy;
9535 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9546 new_sxsize != SXSIZE ||
9547 new_sysize != SYSIZE ||
9548 new_dxsize != DXSIZE ||
9549 new_dysize != DYSIZE ||
9550 new_vxsize != VXSIZE ||
9551 new_vysize != VYSIZE ||
9552 new_exsize != EXSIZE ||
9553 new_eysize != EYSIZE ||
9554 new_real_sx != REAL_SX ||
9555 new_real_sy != REAL_SY ||
9556 new_full_sxsize != FULL_SXSIZE ||
9557 new_full_sysize != FULL_SYSIZE ||
9558 new_tilesize_var != TILESIZE_VAR
9561 // ------------------------------------------------------------------------
9562 // determine next fading area for changed viewport definitions
9563 // ------------------------------------------------------------------------
9565 // start with current playfield area (default fading area)
9568 FADE_SXSIZE = FULL_SXSIZE;
9569 FADE_SYSIZE = FULL_SYSIZE;
9571 // add new playfield area if position or size has changed
9572 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9573 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9575 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9576 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9579 // add current and new door 1 area if position or size has changed
9580 if (new_dx != DX || new_dy != DY ||
9581 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9583 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9584 DX, DY, DXSIZE, DYSIZE);
9585 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9586 new_dx, new_dy, new_dxsize, new_dysize);
9589 // add current and new door 2 area if position or size has changed
9590 if (new_vx != VX || new_vy != VY ||
9591 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9593 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9594 VX, VY, VXSIZE, VYSIZE);
9595 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9596 new_vx, new_vy, new_vxsize, new_vysize);
9599 // ------------------------------------------------------------------------
9600 // handle changed tile size
9601 // ------------------------------------------------------------------------
9603 if (new_tilesize_var != TILESIZE_VAR)
9605 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9607 // changing tile size invalidates scroll values of engine snapshots
9608 FreeEngineSnapshotSingle();
9610 // changing tile size requires update of graphic mapping for EM engine
9611 init_em_graphics = TRUE;
9622 SXSIZE = new_sxsize;
9623 SYSIZE = new_sysize;
9624 DXSIZE = new_dxsize;
9625 DYSIZE = new_dysize;
9626 VXSIZE = new_vxsize;
9627 VYSIZE = new_vysize;
9628 EXSIZE = new_exsize;
9629 EYSIZE = new_eysize;
9630 REAL_SX = new_real_sx;
9631 REAL_SY = new_real_sy;
9632 FULL_SXSIZE = new_full_sxsize;
9633 FULL_SYSIZE = new_full_sysize;
9634 TILESIZE_VAR = new_tilesize_var;
9636 init_gfx_buffers = TRUE;
9637 init_gadgets_and_anims = TRUE;
9639 // printf("::: viewports: init_gfx_buffers\n");
9640 // printf("::: viewports: init_gadgets_and_anims\n");
9643 if (init_gfx_buffers)
9645 // printf("::: init_gfx_buffers\n");
9647 SCR_FIELDX = new_scr_fieldx_buffers;
9648 SCR_FIELDY = new_scr_fieldy_buffers;
9652 SCR_FIELDX = new_scr_fieldx;
9653 SCR_FIELDY = new_scr_fieldy;
9655 SetDrawDeactivationMask(REDRAW_NONE);
9656 SetDrawBackgroundMask(REDRAW_FIELD);
9659 if (init_video_buffer)
9661 // printf("::: init_video_buffer\n");
9663 FreeAllImageTextures(); // needs old renderer to free the textures
9665 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9666 InitImageTextures();
9669 if (init_gadgets_and_anims)
9671 // printf("::: init_gadgets_and_anims\n");
9674 InitGlobalAnimations();
9677 if (init_em_graphics)
9679 InitGraphicInfo_EM();