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 // do not draw preview players if level preview redefined, but players aren't
3527 if (preview.redefined && !menu.main.preview_players.redefined)
3530 boolean player_found[MAX_PLAYERS];
3531 int num_players = 0;
3534 for (i = 0; i < MAX_PLAYERS; i++)
3535 player_found[i] = FALSE;
3537 // check which players can be found in the level (simple approach)
3538 for (x = 0; x < lev_fieldx; x++)
3540 for (y = 0; y < lev_fieldy; y++)
3542 int element = level.field[x][y];
3544 if (ELEM_IS_PLAYER(element))
3546 int player_nr = GET_PLAYER_NR(element);
3548 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3550 if (!player_found[player_nr])
3553 player_found[player_nr] = TRUE;
3558 struct TextPosInfo *pos = &menu.main.preview_players;
3559 int tile_size = pos->tile_size;
3560 int border_size = pos->border_size;
3561 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3562 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3563 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3564 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3565 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3566 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3567 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3568 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3569 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3570 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3571 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3572 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3574 // clear area in which the players will be drawn
3575 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3576 max_players_width, max_players_height);
3578 if (!network.enabled && !setup.team_mode)
3581 // only draw players if level is suited for team mode
3582 if (num_players < 2)
3585 // draw all players that were found in the level
3586 for (i = 0; i < MAX_PLAYERS; i++)
3588 if (player_found[i])
3590 int graphic = el2img(EL_PLAYER_1 + i);
3592 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3594 xpos += player_xoffset;
3595 ypos += player_yoffset;
3600 void DrawPreviewLevelInitial(void)
3602 DrawPreviewLevelExt(TRUE);
3603 DrawPreviewPlayers();
3606 void DrawPreviewLevelAnimation(void)
3608 DrawPreviewLevelExt(FALSE);
3611 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3612 int border_size, int font_nr)
3614 int graphic = el2img(EL_PLAYER_1 + player_nr);
3615 int font_height = getFontHeight(font_nr);
3616 int player_height = MAX(tile_size, font_height);
3617 int xoffset_text = tile_size + border_size;
3618 int yoffset_text = (player_height - font_height) / 2;
3619 int yoffset_graphic = (player_height - tile_size) / 2;
3620 char *player_name = getNetworkPlayerName(player_nr + 1);
3622 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3624 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3627 static void DrawNetworkPlayersExt(boolean force)
3629 if (game_status != GAME_MODE_MAIN)
3632 if (!network.connected && !force)
3635 // do not draw network players if level preview redefined, but players aren't
3636 if (preview.redefined && !menu.main.network_players.redefined)
3639 int num_players = 0;
3642 for (i = 0; i < MAX_PLAYERS; i++)
3643 if (stored_player[i].connected_network)
3646 struct TextPosInfo *pos = &menu.main.network_players;
3647 int tile_size = pos->tile_size;
3648 int border_size = pos->border_size;
3649 int xoffset_text = tile_size + border_size;
3650 int font_nr = pos->font;
3651 int font_width = getFontWidth(font_nr);
3652 int font_height = getFontHeight(font_nr);
3653 int player_height = MAX(tile_size, font_height);
3654 int player_yoffset = player_height + border_size;
3655 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3656 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3657 int all_players_height = num_players * player_yoffset - border_size;
3658 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3659 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3660 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3662 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3663 max_players_width, max_players_height);
3665 // first draw local network player ...
3666 for (i = 0; i < MAX_PLAYERS; i++)
3668 if (stored_player[i].connected_network &&
3669 stored_player[i].connected_locally)
3671 char *player_name = getNetworkPlayerName(i + 1);
3672 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3673 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3675 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3677 ypos += player_yoffset;
3681 // ... then draw all other network players
3682 for (i = 0; i < MAX_PLAYERS; i++)
3684 if (stored_player[i].connected_network &&
3685 !stored_player[i].connected_locally)
3687 char *player_name = getNetworkPlayerName(i + 1);
3688 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3689 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3691 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3693 ypos += player_yoffset;
3698 void DrawNetworkPlayers(void)
3700 DrawNetworkPlayersExt(FALSE);
3703 void ClearNetworkPlayers(void)
3705 DrawNetworkPlayersExt(TRUE);
3708 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3709 int graphic, int sync_frame,
3712 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3714 if (mask_mode == USE_MASKING)
3715 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3717 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3720 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3721 int graphic, int sync_frame, int mask_mode)
3723 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3725 if (mask_mode == USE_MASKING)
3726 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3728 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3731 static void DrawGraphicAnimation(int x, int y, int graphic)
3733 int lx = LEVELX(x), ly = LEVELY(y);
3735 if (!IN_SCR_FIELD(x, y))
3738 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3739 graphic, GfxFrame[lx][ly], NO_MASKING);
3741 MarkTileDirty(x, y);
3744 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3746 int lx = LEVELX(x), ly = LEVELY(y);
3748 if (!IN_SCR_FIELD(x, y))
3751 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3752 graphic, GfxFrame[lx][ly], NO_MASKING);
3753 MarkTileDirty(x, y);
3756 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3758 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3761 void DrawLevelElementAnimation(int x, int y, int element)
3763 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3765 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3768 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3770 int sx = SCREENX(x), sy = SCREENY(y);
3772 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3775 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3778 DrawGraphicAnimation(sx, sy, graphic);
3781 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3782 DrawLevelFieldCrumbled(x, y);
3784 if (GFX_CRUMBLED(Feld[x][y]))
3785 DrawLevelFieldCrumbled(x, y);
3789 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3791 int sx = SCREENX(x), sy = SCREENY(y);
3794 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3797 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3799 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3802 DrawGraphicAnimation(sx, sy, graphic);
3804 if (GFX_CRUMBLED(element))
3805 DrawLevelFieldCrumbled(x, y);
3808 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3810 if (player->use_murphy)
3812 // this works only because currently only one player can be "murphy" ...
3813 static int last_horizontal_dir = MV_LEFT;
3814 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3816 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3817 last_horizontal_dir = move_dir;
3819 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3821 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3823 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3829 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3832 static boolean equalGraphics(int graphic1, int graphic2)
3834 struct GraphicInfo *g1 = &graphic_info[graphic1];
3835 struct GraphicInfo *g2 = &graphic_info[graphic2];
3837 return (g1->bitmap == g2->bitmap &&
3838 g1->src_x == g2->src_x &&
3839 g1->src_y == g2->src_y &&
3840 g1->anim_frames == g2->anim_frames &&
3841 g1->anim_delay == g2->anim_delay &&
3842 g1->anim_mode == g2->anim_mode);
3845 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3849 DRAW_PLAYER_STAGE_INIT = 0,
3850 DRAW_PLAYER_STAGE_LAST_FIELD,
3851 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3852 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3853 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3854 DRAW_PLAYER_STAGE_PLAYER,
3856 DRAW_PLAYER_STAGE_PLAYER,
3857 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3859 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3860 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3862 NUM_DRAW_PLAYER_STAGES
3865 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3867 static int static_last_player_graphic[MAX_PLAYERS];
3868 static int static_last_player_frame[MAX_PLAYERS];
3869 static boolean static_player_is_opaque[MAX_PLAYERS];
3870 static boolean draw_player[MAX_PLAYERS];
3871 int pnr = player->index_nr;
3873 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3875 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3876 static_last_player_frame[pnr] = player->Frame;
3877 static_player_is_opaque[pnr] = FALSE;
3879 draw_player[pnr] = TRUE;
3882 if (!draw_player[pnr])
3886 if (!IN_LEV_FIELD(player->jx, player->jy))
3888 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3889 printf("DrawPlayerField(): This should never happen!\n");
3891 draw_player[pnr] = FALSE;
3897 int last_player_graphic = static_last_player_graphic[pnr];
3898 int last_player_frame = static_last_player_frame[pnr];
3899 boolean player_is_opaque = static_player_is_opaque[pnr];
3901 int jx = player->jx;
3902 int jy = player->jy;
3903 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3904 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3905 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3906 int last_jx = (player->is_moving ? jx - dx : jx);
3907 int last_jy = (player->is_moving ? jy - dy : jy);
3908 int next_jx = jx + dx;
3909 int next_jy = jy + dy;
3910 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3911 int sx = SCREENX(jx);
3912 int sy = SCREENY(jy);
3913 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3914 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3915 int element = Feld[jx][jy];
3916 int last_element = Feld[last_jx][last_jy];
3917 int action = (player->is_pushing ? ACTION_PUSHING :
3918 player->is_digging ? ACTION_DIGGING :
3919 player->is_collecting ? ACTION_COLLECTING :
3920 player->is_moving ? ACTION_MOVING :
3921 player->is_snapping ? ACTION_SNAPPING :
3922 player->is_dropping ? ACTION_DROPPING :
3923 player->is_waiting ? player->action_waiting :
3926 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3928 // ------------------------------------------------------------------------
3929 // initialize drawing the player
3930 // ------------------------------------------------------------------------
3932 draw_player[pnr] = FALSE;
3934 // GfxElement[][] is set to the element the player is digging or collecting;
3935 // remove also for off-screen player if the player is not moving anymore
3936 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3937 GfxElement[jx][jy] = EL_UNDEFINED;
3939 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3942 if (element == EL_EXPLOSION)
3945 InitPlayerGfxAnimation(player, action, move_dir);
3947 draw_player[pnr] = TRUE;
3949 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3951 // ------------------------------------------------------------------------
3952 // draw things in the field the player is leaving, if needed
3953 // ------------------------------------------------------------------------
3955 if (!IN_SCR_FIELD(sx, sy))
3956 draw_player[pnr] = FALSE;
3958 if (!player->is_moving)
3961 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3963 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3965 if (last_element == EL_DYNAMITE_ACTIVE ||
3966 last_element == EL_EM_DYNAMITE_ACTIVE ||
3967 last_element == EL_SP_DISK_RED_ACTIVE)
3968 DrawDynamite(last_jx, last_jy);
3970 DrawLevelFieldThruMask(last_jx, last_jy);
3972 else if (last_element == EL_DYNAMITE_ACTIVE ||
3973 last_element == EL_EM_DYNAMITE_ACTIVE ||
3974 last_element == EL_SP_DISK_RED_ACTIVE)
3975 DrawDynamite(last_jx, last_jy);
3977 DrawLevelField(last_jx, last_jy);
3979 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3980 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3982 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3984 // ------------------------------------------------------------------------
3985 // draw things behind the player, if needed
3986 // ------------------------------------------------------------------------
3990 DrawLevelElement(jx, jy, Back[jx][jy]);
3995 if (IS_ACTIVE_BOMB(element))
3997 DrawLevelElement(jx, jy, EL_EMPTY);
4002 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
4004 int old_element = GfxElement[jx][jy];
4005 int old_graphic = el_act_dir2img(old_element, action, move_dir);
4006 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4008 if (GFX_CRUMBLED(old_element))
4009 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4011 DrawGraphic(sx, sy, old_graphic, frame);
4013 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4014 static_player_is_opaque[pnr] = TRUE;
4018 GfxElement[jx][jy] = EL_UNDEFINED;
4020 // make sure that pushed elements are drawn with correct frame rate
4021 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4023 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4024 GfxFrame[jx][jy] = player->StepFrame;
4026 DrawLevelField(jx, jy);
4029 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4031 // ------------------------------------------------------------------------
4032 // draw things the player is pushing, if needed
4033 // ------------------------------------------------------------------------
4035 if (!player->is_pushing || !player->is_moving)
4038 int gfx_frame = GfxFrame[jx][jy];
4040 if (!IS_MOVING(jx, jy)) // push movement already finished
4042 element = Feld[next_jx][next_jy];
4043 gfx_frame = GfxFrame[next_jx][next_jy];
4046 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4047 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4048 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4050 // draw background element under pushed element (like the Sokoban field)
4051 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4053 // this allows transparent pushing animation over non-black background
4056 DrawLevelElement(jx, jy, Back[jx][jy]);
4058 DrawLevelElement(jx, jy, EL_EMPTY);
4060 if (Back[next_jx][next_jy])
4061 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4063 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4065 else if (Back[next_jx][next_jy])
4066 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4068 int px = SCREENX(jx), py = SCREENY(jy);
4069 int pxx = (TILEX - ABS(sxx)) * dx;
4070 int pyy = (TILEY - ABS(syy)) * dy;
4073 // do not draw (EM style) pushing animation when pushing is finished
4074 // (two-tile animations usually do not contain start and end frame)
4075 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4076 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4078 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4080 // masked drawing is needed for EMC style (double) movement graphics
4081 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4082 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4085 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4087 // ------------------------------------------------------------------------
4088 // draw player himself
4089 // ------------------------------------------------------------------------
4091 int graphic = getPlayerGraphic(player, move_dir);
4093 // in the case of changed player action or direction, prevent the current
4094 // animation frame from being restarted for identical animations
4095 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4096 player->Frame = last_player_frame;
4098 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4100 if (player_is_opaque)
4101 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4103 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4105 if (SHIELD_ON(player))
4107 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4108 IMG_SHIELD_NORMAL_ACTIVE);
4109 frame = getGraphicAnimationFrame(graphic, -1);
4111 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4114 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4116 // ------------------------------------------------------------------------
4117 // draw things in front of player (active dynamite or dynabombs)
4118 // ------------------------------------------------------------------------
4120 if (IS_ACTIVE_BOMB(element))
4122 int graphic = el2img(element);
4123 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4125 if (game.emulation == EMU_SUPAPLEX)
4126 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4128 DrawGraphicThruMask(sx, sy, graphic, frame);
4131 if (player_is_moving && last_element == EL_EXPLOSION)
4133 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4134 GfxElement[last_jx][last_jy] : EL_EMPTY);
4135 int graphic = el_act2img(element, ACTION_EXPLODING);
4136 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4137 int phase = ExplodePhase[last_jx][last_jy] - 1;
4138 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4141 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4144 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4146 // ------------------------------------------------------------------------
4147 // draw elements the player is just walking/passing through/under
4148 // ------------------------------------------------------------------------
4150 if (player_is_moving)
4152 // handle the field the player is leaving ...
4153 if (IS_ACCESSIBLE_INSIDE(last_element))
4154 DrawLevelField(last_jx, last_jy);
4155 else if (IS_ACCESSIBLE_UNDER(last_element))
4156 DrawLevelFieldThruMask(last_jx, last_jy);
4159 // do not redraw accessible elements if the player is just pushing them
4160 if (!player_is_moving || !player->is_pushing)
4162 // ... and the field the player is entering
4163 if (IS_ACCESSIBLE_INSIDE(element))
4164 DrawLevelField(jx, jy);
4165 else if (IS_ACCESSIBLE_UNDER(element))
4166 DrawLevelFieldThruMask(jx, jy);
4169 MarkTileDirty(sx, sy);
4173 void DrawPlayer(struct PlayerInfo *player)
4177 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4178 DrawPlayerExt(player, i);
4181 void DrawAllPlayers(void)
4185 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4186 for (j = 0; j < MAX_PLAYERS; j++)
4187 if (stored_player[j].active)
4188 DrawPlayerExt(&stored_player[j], i);
4191 void DrawPlayerField(int x, int y)
4193 if (!IS_PLAYER(x, y))
4196 DrawPlayer(PLAYERINFO(x, y));
4199 // ----------------------------------------------------------------------------
4201 void WaitForEventToContinue(void)
4203 boolean still_wait = TRUE;
4205 if (program.headless)
4208 // simulate releasing mouse button over last gadget, if still pressed
4210 HandleGadgets(-1, -1, 0);
4212 button_status = MB_RELEASED;
4220 if (NextValidEvent(&event))
4224 case EVENT_BUTTONRELEASE:
4225 case EVENT_KEYPRESS:
4226 case SDL_CONTROLLERBUTTONDOWN:
4227 case SDL_JOYBUTTONDOWN:
4231 case EVENT_KEYRELEASE:
4232 ClearPlayerAction();
4236 HandleOtherEvents(&event);
4240 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4249 #define MAX_REQUEST_LINES 13
4250 #define MAX_REQUEST_LINE_FONT1_LEN 7
4251 #define MAX_REQUEST_LINE_FONT2_LEN 10
4253 static int RequestHandleEvents(unsigned int req_state)
4255 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4257 int width = request.width;
4258 int height = request.height;
4262 // when showing request dialog after game ended, deactivate game panel
4263 if (game_just_ended)
4264 game.panel.active = FALSE;
4266 game.request_active = TRUE;
4268 setRequestPosition(&sx, &sy, FALSE);
4270 button_status = MB_RELEASED;
4272 request_gadget_id = -1;
4277 if (game_just_ended)
4279 // the MM game engine does not use a special (scrollable) field buffer
4280 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4281 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4283 HandleGameActions();
4285 SetDrawtoField(DRAW_TO_BACKBUFFER);
4287 if (global.use_envelope_request)
4289 // copy current state of request area to middle of playfield area
4290 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4298 while (NextValidEvent(&event))
4302 case EVENT_BUTTONPRESS:
4303 case EVENT_BUTTONRELEASE:
4304 case EVENT_MOTIONNOTIFY:
4308 if (event.type == EVENT_MOTIONNOTIFY)
4313 motion_status = TRUE;
4314 mx = ((MotionEvent *) &event)->x;
4315 my = ((MotionEvent *) &event)->y;
4319 motion_status = FALSE;
4320 mx = ((ButtonEvent *) &event)->x;
4321 my = ((ButtonEvent *) &event)->y;
4322 if (event.type == EVENT_BUTTONPRESS)
4323 button_status = ((ButtonEvent *) &event)->button;
4325 button_status = MB_RELEASED;
4328 // this sets 'request_gadget_id'
4329 HandleGadgets(mx, my, button_status);
4331 switch (request_gadget_id)
4333 case TOOL_CTRL_ID_YES:
4336 case TOOL_CTRL_ID_NO:
4339 case TOOL_CTRL_ID_CONFIRM:
4340 result = TRUE | FALSE;
4343 case TOOL_CTRL_ID_PLAYER_1:
4346 case TOOL_CTRL_ID_PLAYER_2:
4349 case TOOL_CTRL_ID_PLAYER_3:
4352 case TOOL_CTRL_ID_PLAYER_4:
4363 case SDL_WINDOWEVENT:
4364 HandleWindowEvent((WindowEvent *) &event);
4367 case SDL_APP_WILLENTERBACKGROUND:
4368 case SDL_APP_DIDENTERBACKGROUND:
4369 case SDL_APP_WILLENTERFOREGROUND:
4370 case SDL_APP_DIDENTERFOREGROUND:
4371 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4374 case EVENT_KEYPRESS:
4376 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4381 if (req_state & REQ_CONFIRM)
4390 #if defined(KSYM_Rewind)
4391 case KSYM_Rewind: // for Amazon Fire TV remote
4400 #if defined(KSYM_FastForward)
4401 case KSYM_FastForward: // for Amazon Fire TV remote
4407 HandleKeysDebug(key, KEY_PRESSED);
4411 if (req_state & REQ_PLAYER)
4413 int old_player_nr = setup.network_player_nr;
4416 result = old_player_nr + 1;
4421 result = old_player_nr + 1;
4452 case EVENT_KEYRELEASE:
4453 ClearPlayerAction();
4456 case SDL_CONTROLLERBUTTONDOWN:
4457 switch (event.cbutton.button)
4459 case SDL_CONTROLLER_BUTTON_A:
4460 case SDL_CONTROLLER_BUTTON_X:
4461 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4462 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4466 case SDL_CONTROLLER_BUTTON_B:
4467 case SDL_CONTROLLER_BUTTON_Y:
4468 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4469 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4470 case SDL_CONTROLLER_BUTTON_BACK:
4475 if (req_state & REQ_PLAYER)
4477 int old_player_nr = setup.network_player_nr;
4480 result = old_player_nr + 1;
4482 switch (event.cbutton.button)
4484 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4485 case SDL_CONTROLLER_BUTTON_Y:
4489 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4490 case SDL_CONTROLLER_BUTTON_B:
4494 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4495 case SDL_CONTROLLER_BUTTON_A:
4499 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4500 case SDL_CONTROLLER_BUTTON_X:
4511 case SDL_CONTROLLERBUTTONUP:
4512 HandleJoystickEvent(&event);
4513 ClearPlayerAction();
4517 HandleOtherEvents(&event);
4522 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4524 int joy = AnyJoystick();
4526 if (joy & JOY_BUTTON_1)
4528 else if (joy & JOY_BUTTON_2)
4531 else if (AnyJoystick())
4533 int joy = AnyJoystick();
4535 if (req_state & REQ_PLAYER)
4539 else if (joy & JOY_RIGHT)
4541 else if (joy & JOY_DOWN)
4543 else if (joy & JOY_LEFT)
4548 if (game_just_ended)
4550 if (global.use_envelope_request)
4552 // copy back current state of pressed buttons inside request area
4553 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4560 game.request_active = FALSE;
4565 static boolean RequestDoor(char *text, unsigned int req_state)
4567 unsigned int old_door_state;
4568 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4569 int font_nr = FONT_TEXT_2;
4574 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4576 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4577 font_nr = FONT_TEXT_1;
4580 if (game_status == GAME_MODE_PLAYING)
4581 BlitScreenToBitmap(backbuffer);
4583 // disable deactivated drawing when quick-loading level tape recording
4584 if (tape.playing && tape.deactivate_display)
4585 TapeDeactivateDisplayOff(TRUE);
4587 SetMouseCursor(CURSOR_DEFAULT);
4589 // pause network game while waiting for request to answer
4590 if (network.enabled &&
4591 game_status == GAME_MODE_PLAYING &&
4592 !game.all_players_gone &&
4593 req_state & REQUEST_WAIT_FOR_INPUT)
4594 SendToServer_PausePlaying();
4596 old_door_state = GetDoorState();
4598 // simulate releasing mouse button over last gadget, if still pressed
4600 HandleGadgets(-1, -1, 0);
4604 // draw released gadget before proceeding
4607 if (old_door_state & DOOR_OPEN_1)
4609 CloseDoor(DOOR_CLOSE_1);
4611 // save old door content
4612 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4613 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4616 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4617 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4619 // clear door drawing field
4620 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4622 // force DOOR font inside door area
4623 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4625 // write text for request
4626 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4628 char text_line[max_request_line_len + 1];
4634 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4636 tc = *(text_ptr + tx);
4637 // if (!tc || tc == ' ')
4638 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4642 if ((tc == '?' || tc == '!') && tl == 0)
4652 strncpy(text_line, text_ptr, tl);
4655 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4656 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4657 text_line, font_nr);
4659 text_ptr += tl + (tc == ' ' ? 1 : 0);
4660 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4665 if (req_state & REQ_ASK)
4667 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4668 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4670 else if (req_state & REQ_CONFIRM)
4672 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4674 else if (req_state & REQ_PLAYER)
4676 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4677 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4678 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4679 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4682 // copy request gadgets to door backbuffer
4683 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4685 OpenDoor(DOOR_OPEN_1);
4687 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4689 if (game_status == GAME_MODE_PLAYING)
4691 SetPanelBackground();
4692 SetDrawBackgroundMask(REDRAW_DOOR_1);
4696 SetDrawBackgroundMask(REDRAW_FIELD);
4702 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4704 // ---------- handle request buttons ----------
4705 result = RequestHandleEvents(req_state);
4709 if (!(req_state & REQ_STAY_OPEN))
4711 CloseDoor(DOOR_CLOSE_1);
4713 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4714 (req_state & REQ_REOPEN))
4715 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4720 if (game_status == GAME_MODE_PLAYING)
4722 SetPanelBackground();
4723 SetDrawBackgroundMask(REDRAW_DOOR_1);
4727 SetDrawBackgroundMask(REDRAW_FIELD);
4730 // continue network game after request
4731 if (network.enabled &&
4732 game_status == GAME_MODE_PLAYING &&
4733 !game.all_players_gone &&
4734 req_state & REQUEST_WAIT_FOR_INPUT)
4735 SendToServer_ContinuePlaying();
4737 // restore deactivated drawing when quick-loading level tape recording
4738 if (tape.playing && tape.deactivate_display)
4739 TapeDeactivateDisplayOn();
4744 static boolean RequestEnvelope(char *text, unsigned int req_state)
4748 if (game_status == GAME_MODE_PLAYING)
4749 BlitScreenToBitmap(backbuffer);
4751 // disable deactivated drawing when quick-loading level tape recording
4752 if (tape.playing && tape.deactivate_display)
4753 TapeDeactivateDisplayOff(TRUE);
4755 SetMouseCursor(CURSOR_DEFAULT);
4757 // pause network game while waiting for request to answer
4758 if (network.enabled &&
4759 game_status == GAME_MODE_PLAYING &&
4760 !game.all_players_gone &&
4761 req_state & REQUEST_WAIT_FOR_INPUT)
4762 SendToServer_PausePlaying();
4764 // simulate releasing mouse button over last gadget, if still pressed
4766 HandleGadgets(-1, -1, 0);
4770 // (replace with setting corresponding request background)
4771 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4772 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4774 // clear door drawing field
4775 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4777 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4779 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4781 if (game_status == GAME_MODE_PLAYING)
4783 SetPanelBackground();
4784 SetDrawBackgroundMask(REDRAW_DOOR_1);
4788 SetDrawBackgroundMask(REDRAW_FIELD);
4794 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4796 // ---------- handle request buttons ----------
4797 result = RequestHandleEvents(req_state);
4801 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4805 if (game_status == GAME_MODE_PLAYING)
4807 SetPanelBackground();
4808 SetDrawBackgroundMask(REDRAW_DOOR_1);
4812 SetDrawBackgroundMask(REDRAW_FIELD);
4815 // continue network game after request
4816 if (network.enabled &&
4817 game_status == GAME_MODE_PLAYING &&
4818 !game.all_players_gone &&
4819 req_state & REQUEST_WAIT_FOR_INPUT)
4820 SendToServer_ContinuePlaying();
4822 // restore deactivated drawing when quick-loading level tape recording
4823 if (tape.playing && tape.deactivate_display)
4824 TapeDeactivateDisplayOn();
4829 boolean Request(char *text, unsigned int req_state)
4831 boolean overlay_enabled = GetOverlayEnabled();
4834 SetOverlayEnabled(FALSE);
4836 if (global.use_envelope_request)
4837 result = RequestEnvelope(text, req_state);
4839 result = RequestDoor(text, req_state);
4841 SetOverlayEnabled(overlay_enabled);
4846 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4848 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4849 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4852 if (dpo1->sort_priority != dpo2->sort_priority)
4853 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4855 compare_result = dpo1->nr - dpo2->nr;
4857 return compare_result;
4860 void InitGraphicCompatibilityInfo_Doors(void)
4866 struct DoorInfo *door;
4870 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4871 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4873 { -1, -1, -1, NULL }
4875 struct Rect door_rect_list[] =
4877 { DX, DY, DXSIZE, DYSIZE },
4878 { VX, VY, VXSIZE, VYSIZE }
4882 for (i = 0; doors[i].door_token != -1; i++)
4884 int door_token = doors[i].door_token;
4885 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4886 int part_1 = doors[i].part_1;
4887 int part_8 = doors[i].part_8;
4888 int part_2 = part_1 + 1;
4889 int part_3 = part_1 + 2;
4890 struct DoorInfo *door = doors[i].door;
4891 struct Rect *door_rect = &door_rect_list[door_index];
4892 boolean door_gfx_redefined = FALSE;
4894 // check if any door part graphic definitions have been redefined
4896 for (j = 0; door_part_controls[j].door_token != -1; j++)
4898 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4899 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4901 if (dpc->door_token == door_token && fi->redefined)
4902 door_gfx_redefined = TRUE;
4905 // check for old-style door graphic/animation modifications
4907 if (!door_gfx_redefined)
4909 if (door->anim_mode & ANIM_STATIC_PANEL)
4911 door->panel.step_xoffset = 0;
4912 door->panel.step_yoffset = 0;
4915 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4917 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4918 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4919 int num_door_steps, num_panel_steps;
4921 // remove door part graphics other than the two default wings
4923 for (j = 0; door_part_controls[j].door_token != -1; j++)
4925 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4926 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4928 if (dpc->graphic >= part_3 &&
4929 dpc->graphic <= part_8)
4933 // set graphics and screen positions of the default wings
4935 g_part_1->width = door_rect->width;
4936 g_part_1->height = door_rect->height;
4937 g_part_2->width = door_rect->width;
4938 g_part_2->height = door_rect->height;
4939 g_part_2->src_x = door_rect->width;
4940 g_part_2->src_y = g_part_1->src_y;
4942 door->part_2.x = door->part_1.x;
4943 door->part_2.y = door->part_1.y;
4945 if (door->width != -1)
4947 g_part_1->width = door->width;
4948 g_part_2->width = door->width;
4950 // special treatment for graphics and screen position of right wing
4951 g_part_2->src_x += door_rect->width - door->width;
4952 door->part_2.x += door_rect->width - door->width;
4955 if (door->height != -1)
4957 g_part_1->height = door->height;
4958 g_part_2->height = door->height;
4960 // special treatment for graphics and screen position of bottom wing
4961 g_part_2->src_y += door_rect->height - door->height;
4962 door->part_2.y += door_rect->height - door->height;
4965 // set animation delays for the default wings and panels
4967 door->part_1.step_delay = door->step_delay;
4968 door->part_2.step_delay = door->step_delay;
4969 door->panel.step_delay = door->step_delay;
4971 // set animation draw order for the default wings
4973 door->part_1.sort_priority = 2; // draw left wing over ...
4974 door->part_2.sort_priority = 1; // ... right wing
4976 // set animation draw offset for the default wings
4978 if (door->anim_mode & ANIM_HORIZONTAL)
4980 door->part_1.step_xoffset = door->step_offset;
4981 door->part_1.step_yoffset = 0;
4982 door->part_2.step_xoffset = door->step_offset * -1;
4983 door->part_2.step_yoffset = 0;
4985 num_door_steps = g_part_1->width / door->step_offset;
4987 else // ANIM_VERTICAL
4989 door->part_1.step_xoffset = 0;
4990 door->part_1.step_yoffset = door->step_offset;
4991 door->part_2.step_xoffset = 0;
4992 door->part_2.step_yoffset = door->step_offset * -1;
4994 num_door_steps = g_part_1->height / door->step_offset;
4997 // set animation draw offset for the default panels
4999 if (door->step_offset > 1)
5001 num_panel_steps = 2 * door_rect->height / door->step_offset;
5002 door->panel.start_step = num_panel_steps - num_door_steps;
5003 door->panel.start_step_closing = door->panel.start_step;
5007 num_panel_steps = door_rect->height / door->step_offset;
5008 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5009 door->panel.start_step_closing = door->panel.start_step;
5010 door->panel.step_delay *= 2;
5017 void InitDoors(void)
5021 for (i = 0; door_part_controls[i].door_token != -1; i++)
5023 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5024 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5026 // initialize "start_step_opening" and "start_step_closing", if needed
5027 if (dpc->pos->start_step_opening == 0 &&
5028 dpc->pos->start_step_closing == 0)
5030 // dpc->pos->start_step_opening = dpc->pos->start_step;
5031 dpc->pos->start_step_closing = dpc->pos->start_step;
5034 // fill structure for door part draw order (sorted below)
5036 dpo->sort_priority = dpc->pos->sort_priority;
5039 // sort door part controls according to sort_priority and graphic number
5040 qsort(door_part_order, MAX_DOOR_PARTS,
5041 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5044 unsigned int OpenDoor(unsigned int door_state)
5046 if (door_state & DOOR_COPY_BACK)
5048 if (door_state & DOOR_OPEN_1)
5049 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5050 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5052 if (door_state & DOOR_OPEN_2)
5053 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5054 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5056 door_state &= ~DOOR_COPY_BACK;
5059 return MoveDoor(door_state);
5062 unsigned int CloseDoor(unsigned int door_state)
5064 unsigned int old_door_state = GetDoorState();
5066 if (!(door_state & DOOR_NO_COPY_BACK))
5068 if (old_door_state & DOOR_OPEN_1)
5069 BlitBitmap(backbuffer, bitmap_db_door_1,
5070 DX, DY, DXSIZE, DYSIZE, 0, 0);
5072 if (old_door_state & DOOR_OPEN_2)
5073 BlitBitmap(backbuffer, bitmap_db_door_2,
5074 VX, VY, VXSIZE, VYSIZE, 0, 0);
5076 door_state &= ~DOOR_NO_COPY_BACK;
5079 return MoveDoor(door_state);
5082 unsigned int GetDoorState(void)
5084 return MoveDoor(DOOR_GET_STATE);
5087 unsigned int SetDoorState(unsigned int door_state)
5089 return MoveDoor(door_state | DOOR_SET_STATE);
5092 static int euclid(int a, int b)
5094 return (b ? euclid(b, a % b) : a);
5097 unsigned int MoveDoor(unsigned int door_state)
5099 struct Rect door_rect_list[] =
5101 { DX, DY, DXSIZE, DYSIZE },
5102 { VX, VY, VXSIZE, VYSIZE }
5104 static int door1 = DOOR_CLOSE_1;
5105 static int door2 = DOOR_CLOSE_2;
5106 unsigned int door_delay = 0;
5107 unsigned int door_delay_value;
5110 if (door_state == DOOR_GET_STATE)
5111 return (door1 | door2);
5113 if (door_state & DOOR_SET_STATE)
5115 if (door_state & DOOR_ACTION_1)
5116 door1 = door_state & DOOR_ACTION_1;
5117 if (door_state & DOOR_ACTION_2)
5118 door2 = door_state & DOOR_ACTION_2;
5120 return (door1 | door2);
5123 if (!(door_state & DOOR_FORCE_REDRAW))
5125 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5126 door_state &= ~DOOR_OPEN_1;
5127 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5128 door_state &= ~DOOR_CLOSE_1;
5129 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5130 door_state &= ~DOOR_OPEN_2;
5131 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5132 door_state &= ~DOOR_CLOSE_2;
5135 if (global.autoplay_leveldir)
5137 door_state |= DOOR_NO_DELAY;
5138 door_state &= ~DOOR_CLOSE_ALL;
5141 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5142 door_state |= DOOR_NO_DELAY;
5144 if (door_state & DOOR_ACTION)
5146 boolean door_panel_drawn[NUM_DOORS];
5147 boolean panel_has_doors[NUM_DOORS];
5148 boolean door_part_skip[MAX_DOOR_PARTS];
5149 boolean door_part_done[MAX_DOOR_PARTS];
5150 boolean door_part_done_all;
5151 int num_steps[MAX_DOOR_PARTS];
5152 int max_move_delay = 0; // delay for complete animations of all doors
5153 int max_step_delay = 0; // delay (ms) between two animation frames
5154 int num_move_steps = 0; // number of animation steps for all doors
5155 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5156 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5157 int current_move_delay = 0;
5161 for (i = 0; i < NUM_DOORS; i++)
5162 panel_has_doors[i] = FALSE;
5164 for (i = 0; i < MAX_DOOR_PARTS; i++)
5166 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5167 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5168 int door_token = dpc->door_token;
5170 door_part_done[i] = FALSE;
5171 door_part_skip[i] = (!(door_state & door_token) ||
5175 for (i = 0; i < MAX_DOOR_PARTS; i++)
5177 int nr = door_part_order[i].nr;
5178 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5179 struct DoorPartPosInfo *pos = dpc->pos;
5180 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5181 int door_token = dpc->door_token;
5182 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5183 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5184 int step_xoffset = ABS(pos->step_xoffset);
5185 int step_yoffset = ABS(pos->step_yoffset);
5186 int step_delay = pos->step_delay;
5187 int current_door_state = door_state & door_token;
5188 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5189 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5190 boolean part_opening = (is_panel ? door_closing : door_opening);
5191 int start_step = (part_opening ? pos->start_step_opening :
5192 pos->start_step_closing);
5193 float move_xsize = (step_xoffset ? g->width : 0);
5194 float move_ysize = (step_yoffset ? g->height : 0);
5195 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5196 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5197 int move_steps = (move_xsteps && move_ysteps ?
5198 MIN(move_xsteps, move_ysteps) :
5199 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5200 int move_delay = move_steps * step_delay;
5202 if (door_part_skip[nr])
5205 max_move_delay = MAX(max_move_delay, move_delay);
5206 max_step_delay = (max_step_delay == 0 ? step_delay :
5207 euclid(max_step_delay, step_delay));
5208 num_steps[nr] = move_steps;
5212 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5214 panel_has_doors[door_index] = TRUE;
5218 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5220 num_move_steps = max_move_delay / max_step_delay;
5221 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5223 door_delay_value = max_step_delay;
5225 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5227 start = num_move_steps - 1;
5231 // opening door sound has priority over simultaneously closing door
5232 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5234 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5236 if (door_state & DOOR_OPEN_1)
5237 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5238 if (door_state & DOOR_OPEN_2)
5239 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5241 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5243 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5245 if (door_state & DOOR_CLOSE_1)
5246 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5247 if (door_state & DOOR_CLOSE_2)
5248 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5252 for (k = start; k < num_move_steps; k++)
5254 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5256 door_part_done_all = TRUE;
5258 for (i = 0; i < NUM_DOORS; i++)
5259 door_panel_drawn[i] = FALSE;
5261 for (i = 0; i < MAX_DOOR_PARTS; i++)
5263 int nr = door_part_order[i].nr;
5264 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5265 struct DoorPartPosInfo *pos = dpc->pos;
5266 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5267 int door_token = dpc->door_token;
5268 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5269 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5270 boolean is_panel_and_door_has_closed = FALSE;
5271 struct Rect *door_rect = &door_rect_list[door_index];
5272 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5274 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5275 int current_door_state = door_state & door_token;
5276 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5277 boolean door_closing = !door_opening;
5278 boolean part_opening = (is_panel ? door_closing : door_opening);
5279 boolean part_closing = !part_opening;
5280 int start_step = (part_opening ? pos->start_step_opening :
5281 pos->start_step_closing);
5282 int step_delay = pos->step_delay;
5283 int step_factor = step_delay / max_step_delay;
5284 int k1 = (step_factor ? k / step_factor + 1 : k);
5285 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5286 int kk = MAX(0, k2);
5289 int src_x, src_y, src_xx, src_yy;
5290 int dst_x, dst_y, dst_xx, dst_yy;
5293 if (door_part_skip[nr])
5296 if (!(door_state & door_token))
5304 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5305 int kk_door = MAX(0, k2_door);
5306 int sync_frame = kk_door * door_delay_value;
5307 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5309 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5310 &g_src_x, &g_src_y);
5315 if (!door_panel_drawn[door_index])
5317 ClearRectangle(drawto, door_rect->x, door_rect->y,
5318 door_rect->width, door_rect->height);
5320 door_panel_drawn[door_index] = TRUE;
5323 // draw opening or closing door parts
5325 if (pos->step_xoffset < 0) // door part on right side
5328 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5331 if (dst_xx + width > door_rect->width)
5332 width = door_rect->width - dst_xx;
5334 else // door part on left side
5337 dst_xx = pos->x - kk * pos->step_xoffset;
5341 src_xx = ABS(dst_xx);
5345 width = g->width - src_xx;
5347 if (width > door_rect->width)
5348 width = door_rect->width;
5350 // printf("::: k == %d [%d] \n", k, start_step);
5353 if (pos->step_yoffset < 0) // door part on bottom side
5356 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5359 if (dst_yy + height > door_rect->height)
5360 height = door_rect->height - dst_yy;
5362 else // door part on top side
5365 dst_yy = pos->y - kk * pos->step_yoffset;
5369 src_yy = ABS(dst_yy);
5373 height = g->height - src_yy;
5376 src_x = g_src_x + src_xx;
5377 src_y = g_src_y + src_yy;
5379 dst_x = door_rect->x + dst_xx;
5380 dst_y = door_rect->y + dst_yy;
5382 is_panel_and_door_has_closed =
5385 panel_has_doors[door_index] &&
5386 k >= num_move_steps_doors_only - 1);
5388 if (width >= 0 && width <= g->width &&
5389 height >= 0 && height <= g->height &&
5390 !is_panel_and_door_has_closed)
5392 if (is_panel || !pos->draw_masked)
5393 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5396 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5400 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5402 if ((part_opening && (width < 0 || height < 0)) ||
5403 (part_closing && (width >= g->width && height >= g->height)))
5404 door_part_done[nr] = TRUE;
5406 // continue door part animations, but not panel after door has closed
5407 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5408 door_part_done_all = FALSE;
5411 if (!(door_state & DOOR_NO_DELAY))
5415 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5417 current_move_delay += max_step_delay;
5419 // prevent OS (Windows) from complaining about program not responding
5423 if (door_part_done_all)
5427 if (!(door_state & DOOR_NO_DELAY))
5429 // wait for specified door action post delay
5430 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5431 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5432 else if (door_state & DOOR_ACTION_1)
5433 door_delay_value = door_1.post_delay;
5434 else if (door_state & DOOR_ACTION_2)
5435 door_delay_value = door_2.post_delay;
5437 while (!DelayReached(&door_delay, door_delay_value))
5442 if (door_state & DOOR_ACTION_1)
5443 door1 = door_state & DOOR_ACTION_1;
5444 if (door_state & DOOR_ACTION_2)
5445 door2 = door_state & DOOR_ACTION_2;
5447 // draw masked border over door area
5448 DrawMaskedBorder(REDRAW_DOOR_1);
5449 DrawMaskedBorder(REDRAW_DOOR_2);
5451 ClearAutoRepeatKeyEvents();
5453 return (door1 | door2);
5456 static boolean useSpecialEditorDoor(void)
5458 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5459 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5461 // do not draw special editor door if editor border defined or redefined
5462 if (graphic_info[graphic].bitmap != NULL || redefined)
5465 // do not draw special editor door if global border defined to be empty
5466 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5469 // do not draw special editor door if viewport definitions do not match
5473 EY + EYSIZE != VY + VYSIZE)
5479 void DrawSpecialEditorDoor(void)
5481 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5482 int top_border_width = gfx1->width;
5483 int top_border_height = gfx1->height;
5484 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5485 int ex = EX - outer_border;
5486 int ey = EY - outer_border;
5487 int vy = VY - outer_border;
5488 int exsize = EXSIZE + 2 * outer_border;
5490 if (!useSpecialEditorDoor())
5493 // draw bigger level editor toolbox window
5494 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5495 top_border_width, top_border_height, ex, ey - top_border_height);
5496 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5497 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5499 redraw_mask |= REDRAW_ALL;
5502 void UndrawSpecialEditorDoor(void)
5504 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5505 int top_border_width = gfx1->width;
5506 int top_border_height = gfx1->height;
5507 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5508 int ex = EX - outer_border;
5509 int ey = EY - outer_border;
5510 int ey_top = ey - top_border_height;
5511 int exsize = EXSIZE + 2 * outer_border;
5512 int eysize = EYSIZE + 2 * outer_border;
5514 if (!useSpecialEditorDoor())
5517 // draw normal tape recorder window
5518 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5520 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5521 ex, ey_top, top_border_width, top_border_height,
5523 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5524 ex, ey, exsize, eysize, ex, ey);
5528 // if screen background is set to "[NONE]", clear editor toolbox window
5529 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5530 ClearRectangle(drawto, ex, ey, exsize, eysize);
5533 redraw_mask |= REDRAW_ALL;
5537 // ---------- new tool button stuff -------------------------------------------
5542 struct TextPosInfo *pos;
5545 } toolbutton_info[NUM_TOOL_BUTTONS] =
5548 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5549 TOOL_CTRL_ID_YES, "yes"
5552 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5553 TOOL_CTRL_ID_NO, "no"
5556 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5557 TOOL_CTRL_ID_CONFIRM, "confirm"
5560 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5561 TOOL_CTRL_ID_PLAYER_1, "player 1"
5564 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5565 TOOL_CTRL_ID_PLAYER_2, "player 2"
5568 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5569 TOOL_CTRL_ID_PLAYER_3, "player 3"
5572 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5573 TOOL_CTRL_ID_PLAYER_4, "player 4"
5577 void CreateToolButtons(void)
5581 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5583 int graphic = toolbutton_info[i].graphic;
5584 struct GraphicInfo *gfx = &graphic_info[graphic];
5585 struct TextPosInfo *pos = toolbutton_info[i].pos;
5586 struct GadgetInfo *gi;
5587 Bitmap *deco_bitmap = None;
5588 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5589 unsigned int event_mask = GD_EVENT_RELEASED;
5592 int gd_x = gfx->src_x;
5593 int gd_y = gfx->src_y;
5594 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5595 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5600 if (global.use_envelope_request)
5602 setRequestPosition(&dx, &dy, TRUE);
5604 // check if request buttons are outside of envelope and fix, if needed
5605 if (x < 0 || x + gfx->width > request.width ||
5606 y < 0 || y + gfx->height > request.height)
5608 if (id == TOOL_CTRL_ID_YES)
5611 y = request.height - 2 * request.border_size - gfx->height;
5613 else if (id == TOOL_CTRL_ID_NO)
5615 x = request.width - 2 * request.border_size - gfx->width;
5616 y = request.height - 2 * request.border_size - gfx->height;
5618 else if (id == TOOL_CTRL_ID_CONFIRM)
5620 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5621 y = request.height - 2 * request.border_size - gfx->height;
5623 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5625 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5627 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5628 y = request.height - 2 * request.border_size - gfx->height * 2;
5630 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5631 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5636 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5638 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5640 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5641 pos->size, &deco_bitmap, &deco_x, &deco_y);
5642 deco_xpos = (gfx->width - pos->size) / 2;
5643 deco_ypos = (gfx->height - pos->size) / 2;
5646 gi = CreateGadget(GDI_CUSTOM_ID, id,
5647 GDI_IMAGE_ID, graphic,
5648 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5651 GDI_WIDTH, gfx->width,
5652 GDI_HEIGHT, gfx->height,
5653 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5654 GDI_STATE, GD_BUTTON_UNPRESSED,
5655 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5656 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5657 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5658 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5659 GDI_DECORATION_SIZE, pos->size, pos->size,
5660 GDI_DECORATION_SHIFTING, 1, 1,
5661 GDI_DIRECT_DRAW, FALSE,
5662 GDI_EVENT_MASK, event_mask,
5663 GDI_CALLBACK_ACTION, HandleToolButtons,
5667 Error(ERR_EXIT, "cannot create gadget");
5669 tool_gadget[id] = gi;
5673 void FreeToolButtons(void)
5677 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5678 FreeGadget(tool_gadget[i]);
5681 static void UnmapToolButtons(void)
5685 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5686 UnmapGadget(tool_gadget[i]);
5689 static void HandleToolButtons(struct GadgetInfo *gi)
5691 request_gadget_id = gi->custom_id;
5694 static struct Mapping_EM_to_RND_object
5697 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5698 boolean is_backside; // backside of moving element
5704 em_object_mapping_list[] =
5707 Xblank, TRUE, FALSE,
5711 Yacid_splash_eB, FALSE, FALSE,
5712 EL_ACID_SPLASH_RIGHT, -1, -1
5715 Yacid_splash_wB, FALSE, FALSE,
5716 EL_ACID_SPLASH_LEFT, -1, -1
5719 #ifdef EM_ENGINE_BAD_ROLL
5721 Xstone_force_e, FALSE, FALSE,
5722 EL_ROCK, -1, MV_BIT_RIGHT
5725 Xstone_force_w, FALSE, FALSE,
5726 EL_ROCK, -1, MV_BIT_LEFT
5729 Xnut_force_e, FALSE, FALSE,
5730 EL_NUT, -1, MV_BIT_RIGHT
5733 Xnut_force_w, FALSE, FALSE,
5734 EL_NUT, -1, MV_BIT_LEFT
5737 Xspring_force_e, FALSE, FALSE,
5738 EL_SPRING, -1, MV_BIT_RIGHT
5741 Xspring_force_w, FALSE, FALSE,
5742 EL_SPRING, -1, MV_BIT_LEFT
5745 Xemerald_force_e, FALSE, FALSE,
5746 EL_EMERALD, -1, MV_BIT_RIGHT
5749 Xemerald_force_w, FALSE, FALSE,
5750 EL_EMERALD, -1, MV_BIT_LEFT
5753 Xdiamond_force_e, FALSE, FALSE,
5754 EL_DIAMOND, -1, MV_BIT_RIGHT
5757 Xdiamond_force_w, FALSE, FALSE,
5758 EL_DIAMOND, -1, MV_BIT_LEFT
5761 Xbomb_force_e, FALSE, FALSE,
5762 EL_BOMB, -1, MV_BIT_RIGHT
5765 Xbomb_force_w, FALSE, FALSE,
5766 EL_BOMB, -1, MV_BIT_LEFT
5768 #endif // EM_ENGINE_BAD_ROLL
5771 Xstone, TRUE, FALSE,
5775 Xstone_pause, FALSE, FALSE,
5779 Xstone_fall, FALSE, FALSE,
5783 Ystone_s, FALSE, FALSE,
5784 EL_ROCK, ACTION_FALLING, -1
5787 Ystone_sB, FALSE, TRUE,
5788 EL_ROCK, ACTION_FALLING, -1
5791 Ystone_e, FALSE, FALSE,
5792 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5795 Ystone_eB, FALSE, TRUE,
5796 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5799 Ystone_w, FALSE, FALSE,
5800 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5803 Ystone_wB, FALSE, TRUE,
5804 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5811 Xnut_pause, FALSE, FALSE,
5815 Xnut_fall, FALSE, FALSE,
5819 Ynut_s, FALSE, FALSE,
5820 EL_NUT, ACTION_FALLING, -1
5823 Ynut_sB, FALSE, TRUE,
5824 EL_NUT, ACTION_FALLING, -1
5827 Ynut_e, FALSE, FALSE,
5828 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5831 Ynut_eB, FALSE, TRUE,
5832 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5835 Ynut_w, FALSE, FALSE,
5836 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5839 Ynut_wB, FALSE, TRUE,
5840 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5843 Xbug_n, TRUE, FALSE,
5847 Xbug_e, TRUE, FALSE,
5848 EL_BUG_RIGHT, -1, -1
5851 Xbug_s, TRUE, FALSE,
5855 Xbug_w, TRUE, FALSE,
5859 Xbug_gon, FALSE, FALSE,
5863 Xbug_goe, FALSE, FALSE,
5864 EL_BUG_RIGHT, -1, -1
5867 Xbug_gos, FALSE, FALSE,
5871 Xbug_gow, FALSE, FALSE,
5875 Ybug_n, FALSE, FALSE,
5876 EL_BUG, ACTION_MOVING, MV_BIT_UP
5879 Ybug_nB, FALSE, TRUE,
5880 EL_BUG, ACTION_MOVING, MV_BIT_UP
5883 Ybug_e, FALSE, FALSE,
5884 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5887 Ybug_eB, FALSE, TRUE,
5888 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5891 Ybug_s, FALSE, FALSE,
5892 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5895 Ybug_sB, FALSE, TRUE,
5896 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5899 Ybug_w, FALSE, FALSE,
5900 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5903 Ybug_wB, FALSE, TRUE,
5904 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5907 Ybug_w_n, FALSE, FALSE,
5908 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5911 Ybug_n_e, FALSE, FALSE,
5912 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5915 Ybug_e_s, FALSE, FALSE,
5916 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5919 Ybug_s_w, FALSE, FALSE,
5920 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5923 Ybug_e_n, FALSE, FALSE,
5924 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5927 Ybug_s_e, FALSE, FALSE,
5928 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5931 Ybug_w_s, FALSE, FALSE,
5932 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5935 Ybug_n_w, FALSE, FALSE,
5936 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5939 Ybug_stone, FALSE, FALSE,
5940 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5943 Ybug_spring, FALSE, FALSE,
5944 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5947 Xtank_n, TRUE, FALSE,
5948 EL_SPACESHIP_UP, -1, -1
5951 Xtank_e, TRUE, FALSE,
5952 EL_SPACESHIP_RIGHT, -1, -1
5955 Xtank_s, TRUE, FALSE,
5956 EL_SPACESHIP_DOWN, -1, -1
5959 Xtank_w, TRUE, FALSE,
5960 EL_SPACESHIP_LEFT, -1, -1
5963 Xtank_gon, FALSE, FALSE,
5964 EL_SPACESHIP_UP, -1, -1
5967 Xtank_goe, FALSE, FALSE,
5968 EL_SPACESHIP_RIGHT, -1, -1
5971 Xtank_gos, FALSE, FALSE,
5972 EL_SPACESHIP_DOWN, -1, -1
5975 Xtank_gow, FALSE, FALSE,
5976 EL_SPACESHIP_LEFT, -1, -1
5979 Ytank_n, FALSE, FALSE,
5980 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5983 Ytank_nB, FALSE, TRUE,
5984 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5987 Ytank_e, FALSE, FALSE,
5988 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5991 Ytank_eB, FALSE, TRUE,
5992 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5995 Ytank_s, FALSE, FALSE,
5996 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5999 Ytank_sB, FALSE, TRUE,
6000 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
6003 Ytank_w, FALSE, FALSE,
6004 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6007 Ytank_wB, FALSE, TRUE,
6008 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6011 Ytank_w_n, FALSE, FALSE,
6012 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6015 Ytank_n_e, FALSE, FALSE,
6016 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6019 Ytank_e_s, FALSE, FALSE,
6020 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6023 Ytank_s_w, FALSE, FALSE,
6024 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6027 Ytank_e_n, FALSE, FALSE,
6028 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6031 Ytank_s_e, FALSE, FALSE,
6032 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6035 Ytank_w_s, FALSE, FALSE,
6036 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6039 Ytank_n_w, FALSE, FALSE,
6040 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6043 Ytank_stone, FALSE, FALSE,
6044 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6047 Ytank_spring, FALSE, FALSE,
6048 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6051 Xandroid, TRUE, FALSE,
6052 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6055 Xandroid_1_n, FALSE, FALSE,
6056 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6059 Xandroid_2_n, FALSE, FALSE,
6060 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6063 Xandroid_1_e, FALSE, FALSE,
6064 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6067 Xandroid_2_e, FALSE, FALSE,
6068 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6071 Xandroid_1_w, FALSE, FALSE,
6072 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6075 Xandroid_2_w, FALSE, FALSE,
6076 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6079 Xandroid_1_s, FALSE, FALSE,
6080 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6083 Xandroid_2_s, FALSE, FALSE,
6084 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6087 Yandroid_n, FALSE, FALSE,
6088 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6091 Yandroid_nB, FALSE, TRUE,
6092 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6095 Yandroid_ne, FALSE, FALSE,
6096 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6099 Yandroid_neB, FALSE, TRUE,
6100 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6103 Yandroid_e, FALSE, FALSE,
6104 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6107 Yandroid_eB, FALSE, TRUE,
6108 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6111 Yandroid_se, FALSE, FALSE,
6112 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6115 Yandroid_seB, FALSE, TRUE,
6116 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6119 Yandroid_s, FALSE, FALSE,
6120 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6123 Yandroid_sB, FALSE, TRUE,
6124 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6127 Yandroid_sw, FALSE, FALSE,
6128 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6131 Yandroid_swB, FALSE, TRUE,
6132 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6135 Yandroid_w, FALSE, FALSE,
6136 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6139 Yandroid_wB, FALSE, TRUE,
6140 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6143 Yandroid_nw, FALSE, FALSE,
6144 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6147 Yandroid_nwB, FALSE, TRUE,
6148 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6151 Xspring, TRUE, FALSE,
6155 Xspring_pause, FALSE, FALSE,
6159 Xspring_e, FALSE, FALSE,
6163 Xspring_w, FALSE, FALSE,
6167 Xspring_fall, FALSE, FALSE,
6171 Yspring_s, FALSE, FALSE,
6172 EL_SPRING, ACTION_FALLING, -1
6175 Yspring_sB, FALSE, TRUE,
6176 EL_SPRING, ACTION_FALLING, -1
6179 Yspring_e, FALSE, FALSE,
6180 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6183 Yspring_eB, FALSE, TRUE,
6184 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6187 Yspring_w, FALSE, FALSE,
6188 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6191 Yspring_wB, FALSE, TRUE,
6192 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6195 Yspring_kill_e, FALSE, FALSE,
6196 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6199 Yspring_kill_eB, FALSE, TRUE,
6200 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6203 Yspring_kill_w, FALSE, FALSE,
6204 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6207 Yspring_kill_wB, FALSE, TRUE,
6208 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6211 Xeater_n, TRUE, FALSE,
6212 EL_YAMYAM_UP, -1, -1
6215 Xeater_e, TRUE, FALSE,
6216 EL_YAMYAM_RIGHT, -1, -1
6219 Xeater_w, TRUE, FALSE,
6220 EL_YAMYAM_LEFT, -1, -1
6223 Xeater_s, TRUE, FALSE,
6224 EL_YAMYAM_DOWN, -1, -1
6227 Yeater_n, FALSE, FALSE,
6228 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6231 Yeater_nB, FALSE, TRUE,
6232 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6235 Yeater_e, FALSE, FALSE,
6236 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6239 Yeater_eB, FALSE, TRUE,
6240 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6243 Yeater_s, FALSE, FALSE,
6244 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6247 Yeater_sB, FALSE, TRUE,
6248 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6251 Yeater_w, FALSE, FALSE,
6252 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6255 Yeater_wB, FALSE, TRUE,
6256 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6259 Yeater_stone, FALSE, FALSE,
6260 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6263 Yeater_spring, FALSE, FALSE,
6264 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6267 Xalien, TRUE, FALSE,
6271 Xalien_pause, FALSE, FALSE,
6275 Yalien_n, FALSE, FALSE,
6276 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6279 Yalien_nB, FALSE, TRUE,
6280 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6283 Yalien_e, FALSE, FALSE,
6284 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6287 Yalien_eB, FALSE, TRUE,
6288 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6291 Yalien_s, FALSE, FALSE,
6292 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6295 Yalien_sB, FALSE, TRUE,
6296 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6299 Yalien_w, FALSE, FALSE,
6300 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6303 Yalien_wB, FALSE, TRUE,
6304 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6307 Yalien_stone, FALSE, FALSE,
6308 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6311 Yalien_spring, FALSE, FALSE,
6312 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6315 Xemerald, TRUE, FALSE,
6319 Xemerald_pause, FALSE, FALSE,
6323 Xemerald_fall, FALSE, FALSE,
6327 Xemerald_shine, FALSE, FALSE,
6328 EL_EMERALD, ACTION_TWINKLING, -1
6331 Yemerald_s, FALSE, FALSE,
6332 EL_EMERALD, ACTION_FALLING, -1
6335 Yemerald_sB, FALSE, TRUE,
6336 EL_EMERALD, ACTION_FALLING, -1
6339 Yemerald_e, FALSE, FALSE,
6340 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6343 Yemerald_eB, FALSE, TRUE,
6344 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6347 Yemerald_w, FALSE, FALSE,
6348 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6351 Yemerald_wB, FALSE, TRUE,
6352 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6355 Yemerald_eat, FALSE, FALSE,
6356 EL_EMERALD, ACTION_COLLECTING, -1
6359 Yemerald_stone, FALSE, FALSE,
6360 EL_NUT, ACTION_BREAKING, -1
6363 Xdiamond, TRUE, FALSE,
6367 Xdiamond_pause, FALSE, FALSE,
6371 Xdiamond_fall, FALSE, FALSE,
6375 Xdiamond_shine, FALSE, FALSE,
6376 EL_DIAMOND, ACTION_TWINKLING, -1
6379 Ydiamond_s, FALSE, FALSE,
6380 EL_DIAMOND, ACTION_FALLING, -1
6383 Ydiamond_sB, FALSE, TRUE,
6384 EL_DIAMOND, ACTION_FALLING, -1
6387 Ydiamond_e, FALSE, FALSE,
6388 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6391 Ydiamond_eB, FALSE, TRUE,
6392 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6395 Ydiamond_w, FALSE, FALSE,
6396 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6399 Ydiamond_wB, FALSE, TRUE,
6400 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6403 Ydiamond_eat, FALSE, FALSE,
6404 EL_DIAMOND, ACTION_COLLECTING, -1
6407 Ydiamond_stone, FALSE, FALSE,
6408 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6411 Xdrip_fall, TRUE, FALSE,
6412 EL_AMOEBA_DROP, -1, -1
6415 Xdrip_stretch, FALSE, FALSE,
6416 EL_AMOEBA_DROP, ACTION_FALLING, -1
6419 Xdrip_stretchB, FALSE, TRUE,
6420 EL_AMOEBA_DROP, ACTION_FALLING, -1
6423 Xdrip_eat, FALSE, FALSE,
6424 EL_AMOEBA_DROP, ACTION_GROWING, -1
6427 Ydrip_s1, FALSE, FALSE,
6428 EL_AMOEBA_DROP, ACTION_FALLING, -1
6431 Ydrip_s1B, FALSE, TRUE,
6432 EL_AMOEBA_DROP, ACTION_FALLING, -1
6435 Ydrip_s2, FALSE, FALSE,
6436 EL_AMOEBA_DROP, ACTION_FALLING, -1
6439 Ydrip_s2B, FALSE, TRUE,
6440 EL_AMOEBA_DROP, ACTION_FALLING, -1
6447 Xbomb_pause, FALSE, FALSE,
6451 Xbomb_fall, FALSE, FALSE,
6455 Ybomb_s, FALSE, FALSE,
6456 EL_BOMB, ACTION_FALLING, -1
6459 Ybomb_sB, FALSE, TRUE,
6460 EL_BOMB, ACTION_FALLING, -1
6463 Ybomb_e, FALSE, FALSE,
6464 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6467 Ybomb_eB, FALSE, TRUE,
6468 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6471 Ybomb_w, FALSE, FALSE,
6472 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6475 Ybomb_wB, FALSE, TRUE,
6476 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6479 Ybomb_eat, FALSE, FALSE,
6480 EL_BOMB, ACTION_ACTIVATING, -1
6483 Xballoon, TRUE, FALSE,
6487 Yballoon_n, FALSE, FALSE,
6488 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6491 Yballoon_nB, FALSE, TRUE,
6492 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6495 Yballoon_e, FALSE, FALSE,
6496 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6499 Yballoon_eB, FALSE, TRUE,
6500 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6503 Yballoon_s, FALSE, FALSE,
6504 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6507 Yballoon_sB, FALSE, TRUE,
6508 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6511 Yballoon_w, FALSE, FALSE,
6512 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6515 Yballoon_wB, FALSE, TRUE,
6516 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6519 Xgrass, TRUE, FALSE,
6520 EL_EMC_GRASS, -1, -1
6523 Ygrass_nB, FALSE, FALSE,
6524 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6527 Ygrass_eB, FALSE, FALSE,
6528 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6531 Ygrass_sB, FALSE, FALSE,
6532 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6535 Ygrass_wB, FALSE, FALSE,
6536 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6543 Ydirt_nB, FALSE, FALSE,
6544 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6547 Ydirt_eB, FALSE, FALSE,
6548 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6551 Ydirt_sB, FALSE, FALSE,
6552 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6555 Ydirt_wB, FALSE, FALSE,
6556 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6559 Xacid_ne, TRUE, FALSE,
6560 EL_ACID_POOL_TOPRIGHT, -1, -1
6563 Xacid_se, TRUE, FALSE,
6564 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6567 Xacid_s, TRUE, FALSE,
6568 EL_ACID_POOL_BOTTOM, -1, -1
6571 Xacid_sw, TRUE, FALSE,
6572 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6575 Xacid_nw, TRUE, FALSE,
6576 EL_ACID_POOL_TOPLEFT, -1, -1
6579 Xacid_1, TRUE, FALSE,
6583 Xacid_2, FALSE, FALSE,
6587 Xacid_3, FALSE, FALSE,
6591 Xacid_4, FALSE, FALSE,
6595 Xacid_5, FALSE, FALSE,
6599 Xacid_6, FALSE, FALSE,
6603 Xacid_7, FALSE, FALSE,
6607 Xacid_8, FALSE, FALSE,
6611 Xball_1, TRUE, FALSE,
6612 EL_EMC_MAGIC_BALL, -1, -1
6615 Xball_1B, FALSE, FALSE,
6616 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6619 Xball_2, FALSE, FALSE,
6620 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6623 Xball_2B, FALSE, FALSE,
6624 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6627 Yball_eat, FALSE, FALSE,
6628 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6631 Ykey_1_eat, FALSE, FALSE,
6632 EL_EM_KEY_1, ACTION_COLLECTING, -1
6635 Ykey_2_eat, FALSE, FALSE,
6636 EL_EM_KEY_2, ACTION_COLLECTING, -1
6639 Ykey_3_eat, FALSE, FALSE,
6640 EL_EM_KEY_3, ACTION_COLLECTING, -1
6643 Ykey_4_eat, FALSE, FALSE,
6644 EL_EM_KEY_4, ACTION_COLLECTING, -1
6647 Ykey_5_eat, FALSE, FALSE,
6648 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6651 Ykey_6_eat, FALSE, FALSE,
6652 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6655 Ykey_7_eat, FALSE, FALSE,
6656 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6659 Ykey_8_eat, FALSE, FALSE,
6660 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6663 Ylenses_eat, FALSE, FALSE,
6664 EL_EMC_LENSES, ACTION_COLLECTING, -1
6667 Ymagnify_eat, FALSE, FALSE,
6668 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6671 Ygrass_eat, FALSE, FALSE,
6672 EL_EMC_GRASS, ACTION_SNAPPING, -1
6675 Ydirt_eat, FALSE, FALSE,
6676 EL_SAND, ACTION_SNAPPING, -1
6679 Xgrow_ns, TRUE, FALSE,
6680 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6683 Ygrow_ns_eat, FALSE, FALSE,
6684 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6687 Xgrow_ew, TRUE, FALSE,
6688 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6691 Ygrow_ew_eat, FALSE, FALSE,
6692 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6695 Xwonderwall, TRUE, FALSE,
6696 EL_MAGIC_WALL, -1, -1
6699 XwonderwallB, FALSE, FALSE,
6700 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6703 Xamoeba_1, TRUE, FALSE,
6704 EL_AMOEBA_DRY, ACTION_OTHER, -1
6707 Xamoeba_2, FALSE, FALSE,
6708 EL_AMOEBA_DRY, ACTION_OTHER, -1
6711 Xamoeba_3, FALSE, FALSE,
6712 EL_AMOEBA_DRY, ACTION_OTHER, -1
6715 Xamoeba_4, FALSE, FALSE,
6716 EL_AMOEBA_DRY, ACTION_OTHER, -1
6719 Xamoeba_5, TRUE, FALSE,
6720 EL_AMOEBA_WET, ACTION_OTHER, -1
6723 Xamoeba_6, FALSE, FALSE,
6724 EL_AMOEBA_WET, ACTION_OTHER, -1
6727 Xamoeba_7, FALSE, FALSE,
6728 EL_AMOEBA_WET, ACTION_OTHER, -1
6731 Xamoeba_8, FALSE, FALSE,
6732 EL_AMOEBA_WET, ACTION_OTHER, -1
6735 Xdoor_1, TRUE, FALSE,
6736 EL_EM_GATE_1, -1, -1
6739 Xdoor_2, TRUE, FALSE,
6740 EL_EM_GATE_2, -1, -1
6743 Xdoor_3, TRUE, FALSE,
6744 EL_EM_GATE_3, -1, -1
6747 Xdoor_4, TRUE, FALSE,
6748 EL_EM_GATE_4, -1, -1
6751 Xdoor_5, TRUE, FALSE,
6752 EL_EMC_GATE_5, -1, -1
6755 Xdoor_6, TRUE, FALSE,
6756 EL_EMC_GATE_6, -1, -1
6759 Xdoor_7, TRUE, FALSE,
6760 EL_EMC_GATE_7, -1, -1
6763 Xdoor_8, TRUE, FALSE,
6764 EL_EMC_GATE_8, -1, -1
6767 Xkey_1, TRUE, FALSE,
6771 Xkey_2, TRUE, FALSE,
6775 Xkey_3, TRUE, FALSE,
6779 Xkey_4, TRUE, FALSE,
6783 Xkey_5, TRUE, FALSE,
6784 EL_EMC_KEY_5, -1, -1
6787 Xkey_6, TRUE, FALSE,
6788 EL_EMC_KEY_6, -1, -1
6791 Xkey_7, TRUE, FALSE,
6792 EL_EMC_KEY_7, -1, -1
6795 Xkey_8, TRUE, FALSE,
6796 EL_EMC_KEY_8, -1, -1
6799 Xwind_n, TRUE, FALSE,
6800 EL_BALLOON_SWITCH_UP, -1, -1
6803 Xwind_e, TRUE, FALSE,
6804 EL_BALLOON_SWITCH_RIGHT, -1, -1
6807 Xwind_s, TRUE, FALSE,
6808 EL_BALLOON_SWITCH_DOWN, -1, -1
6811 Xwind_w, TRUE, FALSE,
6812 EL_BALLOON_SWITCH_LEFT, -1, -1
6815 Xwind_nesw, TRUE, FALSE,
6816 EL_BALLOON_SWITCH_ANY, -1, -1
6819 Xwind_stop, TRUE, FALSE,
6820 EL_BALLOON_SWITCH_NONE, -1, -1
6824 EL_EM_EXIT_CLOSED, -1, -1
6827 Xexit_1, TRUE, FALSE,
6828 EL_EM_EXIT_OPEN, -1, -1
6831 Xexit_2, FALSE, FALSE,
6832 EL_EM_EXIT_OPEN, -1, -1
6835 Xexit_3, FALSE, FALSE,
6836 EL_EM_EXIT_OPEN, -1, -1
6839 Xdynamite, TRUE, FALSE,
6840 EL_EM_DYNAMITE, -1, -1
6843 Ydynamite_eat, FALSE, FALSE,
6844 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6847 Xdynamite_1, TRUE, FALSE,
6848 EL_EM_DYNAMITE_ACTIVE, -1, -1
6851 Xdynamite_2, FALSE, FALSE,
6852 EL_EM_DYNAMITE_ACTIVE, -1, -1
6855 Xdynamite_3, FALSE, FALSE,
6856 EL_EM_DYNAMITE_ACTIVE, -1, -1
6859 Xdynamite_4, FALSE, FALSE,
6860 EL_EM_DYNAMITE_ACTIVE, -1, -1
6863 Xbumper, TRUE, FALSE,
6864 EL_EMC_SPRING_BUMPER, -1, -1
6867 XbumperB, FALSE, FALSE,
6868 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6871 Xwheel, TRUE, FALSE,
6872 EL_ROBOT_WHEEL, -1, -1
6875 XwheelB, FALSE, FALSE,
6876 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6879 Xswitch, TRUE, FALSE,
6880 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6883 XswitchB, FALSE, FALSE,
6884 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6888 EL_QUICKSAND_EMPTY, -1, -1
6891 Xsand_stone, TRUE, FALSE,
6892 EL_QUICKSAND_FULL, -1, -1
6895 Xsand_stonein_1, FALSE, TRUE,
6896 EL_ROCK, ACTION_FILLING, -1
6899 Xsand_stonein_2, FALSE, TRUE,
6900 EL_ROCK, ACTION_FILLING, -1
6903 Xsand_stonein_3, FALSE, TRUE,
6904 EL_ROCK, ACTION_FILLING, -1
6907 Xsand_stonein_4, FALSE, TRUE,
6908 EL_ROCK, ACTION_FILLING, -1
6911 Xsand_stonesand_1, FALSE, FALSE,
6912 EL_QUICKSAND_EMPTYING, -1, -1
6915 Xsand_stonesand_2, FALSE, FALSE,
6916 EL_QUICKSAND_EMPTYING, -1, -1
6919 Xsand_stonesand_3, FALSE, FALSE,
6920 EL_QUICKSAND_EMPTYING, -1, -1
6923 Xsand_stonesand_4, FALSE, FALSE,
6924 EL_QUICKSAND_EMPTYING, -1, -1
6927 Xsand_stonesand_quickout_1, FALSE, FALSE,
6928 EL_QUICKSAND_EMPTYING, -1, -1
6931 Xsand_stonesand_quickout_2, FALSE, FALSE,
6932 EL_QUICKSAND_EMPTYING, -1, -1
6935 Xsand_stoneout_1, FALSE, FALSE,
6936 EL_ROCK, ACTION_EMPTYING, -1
6939 Xsand_stoneout_2, FALSE, FALSE,
6940 EL_ROCK, ACTION_EMPTYING, -1
6943 Xsand_sandstone_1, FALSE, FALSE,
6944 EL_QUICKSAND_FILLING, -1, -1
6947 Xsand_sandstone_2, FALSE, FALSE,
6948 EL_QUICKSAND_FILLING, -1, -1
6951 Xsand_sandstone_3, FALSE, FALSE,
6952 EL_QUICKSAND_FILLING, -1, -1
6955 Xsand_sandstone_4, FALSE, FALSE,
6956 EL_QUICKSAND_FILLING, -1, -1
6959 Xplant, TRUE, FALSE,
6960 EL_EMC_PLANT, -1, -1
6963 Yplant, FALSE, FALSE,
6964 EL_EMC_PLANT, -1, -1
6967 Xlenses, TRUE, FALSE,
6968 EL_EMC_LENSES, -1, -1
6971 Xmagnify, TRUE, FALSE,
6972 EL_EMC_MAGNIFIER, -1, -1
6975 Xdripper, TRUE, FALSE,
6976 EL_EMC_DRIPPER, -1, -1
6979 XdripperB, FALSE, FALSE,
6980 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6983 Xfake_blank, TRUE, FALSE,
6984 EL_INVISIBLE_WALL, -1, -1
6987 Xfake_blankB, FALSE, FALSE,
6988 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6991 Xfake_grass, TRUE, FALSE,
6992 EL_EMC_FAKE_GRASS, -1, -1
6995 Xfake_grassB, FALSE, FALSE,
6996 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6999 Xfake_door_1, TRUE, FALSE,
7000 EL_EM_GATE_1_GRAY, -1, -1
7003 Xfake_door_2, TRUE, FALSE,
7004 EL_EM_GATE_2_GRAY, -1, -1
7007 Xfake_door_3, TRUE, FALSE,
7008 EL_EM_GATE_3_GRAY, -1, -1
7011 Xfake_door_4, TRUE, FALSE,
7012 EL_EM_GATE_4_GRAY, -1, -1
7015 Xfake_door_5, TRUE, FALSE,
7016 EL_EMC_GATE_5_GRAY, -1, -1
7019 Xfake_door_6, TRUE, FALSE,
7020 EL_EMC_GATE_6_GRAY, -1, -1
7023 Xfake_door_7, TRUE, FALSE,
7024 EL_EMC_GATE_7_GRAY, -1, -1
7027 Xfake_door_8, TRUE, FALSE,
7028 EL_EMC_GATE_8_GRAY, -1, -1
7031 Xfake_acid_1, TRUE, FALSE,
7032 EL_EMC_FAKE_ACID, -1, -1
7035 Xfake_acid_2, FALSE, FALSE,
7036 EL_EMC_FAKE_ACID, -1, -1
7039 Xfake_acid_3, FALSE, FALSE,
7040 EL_EMC_FAKE_ACID, -1, -1
7043 Xfake_acid_4, FALSE, FALSE,
7044 EL_EMC_FAKE_ACID, -1, -1
7047 Xfake_acid_5, FALSE, FALSE,
7048 EL_EMC_FAKE_ACID, -1, -1
7051 Xfake_acid_6, FALSE, FALSE,
7052 EL_EMC_FAKE_ACID, -1, -1
7055 Xfake_acid_7, FALSE, FALSE,
7056 EL_EMC_FAKE_ACID, -1, -1
7059 Xfake_acid_8, FALSE, FALSE,
7060 EL_EMC_FAKE_ACID, -1, -1
7063 Xsteel_1, TRUE, FALSE,
7064 EL_STEELWALL, -1, -1
7067 Xsteel_2, TRUE, FALSE,
7068 EL_EMC_STEELWALL_2, -1, -1
7071 Xsteel_3, TRUE, FALSE,
7072 EL_EMC_STEELWALL_3, -1, -1
7075 Xsteel_4, TRUE, FALSE,
7076 EL_EMC_STEELWALL_4, -1, -1
7079 Xwall_1, TRUE, FALSE,
7083 Xwall_2, TRUE, FALSE,
7084 EL_EMC_WALL_14, -1, -1
7087 Xwall_3, TRUE, FALSE,
7088 EL_EMC_WALL_15, -1, -1
7091 Xwall_4, TRUE, FALSE,
7092 EL_EMC_WALL_16, -1, -1
7095 Xround_wall_1, TRUE, FALSE,
7096 EL_WALL_SLIPPERY, -1, -1
7099 Xround_wall_2, TRUE, FALSE,
7100 EL_EMC_WALL_SLIPPERY_2, -1, -1
7103 Xround_wall_3, TRUE, FALSE,
7104 EL_EMC_WALL_SLIPPERY_3, -1, -1
7107 Xround_wall_4, TRUE, FALSE,
7108 EL_EMC_WALL_SLIPPERY_4, -1, -1
7111 Xdecor_1, TRUE, FALSE,
7112 EL_EMC_WALL_8, -1, -1
7115 Xdecor_2, TRUE, FALSE,
7116 EL_EMC_WALL_6, -1, -1
7119 Xdecor_3, TRUE, FALSE,
7120 EL_EMC_WALL_4, -1, -1
7123 Xdecor_4, TRUE, FALSE,
7124 EL_EMC_WALL_7, -1, -1
7127 Xdecor_5, TRUE, FALSE,
7128 EL_EMC_WALL_5, -1, -1
7131 Xdecor_6, TRUE, FALSE,
7132 EL_EMC_WALL_9, -1, -1
7135 Xdecor_7, TRUE, FALSE,
7136 EL_EMC_WALL_10, -1, -1
7139 Xdecor_8, TRUE, FALSE,
7140 EL_EMC_WALL_1, -1, -1
7143 Xdecor_9, TRUE, FALSE,
7144 EL_EMC_WALL_2, -1, -1
7147 Xdecor_10, TRUE, FALSE,
7148 EL_EMC_WALL_3, -1, -1
7151 Xdecor_11, TRUE, FALSE,
7152 EL_EMC_WALL_11, -1, -1
7155 Xdecor_12, TRUE, FALSE,
7156 EL_EMC_WALL_12, -1, -1
7159 Xalpha_0, TRUE, FALSE,
7160 EL_CHAR('0'), -1, -1
7163 Xalpha_1, TRUE, FALSE,
7164 EL_CHAR('1'), -1, -1
7167 Xalpha_2, TRUE, FALSE,
7168 EL_CHAR('2'), -1, -1
7171 Xalpha_3, TRUE, FALSE,
7172 EL_CHAR('3'), -1, -1
7175 Xalpha_4, TRUE, FALSE,
7176 EL_CHAR('4'), -1, -1
7179 Xalpha_5, TRUE, FALSE,
7180 EL_CHAR('5'), -1, -1
7183 Xalpha_6, TRUE, FALSE,
7184 EL_CHAR('6'), -1, -1
7187 Xalpha_7, TRUE, FALSE,
7188 EL_CHAR('7'), -1, -1
7191 Xalpha_8, TRUE, FALSE,
7192 EL_CHAR('8'), -1, -1
7195 Xalpha_9, TRUE, FALSE,
7196 EL_CHAR('9'), -1, -1
7199 Xalpha_excla, TRUE, FALSE,
7200 EL_CHAR('!'), -1, -1
7203 Xalpha_quote, TRUE, FALSE,
7204 EL_CHAR('"'), -1, -1
7207 Xalpha_comma, TRUE, FALSE,
7208 EL_CHAR(','), -1, -1
7211 Xalpha_minus, TRUE, FALSE,
7212 EL_CHAR('-'), -1, -1
7215 Xalpha_perio, TRUE, FALSE,
7216 EL_CHAR('.'), -1, -1
7219 Xalpha_colon, TRUE, FALSE,
7220 EL_CHAR(':'), -1, -1
7223 Xalpha_quest, TRUE, FALSE,
7224 EL_CHAR('?'), -1, -1
7227 Xalpha_a, TRUE, FALSE,
7228 EL_CHAR('A'), -1, -1
7231 Xalpha_b, TRUE, FALSE,
7232 EL_CHAR('B'), -1, -1
7235 Xalpha_c, TRUE, FALSE,
7236 EL_CHAR('C'), -1, -1
7239 Xalpha_d, TRUE, FALSE,
7240 EL_CHAR('D'), -1, -1
7243 Xalpha_e, TRUE, FALSE,
7244 EL_CHAR('E'), -1, -1
7247 Xalpha_f, TRUE, FALSE,
7248 EL_CHAR('F'), -1, -1
7251 Xalpha_g, TRUE, FALSE,
7252 EL_CHAR('G'), -1, -1
7255 Xalpha_h, TRUE, FALSE,
7256 EL_CHAR('H'), -1, -1
7259 Xalpha_i, TRUE, FALSE,
7260 EL_CHAR('I'), -1, -1
7263 Xalpha_j, TRUE, FALSE,
7264 EL_CHAR('J'), -1, -1
7267 Xalpha_k, TRUE, FALSE,
7268 EL_CHAR('K'), -1, -1
7271 Xalpha_l, TRUE, FALSE,
7272 EL_CHAR('L'), -1, -1
7275 Xalpha_m, TRUE, FALSE,
7276 EL_CHAR('M'), -1, -1
7279 Xalpha_n, TRUE, FALSE,
7280 EL_CHAR('N'), -1, -1
7283 Xalpha_o, TRUE, FALSE,
7284 EL_CHAR('O'), -1, -1
7287 Xalpha_p, TRUE, FALSE,
7288 EL_CHAR('P'), -1, -1
7291 Xalpha_q, TRUE, FALSE,
7292 EL_CHAR('Q'), -1, -1
7295 Xalpha_r, TRUE, FALSE,
7296 EL_CHAR('R'), -1, -1
7299 Xalpha_s, TRUE, FALSE,
7300 EL_CHAR('S'), -1, -1
7303 Xalpha_t, TRUE, FALSE,
7304 EL_CHAR('T'), -1, -1
7307 Xalpha_u, TRUE, FALSE,
7308 EL_CHAR('U'), -1, -1
7311 Xalpha_v, TRUE, FALSE,
7312 EL_CHAR('V'), -1, -1
7315 Xalpha_w, TRUE, FALSE,
7316 EL_CHAR('W'), -1, -1
7319 Xalpha_x, TRUE, FALSE,
7320 EL_CHAR('X'), -1, -1
7323 Xalpha_y, TRUE, FALSE,
7324 EL_CHAR('Y'), -1, -1
7327 Xalpha_z, TRUE, FALSE,
7328 EL_CHAR('Z'), -1, -1
7331 Xalpha_arrow_e, TRUE, FALSE,
7332 EL_CHAR('>'), -1, -1
7335 Xalpha_arrow_w, TRUE, FALSE,
7336 EL_CHAR('<'), -1, -1
7339 Xalpha_copyr, TRUE, FALSE,
7340 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7344 Xboom_bug, FALSE, FALSE,
7345 EL_BUG, ACTION_EXPLODING, -1
7348 Xboom_bomb, FALSE, FALSE,
7349 EL_BOMB, ACTION_EXPLODING, -1
7352 Xboom_android, FALSE, FALSE,
7353 EL_EMC_ANDROID, ACTION_OTHER, -1
7356 Xboom_1, FALSE, FALSE,
7357 EL_DEFAULT, ACTION_EXPLODING, -1
7360 Xboom_2, FALSE, FALSE,
7361 EL_DEFAULT, ACTION_EXPLODING, -1
7364 Znormal, FALSE, FALSE,
7368 Zdynamite, FALSE, FALSE,
7372 Zplayer, FALSE, FALSE,
7376 ZBORDER, FALSE, FALSE,
7386 static struct Mapping_EM_to_RND_player
7395 em_player_mapping_list[] =
7399 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7403 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7407 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7411 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7415 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7419 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7423 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7427 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7431 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7435 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7439 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7443 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7447 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7451 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7455 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7459 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7463 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7467 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7471 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7475 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7479 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7483 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7487 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7491 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7495 EL_PLAYER_1, ACTION_DEFAULT, -1,
7499 EL_PLAYER_2, ACTION_DEFAULT, -1,
7503 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7507 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7511 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7515 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7519 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7523 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7527 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7531 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7535 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7539 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7543 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7547 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7551 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7555 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7559 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7563 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7567 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7571 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7575 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7579 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7583 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7587 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7591 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7595 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7599 EL_PLAYER_3, ACTION_DEFAULT, -1,
7603 EL_PLAYER_4, ACTION_DEFAULT, -1,
7612 int map_element_RND_to_EM(int element_rnd)
7614 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7615 static boolean mapping_initialized = FALSE;
7617 if (!mapping_initialized)
7621 // return "Xalpha_quest" for all undefined elements in mapping array
7622 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7623 mapping_RND_to_EM[i] = Xalpha_quest;
7625 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7626 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7627 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7628 em_object_mapping_list[i].element_em;
7630 mapping_initialized = TRUE;
7633 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7634 return mapping_RND_to_EM[element_rnd];
7636 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7641 int map_element_EM_to_RND(int element_em)
7643 static unsigned short mapping_EM_to_RND[TILE_MAX];
7644 static boolean mapping_initialized = FALSE;
7646 if (!mapping_initialized)
7650 // return "EL_UNKNOWN" for all undefined elements in mapping array
7651 for (i = 0; i < TILE_MAX; i++)
7652 mapping_EM_to_RND[i] = EL_UNKNOWN;
7654 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7655 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7656 em_object_mapping_list[i].element_rnd;
7658 mapping_initialized = TRUE;
7661 if (element_em >= 0 && element_em < TILE_MAX)
7662 return mapping_EM_to_RND[element_em];
7664 Error(ERR_WARN, "invalid EM level element %d", element_em);
7669 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7671 struct LevelInfo_EM *level_em = level->native_em_level;
7672 struct LEVEL *lev = level_em->lev;
7675 for (i = 0; i < TILE_MAX; i++)
7676 lev->android_array[i] = Xblank;
7678 for (i = 0; i < level->num_android_clone_elements; i++)
7680 int element_rnd = level->android_clone_element[i];
7681 int element_em = map_element_RND_to_EM(element_rnd);
7683 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7684 if (em_object_mapping_list[j].element_rnd == element_rnd)
7685 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7689 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7691 struct LevelInfo_EM *level_em = level->native_em_level;
7692 struct LEVEL *lev = level_em->lev;
7695 level->num_android_clone_elements = 0;
7697 for (i = 0; i < TILE_MAX; i++)
7699 int element_em = lev->android_array[i];
7701 boolean element_found = FALSE;
7703 if (element_em == Xblank)
7706 element_rnd = map_element_EM_to_RND(element_em);
7708 for (j = 0; j < level->num_android_clone_elements; j++)
7709 if (level->android_clone_element[j] == element_rnd)
7710 element_found = TRUE;
7714 level->android_clone_element[level->num_android_clone_elements++] =
7717 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7722 if (level->num_android_clone_elements == 0)
7724 level->num_android_clone_elements = 1;
7725 level->android_clone_element[0] = EL_EMPTY;
7729 int map_direction_RND_to_EM(int direction)
7731 return (direction == MV_UP ? 0 :
7732 direction == MV_RIGHT ? 1 :
7733 direction == MV_DOWN ? 2 :
7734 direction == MV_LEFT ? 3 :
7738 int map_direction_EM_to_RND(int direction)
7740 return (direction == 0 ? MV_UP :
7741 direction == 1 ? MV_RIGHT :
7742 direction == 2 ? MV_DOWN :
7743 direction == 3 ? MV_LEFT :
7747 int map_element_RND_to_SP(int element_rnd)
7749 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7751 if (element_rnd >= EL_SP_START &&
7752 element_rnd <= EL_SP_END)
7753 element_sp = element_rnd - EL_SP_START;
7754 else if (element_rnd == EL_EMPTY_SPACE)
7756 else if (element_rnd == EL_INVISIBLE_WALL)
7762 int map_element_SP_to_RND(int element_sp)
7764 int element_rnd = EL_UNKNOWN;
7766 if (element_sp >= 0x00 &&
7768 element_rnd = EL_SP_START + element_sp;
7769 else if (element_sp == 0x28)
7770 element_rnd = EL_INVISIBLE_WALL;
7775 int map_action_SP_to_RND(int action_sp)
7779 case actActive: return ACTION_ACTIVE;
7780 case actImpact: return ACTION_IMPACT;
7781 case actExploding: return ACTION_EXPLODING;
7782 case actDigging: return ACTION_DIGGING;
7783 case actSnapping: return ACTION_SNAPPING;
7784 case actCollecting: return ACTION_COLLECTING;
7785 case actPassing: return ACTION_PASSING;
7786 case actPushing: return ACTION_PUSHING;
7787 case actDropping: return ACTION_DROPPING;
7789 default: return ACTION_DEFAULT;
7793 int map_element_RND_to_MM(int element_rnd)
7795 return (element_rnd >= EL_MM_START_1 &&
7796 element_rnd <= EL_MM_END_1 ?
7797 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7799 element_rnd >= EL_MM_START_2 &&
7800 element_rnd <= EL_MM_END_2 ?
7801 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7803 element_rnd >= EL_CHAR_START &&
7804 element_rnd <= EL_CHAR_END ?
7805 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7807 element_rnd >= EL_MM_RUNTIME_START &&
7808 element_rnd <= EL_MM_RUNTIME_END ?
7809 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7811 element_rnd >= EL_MM_DUMMY_START &&
7812 element_rnd <= EL_MM_DUMMY_END ?
7813 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7815 EL_MM_EMPTY_NATIVE);
7818 int map_element_MM_to_RND(int element_mm)
7820 return (element_mm == EL_MM_EMPTY_NATIVE ||
7821 element_mm == EL_DF_EMPTY_NATIVE ?
7824 element_mm >= EL_MM_START_1_NATIVE &&
7825 element_mm <= EL_MM_END_1_NATIVE ?
7826 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7828 element_mm >= EL_MM_START_2_NATIVE &&
7829 element_mm <= EL_MM_END_2_NATIVE ?
7830 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7832 element_mm >= EL_MM_CHAR_START_NATIVE &&
7833 element_mm <= EL_MM_CHAR_END_NATIVE ?
7834 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7836 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7837 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7838 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7840 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7841 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7842 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7847 int map_action_MM_to_RND(int action_mm)
7849 // all MM actions are defined to exactly match their RND counterparts
7853 int map_sound_MM_to_RND(int sound_mm)
7857 case SND_MM_GAME_LEVELTIME_CHARGING:
7858 return SND_GAME_LEVELTIME_CHARGING;
7860 case SND_MM_GAME_HEALTH_CHARGING:
7861 return SND_GAME_HEALTH_CHARGING;
7864 return SND_UNDEFINED;
7868 int map_mm_wall_element(int element)
7870 return (element >= EL_MM_STEEL_WALL_START &&
7871 element <= EL_MM_STEEL_WALL_END ?
7874 element >= EL_MM_WOODEN_WALL_START &&
7875 element <= EL_MM_WOODEN_WALL_END ?
7878 element >= EL_MM_ICE_WALL_START &&
7879 element <= EL_MM_ICE_WALL_END ?
7882 element >= EL_MM_AMOEBA_WALL_START &&
7883 element <= EL_MM_AMOEBA_WALL_END ?
7886 element >= EL_DF_STEEL_WALL_START &&
7887 element <= EL_DF_STEEL_WALL_END ?
7890 element >= EL_DF_WOODEN_WALL_START &&
7891 element <= EL_DF_WOODEN_WALL_END ?
7897 int map_mm_wall_element_editor(int element)
7901 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7902 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7903 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7904 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7905 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7906 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7908 default: return element;
7912 int get_next_element(int element)
7916 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7917 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7918 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7919 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7920 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7921 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7922 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7923 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7924 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7925 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7926 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7928 default: return element;
7932 int el2img_mm(int element_mm)
7934 return el2img(map_element_MM_to_RND(element_mm));
7937 int el_act_dir2img(int element, int action, int direction)
7939 element = GFX_ELEMENT(element);
7940 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7942 // direction_graphic[][] == graphic[] for undefined direction graphics
7943 return element_info[element].direction_graphic[action][direction];
7946 static int el_act_dir2crm(int element, int action, int direction)
7948 element = GFX_ELEMENT(element);
7949 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7951 // direction_graphic[][] == graphic[] for undefined direction graphics
7952 return element_info[element].direction_crumbled[action][direction];
7955 int el_act2img(int element, int action)
7957 element = GFX_ELEMENT(element);
7959 return element_info[element].graphic[action];
7962 int el_act2crm(int element, int action)
7964 element = GFX_ELEMENT(element);
7966 return element_info[element].crumbled[action];
7969 int el_dir2img(int element, int direction)
7971 element = GFX_ELEMENT(element);
7973 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7976 int el2baseimg(int element)
7978 return element_info[element].graphic[ACTION_DEFAULT];
7981 int el2img(int element)
7983 element = GFX_ELEMENT(element);
7985 return element_info[element].graphic[ACTION_DEFAULT];
7988 int el2edimg(int element)
7990 element = GFX_ELEMENT(element);
7992 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7995 int el2preimg(int element)
7997 element = GFX_ELEMENT(element);
7999 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
8002 int el2panelimg(int element)
8004 element = GFX_ELEMENT(element);
8006 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8009 int font2baseimg(int font_nr)
8011 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8014 int getBeltNrFromBeltElement(int element)
8016 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8017 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8018 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8021 int getBeltNrFromBeltActiveElement(int element)
8023 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8024 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8025 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8028 int getBeltNrFromBeltSwitchElement(int element)
8030 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8031 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8032 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8035 int getBeltDirNrFromBeltElement(int element)
8037 static int belt_base_element[4] =
8039 EL_CONVEYOR_BELT_1_LEFT,
8040 EL_CONVEYOR_BELT_2_LEFT,
8041 EL_CONVEYOR_BELT_3_LEFT,
8042 EL_CONVEYOR_BELT_4_LEFT
8045 int belt_nr = getBeltNrFromBeltElement(element);
8046 int belt_dir_nr = element - belt_base_element[belt_nr];
8048 return (belt_dir_nr % 3);
8051 int getBeltDirNrFromBeltSwitchElement(int element)
8053 static int belt_base_element[4] =
8055 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8056 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8057 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8058 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8061 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8062 int belt_dir_nr = element - belt_base_element[belt_nr];
8064 return (belt_dir_nr % 3);
8067 int getBeltDirFromBeltElement(int element)
8069 static int belt_move_dir[3] =
8076 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8078 return belt_move_dir[belt_dir_nr];
8081 int getBeltDirFromBeltSwitchElement(int element)
8083 static int belt_move_dir[3] =
8090 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8092 return belt_move_dir[belt_dir_nr];
8095 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8097 static int belt_base_element[4] =
8099 EL_CONVEYOR_BELT_1_LEFT,
8100 EL_CONVEYOR_BELT_2_LEFT,
8101 EL_CONVEYOR_BELT_3_LEFT,
8102 EL_CONVEYOR_BELT_4_LEFT
8105 return belt_base_element[belt_nr] + belt_dir_nr;
8108 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8110 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8112 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8115 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8117 static int belt_base_element[4] =
8119 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8120 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8121 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8122 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8125 return belt_base_element[belt_nr] + belt_dir_nr;
8128 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8130 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8132 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8135 boolean getTeamMode_EM(void)
8137 return game.team_mode || network_playing;
8140 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8142 int game_frame_delay_value;
8144 game_frame_delay_value =
8145 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8146 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8149 if (tape.playing && tape.warp_forward && !tape.pausing)
8150 game_frame_delay_value = 0;
8152 return game_frame_delay_value;
8155 unsigned int InitRND(int seed)
8157 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8158 return InitEngineRandom_EM(seed);
8159 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8160 return InitEngineRandom_SP(seed);
8161 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8162 return InitEngineRandom_MM(seed);
8164 return InitEngineRandom_RND(seed);
8167 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8168 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8170 static int get_effective_element_EM(int tile, int frame_em)
8172 int element = object_mapping[tile].element_rnd;
8173 int action = object_mapping[tile].action;
8174 boolean is_backside = object_mapping[tile].is_backside;
8175 boolean action_removing = (action == ACTION_DIGGING ||
8176 action == ACTION_SNAPPING ||
8177 action == ACTION_COLLECTING);
8183 case Yacid_splash_eB:
8184 case Yacid_splash_wB:
8185 return (frame_em > 5 ? EL_EMPTY : element);
8191 else // frame_em == 7
8195 case Yacid_splash_eB:
8196 case Yacid_splash_wB:
8199 case Yemerald_stone:
8202 case Ydiamond_stone:
8206 case Xdrip_stretchB:
8225 case Xsand_stonein_1:
8226 case Xsand_stonein_2:
8227 case Xsand_stonein_3:
8228 case Xsand_stonein_4:
8232 return (is_backside || action_removing ? EL_EMPTY : element);
8237 static boolean check_linear_animation_EM(int tile)
8241 case Xsand_stonesand_1:
8242 case Xsand_stonesand_quickout_1:
8243 case Xsand_sandstone_1:
8244 case Xsand_stonein_1:
8245 case Xsand_stoneout_1:
8264 case Yacid_splash_eB:
8265 case Yacid_splash_wB:
8266 case Yemerald_stone:
8273 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8274 boolean has_crumbled_graphics,
8275 int crumbled, int sync_frame)
8277 // if element can be crumbled, but certain action graphics are just empty
8278 // space (like instantly snapping sand to empty space in 1 frame), do not
8279 // treat these empty space graphics as crumbled graphics in EMC engine
8280 if (crumbled == IMG_EMPTY_SPACE)
8281 has_crumbled_graphics = FALSE;
8283 if (has_crumbled_graphics)
8285 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8286 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8287 g_crumbled->anim_delay,
8288 g_crumbled->anim_mode,
8289 g_crumbled->anim_start_frame,
8292 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8293 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8295 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8296 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8298 g_em->has_crumbled_graphics = TRUE;
8302 g_em->crumbled_bitmap = NULL;
8303 g_em->crumbled_src_x = 0;
8304 g_em->crumbled_src_y = 0;
8305 g_em->crumbled_border_size = 0;
8306 g_em->crumbled_tile_size = 0;
8308 g_em->has_crumbled_graphics = FALSE;
8313 void ResetGfxAnimation_EM(int x, int y, int tile)
8319 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8320 int tile, int frame_em, int x, int y)
8322 int action = object_mapping[tile].action;
8323 int direction = object_mapping[tile].direction;
8324 int effective_element = get_effective_element_EM(tile, frame_em);
8325 int graphic = (direction == MV_NONE ?
8326 el_act2img(effective_element, action) :
8327 el_act_dir2img(effective_element, action, direction));
8328 struct GraphicInfo *g = &graphic_info[graphic];
8330 boolean action_removing = (action == ACTION_DIGGING ||
8331 action == ACTION_SNAPPING ||
8332 action == ACTION_COLLECTING);
8333 boolean action_moving = (action == ACTION_FALLING ||
8334 action == ACTION_MOVING ||
8335 action == ACTION_PUSHING ||
8336 action == ACTION_EATING ||
8337 action == ACTION_FILLING ||
8338 action == ACTION_EMPTYING);
8339 boolean action_falling = (action == ACTION_FALLING ||
8340 action == ACTION_FILLING ||
8341 action == ACTION_EMPTYING);
8343 // special case: graphic uses "2nd movement tile" and has defined
8344 // 7 frames for movement animation (or less) => use default graphic
8345 // for last (8th) frame which ends the movement animation
8346 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8348 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8349 graphic = (direction == MV_NONE ?
8350 el_act2img(effective_element, action) :
8351 el_act_dir2img(effective_element, action, direction));
8353 g = &graphic_info[graphic];
8356 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8360 else if (action_moving)
8362 boolean is_backside = object_mapping[tile].is_backside;
8366 int direction = object_mapping[tile].direction;
8367 int move_dir = (action_falling ? MV_DOWN : direction);
8372 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8373 if (g->double_movement && frame_em == 0)
8377 if (move_dir == MV_LEFT)
8378 GfxFrame[x - 1][y] = GfxFrame[x][y];
8379 else if (move_dir == MV_RIGHT)
8380 GfxFrame[x + 1][y] = GfxFrame[x][y];
8381 else if (move_dir == MV_UP)
8382 GfxFrame[x][y - 1] = GfxFrame[x][y];
8383 else if (move_dir == MV_DOWN)
8384 GfxFrame[x][y + 1] = GfxFrame[x][y];
8391 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8392 if (tile == Xsand_stonesand_quickout_1 ||
8393 tile == Xsand_stonesand_quickout_2)
8397 if (graphic_info[graphic].anim_global_sync)
8398 sync_frame = FrameCounter;
8399 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8400 sync_frame = GfxFrame[x][y];
8402 sync_frame = 0; // playfield border (pseudo steel)
8404 SetRandomAnimationValue(x, y);
8406 int frame = getAnimationFrame(g->anim_frames,
8409 g->anim_start_frame,
8412 g_em->unique_identifier =
8413 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8416 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8417 int tile, int frame_em, int x, int y)
8419 int action = object_mapping[tile].action;
8420 int direction = object_mapping[tile].direction;
8421 boolean is_backside = object_mapping[tile].is_backside;
8422 int effective_element = get_effective_element_EM(tile, frame_em);
8423 int effective_action = action;
8424 int graphic = (direction == MV_NONE ?
8425 el_act2img(effective_element, effective_action) :
8426 el_act_dir2img(effective_element, effective_action,
8428 int crumbled = (direction == MV_NONE ?
8429 el_act2crm(effective_element, effective_action) :
8430 el_act_dir2crm(effective_element, effective_action,
8432 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8433 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8434 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8435 struct GraphicInfo *g = &graphic_info[graphic];
8438 // special case: graphic uses "2nd movement tile" and has defined
8439 // 7 frames for movement animation (or less) => use default graphic
8440 // for last (8th) frame which ends the movement animation
8441 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8443 effective_action = ACTION_DEFAULT;
8444 graphic = (direction == MV_NONE ?
8445 el_act2img(effective_element, effective_action) :
8446 el_act_dir2img(effective_element, effective_action,
8448 crumbled = (direction == MV_NONE ?
8449 el_act2crm(effective_element, effective_action) :
8450 el_act_dir2crm(effective_element, effective_action,
8453 g = &graphic_info[graphic];
8456 if (graphic_info[graphic].anim_global_sync)
8457 sync_frame = FrameCounter;
8458 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8459 sync_frame = GfxFrame[x][y];
8461 sync_frame = 0; // playfield border (pseudo steel)
8463 SetRandomAnimationValue(x, y);
8465 int frame = getAnimationFrame(g->anim_frames,
8468 g->anim_start_frame,
8471 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8472 g->double_movement && is_backside);
8474 // (updating the "crumbled" graphic definitions is probably not really needed,
8475 // as animations for crumbled graphics can't be longer than one EMC cycle)
8476 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8480 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8481 int player_nr, int anim, int frame_em)
8483 int element = player_mapping[player_nr][anim].element_rnd;
8484 int action = player_mapping[player_nr][anim].action;
8485 int direction = player_mapping[player_nr][anim].direction;
8486 int graphic = (direction == MV_NONE ?
8487 el_act2img(element, action) :
8488 el_act_dir2img(element, action, direction));
8489 struct GraphicInfo *g = &graphic_info[graphic];
8492 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8494 stored_player[player_nr].StepFrame = frame_em;
8496 sync_frame = stored_player[player_nr].Frame;
8498 int frame = getAnimationFrame(g->anim_frames,
8501 g->anim_start_frame,
8504 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8505 &g_em->src_x, &g_em->src_y, FALSE);
8508 void InitGraphicInfo_EM(void)
8513 int num_em_gfx_errors = 0;
8515 if (graphic_info_em_object[0][0].bitmap == NULL)
8517 // EM graphics not yet initialized in em_open_all()
8522 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8525 // always start with reliable default values
8526 for (i = 0; i < TILE_MAX; i++)
8528 object_mapping[i].element_rnd = EL_UNKNOWN;
8529 object_mapping[i].is_backside = FALSE;
8530 object_mapping[i].action = ACTION_DEFAULT;
8531 object_mapping[i].direction = MV_NONE;
8534 // always start with reliable default values
8535 for (p = 0; p < MAX_PLAYERS; p++)
8537 for (i = 0; i < SPR_MAX; i++)
8539 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8540 player_mapping[p][i].action = ACTION_DEFAULT;
8541 player_mapping[p][i].direction = MV_NONE;
8545 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8547 int e = em_object_mapping_list[i].element_em;
8549 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8550 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8552 if (em_object_mapping_list[i].action != -1)
8553 object_mapping[e].action = em_object_mapping_list[i].action;
8555 if (em_object_mapping_list[i].direction != -1)
8556 object_mapping[e].direction =
8557 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8560 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8562 int a = em_player_mapping_list[i].action_em;
8563 int p = em_player_mapping_list[i].player_nr;
8565 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8567 if (em_player_mapping_list[i].action != -1)
8568 player_mapping[p][a].action = em_player_mapping_list[i].action;
8570 if (em_player_mapping_list[i].direction != -1)
8571 player_mapping[p][a].direction =
8572 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8575 for (i = 0; i < TILE_MAX; i++)
8577 int element = object_mapping[i].element_rnd;
8578 int action = object_mapping[i].action;
8579 int direction = object_mapping[i].direction;
8580 boolean is_backside = object_mapping[i].is_backside;
8581 boolean action_exploding = ((action == ACTION_EXPLODING ||
8582 action == ACTION_SMASHED_BY_ROCK ||
8583 action == ACTION_SMASHED_BY_SPRING) &&
8584 element != EL_DIAMOND);
8585 boolean action_active = (action == ACTION_ACTIVE);
8586 boolean action_other = (action == ACTION_OTHER);
8588 for (j = 0; j < 8; j++)
8590 int effective_element = get_effective_element_EM(i, j);
8591 int effective_action = (j < 7 ? action :
8592 i == Xdrip_stretch ? action :
8593 i == Xdrip_stretchB ? action :
8594 i == Ydrip_s1 ? action :
8595 i == Ydrip_s1B ? action :
8596 i == Xball_1B ? action :
8597 i == Xball_2 ? action :
8598 i == Xball_2B ? action :
8599 i == Yball_eat ? action :
8600 i == Ykey_1_eat ? action :
8601 i == Ykey_2_eat ? action :
8602 i == Ykey_3_eat ? action :
8603 i == Ykey_4_eat ? action :
8604 i == Ykey_5_eat ? action :
8605 i == Ykey_6_eat ? action :
8606 i == Ykey_7_eat ? action :
8607 i == Ykey_8_eat ? action :
8608 i == Ylenses_eat ? action :
8609 i == Ymagnify_eat ? action :
8610 i == Ygrass_eat ? action :
8611 i == Ydirt_eat ? action :
8612 i == Xsand_stonein_1 ? action :
8613 i == Xsand_stonein_2 ? action :
8614 i == Xsand_stonein_3 ? action :
8615 i == Xsand_stonein_4 ? action :
8616 i == Xsand_stoneout_1 ? action :
8617 i == Xsand_stoneout_2 ? action :
8618 i == Xboom_android ? ACTION_EXPLODING :
8619 action_exploding ? ACTION_EXPLODING :
8620 action_active ? action :
8621 action_other ? action :
8623 int graphic = (el_act_dir2img(effective_element, effective_action,
8625 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8627 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8628 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8629 boolean has_action_graphics = (graphic != base_graphic);
8630 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8631 struct GraphicInfo *g = &graphic_info[graphic];
8632 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8635 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8636 boolean special_animation = (action != ACTION_DEFAULT &&
8637 g->anim_frames == 3 &&
8638 g->anim_delay == 2 &&
8639 g->anim_mode & ANIM_LINEAR);
8640 int sync_frame = (i == Xdrip_stretch ? 7 :
8641 i == Xdrip_stretchB ? 7 :
8642 i == Ydrip_s2 ? j + 8 :
8643 i == Ydrip_s2B ? j + 8 :
8652 i == Xfake_acid_1 ? 0 :
8653 i == Xfake_acid_2 ? 10 :
8654 i == Xfake_acid_3 ? 20 :
8655 i == Xfake_acid_4 ? 30 :
8656 i == Xfake_acid_5 ? 40 :
8657 i == Xfake_acid_6 ? 50 :
8658 i == Xfake_acid_7 ? 60 :
8659 i == Xfake_acid_8 ? 70 :
8661 i == Xball_2B ? j + 8 :
8662 i == Yball_eat ? j + 1 :
8663 i == Ykey_1_eat ? j + 1 :
8664 i == Ykey_2_eat ? j + 1 :
8665 i == Ykey_3_eat ? j + 1 :
8666 i == Ykey_4_eat ? j + 1 :
8667 i == Ykey_5_eat ? j + 1 :
8668 i == Ykey_6_eat ? j + 1 :
8669 i == Ykey_7_eat ? j + 1 :
8670 i == Ykey_8_eat ? j + 1 :
8671 i == Ylenses_eat ? j + 1 :
8672 i == Ymagnify_eat ? j + 1 :
8673 i == Ygrass_eat ? j + 1 :
8674 i == Ydirt_eat ? j + 1 :
8675 i == Xamoeba_1 ? 0 :
8676 i == Xamoeba_2 ? 1 :
8677 i == Xamoeba_3 ? 2 :
8678 i == Xamoeba_4 ? 3 :
8679 i == Xamoeba_5 ? 0 :
8680 i == Xamoeba_6 ? 1 :
8681 i == Xamoeba_7 ? 2 :
8682 i == Xamoeba_8 ? 3 :
8683 i == Xexit_2 ? j + 8 :
8684 i == Xexit_3 ? j + 16 :
8685 i == Xdynamite_1 ? 0 :
8686 i == Xdynamite_2 ? 8 :
8687 i == Xdynamite_3 ? 16 :
8688 i == Xdynamite_4 ? 24 :
8689 i == Xsand_stonein_1 ? j + 1 :
8690 i == Xsand_stonein_2 ? j + 9 :
8691 i == Xsand_stonein_3 ? j + 17 :
8692 i == Xsand_stonein_4 ? j + 25 :
8693 i == Xsand_stoneout_1 && j == 0 ? 0 :
8694 i == Xsand_stoneout_1 && j == 1 ? 0 :
8695 i == Xsand_stoneout_1 && j == 2 ? 1 :
8696 i == Xsand_stoneout_1 && j == 3 ? 2 :
8697 i == Xsand_stoneout_1 && j == 4 ? 2 :
8698 i == Xsand_stoneout_1 && j == 5 ? 3 :
8699 i == Xsand_stoneout_1 && j == 6 ? 4 :
8700 i == Xsand_stoneout_1 && j == 7 ? 4 :
8701 i == Xsand_stoneout_2 && j == 0 ? 5 :
8702 i == Xsand_stoneout_2 && j == 1 ? 6 :
8703 i == Xsand_stoneout_2 && j == 2 ? 7 :
8704 i == Xsand_stoneout_2 && j == 3 ? 8 :
8705 i == Xsand_stoneout_2 && j == 4 ? 9 :
8706 i == Xsand_stoneout_2 && j == 5 ? 11 :
8707 i == Xsand_stoneout_2 && j == 6 ? 13 :
8708 i == Xsand_stoneout_2 && j == 7 ? 15 :
8709 i == Xboom_bug && j == 1 ? 2 :
8710 i == Xboom_bug && j == 2 ? 2 :
8711 i == Xboom_bug && j == 3 ? 4 :
8712 i == Xboom_bug && j == 4 ? 4 :
8713 i == Xboom_bug && j == 5 ? 2 :
8714 i == Xboom_bug && j == 6 ? 2 :
8715 i == Xboom_bug && j == 7 ? 0 :
8716 i == Xboom_bomb && j == 1 ? 2 :
8717 i == Xboom_bomb && j == 2 ? 2 :
8718 i == Xboom_bomb && j == 3 ? 4 :
8719 i == Xboom_bomb && j == 4 ? 4 :
8720 i == Xboom_bomb && j == 5 ? 2 :
8721 i == Xboom_bomb && j == 6 ? 2 :
8722 i == Xboom_bomb && j == 7 ? 0 :
8723 i == Xboom_android && j == 7 ? 6 :
8724 i == Xboom_1 && j == 1 ? 2 :
8725 i == Xboom_1 && j == 2 ? 2 :
8726 i == Xboom_1 && j == 3 ? 4 :
8727 i == Xboom_1 && j == 4 ? 4 :
8728 i == Xboom_1 && j == 5 ? 6 :
8729 i == Xboom_1 && j == 6 ? 6 :
8730 i == Xboom_1 && j == 7 ? 8 :
8731 i == Xboom_2 && j == 0 ? 8 :
8732 i == Xboom_2 && j == 1 ? 8 :
8733 i == Xboom_2 && j == 2 ? 10 :
8734 i == Xboom_2 && j == 3 ? 10 :
8735 i == Xboom_2 && j == 4 ? 10 :
8736 i == Xboom_2 && j == 5 ? 12 :
8737 i == Xboom_2 && j == 6 ? 12 :
8738 i == Xboom_2 && j == 7 ? 12 :
8739 special_animation && j == 4 ? 3 :
8740 effective_action != action ? 0 :
8744 Bitmap *debug_bitmap = g_em->bitmap;
8745 int debug_src_x = g_em->src_x;
8746 int debug_src_y = g_em->src_y;
8749 int frame = getAnimationFrame(g->anim_frames,
8752 g->anim_start_frame,
8755 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8756 g->double_movement && is_backside);
8758 g_em->bitmap = src_bitmap;
8759 g_em->src_x = src_x;
8760 g_em->src_y = src_y;
8761 g_em->src_offset_x = 0;
8762 g_em->src_offset_y = 0;
8763 g_em->dst_offset_x = 0;
8764 g_em->dst_offset_y = 0;
8765 g_em->width = TILEX;
8766 g_em->height = TILEY;
8768 g_em->preserve_background = FALSE;
8770 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8773 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8774 effective_action == ACTION_MOVING ||
8775 effective_action == ACTION_PUSHING ||
8776 effective_action == ACTION_EATING)) ||
8777 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8778 effective_action == ACTION_EMPTYING)))
8781 (effective_action == ACTION_FALLING ||
8782 effective_action == ACTION_FILLING ||
8783 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8784 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8785 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8786 int num_steps = (i == Ydrip_s1 ? 16 :
8787 i == Ydrip_s1B ? 16 :
8788 i == Ydrip_s2 ? 16 :
8789 i == Ydrip_s2B ? 16 :
8790 i == Xsand_stonein_1 ? 32 :
8791 i == Xsand_stonein_2 ? 32 :
8792 i == Xsand_stonein_3 ? 32 :
8793 i == Xsand_stonein_4 ? 32 :
8794 i == Xsand_stoneout_1 ? 16 :
8795 i == Xsand_stoneout_2 ? 16 : 8);
8796 int cx = ABS(dx) * (TILEX / num_steps);
8797 int cy = ABS(dy) * (TILEY / num_steps);
8798 int step_frame = (i == Ydrip_s2 ? j + 8 :
8799 i == Ydrip_s2B ? j + 8 :
8800 i == Xsand_stonein_2 ? j + 8 :
8801 i == Xsand_stonein_3 ? j + 16 :
8802 i == Xsand_stonein_4 ? j + 24 :
8803 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8804 int step = (is_backside ? step_frame : num_steps - step_frame);
8806 if (is_backside) // tile where movement starts
8808 if (dx < 0 || dy < 0)
8810 g_em->src_offset_x = cx * step;
8811 g_em->src_offset_y = cy * step;
8815 g_em->dst_offset_x = cx * step;
8816 g_em->dst_offset_y = cy * step;
8819 else // tile where movement ends
8821 if (dx < 0 || dy < 0)
8823 g_em->dst_offset_x = cx * step;
8824 g_em->dst_offset_y = cy * step;
8828 g_em->src_offset_x = cx * step;
8829 g_em->src_offset_y = cy * step;
8833 g_em->width = TILEX - cx * step;
8834 g_em->height = TILEY - cy * step;
8837 // create unique graphic identifier to decide if tile must be redrawn
8838 /* bit 31 - 16 (16 bit): EM style graphic
8839 bit 15 - 12 ( 4 bit): EM style frame
8840 bit 11 - 6 ( 6 bit): graphic width
8841 bit 5 - 0 ( 6 bit): graphic height */
8842 g_em->unique_identifier =
8843 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8847 // skip check for EMC elements not contained in original EMC artwork
8848 if (element == EL_EMC_FAKE_ACID)
8851 if (g_em->bitmap != debug_bitmap ||
8852 g_em->src_x != debug_src_x ||
8853 g_em->src_y != debug_src_y ||
8854 g_em->src_offset_x != 0 ||
8855 g_em->src_offset_y != 0 ||
8856 g_em->dst_offset_x != 0 ||
8857 g_em->dst_offset_y != 0 ||
8858 g_em->width != TILEX ||
8859 g_em->height != TILEY)
8861 static int last_i = -1;
8869 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8870 i, element, element_info[element].token_name,
8871 element_action_info[effective_action].suffix, direction);
8873 if (element != effective_element)
8874 printf(" [%d ('%s')]",
8876 element_info[effective_element].token_name);
8880 if (g_em->bitmap != debug_bitmap)
8881 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8882 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8884 if (g_em->src_x != debug_src_x ||
8885 g_em->src_y != debug_src_y)
8886 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8887 j, (is_backside ? 'B' : 'F'),
8888 g_em->src_x, g_em->src_y,
8889 g_em->src_x / 32, g_em->src_y / 32,
8890 debug_src_x, debug_src_y,
8891 debug_src_x / 32, debug_src_y / 32);
8893 if (g_em->src_offset_x != 0 ||
8894 g_em->src_offset_y != 0 ||
8895 g_em->dst_offset_x != 0 ||
8896 g_em->dst_offset_y != 0)
8897 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8899 g_em->src_offset_x, g_em->src_offset_y,
8900 g_em->dst_offset_x, g_em->dst_offset_y);
8902 if (g_em->width != TILEX ||
8903 g_em->height != TILEY)
8904 printf(" %d (%d): size %d,%d should be %d,%d\n",
8906 g_em->width, g_em->height, TILEX, TILEY);
8908 num_em_gfx_errors++;
8915 for (i = 0; i < TILE_MAX; i++)
8917 for (j = 0; j < 8; j++)
8919 int element = object_mapping[i].element_rnd;
8920 int action = object_mapping[i].action;
8921 int direction = object_mapping[i].direction;
8922 boolean is_backside = object_mapping[i].is_backside;
8923 int graphic_action = el_act_dir2img(element, action, direction);
8924 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8926 if ((action == ACTION_SMASHED_BY_ROCK ||
8927 action == ACTION_SMASHED_BY_SPRING ||
8928 action == ACTION_EATING) &&
8929 graphic_action == graphic_default)
8931 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8932 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8933 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8934 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8937 // no separate animation for "smashed by rock" -- use rock instead
8938 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8939 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8941 g_em->bitmap = g_xx->bitmap;
8942 g_em->src_x = g_xx->src_x;
8943 g_em->src_y = g_xx->src_y;
8944 g_em->src_offset_x = g_xx->src_offset_x;
8945 g_em->src_offset_y = g_xx->src_offset_y;
8946 g_em->dst_offset_x = g_xx->dst_offset_x;
8947 g_em->dst_offset_y = g_xx->dst_offset_y;
8948 g_em->width = g_xx->width;
8949 g_em->height = g_xx->height;
8950 g_em->unique_identifier = g_xx->unique_identifier;
8953 g_em->preserve_background = TRUE;
8958 for (p = 0; p < MAX_PLAYERS; p++)
8960 for (i = 0; i < SPR_MAX; i++)
8962 int element = player_mapping[p][i].element_rnd;
8963 int action = player_mapping[p][i].action;
8964 int direction = player_mapping[p][i].direction;
8966 for (j = 0; j < 8; j++)
8968 int effective_element = element;
8969 int effective_action = action;
8970 int graphic = (direction == MV_NONE ?
8971 el_act2img(effective_element, effective_action) :
8972 el_act_dir2img(effective_element, effective_action,
8974 struct GraphicInfo *g = &graphic_info[graphic];
8975 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8981 Bitmap *debug_bitmap = g_em->bitmap;
8982 int debug_src_x = g_em->src_x;
8983 int debug_src_y = g_em->src_y;
8986 int frame = getAnimationFrame(g->anim_frames,
8989 g->anim_start_frame,
8992 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8994 g_em->bitmap = src_bitmap;
8995 g_em->src_x = src_x;
8996 g_em->src_y = src_y;
8997 g_em->src_offset_x = 0;
8998 g_em->src_offset_y = 0;
8999 g_em->dst_offset_x = 0;
9000 g_em->dst_offset_y = 0;
9001 g_em->width = TILEX;
9002 g_em->height = TILEY;
9006 // skip check for EMC elements not contained in original EMC artwork
9007 if (element == EL_PLAYER_3 ||
9008 element == EL_PLAYER_4)
9011 if (g_em->bitmap != debug_bitmap ||
9012 g_em->src_x != debug_src_x ||
9013 g_em->src_y != debug_src_y)
9015 static int last_i = -1;
9023 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9024 p, i, element, element_info[element].token_name,
9025 element_action_info[effective_action].suffix, direction);
9027 if (element != effective_element)
9028 printf(" [%d ('%s')]",
9030 element_info[effective_element].token_name);
9034 if (g_em->bitmap != debug_bitmap)
9035 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9036 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9038 if (g_em->src_x != debug_src_x ||
9039 g_em->src_y != debug_src_y)
9040 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9042 g_em->src_x, g_em->src_y,
9043 g_em->src_x / 32, g_em->src_y / 32,
9044 debug_src_x, debug_src_y,
9045 debug_src_x / 32, debug_src_y / 32);
9047 num_em_gfx_errors++;
9057 printf("::: [%d errors found]\n", num_em_gfx_errors);
9063 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9064 boolean any_player_moving,
9065 boolean any_player_snapping,
9066 boolean any_player_dropping)
9068 if (frame == 0 && !any_player_dropping)
9070 if (!local_player->was_waiting)
9072 if (!CheckSaveEngineSnapshotToList())
9075 local_player->was_waiting = TRUE;
9078 else if (any_player_moving || any_player_snapping || any_player_dropping)
9080 local_player->was_waiting = FALSE;
9084 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9085 boolean murphy_is_dropping)
9087 if (murphy_is_waiting)
9089 if (!local_player->was_waiting)
9091 if (!CheckSaveEngineSnapshotToList())
9094 local_player->was_waiting = TRUE;
9099 local_player->was_waiting = FALSE;
9103 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9104 boolean button_released)
9106 if (button_released)
9108 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9109 CheckSaveEngineSnapshotToList();
9111 else if (element_clicked)
9113 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9114 CheckSaveEngineSnapshotToList();
9116 game.snapshot.changed_action = TRUE;
9120 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9121 boolean any_player_moving,
9122 boolean any_player_snapping,
9123 boolean any_player_dropping)
9125 if (tape.single_step && tape.recording && !tape.pausing)
9126 if (frame == 0 && !any_player_dropping)
9127 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9129 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9130 any_player_snapping, any_player_dropping);
9133 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9134 boolean murphy_is_dropping)
9136 boolean murphy_starts_dropping = FALSE;
9139 for (i = 0; i < MAX_PLAYERS; i++)
9140 if (stored_player[i].force_dropping)
9141 murphy_starts_dropping = TRUE;
9143 if (tape.single_step && tape.recording && !tape.pausing)
9144 if (murphy_is_waiting && !murphy_starts_dropping)
9145 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9147 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9150 void CheckSingleStepMode_MM(boolean element_clicked,
9151 boolean button_released)
9153 if (tape.single_step && tape.recording && !tape.pausing)
9154 if (button_released)
9155 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9157 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9160 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9161 int graphic, int sync_frame, int x, int y)
9163 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9165 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9168 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9170 return (IS_NEXT_FRAME(sync_frame, graphic));
9173 int getGraphicInfo_Delay(int graphic)
9175 return graphic_info[graphic].anim_delay;
9178 void PlayMenuSoundExt(int sound)
9180 if (sound == SND_UNDEFINED)
9183 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9184 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9187 if (IS_LOOP_SOUND(sound))
9188 PlaySoundLoop(sound);
9193 void PlayMenuSound(void)
9195 PlayMenuSoundExt(menu.sound[game_status]);
9198 void PlayMenuSoundStereo(int sound, int stereo_position)
9200 if (sound == SND_UNDEFINED)
9203 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9204 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9207 if (IS_LOOP_SOUND(sound))
9208 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9210 PlaySoundStereo(sound, stereo_position);
9213 void PlayMenuSoundIfLoopExt(int sound)
9215 if (sound == SND_UNDEFINED)
9218 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9219 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9222 if (IS_LOOP_SOUND(sound))
9223 PlaySoundLoop(sound);
9226 void PlayMenuSoundIfLoop(void)
9228 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9231 void PlayMenuMusicExt(int music)
9233 if (music == MUS_UNDEFINED)
9236 if (!setup.sound_music)
9239 if (IS_LOOP_MUSIC(music))
9240 PlayMusicLoop(music);
9245 void PlayMenuMusic(void)
9247 char *curr_music = getCurrentlyPlayingMusicFilename();
9248 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9250 if (!strEqual(curr_music, next_music))
9251 PlayMenuMusicExt(menu.music[game_status]);
9254 void PlayMenuSoundsAndMusic(void)
9260 static void FadeMenuSounds(void)
9265 static void FadeMenuMusic(void)
9267 char *curr_music = getCurrentlyPlayingMusicFilename();
9268 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9270 if (!strEqual(curr_music, next_music))
9274 void FadeMenuSoundsAndMusic(void)
9280 void PlaySoundActivating(void)
9283 PlaySound(SND_MENU_ITEM_ACTIVATING);
9287 void PlaySoundSelecting(void)
9290 PlaySound(SND_MENU_ITEM_SELECTING);
9294 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9296 boolean change_fullscreen = (setup.fullscreen !=
9297 video.fullscreen_enabled);
9298 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9299 setup.window_scaling_percent !=
9300 video.window_scaling_percent);
9302 if (change_window_scaling_percent && video.fullscreen_enabled)
9305 if (!change_window_scaling_percent && !video.fullscreen_available)
9308 if (change_window_scaling_percent)
9310 SDLSetWindowScaling(setup.window_scaling_percent);
9314 else if (change_fullscreen)
9316 SDLSetWindowFullscreen(setup.fullscreen);
9318 // set setup value according to successfully changed fullscreen mode
9319 setup.fullscreen = video.fullscreen_enabled;
9324 if (change_fullscreen ||
9325 change_window_scaling_percent)
9327 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9329 // save backbuffer content which gets lost when toggling fullscreen mode
9330 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9332 if (change_window_scaling_percent)
9334 // keep window mode, but change window scaling
9335 video.fullscreen_enabled = TRUE; // force new window scaling
9338 // toggle fullscreen
9339 ChangeVideoModeIfNeeded(setup.fullscreen);
9341 // set setup value according to successfully changed fullscreen mode
9342 setup.fullscreen = video.fullscreen_enabled;
9344 // restore backbuffer content from temporary backbuffer backup bitmap
9345 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9347 FreeBitmap(tmp_backbuffer);
9349 // update visible window/screen
9350 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9354 static void JoinRectangles(int *x, int *y, int *width, int *height,
9355 int x2, int y2, int width2, int height2)
9357 // do not join with "off-screen" rectangle
9358 if (x2 == -1 || y2 == -1)
9363 *width = MAX(*width, width2);
9364 *height = MAX(*height, height2);
9367 void SetAnimStatus(int anim_status_new)
9369 if (anim_status_new == GAME_MODE_MAIN)
9370 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9371 else if (anim_status_new == GAME_MODE_SCORES)
9372 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9374 global.anim_status_next = anim_status_new;
9376 // directly set screen modes that are entered without fading
9377 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9378 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9379 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9380 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9381 global.anim_status = global.anim_status_next;
9384 void SetGameStatus(int game_status_new)
9386 if (game_status_new != game_status)
9387 game_status_last_screen = game_status;
9389 game_status = game_status_new;
9391 SetAnimStatus(game_status_new);
9394 void SetFontStatus(int game_status_new)
9396 static int last_game_status = -1;
9398 if (game_status_new != -1)
9400 // set game status for font use after storing last game status
9401 last_game_status = game_status;
9402 game_status = game_status_new;
9406 // reset game status after font use from last stored game status
9407 game_status = last_game_status;
9411 void ResetFontStatus(void)
9416 void SetLevelSetInfo(char *identifier, int level_nr)
9418 setString(&levelset.identifier, identifier);
9420 levelset.level_nr = level_nr;
9423 boolean CheckIfAllViewportsHaveChanged(void)
9425 // if game status has not changed, viewports have not changed either
9426 if (game_status == game_status_last)
9429 // check if all viewports have changed with current game status
9431 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9432 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9433 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9434 int new_real_sx = vp_playfield->x;
9435 int new_real_sy = vp_playfield->y;
9436 int new_full_sxsize = vp_playfield->width;
9437 int new_full_sysize = vp_playfield->height;
9438 int new_dx = vp_door_1->x;
9439 int new_dy = vp_door_1->y;
9440 int new_dxsize = vp_door_1->width;
9441 int new_dysize = vp_door_1->height;
9442 int new_vx = vp_door_2->x;
9443 int new_vy = vp_door_2->y;
9444 int new_vxsize = vp_door_2->width;
9445 int new_vysize = vp_door_2->height;
9447 boolean playfield_viewport_has_changed =
9448 (new_real_sx != REAL_SX ||
9449 new_real_sy != REAL_SY ||
9450 new_full_sxsize != FULL_SXSIZE ||
9451 new_full_sysize != FULL_SYSIZE);
9453 boolean door_1_viewport_has_changed =
9456 new_dxsize != DXSIZE ||
9457 new_dysize != DYSIZE);
9459 boolean door_2_viewport_has_changed =
9462 new_vxsize != VXSIZE ||
9463 new_vysize != VYSIZE ||
9464 game_status_last == GAME_MODE_EDITOR);
9466 return (playfield_viewport_has_changed &&
9467 door_1_viewport_has_changed &&
9468 door_2_viewport_has_changed);
9471 boolean CheckFadeAll(void)
9473 return (CheckIfGlobalBorderHasChanged() ||
9474 CheckIfAllViewportsHaveChanged());
9477 void ChangeViewportPropertiesIfNeeded(void)
9479 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9480 FALSE : setup.small_game_graphics);
9481 int gfx_game_mode = game_status;
9482 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9484 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9485 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9486 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9487 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9488 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9489 int new_win_xsize = vp_window->width;
9490 int new_win_ysize = vp_window->height;
9491 int border_left = vp_playfield->border_left;
9492 int border_right = vp_playfield->border_right;
9493 int border_top = vp_playfield->border_top;
9494 int border_bottom = vp_playfield->border_bottom;
9495 int new_sx = vp_playfield->x + border_left;
9496 int new_sy = vp_playfield->y + border_top;
9497 int new_sxsize = vp_playfield->width - border_left - border_right;
9498 int new_sysize = vp_playfield->height - border_top - border_bottom;
9499 int new_real_sx = vp_playfield->x;
9500 int new_real_sy = vp_playfield->y;
9501 int new_full_sxsize = vp_playfield->width;
9502 int new_full_sysize = vp_playfield->height;
9503 int new_dx = vp_door_1->x;
9504 int new_dy = vp_door_1->y;
9505 int new_dxsize = vp_door_1->width;
9506 int new_dysize = vp_door_1->height;
9507 int new_vx = vp_door_2->x;
9508 int new_vy = vp_door_2->y;
9509 int new_vxsize = vp_door_2->width;
9510 int new_vysize = vp_door_2->height;
9511 int new_ex = vp_door_3->x;
9512 int new_ey = vp_door_3->y;
9513 int new_exsize = vp_door_3->width;
9514 int new_eysize = vp_door_3->height;
9515 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9516 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9517 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9518 int new_scr_fieldx = new_sxsize / tilesize;
9519 int new_scr_fieldy = new_sysize / tilesize;
9520 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9521 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9522 boolean init_gfx_buffers = FALSE;
9523 boolean init_video_buffer = FALSE;
9524 boolean init_gadgets_and_anims = FALSE;
9525 boolean init_em_graphics = FALSE;
9527 if (new_win_xsize != WIN_XSIZE ||
9528 new_win_ysize != WIN_YSIZE)
9530 WIN_XSIZE = new_win_xsize;
9531 WIN_YSIZE = new_win_ysize;
9533 init_video_buffer = TRUE;
9534 init_gfx_buffers = TRUE;
9535 init_gadgets_and_anims = TRUE;
9537 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9540 if (new_scr_fieldx != SCR_FIELDX ||
9541 new_scr_fieldy != SCR_FIELDY)
9543 // this always toggles between MAIN and GAME when using small tile size
9545 SCR_FIELDX = new_scr_fieldx;
9546 SCR_FIELDY = new_scr_fieldy;
9548 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9559 new_sxsize != SXSIZE ||
9560 new_sysize != SYSIZE ||
9561 new_dxsize != DXSIZE ||
9562 new_dysize != DYSIZE ||
9563 new_vxsize != VXSIZE ||
9564 new_vysize != VYSIZE ||
9565 new_exsize != EXSIZE ||
9566 new_eysize != EYSIZE ||
9567 new_real_sx != REAL_SX ||
9568 new_real_sy != REAL_SY ||
9569 new_full_sxsize != FULL_SXSIZE ||
9570 new_full_sysize != FULL_SYSIZE ||
9571 new_tilesize_var != TILESIZE_VAR
9574 // ------------------------------------------------------------------------
9575 // determine next fading area for changed viewport definitions
9576 // ------------------------------------------------------------------------
9578 // start with current playfield area (default fading area)
9581 FADE_SXSIZE = FULL_SXSIZE;
9582 FADE_SYSIZE = FULL_SYSIZE;
9584 // add new playfield area if position or size has changed
9585 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9586 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9588 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9589 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9592 // add current and new door 1 area if position or size has changed
9593 if (new_dx != DX || new_dy != DY ||
9594 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9596 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9597 DX, DY, DXSIZE, DYSIZE);
9598 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9599 new_dx, new_dy, new_dxsize, new_dysize);
9602 // add current and new door 2 area if position or size has changed
9603 if (new_vx != VX || new_vy != VY ||
9604 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9606 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9607 VX, VY, VXSIZE, VYSIZE);
9608 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9609 new_vx, new_vy, new_vxsize, new_vysize);
9612 // ------------------------------------------------------------------------
9613 // handle changed tile size
9614 // ------------------------------------------------------------------------
9616 if (new_tilesize_var != TILESIZE_VAR)
9618 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9620 // changing tile size invalidates scroll values of engine snapshots
9621 FreeEngineSnapshotSingle();
9623 // changing tile size requires update of graphic mapping for EM engine
9624 init_em_graphics = TRUE;
9635 SXSIZE = new_sxsize;
9636 SYSIZE = new_sysize;
9637 DXSIZE = new_dxsize;
9638 DYSIZE = new_dysize;
9639 VXSIZE = new_vxsize;
9640 VYSIZE = new_vysize;
9641 EXSIZE = new_exsize;
9642 EYSIZE = new_eysize;
9643 REAL_SX = new_real_sx;
9644 REAL_SY = new_real_sy;
9645 FULL_SXSIZE = new_full_sxsize;
9646 FULL_SYSIZE = new_full_sysize;
9647 TILESIZE_VAR = new_tilesize_var;
9649 init_gfx_buffers = TRUE;
9650 init_gadgets_and_anims = TRUE;
9652 // printf("::: viewports: init_gfx_buffers\n");
9653 // printf("::: viewports: init_gadgets_and_anims\n");
9656 if (init_gfx_buffers)
9658 // printf("::: init_gfx_buffers\n");
9660 SCR_FIELDX = new_scr_fieldx_buffers;
9661 SCR_FIELDY = new_scr_fieldy_buffers;
9665 SCR_FIELDX = new_scr_fieldx;
9666 SCR_FIELDY = new_scr_fieldy;
9668 SetDrawDeactivationMask(REDRAW_NONE);
9669 SetDrawBackgroundMask(REDRAW_FIELD);
9672 if (init_video_buffer)
9674 // printf("::: init_video_buffer\n");
9676 FreeAllImageTextures(); // needs old renderer to free the textures
9678 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9679 InitImageTextures();
9682 if (init_gadgets_and_anims)
9684 // printf("::: init_gadgets_and_anims\n");
9687 InitGlobalAnimations();
9690 if (init_em_graphics)
9692 InitGraphicInfo_EM();