1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 // select level set with EMC X11 graphics before activating EM GFX debugging
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 // tool button identifiers
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 // constants for number of doors and door parts
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 // forward declaration for internal use
169 static void UnmapToolButtons(void);
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 static int getFieldbufferOffsetX_RND(void)
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND(void)
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 static int getLevelFromScreenX_MM(int sx)
363 int level_xsize = level.native_mm_level->fieldx;
364 int full_xsize = level_xsize * TILESIZE_VAR;
366 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
369 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
374 static int getLevelFromScreenY_MM(int sy)
376 int level_ysize = level.native_mm_level->fieldy;
377 int full_ysize = level_ysize * TILESIZE_VAR;
379 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
382 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
387 int getLevelFromScreenX(int x)
389 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
390 return getLevelFromScreenX_EM(x);
391 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
392 return getLevelFromScreenX_SP(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
394 return getLevelFromScreenX_MM(x);
396 return getLevelFromScreenX_RND(x);
399 int getLevelFromScreenY(int y)
401 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
402 return getLevelFromScreenY_EM(y);
403 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
404 return getLevelFromScreenY_SP(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
406 return getLevelFromScreenY_MM(y);
408 return getLevelFromScreenY_RND(y);
411 void DumpTile(int x, int y)
417 printf_line("-", 79);
418 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
419 printf_line("-", 79);
421 if (!IN_LEV_FIELD(x, y))
423 printf("(not in level field)\n");
429 token_name = element_info[Feld[x][y]].token_name;
431 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
432 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
433 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
434 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
435 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
436 printf(" MovPos: %d\n", MovPos[x][y]);
437 printf(" MovDir: %d\n", MovDir[x][y]);
438 printf(" MovDelay: %d\n", MovDelay[x][y]);
439 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
440 printf(" CustomValue: %d\n", CustomValue[x][y]);
441 printf(" GfxElement: %d\n", GfxElement[x][y]);
442 printf(" GfxAction: %d\n", GfxAction[x][y]);
443 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
444 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
448 void DumpTileFromScreen(int sx, int sy)
450 int lx = getLevelFromScreenX(sx);
451 int ly = getLevelFromScreenY(sy);
456 void SetDrawtoField(int mode)
458 if (mode == DRAW_TO_FIELDBUFFER)
464 BX2 = SCR_FIELDX + 1;
465 BY2 = SCR_FIELDY + 1;
467 drawto_field = fieldbuffer;
469 else // DRAW_TO_BACKBUFFER
475 BX2 = SCR_FIELDX - 1;
476 BY2 = SCR_FIELDY - 1;
478 drawto_field = backbuffer;
482 static void RedrawPlayfield_RND(void)
484 if (game.envelope_active)
487 DrawLevel(REDRAW_ALL);
491 void RedrawPlayfield(void)
493 if (game_status != GAME_MODE_PLAYING)
496 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
497 RedrawPlayfield_EM(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
499 RedrawPlayfield_SP(TRUE);
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
501 RedrawPlayfield_MM();
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
503 RedrawPlayfield_RND();
505 BlitScreenToBitmap(backbuffer);
507 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
511 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
514 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
515 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
517 if (x == -1 && y == -1)
520 if (draw_target == DRAW_TO_SCREEN)
521 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
523 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
526 static void DrawMaskedBorderExt_FIELD(int draw_target)
528 if (global.border_status >= GAME_MODE_MAIN &&
529 global.border_status <= GAME_MODE_PLAYING &&
530 border.draw_masked[global.border_status])
531 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
535 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
537 // when drawing to backbuffer, never draw border over open doors
538 if (draw_target == DRAW_TO_BACKBUFFER &&
539 (GetDoorState() & DOOR_OPEN_1))
542 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
543 (global.border_status != GAME_MODE_EDITOR ||
544 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
545 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
548 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
550 // when drawing to backbuffer, never draw border over open doors
551 if (draw_target == DRAW_TO_BACKBUFFER &&
552 (GetDoorState() & DOOR_OPEN_2))
555 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
556 global.border_status != GAME_MODE_EDITOR)
557 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
560 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
562 // currently not available
565 static void DrawMaskedBorderExt_ALL(int draw_target)
567 DrawMaskedBorderExt_FIELD(draw_target);
568 DrawMaskedBorderExt_DOOR_1(draw_target);
569 DrawMaskedBorderExt_DOOR_2(draw_target);
570 DrawMaskedBorderExt_DOOR_3(draw_target);
573 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
575 // never draw masked screen borders on borderless screens
576 if (global.border_status == GAME_MODE_LOADING ||
577 global.border_status == GAME_MODE_TITLE)
580 if (redraw_mask & REDRAW_ALL)
581 DrawMaskedBorderExt_ALL(draw_target);
584 if (redraw_mask & REDRAW_FIELD)
585 DrawMaskedBorderExt_FIELD(draw_target);
586 if (redraw_mask & REDRAW_DOOR_1)
587 DrawMaskedBorderExt_DOOR_1(draw_target);
588 if (redraw_mask & REDRAW_DOOR_2)
589 DrawMaskedBorderExt_DOOR_2(draw_target);
590 if (redraw_mask & REDRAW_DOOR_3)
591 DrawMaskedBorderExt_DOOR_3(draw_target);
595 void DrawMaskedBorder_FIELD(void)
597 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
600 void DrawMaskedBorder(int redraw_mask)
602 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
605 void DrawMaskedBorderToTarget(int draw_target)
607 if (draw_target == DRAW_TO_BACKBUFFER ||
608 draw_target == DRAW_TO_SCREEN)
610 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
614 int last_border_status = global.border_status;
616 if (draw_target == DRAW_TO_FADE_SOURCE)
618 global.border_status = gfx.fade_border_source_status;
619 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
621 else if (draw_target == DRAW_TO_FADE_TARGET)
623 global.border_status = gfx.fade_border_target_status;
624 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
627 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
629 global.border_status = last_border_status;
630 gfx.masked_border_bitmap_ptr = backbuffer;
634 void DrawTileCursor(int draw_target)
640 int graphic = IMG_GLOBAL_TILE_CURSOR;
642 int tilesize = TILESIZE_VAR;
643 int width = tilesize;
644 int height = tilesize;
646 if (game_status != GAME_MODE_PLAYING)
649 if (!tile_cursor.enabled ||
653 if (tile_cursor.moving)
655 int step = TILESIZE_VAR / 4;
656 int dx = tile_cursor.target_x - tile_cursor.x;
657 int dy = tile_cursor.target_y - tile_cursor.y;
660 tile_cursor.x = tile_cursor.target_x;
662 tile_cursor.x += SIGN(dx) * step;
665 tile_cursor.y = tile_cursor.target_y;
667 tile_cursor.y += SIGN(dy) * step;
669 if (tile_cursor.x == tile_cursor.target_x &&
670 tile_cursor.y == tile_cursor.target_y)
671 tile_cursor.moving = FALSE;
674 dst_x = tile_cursor.x;
675 dst_y = tile_cursor.y;
677 frame = getGraphicAnimationFrame(graphic, -1);
679 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
682 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
683 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
685 if (draw_target == DRAW_TO_SCREEN)
686 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
688 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
692 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
694 int fx = getFieldbufferOffsetX_RND();
695 int fy = getFieldbufferOffsetY_RND();
697 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
700 void BlitScreenToBitmap(Bitmap *target_bitmap)
702 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
703 BlitScreenToBitmap_EM(target_bitmap);
704 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
705 BlitScreenToBitmap_SP(target_bitmap);
706 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
707 BlitScreenToBitmap_MM(target_bitmap);
708 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
709 BlitScreenToBitmap_RND(target_bitmap);
711 redraw_mask |= REDRAW_FIELD;
714 static void DrawFramesPerSecond(void)
717 int font_nr = FONT_TEXT_2;
718 int font_width = getFontWidth(font_nr);
719 int draw_deactivation_mask = GetDrawDeactivationMask();
720 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
722 // draw FPS with leading space (needed if field buffer deactivated)
723 sprintf(text, " %04.1f fps", global.frames_per_second);
725 // override draw deactivation mask (required for invisible warp mode)
726 SetDrawDeactivationMask(REDRAW_NONE);
728 // draw opaque FPS if field buffer deactivated, else draw masked FPS
729 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
730 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
732 // set draw deactivation mask to previous value
733 SetDrawDeactivationMask(draw_deactivation_mask);
735 // force full-screen redraw in this frame
736 redraw_mask = REDRAW_ALL;
740 static void PrintFrameTimeDebugging(void)
742 static unsigned int last_counter = 0;
743 unsigned int counter = Counter();
744 int diff_1 = counter - last_counter;
745 int diff_2 = diff_1 - GAME_FRAME_DELAY;
747 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
748 char diff_bar[2 * diff_2_max + 5];
752 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
754 for (i = 0; i < diff_2_max; i++)
755 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
756 i >= diff_2_max - diff_2_cut ? '-' : ' ');
758 diff_bar[pos++] = '|';
760 for (i = 0; i < diff_2_max; i++)
761 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
763 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
765 diff_bar[pos++] = '\0';
767 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
770 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
773 last_counter = counter;
777 static int unifiedRedrawMask(int mask)
779 if (mask & REDRAW_ALL)
782 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
788 static boolean equalRedrawMasks(int mask_1, int mask_2)
790 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
793 void BackToFront(void)
795 static int last_redraw_mask = REDRAW_NONE;
797 // force screen redraw in every frame to continue drawing global animations
798 // (but always use the last redraw mask to prevent unwanted side effects)
799 if (redraw_mask == REDRAW_NONE)
800 redraw_mask = last_redraw_mask;
802 last_redraw_mask = redraw_mask;
805 // masked border now drawn immediately when blitting backbuffer to window
807 // draw masked border to all viewports, if defined
808 DrawMaskedBorder(redraw_mask);
811 // draw frames per second (only if debug mode is enabled)
812 if (redraw_mask & REDRAW_FPS)
813 DrawFramesPerSecond();
815 // remove playfield redraw before potentially merging with doors redraw
816 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
817 redraw_mask &= ~REDRAW_FIELD;
819 // redraw complete window if both playfield and (some) doors need redraw
820 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
821 redraw_mask = REDRAW_ALL;
823 /* although redrawing the whole window would be fine for normal gameplay,
824 being able to only redraw the playfield is required for deactivating
825 certain drawing areas (mainly playfield) to work, which is needed for
826 warp-forward to be fast enough (by skipping redraw of most frames) */
828 if (redraw_mask & REDRAW_ALL)
830 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
832 else if (redraw_mask & REDRAW_FIELD)
834 BlitBitmap(backbuffer, window,
835 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
837 else if (redraw_mask & REDRAW_DOORS)
839 // merge door areas to prevent calling screen redraw more than once
845 if (redraw_mask & REDRAW_DOOR_1)
849 x2 = MAX(x2, DX + DXSIZE);
850 y2 = MAX(y2, DY + DYSIZE);
853 if (redraw_mask & REDRAW_DOOR_2)
857 x2 = MAX(x2, VX + VXSIZE);
858 y2 = MAX(y2, VY + VYSIZE);
861 if (redraw_mask & REDRAW_DOOR_3)
865 x2 = MAX(x2, EX + EXSIZE);
866 y2 = MAX(y2, EY + EYSIZE);
869 // make sure that at least one pixel is blitted, and inside the screen
870 // (else nothing is blitted, causing the animations not to be updated)
871 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
872 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
873 x2 = MIN(MAX(1, x2), WIN_XSIZE);
874 y2 = MIN(MAX(1, y2), WIN_YSIZE);
876 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
879 redraw_mask = REDRAW_NONE;
882 PrintFrameTimeDebugging();
886 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
888 unsigned int frame_delay_value_old = GetVideoFrameDelay();
890 SetVideoFrameDelay(frame_delay_value);
894 SetVideoFrameDelay(frame_delay_value_old);
897 static int fade_type_skip = FADE_TYPE_NONE;
899 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
901 void (*draw_border_function)(void) = NULL;
902 int x, y, width, height;
903 int fade_delay, post_delay;
905 if (fade_type == FADE_TYPE_FADE_OUT)
907 if (fade_type_skip != FADE_TYPE_NONE)
909 // skip all fade operations until specified fade operation
910 if (fade_type & fade_type_skip)
911 fade_type_skip = FADE_TYPE_NONE;
916 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
920 redraw_mask |= fade_mask;
922 if (fade_type == FADE_TYPE_SKIP)
924 fade_type_skip = fade_mode;
929 fade_delay = fading.fade_delay;
930 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
932 if (fade_type_skip != FADE_TYPE_NONE)
934 // skip all fade operations until specified fade operation
935 if (fade_type & fade_type_skip)
936 fade_type_skip = FADE_TYPE_NONE;
941 if (global.autoplay_leveldir)
946 if (fade_mask == REDRAW_FIELD)
951 height = FADE_SYSIZE;
953 if (border.draw_masked_when_fading)
954 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
956 DrawMaskedBorder_FIELD(); // draw once
966 if (!setup.fade_screens ||
968 fading.fade_mode == FADE_MODE_NONE)
970 if (fade_mode == FADE_MODE_FADE_OUT)
973 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
975 redraw_mask &= ~fade_mask;
980 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
981 draw_border_function);
983 redraw_mask &= ~fade_mask;
985 ClearAutoRepeatKeyEvents();
988 static void SetScreenStates_BeforeFadingIn(void)
990 // temporarily set screen mode for animations to screen after fading in
991 global.anim_status = global.anim_status_next;
993 // store backbuffer with all animations that will be started after fading in
994 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
995 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
997 // set screen mode for animations back to fading
998 global.anim_status = GAME_MODE_PSEUDO_FADING;
1001 static void SetScreenStates_AfterFadingIn(void)
1003 // store new source screen (to use correct masked border for fading)
1004 gfx.fade_border_source_status = global.border_status;
1006 global.anim_status = global.anim_status_next;
1009 static void SetScreenStates_BeforeFadingOut(void)
1011 // store new target screen (to use correct masked border for fading)
1012 gfx.fade_border_target_status = game_status;
1014 // set screen mode for animations to fading
1015 global.anim_status = GAME_MODE_PSEUDO_FADING;
1017 // store backbuffer with all animations that will be stopped for fading out
1018 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1019 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1022 static void SetScreenStates_AfterFadingOut(void)
1024 global.border_status = game_status;
1027 void FadeIn(int fade_mask)
1029 SetScreenStates_BeforeFadingIn();
1032 DrawMaskedBorder(REDRAW_ALL);
1035 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1036 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1038 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1042 FADE_SXSIZE = FULL_SXSIZE;
1043 FADE_SYSIZE = FULL_SYSIZE;
1045 // activate virtual buttons depending on upcoming game status
1046 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1047 game_status == GAME_MODE_PLAYING && !tape.playing)
1048 SetOverlayActive(TRUE);
1050 SetScreenStates_AfterFadingIn();
1052 // force update of global animation status in case of rapid screen changes
1053 redraw_mask = REDRAW_ALL;
1057 void FadeOut(int fade_mask)
1059 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1060 if (!equalRedrawMasks(fade_mask, redraw_mask))
1063 SetScreenStates_BeforeFadingOut();
1065 SetTileCursorActive(FALSE);
1066 SetOverlayActive(FALSE);
1069 DrawMaskedBorder(REDRAW_ALL);
1072 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1073 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1075 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1077 SetScreenStates_AfterFadingOut();
1080 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1082 static struct TitleFadingInfo fading_leave_stored;
1085 fading_leave_stored = fading_leave;
1087 fading = fading_leave_stored;
1090 void FadeSetEnterMenu(void)
1092 fading = menu.enter_menu;
1094 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1097 void FadeSetLeaveMenu(void)
1099 fading = menu.leave_menu;
1101 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1104 void FadeSetEnterScreen(void)
1106 fading = menu.enter_screen[game_status];
1108 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1111 void FadeSetNextScreen(void)
1113 fading = menu.next_screen[game_status];
1115 // (do not overwrite fade mode set by FadeSetEnterScreen)
1116 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1119 void FadeSetLeaveScreen(void)
1121 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1124 void FadeSetFromType(int type)
1126 if (type & TYPE_ENTER_SCREEN)
1127 FadeSetEnterScreen();
1128 else if (type & TYPE_ENTER)
1130 else if (type & TYPE_LEAVE)
1134 void FadeSetDisabled(void)
1136 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1138 fading = fading_none;
1141 void FadeSkipNextFadeIn(void)
1143 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1146 void FadeSkipNextFadeOut(void)
1148 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1151 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1153 if (graphic == IMG_UNDEFINED)
1156 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1158 return (graphic_info[graphic].bitmap != NULL || redefined ?
1159 graphic_info[graphic].bitmap :
1160 graphic_info[default_graphic].bitmap);
1163 static Bitmap *getBackgroundBitmap(int graphic)
1165 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1168 static Bitmap *getGlobalBorderBitmap(int graphic)
1170 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1173 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1176 (status == GAME_MODE_MAIN ||
1177 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1178 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1179 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1180 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1183 return getGlobalBorderBitmap(graphic);
1186 void SetWindowBackgroundImageIfDefined(int graphic)
1188 if (graphic_info[graphic].bitmap)
1189 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1192 void SetMainBackgroundImageIfDefined(int graphic)
1194 if (graphic_info[graphic].bitmap)
1195 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1198 void SetDoorBackgroundImageIfDefined(int graphic)
1200 if (graphic_info[graphic].bitmap)
1201 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1204 void SetWindowBackgroundImage(int graphic)
1206 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1209 void SetMainBackgroundImage(int graphic)
1211 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1214 void SetDoorBackgroundImage(int graphic)
1216 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1219 void SetPanelBackground(void)
1221 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1223 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1224 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1226 SetDoorBackgroundBitmap(bitmap_db_panel);
1229 void DrawBackground(int x, int y, int width, int height)
1231 // "drawto" might still point to playfield buffer here (hall of fame)
1232 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1234 if (IN_GFX_FIELD_FULL(x, y))
1235 redraw_mask |= REDRAW_FIELD;
1236 else if (IN_GFX_DOOR_1(x, y))
1237 redraw_mask |= REDRAW_DOOR_1;
1238 else if (IN_GFX_DOOR_2(x, y))
1239 redraw_mask |= REDRAW_DOOR_2;
1240 else if (IN_GFX_DOOR_3(x, y))
1241 redraw_mask |= REDRAW_DOOR_3;
1244 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1246 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1248 if (font->bitmap == NULL)
1251 DrawBackground(x, y, width, height);
1254 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1256 struct GraphicInfo *g = &graphic_info[graphic];
1258 if (g->bitmap == NULL)
1261 DrawBackground(x, y, width, height);
1264 static int game_status_last = -1;
1265 static Bitmap *global_border_bitmap_last = NULL;
1266 static Bitmap *global_border_bitmap = NULL;
1267 static int real_sx_last = -1, real_sy_last = -1;
1268 static int full_sxsize_last = -1, full_sysize_last = -1;
1269 static int dx_last = -1, dy_last = -1;
1270 static int dxsize_last = -1, dysize_last = -1;
1271 static int vx_last = -1, vy_last = -1;
1272 static int vxsize_last = -1, vysize_last = -1;
1273 static int ex_last = -1, ey_last = -1;
1274 static int exsize_last = -1, eysize_last = -1;
1276 boolean CheckIfGlobalBorderHasChanged(void)
1278 // if game status has not changed, global border has not changed either
1279 if (game_status == game_status_last)
1282 // determine and store new global border bitmap for current game status
1283 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1285 return (global_border_bitmap_last != global_border_bitmap);
1288 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1290 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1291 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1293 // if game status has not changed, nothing has to be redrawn
1294 if (game_status == game_status_last)
1297 // redraw if last screen was title screen
1298 if (game_status_last == GAME_MODE_TITLE)
1301 // redraw if global screen border has changed
1302 if (CheckIfGlobalBorderHasChanged())
1305 // redraw if position or size of playfield area has changed
1306 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1307 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1310 // redraw if position or size of door area has changed
1311 if (dx_last != DX || dy_last != DY ||
1312 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1315 // redraw if position or size of tape area has changed
1316 if (vx_last != VX || vy_last != VY ||
1317 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1320 // redraw if position or size of editor area has changed
1321 if (ex_last != EX || ey_last != EY ||
1322 exsize_last != EXSIZE || eysize_last != EYSIZE)
1329 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1332 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1334 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1337 void RedrawGlobalBorder(void)
1339 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1341 RedrawGlobalBorderFromBitmap(bitmap);
1343 redraw_mask = REDRAW_ALL;
1346 static void RedrawGlobalBorderIfNeeded(void)
1348 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1349 if (game_status == game_status_last)
1353 // copy current draw buffer to later copy back areas that have not changed
1354 if (game_status_last != GAME_MODE_TITLE)
1355 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1357 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1358 if (CheckIfGlobalBorderRedrawIsNeeded())
1361 // redraw global screen border (or clear, if defined to be empty)
1362 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1364 if (game_status == GAME_MODE_EDITOR)
1365 DrawSpecialEditorDoor();
1367 // copy previous playfield and door areas, if they are defined on both
1368 // previous and current screen and if they still have the same size
1370 if (real_sx_last != -1 && real_sy_last != -1 &&
1371 REAL_SX != -1 && REAL_SY != -1 &&
1372 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1373 BlitBitmap(bitmap_db_store_1, backbuffer,
1374 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1377 if (dx_last != -1 && dy_last != -1 &&
1378 DX != -1 && DY != -1 &&
1379 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1380 BlitBitmap(bitmap_db_store_1, backbuffer,
1381 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1383 if (game_status != GAME_MODE_EDITOR)
1385 if (vx_last != -1 && vy_last != -1 &&
1386 VX != -1 && VY != -1 &&
1387 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1388 BlitBitmap(bitmap_db_store_1, backbuffer,
1389 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1393 if (ex_last != -1 && ey_last != -1 &&
1394 EX != -1 && EY != -1 &&
1395 exsize_last == EXSIZE && eysize_last == EYSIZE)
1396 BlitBitmap(bitmap_db_store_1, backbuffer,
1397 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1400 redraw_mask = REDRAW_ALL;
1403 game_status_last = game_status;
1405 global_border_bitmap_last = global_border_bitmap;
1407 real_sx_last = REAL_SX;
1408 real_sy_last = REAL_SY;
1409 full_sxsize_last = FULL_SXSIZE;
1410 full_sysize_last = FULL_SYSIZE;
1413 dxsize_last = DXSIZE;
1414 dysize_last = DYSIZE;
1417 vxsize_last = VXSIZE;
1418 vysize_last = VYSIZE;
1421 exsize_last = EXSIZE;
1422 eysize_last = EYSIZE;
1425 void ClearField(void)
1427 RedrawGlobalBorderIfNeeded();
1429 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1430 // (when entering hall of fame after playing)
1431 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1433 // !!! maybe this should be done before clearing the background !!!
1434 if (game_status == GAME_MODE_PLAYING)
1436 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1437 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1441 SetDrawtoField(DRAW_TO_BACKBUFFER);
1445 void MarkTileDirty(int x, int y)
1447 redraw_mask |= REDRAW_FIELD;
1450 void SetBorderElement(void)
1454 BorderElement = EL_EMPTY;
1456 // the MM game engine does not use a visible border element
1457 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1460 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1462 for (x = 0; x < lev_fieldx; x++)
1464 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1465 BorderElement = EL_STEELWALL;
1467 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1473 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1474 int max_array_fieldx, int max_array_fieldy,
1475 short field[max_array_fieldx][max_array_fieldy],
1476 int max_fieldx, int max_fieldy)
1480 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1481 static int safety = 0;
1483 // check if starting field still has the desired content
1484 if (field[from_x][from_y] == fill_element)
1489 if (safety > max_fieldx * max_fieldy)
1490 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1492 old_element = field[from_x][from_y];
1493 field[from_x][from_y] = fill_element;
1495 for (i = 0; i < 4; i++)
1497 x = from_x + check[i][0];
1498 y = from_y + check[i][1];
1500 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1501 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1502 field, max_fieldx, max_fieldy);
1508 void FloodFillLevel(int from_x, int from_y, int fill_element,
1509 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1510 int max_fieldx, int max_fieldy)
1512 FloodFillLevelExt(from_x, from_y, fill_element,
1513 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1514 max_fieldx, max_fieldy);
1517 void SetRandomAnimationValue(int x, int y)
1519 gfx.anim_random_frame = GfxRandom[x][y];
1522 int getGraphicAnimationFrame(int graphic, int sync_frame)
1524 // animation synchronized with global frame counter, not move position
1525 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1526 sync_frame = FrameCounter;
1528 return getAnimationFrame(graphic_info[graphic].anim_frames,
1529 graphic_info[graphic].anim_delay,
1530 graphic_info[graphic].anim_mode,
1531 graphic_info[graphic].anim_start_frame,
1535 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1537 struct GraphicInfo *g = &graphic_info[graphic];
1538 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1540 if (tilesize == gfx.standard_tile_size)
1541 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1542 else if (tilesize == game.tile_size)
1543 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1545 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1548 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1549 boolean get_backside)
1551 struct GraphicInfo *g = &graphic_info[graphic];
1552 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1553 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1555 if (g->offset_y == 0) // frames are ordered horizontally
1557 int max_width = g->anim_frames_per_line * g->width;
1558 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1560 *x = pos % max_width;
1561 *y = src_y % g->height + pos / max_width * g->height;
1563 else if (g->offset_x == 0) // frames are ordered vertically
1565 int max_height = g->anim_frames_per_line * g->height;
1566 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1568 *x = src_x % g->width + pos / max_height * g->width;
1569 *y = pos % max_height;
1571 else // frames are ordered diagonally
1573 *x = src_x + frame * g->offset_x;
1574 *y = src_y + frame * g->offset_y;
1578 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1579 Bitmap **bitmap, int *x, int *y,
1580 boolean get_backside)
1582 struct GraphicInfo *g = &graphic_info[graphic];
1584 // if no graphics defined at all, use fallback graphics
1585 if (g->bitmaps == NULL)
1586 *g = graphic_info[IMG_CHAR_EXCLAM];
1588 // if no in-game graphics defined, always use standard graphic size
1589 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1590 tilesize = TILESIZE;
1592 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1593 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1595 *x = *x * tilesize / g->tile_size;
1596 *y = *y * tilesize / g->tile_size;
1599 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1600 Bitmap **bitmap, int *x, int *y)
1602 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1605 void getFixedGraphicSource(int graphic, int frame,
1606 Bitmap **bitmap, int *x, int *y)
1608 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1611 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1613 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1616 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1617 int *x, int *y, boolean get_backside)
1619 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1623 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1625 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1628 void DrawGraphic(int x, int y, int graphic, int frame)
1631 if (!IN_SCR_FIELD(x, y))
1633 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1634 printf("DrawGraphic(): This should never happen!\n");
1639 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1642 MarkTileDirty(x, y);
1645 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1648 if (!IN_SCR_FIELD(x, y))
1650 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1651 printf("DrawGraphic(): This should never happen!\n");
1656 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1658 MarkTileDirty(x, y);
1661 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1667 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1669 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1672 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1678 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1679 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1682 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1685 if (!IN_SCR_FIELD(x, y))
1687 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1688 printf("DrawGraphicThruMask(): This should never happen!\n");
1693 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1696 MarkTileDirty(x, y);
1699 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1702 if (!IN_SCR_FIELD(x, y))
1704 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1705 printf("DrawGraphicThruMask(): This should never happen!\n");
1710 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1712 MarkTileDirty(x, y);
1715 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1721 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1723 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1727 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1728 int graphic, int frame)
1733 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1735 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1739 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1741 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1743 MarkTileDirty(x / tilesize, y / tilesize);
1746 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1749 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1750 graphic, frame, tilesize);
1751 MarkTileDirty(x / tilesize, y / tilesize);
1754 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1760 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1761 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1764 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1765 int frame, int tilesize)
1770 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1771 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1774 void DrawMiniGraphic(int x, int y, int graphic)
1776 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1777 MarkTileDirty(x / 2, y / 2);
1780 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1785 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1786 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1789 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1790 int graphic, int frame,
1791 int cut_mode, int mask_mode)
1796 int width = TILEX, height = TILEY;
1799 if (dx || dy) // shifted graphic
1801 if (x < BX1) // object enters playfield from the left
1808 else if (x > BX2) // object enters playfield from the right
1814 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1820 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1822 else if (dx) // general horizontal movement
1823 MarkTileDirty(x + SIGN(dx), y);
1825 if (y < BY1) // object enters playfield from the top
1827 if (cut_mode == CUT_BELOW) // object completely above top border
1835 else if (y > BY2) // object enters playfield from the bottom
1841 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1847 else if (dy > 0 && cut_mode == CUT_ABOVE)
1849 if (y == BY2) // object completely above bottom border
1855 MarkTileDirty(x, y + 1);
1856 } // object leaves playfield to the bottom
1857 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1859 else if (dy) // general vertical movement
1860 MarkTileDirty(x, y + SIGN(dy));
1864 if (!IN_SCR_FIELD(x, y))
1866 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1867 printf("DrawGraphicShifted(): This should never happen!\n");
1872 width = width * TILESIZE_VAR / TILESIZE;
1873 height = height * TILESIZE_VAR / TILESIZE;
1874 cx = cx * TILESIZE_VAR / TILESIZE;
1875 cy = cy * TILESIZE_VAR / TILESIZE;
1876 dx = dx * TILESIZE_VAR / TILESIZE;
1877 dy = dy * TILESIZE_VAR / TILESIZE;
1879 if (width > 0 && height > 0)
1881 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1886 dst_x = FX + x * TILEX_VAR + dx;
1887 dst_y = FY + y * TILEY_VAR + dy;
1889 if (mask_mode == USE_MASKING)
1890 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1893 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1896 MarkTileDirty(x, y);
1900 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1901 int graphic, int frame,
1902 int cut_mode, int mask_mode)
1907 int width = TILEX_VAR, height = TILEY_VAR;
1910 int x2 = x + SIGN(dx);
1911 int y2 = y + SIGN(dy);
1913 // movement with two-tile animations must be sync'ed with movement position,
1914 // not with current GfxFrame (which can be higher when using slow movement)
1915 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1916 int anim_frames = graphic_info[graphic].anim_frames;
1918 // (we also need anim_delay here for movement animations with less frames)
1919 int anim_delay = graphic_info[graphic].anim_delay;
1920 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1922 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1923 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1925 // re-calculate animation frame for two-tile movement animation
1926 frame = getGraphicAnimationFrame(graphic, sync_frame);
1928 // check if movement start graphic inside screen area and should be drawn
1929 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1931 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1933 dst_x = FX + x1 * TILEX_VAR;
1934 dst_y = FY + y1 * TILEY_VAR;
1936 if (mask_mode == USE_MASKING)
1937 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1940 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1943 MarkTileDirty(x1, y1);
1946 // check if movement end graphic inside screen area and should be drawn
1947 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1949 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1951 dst_x = FX + x2 * TILEX_VAR;
1952 dst_y = FY + y2 * TILEY_VAR;
1954 if (mask_mode == USE_MASKING)
1955 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1958 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1961 MarkTileDirty(x2, y2);
1965 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1966 int graphic, int frame,
1967 int cut_mode, int mask_mode)
1971 DrawGraphic(x, y, graphic, frame);
1976 if (graphic_info[graphic].double_movement) // EM style movement images
1977 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1979 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1982 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1983 int graphic, int frame, int cut_mode)
1985 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1988 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1989 int cut_mode, int mask_mode)
1991 int lx = LEVELX(x), ly = LEVELY(y);
1995 if (IN_LEV_FIELD(lx, ly))
1997 SetRandomAnimationValue(lx, ly);
1999 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2000 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2002 // do not use double (EM style) movement graphic when not moving
2003 if (graphic_info[graphic].double_movement && !dx && !dy)
2005 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2006 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2009 else // border element
2011 graphic = el2img(element);
2012 frame = getGraphicAnimationFrame(graphic, -1);
2015 if (element == EL_EXPANDABLE_WALL)
2017 boolean left_stopped = FALSE, right_stopped = FALSE;
2019 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2020 left_stopped = TRUE;
2021 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2022 right_stopped = TRUE;
2024 if (left_stopped && right_stopped)
2026 else if (left_stopped)
2028 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2029 frame = graphic_info[graphic].anim_frames - 1;
2031 else if (right_stopped)
2033 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2034 frame = graphic_info[graphic].anim_frames - 1;
2039 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2040 else if (mask_mode == USE_MASKING)
2041 DrawGraphicThruMask(x, y, graphic, frame);
2043 DrawGraphic(x, y, graphic, frame);
2046 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2047 int cut_mode, int mask_mode)
2049 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2050 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2051 cut_mode, mask_mode);
2054 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2057 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2060 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2063 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2066 void DrawLevelElementThruMask(int x, int y, int element)
2068 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2071 void DrawLevelFieldThruMask(int x, int y)
2073 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2076 // !!! implementation of quicksand is totally broken !!!
2077 #define IS_CRUMBLED_TILE(x, y, e) \
2078 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2079 !IS_MOVING(x, y) || \
2080 (e) == EL_QUICKSAND_EMPTYING || \
2081 (e) == EL_QUICKSAND_FAST_EMPTYING))
2083 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2088 int width, height, cx, cy;
2089 int sx = SCREENX(x), sy = SCREENY(y);
2090 int crumbled_border_size = graphic_info[graphic].border_size;
2091 int crumbled_tile_size = graphic_info[graphic].tile_size;
2092 int crumbled_border_size_var =
2093 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2096 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2098 for (i = 1; i < 4; i++)
2100 int dxx = (i & 1 ? dx : 0);
2101 int dyy = (i & 2 ? dy : 0);
2104 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2107 // check if neighbour field is of same crumble type
2108 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2109 graphic_info[graphic].class ==
2110 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2112 // return if check prevents inner corner
2113 if (same == (dxx == dx && dyy == dy))
2117 // if we reach this point, we have an inner corner
2119 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2121 width = crumbled_border_size_var;
2122 height = crumbled_border_size_var;
2123 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2124 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2126 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2127 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2130 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2135 int width, height, bx, by, cx, cy;
2136 int sx = SCREENX(x), sy = SCREENY(y);
2137 int crumbled_border_size = graphic_info[graphic].border_size;
2138 int crumbled_tile_size = graphic_info[graphic].tile_size;
2139 int crumbled_border_size_var =
2140 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2141 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2144 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2146 // draw simple, sloppy, non-corner-accurate crumbled border
2148 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2149 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2150 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2151 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2153 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2154 FX + sx * TILEX_VAR + cx,
2155 FY + sy * TILEY_VAR + cy);
2157 // (remaining middle border part must be at least as big as corner part)
2158 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2159 crumbled_border_size_var >= TILESIZE_VAR / 3)
2162 // correct corners of crumbled border, if needed
2164 for (i = -1; i <= 1; i += 2)
2166 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2167 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2168 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2171 // check if neighbour field is of same crumble type
2172 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2173 graphic_info[graphic].class ==
2174 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2176 // no crumbled corner, but continued crumbled border
2178 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2179 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2180 int b1 = (i == 1 ? crumbled_border_size_var :
2181 TILESIZE_VAR - 2 * crumbled_border_size_var);
2183 width = crumbled_border_size_var;
2184 height = crumbled_border_size_var;
2186 if (dir == 1 || dir == 2)
2201 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2203 FX + sx * TILEX_VAR + cx,
2204 FY + sy * TILEY_VAR + cy);
2209 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2211 int sx = SCREENX(x), sy = SCREENY(y);
2214 static int xy[4][2] =
2222 if (!IN_LEV_FIELD(x, y))
2225 element = TILE_GFX_ELEMENT(x, y);
2227 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2229 if (!IN_SCR_FIELD(sx, sy))
2232 // crumble field borders towards direct neighbour fields
2233 for (i = 0; i < 4; i++)
2235 int xx = x + xy[i][0];
2236 int yy = y + xy[i][1];
2238 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2241 // check if neighbour field is of same crumble type
2242 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2243 graphic_info[graphic].class ==
2244 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2247 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2250 // crumble inner field corners towards corner neighbour fields
2251 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2252 graphic_info[graphic].anim_frames == 2)
2254 for (i = 0; i < 4; i++)
2256 int dx = (i & 1 ? +1 : -1);
2257 int dy = (i & 2 ? +1 : -1);
2259 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2263 MarkTileDirty(sx, sy);
2265 else // center field is not crumbled -- crumble neighbour fields
2267 // crumble field borders of direct neighbour fields
2268 for (i = 0; i < 4; i++)
2270 int xx = x + xy[i][0];
2271 int yy = y + xy[i][1];
2272 int sxx = sx + xy[i][0];
2273 int syy = sy + xy[i][1];
2275 if (!IN_LEV_FIELD(xx, yy) ||
2276 !IN_SCR_FIELD(sxx, syy))
2279 // do not crumble fields that are being digged or snapped
2280 if (Feld[xx][yy] == EL_EMPTY ||
2281 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2284 element = TILE_GFX_ELEMENT(xx, yy);
2286 if (!IS_CRUMBLED_TILE(xx, yy, element))
2289 graphic = el_act2crm(element, ACTION_DEFAULT);
2291 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2293 MarkTileDirty(sxx, syy);
2296 // crumble inner field corners of corner neighbour fields
2297 for (i = 0; i < 4; i++)
2299 int dx = (i & 1 ? +1 : -1);
2300 int dy = (i & 2 ? +1 : -1);
2306 if (!IN_LEV_FIELD(xx, yy) ||
2307 !IN_SCR_FIELD(sxx, syy))
2310 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2313 element = TILE_GFX_ELEMENT(xx, yy);
2315 if (!IS_CRUMBLED_TILE(xx, yy, element))
2318 graphic = el_act2crm(element, ACTION_DEFAULT);
2320 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2321 graphic_info[graphic].anim_frames == 2)
2322 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2324 MarkTileDirty(sxx, syy);
2329 void DrawLevelFieldCrumbled(int x, int y)
2333 if (!IN_LEV_FIELD(x, y))
2336 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2337 GfxElement[x][y] != EL_UNDEFINED &&
2338 GFX_CRUMBLED(GfxElement[x][y]))
2340 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2345 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2347 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2350 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2353 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2354 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2355 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2356 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2357 int sx = SCREENX(x), sy = SCREENY(y);
2359 DrawGraphic(sx, sy, graphic1, frame1);
2360 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2363 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2365 int sx = SCREENX(x), sy = SCREENY(y);
2366 static int xy[4][2] =
2375 // crumble direct neighbour fields (required for field borders)
2376 for (i = 0; i < 4; i++)
2378 int xx = x + xy[i][0];
2379 int yy = y + xy[i][1];
2380 int sxx = sx + xy[i][0];
2381 int syy = sy + xy[i][1];
2383 if (!IN_LEV_FIELD(xx, yy) ||
2384 !IN_SCR_FIELD(sxx, syy) ||
2385 !GFX_CRUMBLED(Feld[xx][yy]) ||
2389 DrawLevelField(xx, yy);
2392 // crumble corner neighbour fields (required for inner field corners)
2393 for (i = 0; i < 4; i++)
2395 int dx = (i & 1 ? +1 : -1);
2396 int dy = (i & 2 ? +1 : -1);
2402 if (!IN_LEV_FIELD(xx, yy) ||
2403 !IN_SCR_FIELD(sxx, syy) ||
2404 !GFX_CRUMBLED(Feld[xx][yy]) ||
2408 int element = TILE_GFX_ELEMENT(xx, yy);
2409 int graphic = el_act2crm(element, ACTION_DEFAULT);
2411 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2412 graphic_info[graphic].anim_frames == 2)
2413 DrawLevelField(xx, yy);
2417 static int getBorderElement(int x, int y)
2421 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2422 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2423 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2424 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2425 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2426 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2427 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2429 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2430 int steel_position = (x == -1 && y == -1 ? 0 :
2431 x == lev_fieldx && y == -1 ? 1 :
2432 x == -1 && y == lev_fieldy ? 2 :
2433 x == lev_fieldx && y == lev_fieldy ? 3 :
2434 x == -1 || x == lev_fieldx ? 4 :
2435 y == -1 || y == lev_fieldy ? 5 : 6);
2437 return border[steel_position][steel_type];
2440 void DrawScreenElement(int x, int y, int element)
2442 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2443 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2446 void DrawLevelElement(int x, int y, int element)
2448 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2449 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2452 void DrawScreenField(int x, int y)
2454 int lx = LEVELX(x), ly = LEVELY(y);
2455 int element, content;
2457 if (!IN_LEV_FIELD(lx, ly))
2459 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2462 element = getBorderElement(lx, ly);
2464 DrawScreenElement(x, y, element);
2469 element = Feld[lx][ly];
2470 content = Store[lx][ly];
2472 if (IS_MOVING(lx, ly))
2474 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2475 boolean cut_mode = NO_CUTTING;
2477 if (element == EL_QUICKSAND_EMPTYING ||
2478 element == EL_QUICKSAND_FAST_EMPTYING ||
2479 element == EL_MAGIC_WALL_EMPTYING ||
2480 element == EL_BD_MAGIC_WALL_EMPTYING ||
2481 element == EL_DC_MAGIC_WALL_EMPTYING ||
2482 element == EL_AMOEBA_DROPPING)
2483 cut_mode = CUT_ABOVE;
2484 else if (element == EL_QUICKSAND_FILLING ||
2485 element == EL_QUICKSAND_FAST_FILLING ||
2486 element == EL_MAGIC_WALL_FILLING ||
2487 element == EL_BD_MAGIC_WALL_FILLING ||
2488 element == EL_DC_MAGIC_WALL_FILLING)
2489 cut_mode = CUT_BELOW;
2491 if (cut_mode == CUT_ABOVE)
2492 DrawScreenElement(x, y, element);
2494 DrawScreenElement(x, y, EL_EMPTY);
2497 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2498 else if (cut_mode == NO_CUTTING)
2499 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2502 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2504 if (cut_mode == CUT_BELOW &&
2505 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2506 DrawLevelElement(lx, ly + 1, element);
2509 if (content == EL_ACID)
2511 int dir = MovDir[lx][ly];
2512 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2513 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2515 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2517 // prevent target field from being drawn again (but without masking)
2518 // (this would happen if target field is scanned after moving element)
2519 Stop[newlx][newly] = TRUE;
2522 else if (IS_BLOCKED(lx, ly))
2527 boolean cut_mode = NO_CUTTING;
2528 int element_old, content_old;
2530 Blocked2Moving(lx, ly, &oldx, &oldy);
2533 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2534 MovDir[oldx][oldy] == MV_RIGHT);
2536 element_old = Feld[oldx][oldy];
2537 content_old = Store[oldx][oldy];
2539 if (element_old == EL_QUICKSAND_EMPTYING ||
2540 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2541 element_old == EL_MAGIC_WALL_EMPTYING ||
2542 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2543 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2544 element_old == EL_AMOEBA_DROPPING)
2545 cut_mode = CUT_ABOVE;
2547 DrawScreenElement(x, y, EL_EMPTY);
2550 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2552 else if (cut_mode == NO_CUTTING)
2553 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2556 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2559 else if (IS_DRAWABLE(element))
2560 DrawScreenElement(x, y, element);
2562 DrawScreenElement(x, y, EL_EMPTY);
2565 void DrawLevelField(int x, int y)
2567 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2568 DrawScreenField(SCREENX(x), SCREENY(y));
2569 else if (IS_MOVING(x, y))
2573 Moving2Blocked(x, y, &newx, &newy);
2574 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2575 DrawScreenField(SCREENX(newx), SCREENY(newy));
2577 else if (IS_BLOCKED(x, y))
2581 Blocked2Moving(x, y, &oldx, &oldy);
2582 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2583 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2587 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2588 int (*el2img_function)(int), boolean masked,
2589 int element_bits_draw)
2591 int element_base = map_mm_wall_element(element);
2592 int element_bits = (IS_DF_WALL(element) ?
2593 element - EL_DF_WALL_START :
2594 IS_MM_WALL(element) ?
2595 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2596 int graphic = el2img_function(element_base);
2597 int tilesize_draw = tilesize / 2;
2602 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2604 for (i = 0; i < 4; i++)
2606 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2607 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2609 if (!(element_bits_draw & (1 << i)))
2612 if (element_bits & (1 << i))
2615 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2616 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2618 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2619 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2624 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2625 tilesize_draw, tilesize_draw);
2630 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2631 boolean masked, int element_bits_draw)
2633 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2634 element, tilesize, el2edimg, masked, element_bits_draw);
2637 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2638 int (*el2img_function)(int))
2640 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2644 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2647 if (IS_MM_WALL(element))
2649 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2650 element, tilesize, el2edimg, masked, 0x000f);
2654 int graphic = el2edimg(element);
2657 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2659 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2663 void DrawSizedElement(int x, int y, int element, int tilesize)
2665 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2668 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2670 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2673 void DrawMiniElement(int x, int y, int element)
2677 graphic = el2edimg(element);
2678 DrawMiniGraphic(x, y, graphic);
2681 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2684 int x = sx + scroll_x, y = sy + scroll_y;
2686 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2687 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2688 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2689 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2691 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2694 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2696 int x = sx + scroll_x, y = sy + scroll_y;
2698 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2699 DrawMiniElement(sx, sy, EL_EMPTY);
2700 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2701 DrawMiniElement(sx, sy, Feld[x][y]);
2703 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2706 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2707 int x, int y, int xsize, int ysize,
2708 int tile_width, int tile_height)
2712 int dst_x = startx + x * tile_width;
2713 int dst_y = starty + y * tile_height;
2714 int width = graphic_info[graphic].width;
2715 int height = graphic_info[graphic].height;
2716 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2717 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2718 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2719 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2720 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2721 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2722 boolean draw_masked = graphic_info[graphic].draw_masked;
2724 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2726 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2728 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2732 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2733 inner_sx + (x - 1) * tile_width % inner_width);
2734 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2735 inner_sy + (y - 1) * tile_height % inner_height);
2738 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2741 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2745 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2746 int x, int y, int xsize, int ysize,
2749 int font_width = getFontWidth(font_nr);
2750 int font_height = getFontHeight(font_nr);
2752 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2753 font_width, font_height);
2756 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2758 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2759 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2760 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2761 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2762 boolean no_delay = (tape.warp_forward);
2763 unsigned int anim_delay = 0;
2764 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2765 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2766 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2767 int font_width = getFontWidth(font_nr);
2768 int font_height = getFontHeight(font_nr);
2769 int max_xsize = level.envelope[envelope_nr].xsize;
2770 int max_ysize = level.envelope[envelope_nr].ysize;
2771 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2772 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2773 int xend = max_xsize;
2774 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2775 int xstep = (xstart < xend ? 1 : 0);
2776 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2778 int end = MAX(xend - xstart, yend - ystart);
2781 for (i = start; i <= end; i++)
2783 int last_frame = end; // last frame of this "for" loop
2784 int x = xstart + i * xstep;
2785 int y = ystart + i * ystep;
2786 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2787 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2788 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2789 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2792 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2794 BlitScreenToBitmap(backbuffer);
2796 SetDrawtoField(DRAW_TO_BACKBUFFER);
2798 for (yy = 0; yy < ysize; yy++)
2799 for (xx = 0; xx < xsize; xx++)
2800 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2802 DrawTextBuffer(sx + font_width, sy + font_height,
2803 level.envelope[envelope_nr].text, font_nr, max_xsize,
2804 xsize - 2, ysize - 2, 0, mask_mode,
2805 level.envelope[envelope_nr].autowrap,
2806 level.envelope[envelope_nr].centered, FALSE);
2808 redraw_mask |= REDRAW_FIELD;
2811 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2814 ClearAutoRepeatKeyEvents();
2817 void ShowEnvelope(int envelope_nr)
2819 int element = EL_ENVELOPE_1 + envelope_nr;
2820 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2821 int sound_opening = element_info[element].sound[ACTION_OPENING];
2822 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2823 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2824 boolean no_delay = (tape.warp_forward);
2825 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2826 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2827 int anim_mode = graphic_info[graphic].anim_mode;
2828 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2829 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2831 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2833 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2835 if (anim_mode == ANIM_DEFAULT)
2836 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2838 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2841 Delay(wait_delay_value);
2843 WaitForEventToContinue();
2845 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2847 if (anim_mode != ANIM_NONE)
2848 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2850 if (anim_mode == ANIM_DEFAULT)
2851 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2853 game.envelope_active = FALSE;
2855 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2857 redraw_mask |= REDRAW_FIELD;
2861 static void setRequestBasePosition(int *x, int *y)
2863 int sx_base, sy_base;
2865 if (request.x != -1)
2866 sx_base = request.x;
2867 else if (request.align == ALIGN_LEFT)
2869 else if (request.align == ALIGN_RIGHT)
2870 sx_base = SX + SXSIZE;
2872 sx_base = SX + SXSIZE / 2;
2874 if (request.y != -1)
2875 sy_base = request.y;
2876 else if (request.valign == VALIGN_TOP)
2878 else if (request.valign == VALIGN_BOTTOM)
2879 sy_base = SY + SYSIZE;
2881 sy_base = SY + SYSIZE / 2;
2887 static void setRequestPositionExt(int *x, int *y, int width, int height,
2888 boolean add_border_size)
2890 int border_size = request.border_size;
2891 int sx_base, sy_base;
2894 setRequestBasePosition(&sx_base, &sy_base);
2896 if (request.align == ALIGN_LEFT)
2898 else if (request.align == ALIGN_RIGHT)
2899 sx = sx_base - width;
2901 sx = sx_base - width / 2;
2903 if (request.valign == VALIGN_TOP)
2905 else if (request.valign == VALIGN_BOTTOM)
2906 sy = sy_base - height;
2908 sy = sy_base - height / 2;
2910 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2911 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2913 if (add_border_size)
2923 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2925 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2928 static void DrawEnvelopeRequest(char *text)
2930 char *text_final = text;
2931 char *text_door_style = NULL;
2932 int graphic = IMG_BACKGROUND_REQUEST;
2933 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2934 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2935 int font_nr = FONT_REQUEST;
2936 int font_width = getFontWidth(font_nr);
2937 int font_height = getFontHeight(font_nr);
2938 int border_size = request.border_size;
2939 int line_spacing = request.line_spacing;
2940 int line_height = font_height + line_spacing;
2941 int max_text_width = request.width - 2 * border_size;
2942 int max_text_height = request.height - 2 * border_size;
2943 int line_length = max_text_width / font_width;
2944 int max_lines = max_text_height / line_height;
2945 int text_width = line_length * font_width;
2946 int width = request.width;
2947 int height = request.height;
2948 int tile_size = MAX(request.step_offset, 1);
2949 int x_steps = width / tile_size;
2950 int y_steps = height / tile_size;
2951 int sx_offset = border_size;
2952 int sy_offset = border_size;
2956 if (request.centered)
2957 sx_offset = (request.width - text_width) / 2;
2959 if (request.wrap_single_words && !request.autowrap)
2961 char *src_text_ptr, *dst_text_ptr;
2963 text_door_style = checked_malloc(2 * strlen(text) + 1);
2965 src_text_ptr = text;
2966 dst_text_ptr = text_door_style;
2968 while (*src_text_ptr)
2970 if (*src_text_ptr == ' ' ||
2971 *src_text_ptr == '?' ||
2972 *src_text_ptr == '!')
2973 *dst_text_ptr++ = '\n';
2975 if (*src_text_ptr != ' ')
2976 *dst_text_ptr++ = *src_text_ptr;
2981 *dst_text_ptr = '\0';
2983 text_final = text_door_style;
2986 setRequestPosition(&sx, &sy, FALSE);
2988 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2990 for (y = 0; y < y_steps; y++)
2991 for (x = 0; x < x_steps; x++)
2992 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2993 x, y, x_steps, y_steps,
2994 tile_size, tile_size);
2996 // force DOOR font inside door area
2997 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2999 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3000 line_length, -1, max_lines, line_spacing, mask_mode,
3001 request.autowrap, request.centered, FALSE);
3005 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3006 RedrawGadget(tool_gadget[i]);
3008 // store readily prepared envelope request for later use when animating
3009 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3011 if (text_door_style)
3012 free(text_door_style);
3015 static void AnimateEnvelopeRequest(int anim_mode, int action)
3017 int graphic = IMG_BACKGROUND_REQUEST;
3018 boolean draw_masked = graphic_info[graphic].draw_masked;
3019 int delay_value_normal = request.step_delay;
3020 int delay_value_fast = delay_value_normal / 2;
3021 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3022 boolean no_delay = (tape.warp_forward);
3023 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3024 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3025 unsigned int anim_delay = 0;
3027 int tile_size = MAX(request.step_offset, 1);
3028 int max_xsize = request.width / tile_size;
3029 int max_ysize = request.height / tile_size;
3030 int max_xsize_inner = max_xsize - 2;
3031 int max_ysize_inner = max_ysize - 2;
3033 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3034 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3035 int xend = max_xsize_inner;
3036 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3037 int xstep = (xstart < xend ? 1 : 0);
3038 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3040 int end = MAX(xend - xstart, yend - ystart);
3043 if (setup.quick_doors)
3050 for (i = start; i <= end; i++)
3052 int last_frame = end; // last frame of this "for" loop
3053 int x = xstart + i * xstep;
3054 int y = ystart + i * ystep;
3055 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3056 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3057 int xsize_size_left = (xsize - 1) * tile_size;
3058 int ysize_size_top = (ysize - 1) * tile_size;
3059 int max_xsize_pos = (max_xsize - 1) * tile_size;
3060 int max_ysize_pos = (max_ysize - 1) * tile_size;
3061 int width = xsize * tile_size;
3062 int height = ysize * tile_size;
3067 setRequestPosition(&src_x, &src_y, FALSE);
3068 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3070 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3072 for (yy = 0; yy < 2; yy++)
3074 for (xx = 0; xx < 2; xx++)
3076 int src_xx = src_x + xx * max_xsize_pos;
3077 int src_yy = src_y + yy * max_ysize_pos;
3078 int dst_xx = dst_x + xx * xsize_size_left;
3079 int dst_yy = dst_y + yy * ysize_size_top;
3080 int xx_size = (xx ? tile_size : xsize_size_left);
3081 int yy_size = (yy ? tile_size : ysize_size_top);
3084 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3085 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3087 BlitBitmap(bitmap_db_store_2, backbuffer,
3088 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3092 redraw_mask |= REDRAW_FIELD;
3096 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3099 ClearAutoRepeatKeyEvents();
3102 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3104 int graphic = IMG_BACKGROUND_REQUEST;
3105 int sound_opening = SND_REQUEST_OPENING;
3106 int sound_closing = SND_REQUEST_CLOSING;
3107 int anim_mode_1 = request.anim_mode; // (higher priority)
3108 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3109 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3110 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3111 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3113 if (game_status == GAME_MODE_PLAYING)
3114 BlitScreenToBitmap(backbuffer);
3116 SetDrawtoField(DRAW_TO_BACKBUFFER);
3118 // SetDrawBackgroundMask(REDRAW_NONE);
3120 if (action == ACTION_OPENING)
3122 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3124 if (req_state & REQ_ASK)
3126 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3127 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3129 else if (req_state & REQ_CONFIRM)
3131 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3133 else if (req_state & REQ_PLAYER)
3135 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3136 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3137 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3138 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3141 DrawEnvelopeRequest(text);
3144 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3146 if (action == ACTION_OPENING)
3148 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3150 if (anim_mode == ANIM_DEFAULT)
3151 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3153 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3157 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3159 if (anim_mode != ANIM_NONE)
3160 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3162 if (anim_mode == ANIM_DEFAULT)
3163 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3166 game.envelope_active = FALSE;
3168 if (action == ACTION_CLOSING)
3169 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3171 // SetDrawBackgroundMask(last_draw_background_mask);
3173 redraw_mask |= REDRAW_FIELD;
3177 if (action == ACTION_CLOSING &&
3178 game_status == GAME_MODE_PLAYING &&
3179 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3180 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3183 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3185 if (IS_MM_WALL(element))
3187 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3193 int graphic = el2preimg(element);
3195 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3196 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3201 void DrawLevel(int draw_background_mask)
3205 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3206 SetDrawBackgroundMask(draw_background_mask);
3210 for (x = BX1; x <= BX2; x++)
3211 for (y = BY1; y <= BY2; y++)
3212 DrawScreenField(x, y);
3214 redraw_mask |= REDRAW_FIELD;
3217 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3222 for (x = 0; x < size_x; x++)
3223 for (y = 0; y < size_y; y++)
3224 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3226 redraw_mask |= REDRAW_FIELD;
3229 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3233 for (x = 0; x < size_x; x++)
3234 for (y = 0; y < size_y; y++)
3235 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3237 redraw_mask |= REDRAW_FIELD;
3240 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3242 boolean show_level_border = (BorderElement != EL_EMPTY);
3243 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3244 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3245 int tile_size = preview.tile_size;
3246 int preview_width = preview.xsize * tile_size;
3247 int preview_height = preview.ysize * tile_size;
3248 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3249 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3250 int real_preview_width = real_preview_xsize * tile_size;
3251 int real_preview_height = real_preview_ysize * tile_size;
3252 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3253 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3256 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3259 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3261 dst_x += (preview_width - real_preview_width) / 2;
3262 dst_y += (preview_height - real_preview_height) / 2;
3264 for (x = 0; x < real_preview_xsize; x++)
3266 for (y = 0; y < real_preview_ysize; y++)
3268 int lx = from_x + x + (show_level_border ? -1 : 0);
3269 int ly = from_y + y + (show_level_border ? -1 : 0);
3270 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3271 getBorderElement(lx, ly));
3273 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3274 element, tile_size);
3278 redraw_mask |= REDRAW_FIELD;
3281 #define MICROLABEL_EMPTY 0
3282 #define MICROLABEL_LEVEL_NAME 1
3283 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3284 #define MICROLABEL_LEVEL_AUTHOR 3
3285 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3286 #define MICROLABEL_IMPORTED_FROM 5
3287 #define MICROLABEL_IMPORTED_BY_HEAD 6
3288 #define MICROLABEL_IMPORTED_BY 7
3290 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3292 int max_text_width = SXSIZE;
3293 int font_width = getFontWidth(font_nr);
3295 if (pos->align == ALIGN_CENTER)
3296 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3297 else if (pos->align == ALIGN_RIGHT)
3298 max_text_width = pos->x;
3300 max_text_width = SXSIZE - pos->x;
3302 return max_text_width / font_width;
3305 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3307 char label_text[MAX_OUTPUT_LINESIZE + 1];
3308 int max_len_label_text;
3309 int font_nr = pos->font;
3312 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3315 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3316 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3317 mode == MICROLABEL_IMPORTED_BY_HEAD)
3318 font_nr = pos->font_alt;
3320 max_len_label_text = getMaxTextLength(pos, font_nr);
3322 if (pos->size != -1)
3323 max_len_label_text = pos->size;
3325 for (i = 0; i < max_len_label_text; i++)
3326 label_text[i] = ' ';
3327 label_text[max_len_label_text] = '\0';
3329 if (strlen(label_text) > 0)
3330 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3333 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3334 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3335 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3336 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3337 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3338 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3339 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3340 max_len_label_text);
3341 label_text[max_len_label_text] = '\0';
3343 if (strlen(label_text) > 0)
3344 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3346 redraw_mask |= REDRAW_FIELD;
3349 static void DrawPreviewLevelLabel(int mode)
3351 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3354 static void DrawPreviewLevelInfo(int mode)
3356 if (mode == MICROLABEL_LEVEL_NAME)
3357 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3358 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3359 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3362 static void DrawPreviewLevelExt(boolean restart)
3364 static unsigned int scroll_delay = 0;
3365 static unsigned int label_delay = 0;
3366 static int from_x, from_y, scroll_direction;
3367 static int label_state, label_counter;
3368 unsigned int scroll_delay_value = preview.step_delay;
3369 boolean show_level_border = (BorderElement != EL_EMPTY);
3370 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3371 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3378 if (preview.anim_mode == ANIM_CENTERED)
3380 if (level_xsize > preview.xsize)
3381 from_x = (level_xsize - preview.xsize) / 2;
3382 if (level_ysize > preview.ysize)
3383 from_y = (level_ysize - preview.ysize) / 2;
3386 from_x += preview.xoffset;
3387 from_y += preview.yoffset;
3389 scroll_direction = MV_RIGHT;
3393 DrawPreviewLevelPlayfield(from_x, from_y);
3394 DrawPreviewLevelLabel(label_state);
3396 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3397 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3399 // initialize delay counters
3400 DelayReached(&scroll_delay, 0);
3401 DelayReached(&label_delay, 0);
3403 if (leveldir_current->name)
3405 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3406 char label_text[MAX_OUTPUT_LINESIZE + 1];
3407 int font_nr = pos->font;
3408 int max_len_label_text = getMaxTextLength(pos, font_nr);
3410 if (pos->size != -1)
3411 max_len_label_text = pos->size;
3413 strncpy(label_text, leveldir_current->name, max_len_label_text);
3414 label_text[max_len_label_text] = '\0';
3416 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3417 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3423 // scroll preview level, if needed
3424 if (preview.anim_mode != ANIM_NONE &&
3425 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3426 DelayReached(&scroll_delay, scroll_delay_value))
3428 switch (scroll_direction)
3433 from_x -= preview.step_offset;
3434 from_x = (from_x < 0 ? 0 : from_x);
3437 scroll_direction = MV_UP;
3441 if (from_x < level_xsize - preview.xsize)
3443 from_x += preview.step_offset;
3444 from_x = (from_x > level_xsize - preview.xsize ?
3445 level_xsize - preview.xsize : from_x);
3448 scroll_direction = MV_DOWN;
3454 from_y -= preview.step_offset;
3455 from_y = (from_y < 0 ? 0 : from_y);
3458 scroll_direction = MV_RIGHT;
3462 if (from_y < level_ysize - preview.ysize)
3464 from_y += preview.step_offset;
3465 from_y = (from_y > level_ysize - preview.ysize ?
3466 level_ysize - preview.ysize : from_y);
3469 scroll_direction = MV_LEFT;
3476 DrawPreviewLevelPlayfield(from_x, from_y);
3479 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3480 // redraw micro level label, if needed
3481 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3482 !strEqual(level.author, ANONYMOUS_NAME) &&
3483 !strEqual(level.author, leveldir_current->name) &&
3484 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3486 int max_label_counter = 23;
3488 if (leveldir_current->imported_from != NULL &&
3489 strlen(leveldir_current->imported_from) > 0)
3490 max_label_counter += 14;
3491 if (leveldir_current->imported_by != NULL &&
3492 strlen(leveldir_current->imported_by) > 0)
3493 max_label_counter += 14;
3495 label_counter = (label_counter + 1) % max_label_counter;
3496 label_state = (label_counter >= 0 && label_counter <= 7 ?
3497 MICROLABEL_LEVEL_NAME :
3498 label_counter >= 9 && label_counter <= 12 ?
3499 MICROLABEL_LEVEL_AUTHOR_HEAD :
3500 label_counter >= 14 && label_counter <= 21 ?
3501 MICROLABEL_LEVEL_AUTHOR :
3502 label_counter >= 23 && label_counter <= 26 ?
3503 MICROLABEL_IMPORTED_FROM_HEAD :
3504 label_counter >= 28 && label_counter <= 35 ?
3505 MICROLABEL_IMPORTED_FROM :
3506 label_counter >= 37 && label_counter <= 40 ?
3507 MICROLABEL_IMPORTED_BY_HEAD :
3508 label_counter >= 42 && label_counter <= 49 ?
3509 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3511 if (leveldir_current->imported_from == NULL &&
3512 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3513 label_state == MICROLABEL_IMPORTED_FROM))
3514 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3515 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3517 DrawPreviewLevelLabel(label_state);
3521 void DrawPreviewPlayers(void)
3523 if (game_status != GAME_MODE_MAIN)
3526 boolean player_found[MAX_PLAYERS];
3527 int num_players = 0;
3530 for (i = 0; i < MAX_PLAYERS; i++)
3531 player_found[i] = FALSE;
3533 // check which players can be found in the level (simple approach)
3534 for (x = 0; x < lev_fieldx; x++)
3536 for (y = 0; y < lev_fieldy; y++)
3538 int element = level.field[x][y];
3540 if (ELEM_IS_PLAYER(element))
3542 int player_nr = GET_PLAYER_NR(element);
3544 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3546 if (!player_found[player_nr])
3549 player_found[player_nr] = TRUE;
3554 struct TextPosInfo *pos = &menu.main.preview_players;
3555 int tile_size = pos->tile_size;
3556 int border_size = pos->border_size;
3557 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3558 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3559 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3560 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3561 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3562 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3563 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3564 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3565 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3566 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3567 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3568 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3570 // clear area in which the players will be drawn
3571 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3572 max_players_width, max_players_height);
3574 if (!network.enabled && !setup.team_mode)
3577 // only draw players if level is suited for team mode
3578 if (num_players < 2)
3581 // draw all players that were found in the level
3582 for (i = 0; i < MAX_PLAYERS; i++)
3584 if (player_found[i])
3586 int graphic = el2img(EL_PLAYER_1 + i);
3588 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3590 xpos += player_xoffset;
3591 ypos += player_yoffset;
3596 void DrawPreviewLevelInitial(void)
3598 DrawPreviewLevelExt(TRUE);
3599 DrawPreviewPlayers();
3602 void DrawPreviewLevelAnimation(void)
3604 DrawPreviewLevelExt(FALSE);
3607 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3608 int border_size, int font_nr)
3610 int graphic = el2img(EL_PLAYER_1 + player_nr);
3611 int font_height = getFontHeight(font_nr);
3612 int player_height = MAX(tile_size, font_height);
3613 int xoffset_text = tile_size + border_size;
3614 int yoffset_text = (player_height - font_height) / 2;
3615 int yoffset_graphic = (player_height - tile_size) / 2;
3616 char *player_name = getNetworkPlayerName(player_nr + 1);
3618 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3620 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3623 static void DrawNetworkPlayersExt(boolean force)
3625 if (game_status != GAME_MODE_MAIN)
3628 if (!network.connected && !force)
3631 int num_players = 0;
3634 for (i = 0; i < MAX_PLAYERS; i++)
3635 if (stored_player[i].connected_network)
3638 struct TextPosInfo *pos = &menu.main.network_players;
3639 int tile_size = pos->tile_size;
3640 int border_size = pos->border_size;
3641 int xoffset_text = tile_size + border_size;
3642 int font_nr = pos->font;
3643 int font_width = getFontWidth(font_nr);
3644 int font_height = getFontHeight(font_nr);
3645 int player_height = MAX(tile_size, font_height);
3646 int player_yoffset = player_height + border_size;
3647 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3648 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3649 int all_players_height = num_players * player_yoffset - border_size;
3650 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3651 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3652 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3654 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3655 max_players_width, max_players_height);
3657 // first draw local network player ...
3658 for (i = 0; i < MAX_PLAYERS; i++)
3660 if (stored_player[i].connected_network &&
3661 stored_player[i].connected_locally)
3663 char *player_name = getNetworkPlayerName(i + 1);
3664 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3665 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3667 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3669 ypos += player_yoffset;
3673 // ... then draw all other network players
3674 for (i = 0; i < MAX_PLAYERS; i++)
3676 if (stored_player[i].connected_network &&
3677 !stored_player[i].connected_locally)
3679 char *player_name = getNetworkPlayerName(i + 1);
3680 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3681 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3683 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3685 ypos += player_yoffset;
3690 void DrawNetworkPlayers(void)
3692 DrawNetworkPlayersExt(FALSE);
3695 void ClearNetworkPlayers(void)
3697 DrawNetworkPlayersExt(TRUE);
3700 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3701 int graphic, int sync_frame,
3704 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3706 if (mask_mode == USE_MASKING)
3707 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3709 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3712 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3713 int graphic, int sync_frame, int mask_mode)
3715 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3717 if (mask_mode == USE_MASKING)
3718 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3720 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3723 static void DrawGraphicAnimation(int x, int y, int graphic)
3725 int lx = LEVELX(x), ly = LEVELY(y);
3727 if (!IN_SCR_FIELD(x, y))
3730 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3731 graphic, GfxFrame[lx][ly], NO_MASKING);
3733 MarkTileDirty(x, y);
3736 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3738 int lx = LEVELX(x), ly = LEVELY(y);
3740 if (!IN_SCR_FIELD(x, y))
3743 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3744 graphic, GfxFrame[lx][ly], NO_MASKING);
3745 MarkTileDirty(x, y);
3748 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3750 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3753 void DrawLevelElementAnimation(int x, int y, int element)
3755 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3757 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3760 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3762 int sx = SCREENX(x), sy = SCREENY(y);
3764 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3767 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3770 DrawGraphicAnimation(sx, sy, graphic);
3773 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3774 DrawLevelFieldCrumbled(x, y);
3776 if (GFX_CRUMBLED(Feld[x][y]))
3777 DrawLevelFieldCrumbled(x, y);
3781 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3783 int sx = SCREENX(x), sy = SCREENY(y);
3786 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3789 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3791 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3794 DrawGraphicAnimation(sx, sy, graphic);
3796 if (GFX_CRUMBLED(element))
3797 DrawLevelFieldCrumbled(x, y);
3800 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3802 if (player->use_murphy)
3804 // this works only because currently only one player can be "murphy" ...
3805 static int last_horizontal_dir = MV_LEFT;
3806 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3808 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3809 last_horizontal_dir = move_dir;
3811 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3813 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3815 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3821 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3824 static boolean equalGraphics(int graphic1, int graphic2)
3826 struct GraphicInfo *g1 = &graphic_info[graphic1];
3827 struct GraphicInfo *g2 = &graphic_info[graphic2];
3829 return (g1->bitmap == g2->bitmap &&
3830 g1->src_x == g2->src_x &&
3831 g1->src_y == g2->src_y &&
3832 g1->anim_frames == g2->anim_frames &&
3833 g1->anim_delay == g2->anim_delay &&
3834 g1->anim_mode == g2->anim_mode);
3837 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3841 DRAW_PLAYER_STAGE_INIT = 0,
3842 DRAW_PLAYER_STAGE_LAST_FIELD,
3843 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3844 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3845 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3846 DRAW_PLAYER_STAGE_PLAYER,
3848 DRAW_PLAYER_STAGE_PLAYER,
3849 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3851 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3852 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3854 NUM_DRAW_PLAYER_STAGES
3857 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3859 static int static_last_player_graphic[MAX_PLAYERS];
3860 static int static_last_player_frame[MAX_PLAYERS];
3861 static boolean static_player_is_opaque[MAX_PLAYERS];
3862 static boolean draw_player[MAX_PLAYERS];
3863 int pnr = player->index_nr;
3865 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3867 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3868 static_last_player_frame[pnr] = player->Frame;
3869 static_player_is_opaque[pnr] = FALSE;
3871 draw_player[pnr] = TRUE;
3874 if (!draw_player[pnr])
3878 if (!IN_LEV_FIELD(player->jx, player->jy))
3880 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3881 printf("DrawPlayerField(): This should never happen!\n");
3883 draw_player[pnr] = FALSE;
3889 int last_player_graphic = static_last_player_graphic[pnr];
3890 int last_player_frame = static_last_player_frame[pnr];
3891 boolean player_is_opaque = static_player_is_opaque[pnr];
3893 int jx = player->jx;
3894 int jy = player->jy;
3895 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3896 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3897 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3898 int last_jx = (player->is_moving ? jx - dx : jx);
3899 int last_jy = (player->is_moving ? jy - dy : jy);
3900 int next_jx = jx + dx;
3901 int next_jy = jy + dy;
3902 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3903 int sx = SCREENX(jx);
3904 int sy = SCREENY(jy);
3905 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3906 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3907 int element = Feld[jx][jy];
3908 int last_element = Feld[last_jx][last_jy];
3909 int action = (player->is_pushing ? ACTION_PUSHING :
3910 player->is_digging ? ACTION_DIGGING :
3911 player->is_collecting ? ACTION_COLLECTING :
3912 player->is_moving ? ACTION_MOVING :
3913 player->is_snapping ? ACTION_SNAPPING :
3914 player->is_dropping ? ACTION_DROPPING :
3915 player->is_waiting ? player->action_waiting :
3918 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3920 // ------------------------------------------------------------------------
3921 // initialize drawing the player
3922 // ------------------------------------------------------------------------
3924 draw_player[pnr] = FALSE;
3926 // GfxElement[][] is set to the element the player is digging or collecting;
3927 // remove also for off-screen player if the player is not moving anymore
3928 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3929 GfxElement[jx][jy] = EL_UNDEFINED;
3931 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3934 if (element == EL_EXPLOSION)
3937 InitPlayerGfxAnimation(player, action, move_dir);
3939 draw_player[pnr] = TRUE;
3941 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3943 // ------------------------------------------------------------------------
3944 // draw things in the field the player is leaving, if needed
3945 // ------------------------------------------------------------------------
3947 if (!IN_SCR_FIELD(sx, sy))
3948 draw_player[pnr] = FALSE;
3950 if (!player->is_moving)
3953 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3955 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3957 if (last_element == EL_DYNAMITE_ACTIVE ||
3958 last_element == EL_EM_DYNAMITE_ACTIVE ||
3959 last_element == EL_SP_DISK_RED_ACTIVE)
3960 DrawDynamite(last_jx, last_jy);
3962 DrawLevelFieldThruMask(last_jx, last_jy);
3964 else if (last_element == EL_DYNAMITE_ACTIVE ||
3965 last_element == EL_EM_DYNAMITE_ACTIVE ||
3966 last_element == EL_SP_DISK_RED_ACTIVE)
3967 DrawDynamite(last_jx, last_jy);
3969 DrawLevelField(last_jx, last_jy);
3971 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3972 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3974 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
3976 // ------------------------------------------------------------------------
3977 // draw things behind the player, if needed
3978 // ------------------------------------------------------------------------
3982 DrawLevelElement(jx, jy, Back[jx][jy]);
3987 if (IS_ACTIVE_BOMB(element))
3989 DrawLevelElement(jx, jy, EL_EMPTY);
3994 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3996 int old_element = GfxElement[jx][jy];
3997 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3998 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
4000 if (GFX_CRUMBLED(old_element))
4001 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
4003 DrawGraphic(sx, sy, old_graphic, frame);
4005 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
4006 static_player_is_opaque[pnr] = TRUE;
4010 GfxElement[jx][jy] = EL_UNDEFINED;
4012 // make sure that pushed elements are drawn with correct frame rate
4013 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4015 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
4016 GfxFrame[jx][jy] = player->StepFrame;
4018 DrawLevelField(jx, jy);
4021 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
4023 // ------------------------------------------------------------------------
4024 // draw things the player is pushing, if needed
4025 // ------------------------------------------------------------------------
4027 if (!player->is_pushing || !player->is_moving)
4030 int gfx_frame = GfxFrame[jx][jy];
4032 if (!IS_MOVING(jx, jy)) // push movement already finished
4034 element = Feld[next_jx][next_jy];
4035 gfx_frame = GfxFrame[next_jx][next_jy];
4038 int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4039 int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4040 int frame = getGraphicAnimationFrame(graphic, sync_frame);
4042 // draw background element under pushed element (like the Sokoban field)
4043 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4045 // this allows transparent pushing animation over non-black background
4048 DrawLevelElement(jx, jy, Back[jx][jy]);
4050 DrawLevelElement(jx, jy, EL_EMPTY);
4052 if (Back[next_jx][next_jy])
4053 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4055 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4057 else if (Back[next_jx][next_jy])
4058 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4060 int px = SCREENX(jx), py = SCREENY(jy);
4061 int pxx = (TILEX - ABS(sxx)) * dx;
4062 int pyy = (TILEY - ABS(syy)) * dy;
4065 // do not draw (EM style) pushing animation when pushing is finished
4066 // (two-tile animations usually do not contain start and end frame)
4067 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4068 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4070 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4072 // masked drawing is needed for EMC style (double) movement graphics
4073 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4074 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4077 else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
4079 // ------------------------------------------------------------------------
4080 // draw player himself
4081 // ------------------------------------------------------------------------
4083 int graphic = getPlayerGraphic(player, move_dir);
4085 // in the case of changed player action or direction, prevent the current
4086 // animation frame from being restarted for identical animations
4087 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4088 player->Frame = last_player_frame;
4090 int frame = getGraphicAnimationFrame(graphic, player->Frame);
4092 if (player_is_opaque)
4093 DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
4095 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4097 if (SHIELD_ON(player))
4099 graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4100 IMG_SHIELD_NORMAL_ACTIVE);
4101 frame = getGraphicAnimationFrame(graphic, -1);
4103 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4106 else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
4108 // ------------------------------------------------------------------------
4109 // draw things in front of player (active dynamite or dynabombs)
4110 // ------------------------------------------------------------------------
4112 if (IS_ACTIVE_BOMB(element))
4114 int graphic = el2img(element);
4115 int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4117 if (game.emulation == EMU_SUPAPLEX)
4118 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4120 DrawGraphicThruMask(sx, sy, graphic, frame);
4123 if (player_is_moving && last_element == EL_EXPLOSION)
4125 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4126 GfxElement[last_jx][last_jy] : EL_EMPTY);
4127 int graphic = el_act2img(element, ACTION_EXPLODING);
4128 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4129 int phase = ExplodePhase[last_jx][last_jy] - 1;
4130 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4133 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4136 else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
4138 // ------------------------------------------------------------------------
4139 // draw elements the player is just walking/passing through/under
4140 // ------------------------------------------------------------------------
4142 if (player_is_moving)
4144 // handle the field the player is leaving ...
4145 if (IS_ACCESSIBLE_INSIDE(last_element))
4146 DrawLevelField(last_jx, last_jy);
4147 else if (IS_ACCESSIBLE_UNDER(last_element))
4148 DrawLevelFieldThruMask(last_jx, last_jy);
4151 // do not redraw accessible elements if the player is just pushing them
4152 if (!player_is_moving || !player->is_pushing)
4154 // ... and the field the player is entering
4155 if (IS_ACCESSIBLE_INSIDE(element))
4156 DrawLevelField(jx, jy);
4157 else if (IS_ACCESSIBLE_UNDER(element))
4158 DrawLevelFieldThruMask(jx, jy);
4161 MarkTileDirty(sx, sy);
4165 void DrawPlayer(struct PlayerInfo *player)
4169 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4170 DrawPlayerExt(player, i);
4173 void DrawAllPlayers(void)
4177 for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
4178 for (j = 0; j < MAX_PLAYERS; j++)
4179 if (stored_player[j].active)
4180 DrawPlayerExt(&stored_player[j], i);
4183 void DrawPlayerField(int x, int y)
4185 if (!IS_PLAYER(x, y))
4188 DrawPlayer(PLAYERINFO(x, y));
4191 // ----------------------------------------------------------------------------
4193 void WaitForEventToContinue(void)
4195 boolean still_wait = TRUE;
4197 if (program.headless)
4200 // simulate releasing mouse button over last gadget, if still pressed
4202 HandleGadgets(-1, -1, 0);
4204 button_status = MB_RELEASED;
4212 if (NextValidEvent(&event))
4216 case EVENT_BUTTONRELEASE:
4217 case EVENT_KEYPRESS:
4218 case SDL_CONTROLLERBUTTONDOWN:
4219 case SDL_JOYBUTTONDOWN:
4223 case EVENT_KEYRELEASE:
4224 ClearPlayerAction();
4228 HandleOtherEvents(&event);
4232 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4241 #define MAX_REQUEST_LINES 13
4242 #define MAX_REQUEST_LINE_FONT1_LEN 7
4243 #define MAX_REQUEST_LINE_FONT2_LEN 10
4245 static int RequestHandleEvents(unsigned int req_state)
4247 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4249 int width = request.width;
4250 int height = request.height;
4254 // when showing request dialog after game ended, deactivate game panel
4255 if (game_just_ended)
4256 game.panel.active = FALSE;
4258 game.request_active = TRUE;
4260 setRequestPosition(&sx, &sy, FALSE);
4262 button_status = MB_RELEASED;
4264 request_gadget_id = -1;
4269 if (game_just_ended)
4271 // the MM game engine does not use a special (scrollable) field buffer
4272 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4273 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4275 HandleGameActions();
4277 SetDrawtoField(DRAW_TO_BACKBUFFER);
4279 if (global.use_envelope_request)
4281 // copy current state of request area to middle of playfield area
4282 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4290 while (NextValidEvent(&event))
4294 case EVENT_BUTTONPRESS:
4295 case EVENT_BUTTONRELEASE:
4296 case EVENT_MOTIONNOTIFY:
4300 if (event.type == EVENT_MOTIONNOTIFY)
4305 motion_status = TRUE;
4306 mx = ((MotionEvent *) &event)->x;
4307 my = ((MotionEvent *) &event)->y;
4311 motion_status = FALSE;
4312 mx = ((ButtonEvent *) &event)->x;
4313 my = ((ButtonEvent *) &event)->y;
4314 if (event.type == EVENT_BUTTONPRESS)
4315 button_status = ((ButtonEvent *) &event)->button;
4317 button_status = MB_RELEASED;
4320 // this sets 'request_gadget_id'
4321 HandleGadgets(mx, my, button_status);
4323 switch (request_gadget_id)
4325 case TOOL_CTRL_ID_YES:
4328 case TOOL_CTRL_ID_NO:
4331 case TOOL_CTRL_ID_CONFIRM:
4332 result = TRUE | FALSE;
4335 case TOOL_CTRL_ID_PLAYER_1:
4338 case TOOL_CTRL_ID_PLAYER_2:
4341 case TOOL_CTRL_ID_PLAYER_3:
4344 case TOOL_CTRL_ID_PLAYER_4:
4355 case SDL_WINDOWEVENT:
4356 HandleWindowEvent((WindowEvent *) &event);
4359 case SDL_APP_WILLENTERBACKGROUND:
4360 case SDL_APP_DIDENTERBACKGROUND:
4361 case SDL_APP_WILLENTERFOREGROUND:
4362 case SDL_APP_DIDENTERFOREGROUND:
4363 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4366 case EVENT_KEYPRESS:
4368 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4373 if (req_state & REQ_CONFIRM)
4382 #if defined(KSYM_Rewind)
4383 case KSYM_Rewind: // for Amazon Fire TV remote
4392 #if defined(KSYM_FastForward)
4393 case KSYM_FastForward: // for Amazon Fire TV remote
4399 HandleKeysDebug(key, KEY_PRESSED);
4403 if (req_state & REQ_PLAYER)
4405 int old_player_nr = setup.network_player_nr;
4408 result = old_player_nr + 1;
4413 result = old_player_nr + 1;
4444 case EVENT_KEYRELEASE:
4445 ClearPlayerAction();
4448 case SDL_CONTROLLERBUTTONDOWN:
4449 switch (event.cbutton.button)
4451 case SDL_CONTROLLER_BUTTON_A:
4452 case SDL_CONTROLLER_BUTTON_X:
4453 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4454 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4458 case SDL_CONTROLLER_BUTTON_B:
4459 case SDL_CONTROLLER_BUTTON_Y:
4460 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4461 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4462 case SDL_CONTROLLER_BUTTON_BACK:
4467 if (req_state & REQ_PLAYER)
4469 int old_player_nr = setup.network_player_nr;
4472 result = old_player_nr + 1;
4474 switch (event.cbutton.button)
4476 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4477 case SDL_CONTROLLER_BUTTON_Y:
4481 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4482 case SDL_CONTROLLER_BUTTON_B:
4486 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4487 case SDL_CONTROLLER_BUTTON_A:
4491 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4492 case SDL_CONTROLLER_BUTTON_X:
4503 case SDL_CONTROLLERBUTTONUP:
4504 HandleJoystickEvent(&event);
4505 ClearPlayerAction();
4509 HandleOtherEvents(&event);
4514 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4516 int joy = AnyJoystick();
4518 if (joy & JOY_BUTTON_1)
4520 else if (joy & JOY_BUTTON_2)
4523 else if (AnyJoystick())
4525 int joy = AnyJoystick();
4527 if (req_state & REQ_PLAYER)
4531 else if (joy & JOY_RIGHT)
4533 else if (joy & JOY_DOWN)
4535 else if (joy & JOY_LEFT)
4540 if (game_just_ended)
4542 if (global.use_envelope_request)
4544 // copy back current state of pressed buttons inside request area
4545 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4552 game.request_active = FALSE;
4557 static boolean RequestDoor(char *text, unsigned int req_state)
4559 unsigned int old_door_state;
4560 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4561 int font_nr = FONT_TEXT_2;
4566 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4568 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4569 font_nr = FONT_TEXT_1;
4572 if (game_status == GAME_MODE_PLAYING)
4573 BlitScreenToBitmap(backbuffer);
4575 // disable deactivated drawing when quick-loading level tape recording
4576 if (tape.playing && tape.deactivate_display)
4577 TapeDeactivateDisplayOff(TRUE);
4579 SetMouseCursor(CURSOR_DEFAULT);
4581 // pause network game while waiting for request to answer
4582 if (network.enabled &&
4583 game_status == GAME_MODE_PLAYING &&
4584 !game.all_players_gone &&
4585 req_state & REQUEST_WAIT_FOR_INPUT)
4586 SendToServer_PausePlaying();
4588 old_door_state = GetDoorState();
4590 // simulate releasing mouse button over last gadget, if still pressed
4592 HandleGadgets(-1, -1, 0);
4596 // draw released gadget before proceeding
4599 if (old_door_state & DOOR_OPEN_1)
4601 CloseDoor(DOOR_CLOSE_1);
4603 // save old door content
4604 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4605 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4608 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4609 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4611 // clear door drawing field
4612 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4614 // force DOOR font inside door area
4615 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4617 // write text for request
4618 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4620 char text_line[max_request_line_len + 1];
4626 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4628 tc = *(text_ptr + tx);
4629 // if (!tc || tc == ' ')
4630 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4634 if ((tc == '?' || tc == '!') && tl == 0)
4644 strncpy(text_line, text_ptr, tl);
4647 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4648 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4649 text_line, font_nr);
4651 text_ptr += tl + (tc == ' ' ? 1 : 0);
4652 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4657 if (req_state & REQ_ASK)
4659 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4660 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4662 else if (req_state & REQ_CONFIRM)
4664 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4666 else if (req_state & REQ_PLAYER)
4668 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4669 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4670 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4671 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4674 // copy request gadgets to door backbuffer
4675 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4677 OpenDoor(DOOR_OPEN_1);
4679 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4681 if (game_status == GAME_MODE_PLAYING)
4683 SetPanelBackground();
4684 SetDrawBackgroundMask(REDRAW_DOOR_1);
4688 SetDrawBackgroundMask(REDRAW_FIELD);
4694 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4696 // ---------- handle request buttons ----------
4697 result = RequestHandleEvents(req_state);
4701 if (!(req_state & REQ_STAY_OPEN))
4703 CloseDoor(DOOR_CLOSE_1);
4705 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4706 (req_state & REQ_REOPEN))
4707 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4712 if (game_status == GAME_MODE_PLAYING)
4714 SetPanelBackground();
4715 SetDrawBackgroundMask(REDRAW_DOOR_1);
4719 SetDrawBackgroundMask(REDRAW_FIELD);
4722 // continue network game after request
4723 if (network.enabled &&
4724 game_status == GAME_MODE_PLAYING &&
4725 !game.all_players_gone &&
4726 req_state & REQUEST_WAIT_FOR_INPUT)
4727 SendToServer_ContinuePlaying();
4729 // restore deactivated drawing when quick-loading level tape recording
4730 if (tape.playing && tape.deactivate_display)
4731 TapeDeactivateDisplayOn();
4736 static boolean RequestEnvelope(char *text, unsigned int req_state)
4740 if (game_status == GAME_MODE_PLAYING)
4741 BlitScreenToBitmap(backbuffer);
4743 // disable deactivated drawing when quick-loading level tape recording
4744 if (tape.playing && tape.deactivate_display)
4745 TapeDeactivateDisplayOff(TRUE);
4747 SetMouseCursor(CURSOR_DEFAULT);
4749 // pause network game while waiting for request to answer
4750 if (network.enabled &&
4751 game_status == GAME_MODE_PLAYING &&
4752 !game.all_players_gone &&
4753 req_state & REQUEST_WAIT_FOR_INPUT)
4754 SendToServer_PausePlaying();
4756 // simulate releasing mouse button over last gadget, if still pressed
4758 HandleGadgets(-1, -1, 0);
4762 // (replace with setting corresponding request background)
4763 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4764 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4766 // clear door drawing field
4767 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4769 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4771 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4773 if (game_status == GAME_MODE_PLAYING)
4775 SetPanelBackground();
4776 SetDrawBackgroundMask(REDRAW_DOOR_1);
4780 SetDrawBackgroundMask(REDRAW_FIELD);
4786 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4788 // ---------- handle request buttons ----------
4789 result = RequestHandleEvents(req_state);
4793 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4797 if (game_status == GAME_MODE_PLAYING)
4799 SetPanelBackground();
4800 SetDrawBackgroundMask(REDRAW_DOOR_1);
4804 SetDrawBackgroundMask(REDRAW_FIELD);
4807 // continue network game after request
4808 if (network.enabled &&
4809 game_status == GAME_MODE_PLAYING &&
4810 !game.all_players_gone &&
4811 req_state & REQUEST_WAIT_FOR_INPUT)
4812 SendToServer_ContinuePlaying();
4814 // restore deactivated drawing when quick-loading level tape recording
4815 if (tape.playing && tape.deactivate_display)
4816 TapeDeactivateDisplayOn();
4821 boolean Request(char *text, unsigned int req_state)
4823 boolean overlay_enabled = GetOverlayEnabled();
4826 SetOverlayEnabled(FALSE);
4828 if (global.use_envelope_request)
4829 result = RequestEnvelope(text, req_state);
4831 result = RequestDoor(text, req_state);
4833 SetOverlayEnabled(overlay_enabled);
4838 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4840 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4841 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4844 if (dpo1->sort_priority != dpo2->sort_priority)
4845 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4847 compare_result = dpo1->nr - dpo2->nr;
4849 return compare_result;
4852 void InitGraphicCompatibilityInfo_Doors(void)
4858 struct DoorInfo *door;
4862 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4863 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4865 { -1, -1, -1, NULL }
4867 struct Rect door_rect_list[] =
4869 { DX, DY, DXSIZE, DYSIZE },
4870 { VX, VY, VXSIZE, VYSIZE }
4874 for (i = 0; doors[i].door_token != -1; i++)
4876 int door_token = doors[i].door_token;
4877 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4878 int part_1 = doors[i].part_1;
4879 int part_8 = doors[i].part_8;
4880 int part_2 = part_1 + 1;
4881 int part_3 = part_1 + 2;
4882 struct DoorInfo *door = doors[i].door;
4883 struct Rect *door_rect = &door_rect_list[door_index];
4884 boolean door_gfx_redefined = FALSE;
4886 // check if any door part graphic definitions have been redefined
4888 for (j = 0; door_part_controls[j].door_token != -1; j++)
4890 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4891 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4893 if (dpc->door_token == door_token && fi->redefined)
4894 door_gfx_redefined = TRUE;
4897 // check for old-style door graphic/animation modifications
4899 if (!door_gfx_redefined)
4901 if (door->anim_mode & ANIM_STATIC_PANEL)
4903 door->panel.step_xoffset = 0;
4904 door->panel.step_yoffset = 0;
4907 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4909 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4910 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4911 int num_door_steps, num_panel_steps;
4913 // remove door part graphics other than the two default wings
4915 for (j = 0; door_part_controls[j].door_token != -1; j++)
4917 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4918 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4920 if (dpc->graphic >= part_3 &&
4921 dpc->graphic <= part_8)
4925 // set graphics and screen positions of the default wings
4927 g_part_1->width = door_rect->width;
4928 g_part_1->height = door_rect->height;
4929 g_part_2->width = door_rect->width;
4930 g_part_2->height = door_rect->height;
4931 g_part_2->src_x = door_rect->width;
4932 g_part_2->src_y = g_part_1->src_y;
4934 door->part_2.x = door->part_1.x;
4935 door->part_2.y = door->part_1.y;
4937 if (door->width != -1)
4939 g_part_1->width = door->width;
4940 g_part_2->width = door->width;
4942 // special treatment for graphics and screen position of right wing
4943 g_part_2->src_x += door_rect->width - door->width;
4944 door->part_2.x += door_rect->width - door->width;
4947 if (door->height != -1)
4949 g_part_1->height = door->height;
4950 g_part_2->height = door->height;
4952 // special treatment for graphics and screen position of bottom wing
4953 g_part_2->src_y += door_rect->height - door->height;
4954 door->part_2.y += door_rect->height - door->height;
4957 // set animation delays for the default wings and panels
4959 door->part_1.step_delay = door->step_delay;
4960 door->part_2.step_delay = door->step_delay;
4961 door->panel.step_delay = door->step_delay;
4963 // set animation draw order for the default wings
4965 door->part_1.sort_priority = 2; // draw left wing over ...
4966 door->part_2.sort_priority = 1; // ... right wing
4968 // set animation draw offset for the default wings
4970 if (door->anim_mode & ANIM_HORIZONTAL)
4972 door->part_1.step_xoffset = door->step_offset;
4973 door->part_1.step_yoffset = 0;
4974 door->part_2.step_xoffset = door->step_offset * -1;
4975 door->part_2.step_yoffset = 0;
4977 num_door_steps = g_part_1->width / door->step_offset;
4979 else // ANIM_VERTICAL
4981 door->part_1.step_xoffset = 0;
4982 door->part_1.step_yoffset = door->step_offset;
4983 door->part_2.step_xoffset = 0;
4984 door->part_2.step_yoffset = door->step_offset * -1;
4986 num_door_steps = g_part_1->height / door->step_offset;
4989 // set animation draw offset for the default panels
4991 if (door->step_offset > 1)
4993 num_panel_steps = 2 * door_rect->height / door->step_offset;
4994 door->panel.start_step = num_panel_steps - num_door_steps;
4995 door->panel.start_step_closing = door->panel.start_step;
4999 num_panel_steps = door_rect->height / door->step_offset;
5000 door->panel.start_step = num_panel_steps - num_door_steps / 2;
5001 door->panel.start_step_closing = door->panel.start_step;
5002 door->panel.step_delay *= 2;
5009 void InitDoors(void)
5013 for (i = 0; door_part_controls[i].door_token != -1; i++)
5015 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5016 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5018 // initialize "start_step_opening" and "start_step_closing", if needed
5019 if (dpc->pos->start_step_opening == 0 &&
5020 dpc->pos->start_step_closing == 0)
5022 // dpc->pos->start_step_opening = dpc->pos->start_step;
5023 dpc->pos->start_step_closing = dpc->pos->start_step;
5026 // fill structure for door part draw order (sorted below)
5028 dpo->sort_priority = dpc->pos->sort_priority;
5031 // sort door part controls according to sort_priority and graphic number
5032 qsort(door_part_order, MAX_DOOR_PARTS,
5033 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5036 unsigned int OpenDoor(unsigned int door_state)
5038 if (door_state & DOOR_COPY_BACK)
5040 if (door_state & DOOR_OPEN_1)
5041 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5042 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5044 if (door_state & DOOR_OPEN_2)
5045 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5046 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5048 door_state &= ~DOOR_COPY_BACK;
5051 return MoveDoor(door_state);
5054 unsigned int CloseDoor(unsigned int door_state)
5056 unsigned int old_door_state = GetDoorState();
5058 if (!(door_state & DOOR_NO_COPY_BACK))
5060 if (old_door_state & DOOR_OPEN_1)
5061 BlitBitmap(backbuffer, bitmap_db_door_1,
5062 DX, DY, DXSIZE, DYSIZE, 0, 0);
5064 if (old_door_state & DOOR_OPEN_2)
5065 BlitBitmap(backbuffer, bitmap_db_door_2,
5066 VX, VY, VXSIZE, VYSIZE, 0, 0);
5068 door_state &= ~DOOR_NO_COPY_BACK;
5071 return MoveDoor(door_state);
5074 unsigned int GetDoorState(void)
5076 return MoveDoor(DOOR_GET_STATE);
5079 unsigned int SetDoorState(unsigned int door_state)
5081 return MoveDoor(door_state | DOOR_SET_STATE);
5084 static int euclid(int a, int b)
5086 return (b ? euclid(b, a % b) : a);
5089 unsigned int MoveDoor(unsigned int door_state)
5091 struct Rect door_rect_list[] =
5093 { DX, DY, DXSIZE, DYSIZE },
5094 { VX, VY, VXSIZE, VYSIZE }
5096 static int door1 = DOOR_CLOSE_1;
5097 static int door2 = DOOR_CLOSE_2;
5098 unsigned int door_delay = 0;
5099 unsigned int door_delay_value;
5102 if (door_state == DOOR_GET_STATE)
5103 return (door1 | door2);
5105 if (door_state & DOOR_SET_STATE)
5107 if (door_state & DOOR_ACTION_1)
5108 door1 = door_state & DOOR_ACTION_1;
5109 if (door_state & DOOR_ACTION_2)
5110 door2 = door_state & DOOR_ACTION_2;
5112 return (door1 | door2);
5115 if (!(door_state & DOOR_FORCE_REDRAW))
5117 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5118 door_state &= ~DOOR_OPEN_1;
5119 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5120 door_state &= ~DOOR_CLOSE_1;
5121 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5122 door_state &= ~DOOR_OPEN_2;
5123 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5124 door_state &= ~DOOR_CLOSE_2;
5127 if (global.autoplay_leveldir)
5129 door_state |= DOOR_NO_DELAY;
5130 door_state &= ~DOOR_CLOSE_ALL;
5133 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5134 door_state |= DOOR_NO_DELAY;
5136 if (door_state & DOOR_ACTION)
5138 boolean door_panel_drawn[NUM_DOORS];
5139 boolean panel_has_doors[NUM_DOORS];
5140 boolean door_part_skip[MAX_DOOR_PARTS];
5141 boolean door_part_done[MAX_DOOR_PARTS];
5142 boolean door_part_done_all;
5143 int num_steps[MAX_DOOR_PARTS];
5144 int max_move_delay = 0; // delay for complete animations of all doors
5145 int max_step_delay = 0; // delay (ms) between two animation frames
5146 int num_move_steps = 0; // number of animation steps for all doors
5147 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5148 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5149 int current_move_delay = 0;
5153 for (i = 0; i < NUM_DOORS; i++)
5154 panel_has_doors[i] = FALSE;
5156 for (i = 0; i < MAX_DOOR_PARTS; i++)
5158 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5159 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5160 int door_token = dpc->door_token;
5162 door_part_done[i] = FALSE;
5163 door_part_skip[i] = (!(door_state & door_token) ||
5167 for (i = 0; i < MAX_DOOR_PARTS; i++)
5169 int nr = door_part_order[i].nr;
5170 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5171 struct DoorPartPosInfo *pos = dpc->pos;
5172 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5173 int door_token = dpc->door_token;
5174 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5175 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5176 int step_xoffset = ABS(pos->step_xoffset);
5177 int step_yoffset = ABS(pos->step_yoffset);
5178 int step_delay = pos->step_delay;
5179 int current_door_state = door_state & door_token;
5180 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5181 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5182 boolean part_opening = (is_panel ? door_closing : door_opening);
5183 int start_step = (part_opening ? pos->start_step_opening :
5184 pos->start_step_closing);
5185 float move_xsize = (step_xoffset ? g->width : 0);
5186 float move_ysize = (step_yoffset ? g->height : 0);
5187 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5188 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5189 int move_steps = (move_xsteps && move_ysteps ?
5190 MIN(move_xsteps, move_ysteps) :
5191 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5192 int move_delay = move_steps * step_delay;
5194 if (door_part_skip[nr])
5197 max_move_delay = MAX(max_move_delay, move_delay);
5198 max_step_delay = (max_step_delay == 0 ? step_delay :
5199 euclid(max_step_delay, step_delay));
5200 num_steps[nr] = move_steps;
5204 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5206 panel_has_doors[door_index] = TRUE;
5210 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5212 num_move_steps = max_move_delay / max_step_delay;
5213 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5215 door_delay_value = max_step_delay;
5217 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5219 start = num_move_steps - 1;
5223 // opening door sound has priority over simultaneously closing door
5224 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5226 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5228 if (door_state & DOOR_OPEN_1)
5229 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5230 if (door_state & DOOR_OPEN_2)
5231 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5233 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5235 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5237 if (door_state & DOOR_CLOSE_1)
5238 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5239 if (door_state & DOOR_CLOSE_2)
5240 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5244 for (k = start; k < num_move_steps; k++)
5246 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5248 door_part_done_all = TRUE;
5250 for (i = 0; i < NUM_DOORS; i++)
5251 door_panel_drawn[i] = FALSE;
5253 for (i = 0; i < MAX_DOOR_PARTS; i++)
5255 int nr = door_part_order[i].nr;
5256 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5257 struct DoorPartPosInfo *pos = dpc->pos;
5258 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5259 int door_token = dpc->door_token;
5260 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5261 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5262 boolean is_panel_and_door_has_closed = FALSE;
5263 struct Rect *door_rect = &door_rect_list[door_index];
5264 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5266 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5267 int current_door_state = door_state & door_token;
5268 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5269 boolean door_closing = !door_opening;
5270 boolean part_opening = (is_panel ? door_closing : door_opening);
5271 boolean part_closing = !part_opening;
5272 int start_step = (part_opening ? pos->start_step_opening :
5273 pos->start_step_closing);
5274 int step_delay = pos->step_delay;
5275 int step_factor = step_delay / max_step_delay;
5276 int k1 = (step_factor ? k / step_factor + 1 : k);
5277 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5278 int kk = MAX(0, k2);
5281 int src_x, src_y, src_xx, src_yy;
5282 int dst_x, dst_y, dst_xx, dst_yy;
5285 if (door_part_skip[nr])
5288 if (!(door_state & door_token))
5296 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5297 int kk_door = MAX(0, k2_door);
5298 int sync_frame = kk_door * door_delay_value;
5299 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5301 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5302 &g_src_x, &g_src_y);
5307 if (!door_panel_drawn[door_index])
5309 ClearRectangle(drawto, door_rect->x, door_rect->y,
5310 door_rect->width, door_rect->height);
5312 door_panel_drawn[door_index] = TRUE;
5315 // draw opening or closing door parts
5317 if (pos->step_xoffset < 0) // door part on right side
5320 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5323 if (dst_xx + width > door_rect->width)
5324 width = door_rect->width - dst_xx;
5326 else // door part on left side
5329 dst_xx = pos->x - kk * pos->step_xoffset;
5333 src_xx = ABS(dst_xx);
5337 width = g->width - src_xx;
5339 if (width > door_rect->width)
5340 width = door_rect->width;
5342 // printf("::: k == %d [%d] \n", k, start_step);
5345 if (pos->step_yoffset < 0) // door part on bottom side
5348 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5351 if (dst_yy + height > door_rect->height)
5352 height = door_rect->height - dst_yy;
5354 else // door part on top side
5357 dst_yy = pos->y - kk * pos->step_yoffset;
5361 src_yy = ABS(dst_yy);
5365 height = g->height - src_yy;
5368 src_x = g_src_x + src_xx;
5369 src_y = g_src_y + src_yy;
5371 dst_x = door_rect->x + dst_xx;
5372 dst_y = door_rect->y + dst_yy;
5374 is_panel_and_door_has_closed =
5377 panel_has_doors[door_index] &&
5378 k >= num_move_steps_doors_only - 1);
5380 if (width >= 0 && width <= g->width &&
5381 height >= 0 && height <= g->height &&
5382 !is_panel_and_door_has_closed)
5384 if (is_panel || !pos->draw_masked)
5385 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5388 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5392 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5394 if ((part_opening && (width < 0 || height < 0)) ||
5395 (part_closing && (width >= g->width && height >= g->height)))
5396 door_part_done[nr] = TRUE;
5398 // continue door part animations, but not panel after door has closed
5399 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5400 door_part_done_all = FALSE;
5403 if (!(door_state & DOOR_NO_DELAY))
5407 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5409 current_move_delay += max_step_delay;
5411 // prevent OS (Windows) from complaining about program not responding
5415 if (door_part_done_all)
5419 if (!(door_state & DOOR_NO_DELAY))
5421 // wait for specified door action post delay
5422 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5423 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5424 else if (door_state & DOOR_ACTION_1)
5425 door_delay_value = door_1.post_delay;
5426 else if (door_state & DOOR_ACTION_2)
5427 door_delay_value = door_2.post_delay;
5429 while (!DelayReached(&door_delay, door_delay_value))
5434 if (door_state & DOOR_ACTION_1)
5435 door1 = door_state & DOOR_ACTION_1;
5436 if (door_state & DOOR_ACTION_2)
5437 door2 = door_state & DOOR_ACTION_2;
5439 // draw masked border over door area
5440 DrawMaskedBorder(REDRAW_DOOR_1);
5441 DrawMaskedBorder(REDRAW_DOOR_2);
5443 ClearAutoRepeatKeyEvents();
5445 return (door1 | door2);
5448 static boolean useSpecialEditorDoor(void)
5450 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5451 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5453 // do not draw special editor door if editor border defined or redefined
5454 if (graphic_info[graphic].bitmap != NULL || redefined)
5457 // do not draw special editor door if global border defined to be empty
5458 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5461 // do not draw special editor door if viewport definitions do not match
5465 EY + EYSIZE != VY + VYSIZE)
5471 void DrawSpecialEditorDoor(void)
5473 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5474 int top_border_width = gfx1->width;
5475 int top_border_height = gfx1->height;
5476 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5477 int ex = EX - outer_border;
5478 int ey = EY - outer_border;
5479 int vy = VY - outer_border;
5480 int exsize = EXSIZE + 2 * outer_border;
5482 if (!useSpecialEditorDoor())
5485 // draw bigger level editor toolbox window
5486 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5487 top_border_width, top_border_height, ex, ey - top_border_height);
5488 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5489 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5491 redraw_mask |= REDRAW_ALL;
5494 void UndrawSpecialEditorDoor(void)
5496 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5497 int top_border_width = gfx1->width;
5498 int top_border_height = gfx1->height;
5499 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5500 int ex = EX - outer_border;
5501 int ey = EY - outer_border;
5502 int ey_top = ey - top_border_height;
5503 int exsize = EXSIZE + 2 * outer_border;
5504 int eysize = EYSIZE + 2 * outer_border;
5506 if (!useSpecialEditorDoor())
5509 // draw normal tape recorder window
5510 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5512 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5513 ex, ey_top, top_border_width, top_border_height,
5515 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5516 ex, ey, exsize, eysize, ex, ey);
5520 // if screen background is set to "[NONE]", clear editor toolbox window
5521 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5522 ClearRectangle(drawto, ex, ey, exsize, eysize);
5525 redraw_mask |= REDRAW_ALL;
5529 // ---------- new tool button stuff -------------------------------------------
5534 struct TextPosInfo *pos;
5537 } toolbutton_info[NUM_TOOL_BUTTONS] =
5540 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5541 TOOL_CTRL_ID_YES, "yes"
5544 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5545 TOOL_CTRL_ID_NO, "no"
5548 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5549 TOOL_CTRL_ID_CONFIRM, "confirm"
5552 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5553 TOOL_CTRL_ID_PLAYER_1, "player 1"
5556 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5557 TOOL_CTRL_ID_PLAYER_2, "player 2"
5560 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5561 TOOL_CTRL_ID_PLAYER_3, "player 3"
5564 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5565 TOOL_CTRL_ID_PLAYER_4, "player 4"
5569 void CreateToolButtons(void)
5573 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5575 int graphic = toolbutton_info[i].graphic;
5576 struct GraphicInfo *gfx = &graphic_info[graphic];
5577 struct TextPosInfo *pos = toolbutton_info[i].pos;
5578 struct GadgetInfo *gi;
5579 Bitmap *deco_bitmap = None;
5580 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5581 unsigned int event_mask = GD_EVENT_RELEASED;
5584 int gd_x = gfx->src_x;
5585 int gd_y = gfx->src_y;
5586 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5587 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5592 if (global.use_envelope_request)
5594 setRequestPosition(&dx, &dy, TRUE);
5596 // check if request buttons are outside of envelope and fix, if needed
5597 if (x < 0 || x + gfx->width > request.width ||
5598 y < 0 || y + gfx->height > request.height)
5600 if (id == TOOL_CTRL_ID_YES)
5603 y = request.height - 2 * request.border_size - gfx->height;
5605 else if (id == TOOL_CTRL_ID_NO)
5607 x = request.width - 2 * request.border_size - gfx->width;
5608 y = request.height - 2 * request.border_size - gfx->height;
5610 else if (id == TOOL_CTRL_ID_CONFIRM)
5612 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5613 y = request.height - 2 * request.border_size - gfx->height;
5615 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5617 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5619 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5620 y = request.height - 2 * request.border_size - gfx->height * 2;
5622 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5623 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5628 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5630 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5632 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5633 pos->size, &deco_bitmap, &deco_x, &deco_y);
5634 deco_xpos = (gfx->width - pos->size) / 2;
5635 deco_ypos = (gfx->height - pos->size) / 2;
5638 gi = CreateGadget(GDI_CUSTOM_ID, id,
5639 GDI_IMAGE_ID, graphic,
5640 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5643 GDI_WIDTH, gfx->width,
5644 GDI_HEIGHT, gfx->height,
5645 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5646 GDI_STATE, GD_BUTTON_UNPRESSED,
5647 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5648 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5649 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5650 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5651 GDI_DECORATION_SIZE, pos->size, pos->size,
5652 GDI_DECORATION_SHIFTING, 1, 1,
5653 GDI_DIRECT_DRAW, FALSE,
5654 GDI_EVENT_MASK, event_mask,
5655 GDI_CALLBACK_ACTION, HandleToolButtons,
5659 Error(ERR_EXIT, "cannot create gadget");
5661 tool_gadget[id] = gi;
5665 void FreeToolButtons(void)
5669 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5670 FreeGadget(tool_gadget[i]);
5673 static void UnmapToolButtons(void)
5677 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5678 UnmapGadget(tool_gadget[i]);
5681 static void HandleToolButtons(struct GadgetInfo *gi)
5683 request_gadget_id = gi->custom_id;
5686 static struct Mapping_EM_to_RND_object
5689 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5690 boolean is_backside; // backside of moving element
5696 em_object_mapping_list[] =
5699 Xblank, TRUE, FALSE,
5703 Yacid_splash_eB, FALSE, FALSE,
5704 EL_ACID_SPLASH_RIGHT, -1, -1
5707 Yacid_splash_wB, FALSE, FALSE,
5708 EL_ACID_SPLASH_LEFT, -1, -1
5711 #ifdef EM_ENGINE_BAD_ROLL
5713 Xstone_force_e, FALSE, FALSE,
5714 EL_ROCK, -1, MV_BIT_RIGHT
5717 Xstone_force_w, FALSE, FALSE,
5718 EL_ROCK, -1, MV_BIT_LEFT
5721 Xnut_force_e, FALSE, FALSE,
5722 EL_NUT, -1, MV_BIT_RIGHT
5725 Xnut_force_w, FALSE, FALSE,
5726 EL_NUT, -1, MV_BIT_LEFT
5729 Xspring_force_e, FALSE, FALSE,
5730 EL_SPRING, -1, MV_BIT_RIGHT
5733 Xspring_force_w, FALSE, FALSE,
5734 EL_SPRING, -1, MV_BIT_LEFT
5737 Xemerald_force_e, FALSE, FALSE,
5738 EL_EMERALD, -1, MV_BIT_RIGHT
5741 Xemerald_force_w, FALSE, FALSE,
5742 EL_EMERALD, -1, MV_BIT_LEFT
5745 Xdiamond_force_e, FALSE, FALSE,
5746 EL_DIAMOND, -1, MV_BIT_RIGHT
5749 Xdiamond_force_w, FALSE, FALSE,
5750 EL_DIAMOND, -1, MV_BIT_LEFT
5753 Xbomb_force_e, FALSE, FALSE,
5754 EL_BOMB, -1, MV_BIT_RIGHT
5757 Xbomb_force_w, FALSE, FALSE,
5758 EL_BOMB, -1, MV_BIT_LEFT
5760 #endif // EM_ENGINE_BAD_ROLL
5763 Xstone, TRUE, FALSE,
5767 Xstone_pause, FALSE, FALSE,
5771 Xstone_fall, FALSE, FALSE,
5775 Ystone_s, FALSE, FALSE,
5776 EL_ROCK, ACTION_FALLING, -1
5779 Ystone_sB, FALSE, TRUE,
5780 EL_ROCK, ACTION_FALLING, -1
5783 Ystone_e, FALSE, FALSE,
5784 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5787 Ystone_eB, FALSE, TRUE,
5788 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5791 Ystone_w, FALSE, FALSE,
5792 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5795 Ystone_wB, FALSE, TRUE,
5796 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5803 Xnut_pause, FALSE, FALSE,
5807 Xnut_fall, FALSE, FALSE,
5811 Ynut_s, FALSE, FALSE,
5812 EL_NUT, ACTION_FALLING, -1
5815 Ynut_sB, FALSE, TRUE,
5816 EL_NUT, ACTION_FALLING, -1
5819 Ynut_e, FALSE, FALSE,
5820 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5823 Ynut_eB, FALSE, TRUE,
5824 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5827 Ynut_w, FALSE, FALSE,
5828 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5831 Ynut_wB, FALSE, TRUE,
5832 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5835 Xbug_n, TRUE, FALSE,
5839 Xbug_e, TRUE, FALSE,
5840 EL_BUG_RIGHT, -1, -1
5843 Xbug_s, TRUE, FALSE,
5847 Xbug_w, TRUE, FALSE,
5851 Xbug_gon, FALSE, FALSE,
5855 Xbug_goe, FALSE, FALSE,
5856 EL_BUG_RIGHT, -1, -1
5859 Xbug_gos, FALSE, FALSE,
5863 Xbug_gow, FALSE, FALSE,
5867 Ybug_n, FALSE, FALSE,
5868 EL_BUG, ACTION_MOVING, MV_BIT_UP
5871 Ybug_nB, FALSE, TRUE,
5872 EL_BUG, ACTION_MOVING, MV_BIT_UP
5875 Ybug_e, FALSE, FALSE,
5876 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5879 Ybug_eB, FALSE, TRUE,
5880 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5883 Ybug_s, FALSE, FALSE,
5884 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5887 Ybug_sB, FALSE, TRUE,
5888 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5891 Ybug_w, FALSE, FALSE,
5892 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5895 Ybug_wB, FALSE, TRUE,
5896 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5899 Ybug_w_n, FALSE, FALSE,
5900 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5903 Ybug_n_e, FALSE, FALSE,
5904 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5907 Ybug_e_s, FALSE, FALSE,
5908 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5911 Ybug_s_w, FALSE, FALSE,
5912 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5915 Ybug_e_n, FALSE, FALSE,
5916 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5919 Ybug_s_e, FALSE, FALSE,
5920 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5923 Ybug_w_s, FALSE, FALSE,
5924 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5927 Ybug_n_w, FALSE, FALSE,
5928 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5931 Ybug_stone, FALSE, FALSE,
5932 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5935 Ybug_spring, FALSE, FALSE,
5936 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5939 Xtank_n, TRUE, FALSE,
5940 EL_SPACESHIP_UP, -1, -1
5943 Xtank_e, TRUE, FALSE,
5944 EL_SPACESHIP_RIGHT, -1, -1
5947 Xtank_s, TRUE, FALSE,
5948 EL_SPACESHIP_DOWN, -1, -1
5951 Xtank_w, TRUE, FALSE,
5952 EL_SPACESHIP_LEFT, -1, -1
5955 Xtank_gon, FALSE, FALSE,
5956 EL_SPACESHIP_UP, -1, -1
5959 Xtank_goe, FALSE, FALSE,
5960 EL_SPACESHIP_RIGHT, -1, -1
5963 Xtank_gos, FALSE, FALSE,
5964 EL_SPACESHIP_DOWN, -1, -1
5967 Xtank_gow, FALSE, FALSE,
5968 EL_SPACESHIP_LEFT, -1, -1
5971 Ytank_n, FALSE, FALSE,
5972 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5975 Ytank_nB, FALSE, TRUE,
5976 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5979 Ytank_e, FALSE, FALSE,
5980 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5983 Ytank_eB, FALSE, TRUE,
5984 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5987 Ytank_s, FALSE, FALSE,
5988 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5991 Ytank_sB, FALSE, TRUE,
5992 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5995 Ytank_w, FALSE, FALSE,
5996 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5999 Ytank_wB, FALSE, TRUE,
6000 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
6003 Ytank_w_n, FALSE, FALSE,
6004 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
6007 Ytank_n_e, FALSE, FALSE,
6008 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6011 Ytank_e_s, FALSE, FALSE,
6012 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6015 Ytank_s_w, FALSE, FALSE,
6016 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6019 Ytank_e_n, FALSE, FALSE,
6020 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6023 Ytank_s_e, FALSE, FALSE,
6024 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6027 Ytank_w_s, FALSE, FALSE,
6028 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6031 Ytank_n_w, FALSE, FALSE,
6032 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6035 Ytank_stone, FALSE, FALSE,
6036 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6039 Ytank_spring, FALSE, FALSE,
6040 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6043 Xandroid, TRUE, FALSE,
6044 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6047 Xandroid_1_n, FALSE, FALSE,
6048 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6051 Xandroid_2_n, FALSE, FALSE,
6052 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6055 Xandroid_1_e, FALSE, FALSE,
6056 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6059 Xandroid_2_e, FALSE, FALSE,
6060 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6063 Xandroid_1_w, FALSE, FALSE,
6064 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6067 Xandroid_2_w, FALSE, FALSE,
6068 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6071 Xandroid_1_s, FALSE, FALSE,
6072 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6075 Xandroid_2_s, FALSE, FALSE,
6076 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6079 Yandroid_n, FALSE, FALSE,
6080 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6083 Yandroid_nB, FALSE, TRUE,
6084 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6087 Yandroid_ne, FALSE, FALSE,
6088 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6091 Yandroid_neB, FALSE, TRUE,
6092 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6095 Yandroid_e, FALSE, FALSE,
6096 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6099 Yandroid_eB, FALSE, TRUE,
6100 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6103 Yandroid_se, FALSE, FALSE,
6104 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6107 Yandroid_seB, FALSE, TRUE,
6108 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6111 Yandroid_s, FALSE, FALSE,
6112 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6115 Yandroid_sB, FALSE, TRUE,
6116 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6119 Yandroid_sw, FALSE, FALSE,
6120 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6123 Yandroid_swB, FALSE, TRUE,
6124 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6127 Yandroid_w, FALSE, FALSE,
6128 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6131 Yandroid_wB, FALSE, TRUE,
6132 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6135 Yandroid_nw, FALSE, FALSE,
6136 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6139 Yandroid_nwB, FALSE, TRUE,
6140 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6143 Xspring, TRUE, FALSE,
6147 Xspring_pause, FALSE, FALSE,
6151 Xspring_e, FALSE, FALSE,
6155 Xspring_w, FALSE, FALSE,
6159 Xspring_fall, FALSE, FALSE,
6163 Yspring_s, FALSE, FALSE,
6164 EL_SPRING, ACTION_FALLING, -1
6167 Yspring_sB, FALSE, TRUE,
6168 EL_SPRING, ACTION_FALLING, -1
6171 Yspring_e, FALSE, FALSE,
6172 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6175 Yspring_eB, FALSE, TRUE,
6176 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6179 Yspring_w, FALSE, FALSE,
6180 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6183 Yspring_wB, FALSE, TRUE,
6184 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6187 Yspring_kill_e, FALSE, FALSE,
6188 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6191 Yspring_kill_eB, FALSE, TRUE,
6192 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6195 Yspring_kill_w, FALSE, FALSE,
6196 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6199 Yspring_kill_wB, FALSE, TRUE,
6200 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6203 Xeater_n, TRUE, FALSE,
6204 EL_YAMYAM_UP, -1, -1
6207 Xeater_e, TRUE, FALSE,
6208 EL_YAMYAM_RIGHT, -1, -1
6211 Xeater_w, TRUE, FALSE,
6212 EL_YAMYAM_LEFT, -1, -1
6215 Xeater_s, TRUE, FALSE,
6216 EL_YAMYAM_DOWN, -1, -1
6219 Yeater_n, FALSE, FALSE,
6220 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6223 Yeater_nB, FALSE, TRUE,
6224 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6227 Yeater_e, FALSE, FALSE,
6228 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6231 Yeater_eB, FALSE, TRUE,
6232 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6235 Yeater_s, FALSE, FALSE,
6236 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6239 Yeater_sB, FALSE, TRUE,
6240 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6243 Yeater_w, FALSE, FALSE,
6244 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6247 Yeater_wB, FALSE, TRUE,
6248 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6251 Yeater_stone, FALSE, FALSE,
6252 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6255 Yeater_spring, FALSE, FALSE,
6256 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6259 Xalien, TRUE, FALSE,
6263 Xalien_pause, FALSE, FALSE,
6267 Yalien_n, FALSE, FALSE,
6268 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6271 Yalien_nB, FALSE, TRUE,
6272 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6275 Yalien_e, FALSE, FALSE,
6276 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6279 Yalien_eB, FALSE, TRUE,
6280 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6283 Yalien_s, FALSE, FALSE,
6284 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6287 Yalien_sB, FALSE, TRUE,
6288 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6291 Yalien_w, FALSE, FALSE,
6292 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6295 Yalien_wB, FALSE, TRUE,
6296 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6299 Yalien_stone, FALSE, FALSE,
6300 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6303 Yalien_spring, FALSE, FALSE,
6304 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6307 Xemerald, TRUE, FALSE,
6311 Xemerald_pause, FALSE, FALSE,
6315 Xemerald_fall, FALSE, FALSE,
6319 Xemerald_shine, FALSE, FALSE,
6320 EL_EMERALD, ACTION_TWINKLING, -1
6323 Yemerald_s, FALSE, FALSE,
6324 EL_EMERALD, ACTION_FALLING, -1
6327 Yemerald_sB, FALSE, TRUE,
6328 EL_EMERALD, ACTION_FALLING, -1
6331 Yemerald_e, FALSE, FALSE,
6332 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6335 Yemerald_eB, FALSE, TRUE,
6336 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6339 Yemerald_w, FALSE, FALSE,
6340 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6343 Yemerald_wB, FALSE, TRUE,
6344 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6347 Yemerald_eat, FALSE, FALSE,
6348 EL_EMERALD, ACTION_COLLECTING, -1
6351 Yemerald_stone, FALSE, FALSE,
6352 EL_NUT, ACTION_BREAKING, -1
6355 Xdiamond, TRUE, FALSE,
6359 Xdiamond_pause, FALSE, FALSE,
6363 Xdiamond_fall, FALSE, FALSE,
6367 Xdiamond_shine, FALSE, FALSE,
6368 EL_DIAMOND, ACTION_TWINKLING, -1
6371 Ydiamond_s, FALSE, FALSE,
6372 EL_DIAMOND, ACTION_FALLING, -1
6375 Ydiamond_sB, FALSE, TRUE,
6376 EL_DIAMOND, ACTION_FALLING, -1
6379 Ydiamond_e, FALSE, FALSE,
6380 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6383 Ydiamond_eB, FALSE, TRUE,
6384 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6387 Ydiamond_w, FALSE, FALSE,
6388 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6391 Ydiamond_wB, FALSE, TRUE,
6392 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6395 Ydiamond_eat, FALSE, FALSE,
6396 EL_DIAMOND, ACTION_COLLECTING, -1
6399 Ydiamond_stone, FALSE, FALSE,
6400 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6403 Xdrip_fall, TRUE, FALSE,
6404 EL_AMOEBA_DROP, -1, -1
6407 Xdrip_stretch, FALSE, FALSE,
6408 EL_AMOEBA_DROP, ACTION_FALLING, -1
6411 Xdrip_stretchB, FALSE, TRUE,
6412 EL_AMOEBA_DROP, ACTION_FALLING, -1
6415 Xdrip_eat, FALSE, FALSE,
6416 EL_AMOEBA_DROP, ACTION_GROWING, -1
6419 Ydrip_s1, FALSE, FALSE,
6420 EL_AMOEBA_DROP, ACTION_FALLING, -1
6423 Ydrip_s1B, FALSE, TRUE,
6424 EL_AMOEBA_DROP, ACTION_FALLING, -1
6427 Ydrip_s2, FALSE, FALSE,
6428 EL_AMOEBA_DROP, ACTION_FALLING, -1
6431 Ydrip_s2B, FALSE, TRUE,
6432 EL_AMOEBA_DROP, ACTION_FALLING, -1
6439 Xbomb_pause, FALSE, FALSE,
6443 Xbomb_fall, FALSE, FALSE,
6447 Ybomb_s, FALSE, FALSE,
6448 EL_BOMB, ACTION_FALLING, -1
6451 Ybomb_sB, FALSE, TRUE,
6452 EL_BOMB, ACTION_FALLING, -1
6455 Ybomb_e, FALSE, FALSE,
6456 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6459 Ybomb_eB, FALSE, TRUE,
6460 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6463 Ybomb_w, FALSE, FALSE,
6464 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6467 Ybomb_wB, FALSE, TRUE,
6468 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6471 Ybomb_eat, FALSE, FALSE,
6472 EL_BOMB, ACTION_ACTIVATING, -1
6475 Xballoon, TRUE, FALSE,
6479 Yballoon_n, FALSE, FALSE,
6480 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6483 Yballoon_nB, FALSE, TRUE,
6484 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6487 Yballoon_e, FALSE, FALSE,
6488 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6491 Yballoon_eB, FALSE, TRUE,
6492 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6495 Yballoon_s, FALSE, FALSE,
6496 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6499 Yballoon_sB, FALSE, TRUE,
6500 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6503 Yballoon_w, FALSE, FALSE,
6504 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6507 Yballoon_wB, FALSE, TRUE,
6508 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6511 Xgrass, TRUE, FALSE,
6512 EL_EMC_GRASS, -1, -1
6515 Ygrass_nB, FALSE, FALSE,
6516 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6519 Ygrass_eB, FALSE, FALSE,
6520 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6523 Ygrass_sB, FALSE, FALSE,
6524 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6527 Ygrass_wB, FALSE, FALSE,
6528 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6535 Ydirt_nB, FALSE, FALSE,
6536 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6539 Ydirt_eB, FALSE, FALSE,
6540 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6543 Ydirt_sB, FALSE, FALSE,
6544 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6547 Ydirt_wB, FALSE, FALSE,
6548 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6551 Xacid_ne, TRUE, FALSE,
6552 EL_ACID_POOL_TOPRIGHT, -1, -1
6555 Xacid_se, TRUE, FALSE,
6556 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6559 Xacid_s, TRUE, FALSE,
6560 EL_ACID_POOL_BOTTOM, -1, -1
6563 Xacid_sw, TRUE, FALSE,
6564 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6567 Xacid_nw, TRUE, FALSE,
6568 EL_ACID_POOL_TOPLEFT, -1, -1
6571 Xacid_1, TRUE, FALSE,
6575 Xacid_2, FALSE, FALSE,
6579 Xacid_3, FALSE, FALSE,
6583 Xacid_4, FALSE, FALSE,
6587 Xacid_5, FALSE, FALSE,
6591 Xacid_6, FALSE, FALSE,
6595 Xacid_7, FALSE, FALSE,
6599 Xacid_8, FALSE, FALSE,
6603 Xball_1, TRUE, FALSE,
6604 EL_EMC_MAGIC_BALL, -1, -1
6607 Xball_1B, FALSE, FALSE,
6608 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6611 Xball_2, FALSE, FALSE,
6612 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6615 Xball_2B, FALSE, FALSE,
6616 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6619 Yball_eat, FALSE, FALSE,
6620 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6623 Ykey_1_eat, FALSE, FALSE,
6624 EL_EM_KEY_1, ACTION_COLLECTING, -1
6627 Ykey_2_eat, FALSE, FALSE,
6628 EL_EM_KEY_2, ACTION_COLLECTING, -1
6631 Ykey_3_eat, FALSE, FALSE,
6632 EL_EM_KEY_3, ACTION_COLLECTING, -1
6635 Ykey_4_eat, FALSE, FALSE,
6636 EL_EM_KEY_4, ACTION_COLLECTING, -1
6639 Ykey_5_eat, FALSE, FALSE,
6640 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6643 Ykey_6_eat, FALSE, FALSE,
6644 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6647 Ykey_7_eat, FALSE, FALSE,
6648 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6651 Ykey_8_eat, FALSE, FALSE,
6652 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6655 Ylenses_eat, FALSE, FALSE,
6656 EL_EMC_LENSES, ACTION_COLLECTING, -1
6659 Ymagnify_eat, FALSE, FALSE,
6660 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6663 Ygrass_eat, FALSE, FALSE,
6664 EL_EMC_GRASS, ACTION_SNAPPING, -1
6667 Ydirt_eat, FALSE, FALSE,
6668 EL_SAND, ACTION_SNAPPING, -1
6671 Xgrow_ns, TRUE, FALSE,
6672 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6675 Ygrow_ns_eat, FALSE, FALSE,
6676 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6679 Xgrow_ew, TRUE, FALSE,
6680 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6683 Ygrow_ew_eat, FALSE, FALSE,
6684 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6687 Xwonderwall, TRUE, FALSE,
6688 EL_MAGIC_WALL, -1, -1
6691 XwonderwallB, FALSE, FALSE,
6692 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6695 Xamoeba_1, TRUE, FALSE,
6696 EL_AMOEBA_DRY, ACTION_OTHER, -1
6699 Xamoeba_2, FALSE, FALSE,
6700 EL_AMOEBA_DRY, ACTION_OTHER, -1
6703 Xamoeba_3, FALSE, FALSE,
6704 EL_AMOEBA_DRY, ACTION_OTHER, -1
6707 Xamoeba_4, FALSE, FALSE,
6708 EL_AMOEBA_DRY, ACTION_OTHER, -1
6711 Xamoeba_5, TRUE, FALSE,
6712 EL_AMOEBA_WET, ACTION_OTHER, -1
6715 Xamoeba_6, FALSE, FALSE,
6716 EL_AMOEBA_WET, ACTION_OTHER, -1
6719 Xamoeba_7, FALSE, FALSE,
6720 EL_AMOEBA_WET, ACTION_OTHER, -1
6723 Xamoeba_8, FALSE, FALSE,
6724 EL_AMOEBA_WET, ACTION_OTHER, -1
6727 Xdoor_1, TRUE, FALSE,
6728 EL_EM_GATE_1, -1, -1
6731 Xdoor_2, TRUE, FALSE,
6732 EL_EM_GATE_2, -1, -1
6735 Xdoor_3, TRUE, FALSE,
6736 EL_EM_GATE_3, -1, -1
6739 Xdoor_4, TRUE, FALSE,
6740 EL_EM_GATE_4, -1, -1
6743 Xdoor_5, TRUE, FALSE,
6744 EL_EMC_GATE_5, -1, -1
6747 Xdoor_6, TRUE, FALSE,
6748 EL_EMC_GATE_6, -1, -1
6751 Xdoor_7, TRUE, FALSE,
6752 EL_EMC_GATE_7, -1, -1
6755 Xdoor_8, TRUE, FALSE,
6756 EL_EMC_GATE_8, -1, -1
6759 Xkey_1, TRUE, FALSE,
6763 Xkey_2, TRUE, FALSE,
6767 Xkey_3, TRUE, FALSE,
6771 Xkey_4, TRUE, FALSE,
6775 Xkey_5, TRUE, FALSE,
6776 EL_EMC_KEY_5, -1, -1
6779 Xkey_6, TRUE, FALSE,
6780 EL_EMC_KEY_6, -1, -1
6783 Xkey_7, TRUE, FALSE,
6784 EL_EMC_KEY_7, -1, -1
6787 Xkey_8, TRUE, FALSE,
6788 EL_EMC_KEY_8, -1, -1
6791 Xwind_n, TRUE, FALSE,
6792 EL_BALLOON_SWITCH_UP, -1, -1
6795 Xwind_e, TRUE, FALSE,
6796 EL_BALLOON_SWITCH_RIGHT, -1, -1
6799 Xwind_s, TRUE, FALSE,
6800 EL_BALLOON_SWITCH_DOWN, -1, -1
6803 Xwind_w, TRUE, FALSE,
6804 EL_BALLOON_SWITCH_LEFT, -1, -1
6807 Xwind_nesw, TRUE, FALSE,
6808 EL_BALLOON_SWITCH_ANY, -1, -1
6811 Xwind_stop, TRUE, FALSE,
6812 EL_BALLOON_SWITCH_NONE, -1, -1
6816 EL_EM_EXIT_CLOSED, -1, -1
6819 Xexit_1, TRUE, FALSE,
6820 EL_EM_EXIT_OPEN, -1, -1
6823 Xexit_2, FALSE, FALSE,
6824 EL_EM_EXIT_OPEN, -1, -1
6827 Xexit_3, FALSE, FALSE,
6828 EL_EM_EXIT_OPEN, -1, -1
6831 Xdynamite, TRUE, FALSE,
6832 EL_EM_DYNAMITE, -1, -1
6835 Ydynamite_eat, FALSE, FALSE,
6836 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6839 Xdynamite_1, TRUE, FALSE,
6840 EL_EM_DYNAMITE_ACTIVE, -1, -1
6843 Xdynamite_2, FALSE, FALSE,
6844 EL_EM_DYNAMITE_ACTIVE, -1, -1
6847 Xdynamite_3, FALSE, FALSE,
6848 EL_EM_DYNAMITE_ACTIVE, -1, -1
6851 Xdynamite_4, FALSE, FALSE,
6852 EL_EM_DYNAMITE_ACTIVE, -1, -1
6855 Xbumper, TRUE, FALSE,
6856 EL_EMC_SPRING_BUMPER, -1, -1
6859 XbumperB, FALSE, FALSE,
6860 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6863 Xwheel, TRUE, FALSE,
6864 EL_ROBOT_WHEEL, -1, -1
6867 XwheelB, FALSE, FALSE,
6868 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6871 Xswitch, TRUE, FALSE,
6872 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6875 XswitchB, FALSE, FALSE,
6876 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6880 EL_QUICKSAND_EMPTY, -1, -1
6883 Xsand_stone, TRUE, FALSE,
6884 EL_QUICKSAND_FULL, -1, -1
6887 Xsand_stonein_1, FALSE, TRUE,
6888 EL_ROCK, ACTION_FILLING, -1
6891 Xsand_stonein_2, FALSE, TRUE,
6892 EL_ROCK, ACTION_FILLING, -1
6895 Xsand_stonein_3, FALSE, TRUE,
6896 EL_ROCK, ACTION_FILLING, -1
6899 Xsand_stonein_4, FALSE, TRUE,
6900 EL_ROCK, ACTION_FILLING, -1
6903 Xsand_stonesand_1, FALSE, FALSE,
6904 EL_QUICKSAND_EMPTYING, -1, -1
6907 Xsand_stonesand_2, FALSE, FALSE,
6908 EL_QUICKSAND_EMPTYING, -1, -1
6911 Xsand_stonesand_3, FALSE, FALSE,
6912 EL_QUICKSAND_EMPTYING, -1, -1
6915 Xsand_stonesand_4, FALSE, FALSE,
6916 EL_QUICKSAND_EMPTYING, -1, -1
6919 Xsand_stonesand_quickout_1, FALSE, FALSE,
6920 EL_QUICKSAND_EMPTYING, -1, -1
6923 Xsand_stonesand_quickout_2, FALSE, FALSE,
6924 EL_QUICKSAND_EMPTYING, -1, -1
6927 Xsand_stoneout_1, FALSE, FALSE,
6928 EL_ROCK, ACTION_EMPTYING, -1
6931 Xsand_stoneout_2, FALSE, FALSE,
6932 EL_ROCK, ACTION_EMPTYING, -1
6935 Xsand_sandstone_1, FALSE, FALSE,
6936 EL_QUICKSAND_FILLING, -1, -1
6939 Xsand_sandstone_2, FALSE, FALSE,
6940 EL_QUICKSAND_FILLING, -1, -1
6943 Xsand_sandstone_3, FALSE, FALSE,
6944 EL_QUICKSAND_FILLING, -1, -1
6947 Xsand_sandstone_4, FALSE, FALSE,
6948 EL_QUICKSAND_FILLING, -1, -1
6951 Xplant, TRUE, FALSE,
6952 EL_EMC_PLANT, -1, -1
6955 Yplant, FALSE, FALSE,
6956 EL_EMC_PLANT, -1, -1
6959 Xlenses, TRUE, FALSE,
6960 EL_EMC_LENSES, -1, -1
6963 Xmagnify, TRUE, FALSE,
6964 EL_EMC_MAGNIFIER, -1, -1
6967 Xdripper, TRUE, FALSE,
6968 EL_EMC_DRIPPER, -1, -1
6971 XdripperB, FALSE, FALSE,
6972 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6975 Xfake_blank, TRUE, FALSE,
6976 EL_INVISIBLE_WALL, -1, -1
6979 Xfake_blankB, FALSE, FALSE,
6980 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6983 Xfake_grass, TRUE, FALSE,
6984 EL_EMC_FAKE_GRASS, -1, -1
6987 Xfake_grassB, FALSE, FALSE,
6988 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6991 Xfake_door_1, TRUE, FALSE,
6992 EL_EM_GATE_1_GRAY, -1, -1
6995 Xfake_door_2, TRUE, FALSE,
6996 EL_EM_GATE_2_GRAY, -1, -1
6999 Xfake_door_3, TRUE, FALSE,
7000 EL_EM_GATE_3_GRAY, -1, -1
7003 Xfake_door_4, TRUE, FALSE,
7004 EL_EM_GATE_4_GRAY, -1, -1
7007 Xfake_door_5, TRUE, FALSE,
7008 EL_EMC_GATE_5_GRAY, -1, -1
7011 Xfake_door_6, TRUE, FALSE,
7012 EL_EMC_GATE_6_GRAY, -1, -1
7015 Xfake_door_7, TRUE, FALSE,
7016 EL_EMC_GATE_7_GRAY, -1, -1
7019 Xfake_door_8, TRUE, FALSE,
7020 EL_EMC_GATE_8_GRAY, -1, -1
7023 Xfake_acid_1, TRUE, FALSE,
7024 EL_EMC_FAKE_ACID, -1, -1
7027 Xfake_acid_2, FALSE, FALSE,
7028 EL_EMC_FAKE_ACID, -1, -1
7031 Xfake_acid_3, FALSE, FALSE,
7032 EL_EMC_FAKE_ACID, -1, -1
7035 Xfake_acid_4, FALSE, FALSE,
7036 EL_EMC_FAKE_ACID, -1, -1
7039 Xfake_acid_5, FALSE, FALSE,
7040 EL_EMC_FAKE_ACID, -1, -1
7043 Xfake_acid_6, FALSE, FALSE,
7044 EL_EMC_FAKE_ACID, -1, -1
7047 Xfake_acid_7, FALSE, FALSE,
7048 EL_EMC_FAKE_ACID, -1, -1
7051 Xfake_acid_8, FALSE, FALSE,
7052 EL_EMC_FAKE_ACID, -1, -1
7055 Xsteel_1, TRUE, FALSE,
7056 EL_STEELWALL, -1, -1
7059 Xsteel_2, TRUE, FALSE,
7060 EL_EMC_STEELWALL_2, -1, -1
7063 Xsteel_3, TRUE, FALSE,
7064 EL_EMC_STEELWALL_3, -1, -1
7067 Xsteel_4, TRUE, FALSE,
7068 EL_EMC_STEELWALL_4, -1, -1
7071 Xwall_1, TRUE, FALSE,
7075 Xwall_2, TRUE, FALSE,
7076 EL_EMC_WALL_14, -1, -1
7079 Xwall_3, TRUE, FALSE,
7080 EL_EMC_WALL_15, -1, -1
7083 Xwall_4, TRUE, FALSE,
7084 EL_EMC_WALL_16, -1, -1
7087 Xround_wall_1, TRUE, FALSE,
7088 EL_WALL_SLIPPERY, -1, -1
7091 Xround_wall_2, TRUE, FALSE,
7092 EL_EMC_WALL_SLIPPERY_2, -1, -1
7095 Xround_wall_3, TRUE, FALSE,
7096 EL_EMC_WALL_SLIPPERY_3, -1, -1
7099 Xround_wall_4, TRUE, FALSE,
7100 EL_EMC_WALL_SLIPPERY_4, -1, -1
7103 Xdecor_1, TRUE, FALSE,
7104 EL_EMC_WALL_8, -1, -1
7107 Xdecor_2, TRUE, FALSE,
7108 EL_EMC_WALL_6, -1, -1
7111 Xdecor_3, TRUE, FALSE,
7112 EL_EMC_WALL_4, -1, -1
7115 Xdecor_4, TRUE, FALSE,
7116 EL_EMC_WALL_7, -1, -1
7119 Xdecor_5, TRUE, FALSE,
7120 EL_EMC_WALL_5, -1, -1
7123 Xdecor_6, TRUE, FALSE,
7124 EL_EMC_WALL_9, -1, -1
7127 Xdecor_7, TRUE, FALSE,
7128 EL_EMC_WALL_10, -1, -1
7131 Xdecor_8, TRUE, FALSE,
7132 EL_EMC_WALL_1, -1, -1
7135 Xdecor_9, TRUE, FALSE,
7136 EL_EMC_WALL_2, -1, -1
7139 Xdecor_10, TRUE, FALSE,
7140 EL_EMC_WALL_3, -1, -1
7143 Xdecor_11, TRUE, FALSE,
7144 EL_EMC_WALL_11, -1, -1
7147 Xdecor_12, TRUE, FALSE,
7148 EL_EMC_WALL_12, -1, -1
7151 Xalpha_0, TRUE, FALSE,
7152 EL_CHAR('0'), -1, -1
7155 Xalpha_1, TRUE, FALSE,
7156 EL_CHAR('1'), -1, -1
7159 Xalpha_2, TRUE, FALSE,
7160 EL_CHAR('2'), -1, -1
7163 Xalpha_3, TRUE, FALSE,
7164 EL_CHAR('3'), -1, -1
7167 Xalpha_4, TRUE, FALSE,
7168 EL_CHAR('4'), -1, -1
7171 Xalpha_5, TRUE, FALSE,
7172 EL_CHAR('5'), -1, -1
7175 Xalpha_6, TRUE, FALSE,
7176 EL_CHAR('6'), -1, -1
7179 Xalpha_7, TRUE, FALSE,
7180 EL_CHAR('7'), -1, -1
7183 Xalpha_8, TRUE, FALSE,
7184 EL_CHAR('8'), -1, -1
7187 Xalpha_9, TRUE, FALSE,
7188 EL_CHAR('9'), -1, -1
7191 Xalpha_excla, TRUE, FALSE,
7192 EL_CHAR('!'), -1, -1
7195 Xalpha_quote, TRUE, FALSE,
7196 EL_CHAR('"'), -1, -1
7199 Xalpha_comma, TRUE, FALSE,
7200 EL_CHAR(','), -1, -1
7203 Xalpha_minus, TRUE, FALSE,
7204 EL_CHAR('-'), -1, -1
7207 Xalpha_perio, TRUE, FALSE,
7208 EL_CHAR('.'), -1, -1
7211 Xalpha_colon, TRUE, FALSE,
7212 EL_CHAR(':'), -1, -1
7215 Xalpha_quest, TRUE, FALSE,
7216 EL_CHAR('?'), -1, -1
7219 Xalpha_a, TRUE, FALSE,
7220 EL_CHAR('A'), -1, -1
7223 Xalpha_b, TRUE, FALSE,
7224 EL_CHAR('B'), -1, -1
7227 Xalpha_c, TRUE, FALSE,
7228 EL_CHAR('C'), -1, -1
7231 Xalpha_d, TRUE, FALSE,
7232 EL_CHAR('D'), -1, -1
7235 Xalpha_e, TRUE, FALSE,
7236 EL_CHAR('E'), -1, -1
7239 Xalpha_f, TRUE, FALSE,
7240 EL_CHAR('F'), -1, -1
7243 Xalpha_g, TRUE, FALSE,
7244 EL_CHAR('G'), -1, -1
7247 Xalpha_h, TRUE, FALSE,
7248 EL_CHAR('H'), -1, -1
7251 Xalpha_i, TRUE, FALSE,
7252 EL_CHAR('I'), -1, -1
7255 Xalpha_j, TRUE, FALSE,
7256 EL_CHAR('J'), -1, -1
7259 Xalpha_k, TRUE, FALSE,
7260 EL_CHAR('K'), -1, -1
7263 Xalpha_l, TRUE, FALSE,
7264 EL_CHAR('L'), -1, -1
7267 Xalpha_m, TRUE, FALSE,
7268 EL_CHAR('M'), -1, -1
7271 Xalpha_n, TRUE, FALSE,
7272 EL_CHAR('N'), -1, -1
7275 Xalpha_o, TRUE, FALSE,
7276 EL_CHAR('O'), -1, -1
7279 Xalpha_p, TRUE, FALSE,
7280 EL_CHAR('P'), -1, -1
7283 Xalpha_q, TRUE, FALSE,
7284 EL_CHAR('Q'), -1, -1
7287 Xalpha_r, TRUE, FALSE,
7288 EL_CHAR('R'), -1, -1
7291 Xalpha_s, TRUE, FALSE,
7292 EL_CHAR('S'), -1, -1
7295 Xalpha_t, TRUE, FALSE,
7296 EL_CHAR('T'), -1, -1
7299 Xalpha_u, TRUE, FALSE,
7300 EL_CHAR('U'), -1, -1
7303 Xalpha_v, TRUE, FALSE,
7304 EL_CHAR('V'), -1, -1
7307 Xalpha_w, TRUE, FALSE,
7308 EL_CHAR('W'), -1, -1
7311 Xalpha_x, TRUE, FALSE,
7312 EL_CHAR('X'), -1, -1
7315 Xalpha_y, TRUE, FALSE,
7316 EL_CHAR('Y'), -1, -1
7319 Xalpha_z, TRUE, FALSE,
7320 EL_CHAR('Z'), -1, -1
7323 Xalpha_arrow_e, TRUE, FALSE,
7324 EL_CHAR('>'), -1, -1
7327 Xalpha_arrow_w, TRUE, FALSE,
7328 EL_CHAR('<'), -1, -1
7331 Xalpha_copyr, TRUE, FALSE,
7332 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7336 Xboom_bug, FALSE, FALSE,
7337 EL_BUG, ACTION_EXPLODING, -1
7340 Xboom_bomb, FALSE, FALSE,
7341 EL_BOMB, ACTION_EXPLODING, -1
7344 Xboom_android, FALSE, FALSE,
7345 EL_EMC_ANDROID, ACTION_OTHER, -1
7348 Xboom_1, FALSE, FALSE,
7349 EL_DEFAULT, ACTION_EXPLODING, -1
7352 Xboom_2, FALSE, FALSE,
7353 EL_DEFAULT, ACTION_EXPLODING, -1
7356 Znormal, FALSE, FALSE,
7360 Zdynamite, FALSE, FALSE,
7364 Zplayer, FALSE, FALSE,
7368 ZBORDER, FALSE, FALSE,
7378 static struct Mapping_EM_to_RND_player
7387 em_player_mapping_list[] =
7391 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7395 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7399 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7403 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7407 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7411 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7415 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7419 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7423 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7427 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7431 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7435 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7439 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7443 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7447 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7451 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7455 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7459 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7463 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7467 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7471 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7475 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7479 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7483 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7487 EL_PLAYER_1, ACTION_DEFAULT, -1,
7491 EL_PLAYER_2, ACTION_DEFAULT, -1,
7495 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7499 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7503 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7507 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7511 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7515 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7519 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7523 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7527 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7531 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7535 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7539 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7543 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7547 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7551 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7555 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7559 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7563 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7567 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7571 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7575 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7579 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7583 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7587 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7591 EL_PLAYER_3, ACTION_DEFAULT, -1,
7595 EL_PLAYER_4, ACTION_DEFAULT, -1,
7604 int map_element_RND_to_EM(int element_rnd)
7606 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7607 static boolean mapping_initialized = FALSE;
7609 if (!mapping_initialized)
7613 // return "Xalpha_quest" for all undefined elements in mapping array
7614 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7615 mapping_RND_to_EM[i] = Xalpha_quest;
7617 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7618 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7619 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7620 em_object_mapping_list[i].element_em;
7622 mapping_initialized = TRUE;
7625 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7626 return mapping_RND_to_EM[element_rnd];
7628 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7633 int map_element_EM_to_RND(int element_em)
7635 static unsigned short mapping_EM_to_RND[TILE_MAX];
7636 static boolean mapping_initialized = FALSE;
7638 if (!mapping_initialized)
7642 // return "EL_UNKNOWN" for all undefined elements in mapping array
7643 for (i = 0; i < TILE_MAX; i++)
7644 mapping_EM_to_RND[i] = EL_UNKNOWN;
7646 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7647 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7648 em_object_mapping_list[i].element_rnd;
7650 mapping_initialized = TRUE;
7653 if (element_em >= 0 && element_em < TILE_MAX)
7654 return mapping_EM_to_RND[element_em];
7656 Error(ERR_WARN, "invalid EM level element %d", element_em);
7661 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7663 struct LevelInfo_EM *level_em = level->native_em_level;
7664 struct LEVEL *lev = level_em->lev;
7667 for (i = 0; i < TILE_MAX; i++)
7668 lev->android_array[i] = Xblank;
7670 for (i = 0; i < level->num_android_clone_elements; i++)
7672 int element_rnd = level->android_clone_element[i];
7673 int element_em = map_element_RND_to_EM(element_rnd);
7675 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7676 if (em_object_mapping_list[j].element_rnd == element_rnd)
7677 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7681 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7683 struct LevelInfo_EM *level_em = level->native_em_level;
7684 struct LEVEL *lev = level_em->lev;
7687 level->num_android_clone_elements = 0;
7689 for (i = 0; i < TILE_MAX; i++)
7691 int element_em = lev->android_array[i];
7693 boolean element_found = FALSE;
7695 if (element_em == Xblank)
7698 element_rnd = map_element_EM_to_RND(element_em);
7700 for (j = 0; j < level->num_android_clone_elements; j++)
7701 if (level->android_clone_element[j] == element_rnd)
7702 element_found = TRUE;
7706 level->android_clone_element[level->num_android_clone_elements++] =
7709 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7714 if (level->num_android_clone_elements == 0)
7716 level->num_android_clone_elements = 1;
7717 level->android_clone_element[0] = EL_EMPTY;
7721 int map_direction_RND_to_EM(int direction)
7723 return (direction == MV_UP ? 0 :
7724 direction == MV_RIGHT ? 1 :
7725 direction == MV_DOWN ? 2 :
7726 direction == MV_LEFT ? 3 :
7730 int map_direction_EM_to_RND(int direction)
7732 return (direction == 0 ? MV_UP :
7733 direction == 1 ? MV_RIGHT :
7734 direction == 2 ? MV_DOWN :
7735 direction == 3 ? MV_LEFT :
7739 int map_element_RND_to_SP(int element_rnd)
7741 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7743 if (element_rnd >= EL_SP_START &&
7744 element_rnd <= EL_SP_END)
7745 element_sp = element_rnd - EL_SP_START;
7746 else if (element_rnd == EL_EMPTY_SPACE)
7748 else if (element_rnd == EL_INVISIBLE_WALL)
7754 int map_element_SP_to_RND(int element_sp)
7756 int element_rnd = EL_UNKNOWN;
7758 if (element_sp >= 0x00 &&
7760 element_rnd = EL_SP_START + element_sp;
7761 else if (element_sp == 0x28)
7762 element_rnd = EL_INVISIBLE_WALL;
7767 int map_action_SP_to_RND(int action_sp)
7771 case actActive: return ACTION_ACTIVE;
7772 case actImpact: return ACTION_IMPACT;
7773 case actExploding: return ACTION_EXPLODING;
7774 case actDigging: return ACTION_DIGGING;
7775 case actSnapping: return ACTION_SNAPPING;
7776 case actCollecting: return ACTION_COLLECTING;
7777 case actPassing: return ACTION_PASSING;
7778 case actPushing: return ACTION_PUSHING;
7779 case actDropping: return ACTION_DROPPING;
7781 default: return ACTION_DEFAULT;
7785 int map_element_RND_to_MM(int element_rnd)
7787 return (element_rnd >= EL_MM_START_1 &&
7788 element_rnd <= EL_MM_END_1 ?
7789 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7791 element_rnd >= EL_MM_START_2 &&
7792 element_rnd <= EL_MM_END_2 ?
7793 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7795 element_rnd >= EL_CHAR_START &&
7796 element_rnd <= EL_CHAR_END ?
7797 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7799 element_rnd >= EL_MM_RUNTIME_START &&
7800 element_rnd <= EL_MM_RUNTIME_END ?
7801 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7803 element_rnd >= EL_MM_DUMMY_START &&
7804 element_rnd <= EL_MM_DUMMY_END ?
7805 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7807 EL_MM_EMPTY_NATIVE);
7810 int map_element_MM_to_RND(int element_mm)
7812 return (element_mm == EL_MM_EMPTY_NATIVE ||
7813 element_mm == EL_DF_EMPTY_NATIVE ?
7816 element_mm >= EL_MM_START_1_NATIVE &&
7817 element_mm <= EL_MM_END_1_NATIVE ?
7818 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7820 element_mm >= EL_MM_START_2_NATIVE &&
7821 element_mm <= EL_MM_END_2_NATIVE ?
7822 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7824 element_mm >= EL_MM_CHAR_START_NATIVE &&
7825 element_mm <= EL_MM_CHAR_END_NATIVE ?
7826 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7828 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7829 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7830 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7832 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7833 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7834 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7839 int map_action_MM_to_RND(int action_mm)
7841 // all MM actions are defined to exactly match their RND counterparts
7845 int map_sound_MM_to_RND(int sound_mm)
7849 case SND_MM_GAME_LEVELTIME_CHARGING:
7850 return SND_GAME_LEVELTIME_CHARGING;
7852 case SND_MM_GAME_HEALTH_CHARGING:
7853 return SND_GAME_HEALTH_CHARGING;
7856 return SND_UNDEFINED;
7860 int map_mm_wall_element(int element)
7862 return (element >= EL_MM_STEEL_WALL_START &&
7863 element <= EL_MM_STEEL_WALL_END ?
7866 element >= EL_MM_WOODEN_WALL_START &&
7867 element <= EL_MM_WOODEN_WALL_END ?
7870 element >= EL_MM_ICE_WALL_START &&
7871 element <= EL_MM_ICE_WALL_END ?
7874 element >= EL_MM_AMOEBA_WALL_START &&
7875 element <= EL_MM_AMOEBA_WALL_END ?
7878 element >= EL_DF_STEEL_WALL_START &&
7879 element <= EL_DF_STEEL_WALL_END ?
7882 element >= EL_DF_WOODEN_WALL_START &&
7883 element <= EL_DF_WOODEN_WALL_END ?
7889 int map_mm_wall_element_editor(int element)
7893 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7894 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7895 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7896 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7897 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7898 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7900 default: return element;
7904 int get_next_element(int element)
7908 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7909 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7910 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7911 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7912 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7913 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7914 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7915 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7916 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7917 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7918 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7920 default: return element;
7924 int el2img_mm(int element_mm)
7926 return el2img(map_element_MM_to_RND(element_mm));
7929 int el_act_dir2img(int element, int action, int direction)
7931 element = GFX_ELEMENT(element);
7932 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7934 // direction_graphic[][] == graphic[] for undefined direction graphics
7935 return element_info[element].direction_graphic[action][direction];
7938 static int el_act_dir2crm(int element, int action, int direction)
7940 element = GFX_ELEMENT(element);
7941 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7943 // direction_graphic[][] == graphic[] for undefined direction graphics
7944 return element_info[element].direction_crumbled[action][direction];
7947 int el_act2img(int element, int action)
7949 element = GFX_ELEMENT(element);
7951 return element_info[element].graphic[action];
7954 int el_act2crm(int element, int action)
7956 element = GFX_ELEMENT(element);
7958 return element_info[element].crumbled[action];
7961 int el_dir2img(int element, int direction)
7963 element = GFX_ELEMENT(element);
7965 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7968 int el2baseimg(int element)
7970 return element_info[element].graphic[ACTION_DEFAULT];
7973 int el2img(int element)
7975 element = GFX_ELEMENT(element);
7977 return element_info[element].graphic[ACTION_DEFAULT];
7980 int el2edimg(int element)
7982 element = GFX_ELEMENT(element);
7984 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7987 int el2preimg(int element)
7989 element = GFX_ELEMENT(element);
7991 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7994 int el2panelimg(int element)
7996 element = GFX_ELEMENT(element);
7998 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
8001 int font2baseimg(int font_nr)
8003 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
8006 int getBeltNrFromBeltElement(int element)
8008 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8009 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8010 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8013 int getBeltNrFromBeltActiveElement(int element)
8015 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8016 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8017 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8020 int getBeltNrFromBeltSwitchElement(int element)
8022 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8023 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8024 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8027 int getBeltDirNrFromBeltElement(int element)
8029 static int belt_base_element[4] =
8031 EL_CONVEYOR_BELT_1_LEFT,
8032 EL_CONVEYOR_BELT_2_LEFT,
8033 EL_CONVEYOR_BELT_3_LEFT,
8034 EL_CONVEYOR_BELT_4_LEFT
8037 int belt_nr = getBeltNrFromBeltElement(element);
8038 int belt_dir_nr = element - belt_base_element[belt_nr];
8040 return (belt_dir_nr % 3);
8043 int getBeltDirNrFromBeltSwitchElement(int element)
8045 static int belt_base_element[4] =
8047 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8048 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8049 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8050 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8053 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8054 int belt_dir_nr = element - belt_base_element[belt_nr];
8056 return (belt_dir_nr % 3);
8059 int getBeltDirFromBeltElement(int element)
8061 static int belt_move_dir[3] =
8068 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8070 return belt_move_dir[belt_dir_nr];
8073 int getBeltDirFromBeltSwitchElement(int element)
8075 static int belt_move_dir[3] =
8082 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8084 return belt_move_dir[belt_dir_nr];
8087 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8089 static int belt_base_element[4] =
8091 EL_CONVEYOR_BELT_1_LEFT,
8092 EL_CONVEYOR_BELT_2_LEFT,
8093 EL_CONVEYOR_BELT_3_LEFT,
8094 EL_CONVEYOR_BELT_4_LEFT
8097 return belt_base_element[belt_nr] + belt_dir_nr;
8100 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8102 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8104 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8107 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8109 static int belt_base_element[4] =
8111 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8112 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8113 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8114 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8117 return belt_base_element[belt_nr] + belt_dir_nr;
8120 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8122 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8124 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8127 boolean getTeamMode_EM(void)
8129 return game.team_mode || network_playing;
8132 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8134 int game_frame_delay_value;
8136 game_frame_delay_value =
8137 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8138 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8141 if (tape.playing && tape.warp_forward && !tape.pausing)
8142 game_frame_delay_value = 0;
8144 return game_frame_delay_value;
8147 unsigned int InitRND(int seed)
8149 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8150 return InitEngineRandom_EM(seed);
8151 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8152 return InitEngineRandom_SP(seed);
8153 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8154 return InitEngineRandom_MM(seed);
8156 return InitEngineRandom_RND(seed);
8159 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8160 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8162 static int get_effective_element_EM(int tile, int frame_em)
8164 int element = object_mapping[tile].element_rnd;
8165 int action = object_mapping[tile].action;
8166 boolean is_backside = object_mapping[tile].is_backside;
8167 boolean action_removing = (action == ACTION_DIGGING ||
8168 action == ACTION_SNAPPING ||
8169 action == ACTION_COLLECTING);
8175 case Yacid_splash_eB:
8176 case Yacid_splash_wB:
8177 return (frame_em > 5 ? EL_EMPTY : element);
8183 else // frame_em == 7
8187 case Yacid_splash_eB:
8188 case Yacid_splash_wB:
8191 case Yemerald_stone:
8194 case Ydiamond_stone:
8198 case Xdrip_stretchB:
8217 case Xsand_stonein_1:
8218 case Xsand_stonein_2:
8219 case Xsand_stonein_3:
8220 case Xsand_stonein_4:
8224 return (is_backside || action_removing ? EL_EMPTY : element);
8229 static boolean check_linear_animation_EM(int tile)
8233 case Xsand_stonesand_1:
8234 case Xsand_stonesand_quickout_1:
8235 case Xsand_sandstone_1:
8236 case Xsand_stonein_1:
8237 case Xsand_stoneout_1:
8256 case Yacid_splash_eB:
8257 case Yacid_splash_wB:
8258 case Yemerald_stone:
8265 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8266 boolean has_crumbled_graphics,
8267 int crumbled, int sync_frame)
8269 // if element can be crumbled, but certain action graphics are just empty
8270 // space (like instantly snapping sand to empty space in 1 frame), do not
8271 // treat these empty space graphics as crumbled graphics in EMC engine
8272 if (crumbled == IMG_EMPTY_SPACE)
8273 has_crumbled_graphics = FALSE;
8275 if (has_crumbled_graphics)
8277 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8278 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8279 g_crumbled->anim_delay,
8280 g_crumbled->anim_mode,
8281 g_crumbled->anim_start_frame,
8284 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8285 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8287 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8288 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8290 g_em->has_crumbled_graphics = TRUE;
8294 g_em->crumbled_bitmap = NULL;
8295 g_em->crumbled_src_x = 0;
8296 g_em->crumbled_src_y = 0;
8297 g_em->crumbled_border_size = 0;
8298 g_em->crumbled_tile_size = 0;
8300 g_em->has_crumbled_graphics = FALSE;
8305 void ResetGfxAnimation_EM(int x, int y, int tile)
8311 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8312 int tile, int frame_em, int x, int y)
8314 int action = object_mapping[tile].action;
8315 int direction = object_mapping[tile].direction;
8316 int effective_element = get_effective_element_EM(tile, frame_em);
8317 int graphic = (direction == MV_NONE ?
8318 el_act2img(effective_element, action) :
8319 el_act_dir2img(effective_element, action, direction));
8320 struct GraphicInfo *g = &graphic_info[graphic];
8322 boolean action_removing = (action == ACTION_DIGGING ||
8323 action == ACTION_SNAPPING ||
8324 action == ACTION_COLLECTING);
8325 boolean action_moving = (action == ACTION_FALLING ||
8326 action == ACTION_MOVING ||
8327 action == ACTION_PUSHING ||
8328 action == ACTION_EATING ||
8329 action == ACTION_FILLING ||
8330 action == ACTION_EMPTYING);
8331 boolean action_falling = (action == ACTION_FALLING ||
8332 action == ACTION_FILLING ||
8333 action == ACTION_EMPTYING);
8335 // special case: graphic uses "2nd movement tile" and has defined
8336 // 7 frames for movement animation (or less) => use default graphic
8337 // for last (8th) frame which ends the movement animation
8338 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8340 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8341 graphic = (direction == MV_NONE ?
8342 el_act2img(effective_element, action) :
8343 el_act_dir2img(effective_element, action, direction));
8345 g = &graphic_info[graphic];
8348 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8352 else if (action_moving)
8354 boolean is_backside = object_mapping[tile].is_backside;
8358 int direction = object_mapping[tile].direction;
8359 int move_dir = (action_falling ? MV_DOWN : direction);
8364 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8365 if (g->double_movement && frame_em == 0)
8369 if (move_dir == MV_LEFT)
8370 GfxFrame[x - 1][y] = GfxFrame[x][y];
8371 else if (move_dir == MV_RIGHT)
8372 GfxFrame[x + 1][y] = GfxFrame[x][y];
8373 else if (move_dir == MV_UP)
8374 GfxFrame[x][y - 1] = GfxFrame[x][y];
8375 else if (move_dir == MV_DOWN)
8376 GfxFrame[x][y + 1] = GfxFrame[x][y];
8383 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8384 if (tile == Xsand_stonesand_quickout_1 ||
8385 tile == Xsand_stonesand_quickout_2)
8389 if (graphic_info[graphic].anim_global_sync)
8390 sync_frame = FrameCounter;
8391 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8392 sync_frame = GfxFrame[x][y];
8394 sync_frame = 0; // playfield border (pseudo steel)
8396 SetRandomAnimationValue(x, y);
8398 int frame = getAnimationFrame(g->anim_frames,
8401 g->anim_start_frame,
8404 g_em->unique_identifier =
8405 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8408 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8409 int tile, int frame_em, int x, int y)
8411 int action = object_mapping[tile].action;
8412 int direction = object_mapping[tile].direction;
8413 boolean is_backside = object_mapping[tile].is_backside;
8414 int effective_element = get_effective_element_EM(tile, frame_em);
8415 int effective_action = action;
8416 int graphic = (direction == MV_NONE ?
8417 el_act2img(effective_element, effective_action) :
8418 el_act_dir2img(effective_element, effective_action,
8420 int crumbled = (direction == MV_NONE ?
8421 el_act2crm(effective_element, effective_action) :
8422 el_act_dir2crm(effective_element, effective_action,
8424 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8425 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8426 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8427 struct GraphicInfo *g = &graphic_info[graphic];
8430 // special case: graphic uses "2nd movement tile" and has defined
8431 // 7 frames for movement animation (or less) => use default graphic
8432 // for last (8th) frame which ends the movement animation
8433 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8435 effective_action = ACTION_DEFAULT;
8436 graphic = (direction == MV_NONE ?
8437 el_act2img(effective_element, effective_action) :
8438 el_act_dir2img(effective_element, effective_action,
8440 crumbled = (direction == MV_NONE ?
8441 el_act2crm(effective_element, effective_action) :
8442 el_act_dir2crm(effective_element, effective_action,
8445 g = &graphic_info[graphic];
8448 if (graphic_info[graphic].anim_global_sync)
8449 sync_frame = FrameCounter;
8450 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8451 sync_frame = GfxFrame[x][y];
8453 sync_frame = 0; // playfield border (pseudo steel)
8455 SetRandomAnimationValue(x, y);
8457 int frame = getAnimationFrame(g->anim_frames,
8460 g->anim_start_frame,
8463 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8464 g->double_movement && is_backside);
8466 // (updating the "crumbled" graphic definitions is probably not really needed,
8467 // as animations for crumbled graphics can't be longer than one EMC cycle)
8468 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8472 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8473 int player_nr, int anim, int frame_em)
8475 int element = player_mapping[player_nr][anim].element_rnd;
8476 int action = player_mapping[player_nr][anim].action;
8477 int direction = player_mapping[player_nr][anim].direction;
8478 int graphic = (direction == MV_NONE ?
8479 el_act2img(element, action) :
8480 el_act_dir2img(element, action, direction));
8481 struct GraphicInfo *g = &graphic_info[graphic];
8484 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8486 stored_player[player_nr].StepFrame = frame_em;
8488 sync_frame = stored_player[player_nr].Frame;
8490 int frame = getAnimationFrame(g->anim_frames,
8493 g->anim_start_frame,
8496 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8497 &g_em->src_x, &g_em->src_y, FALSE);
8500 void InitGraphicInfo_EM(void)
8505 int num_em_gfx_errors = 0;
8507 if (graphic_info_em_object[0][0].bitmap == NULL)
8509 // EM graphics not yet initialized in em_open_all()
8514 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8517 // always start with reliable default values
8518 for (i = 0; i < TILE_MAX; i++)
8520 object_mapping[i].element_rnd = EL_UNKNOWN;
8521 object_mapping[i].is_backside = FALSE;
8522 object_mapping[i].action = ACTION_DEFAULT;
8523 object_mapping[i].direction = MV_NONE;
8526 // always start with reliable default values
8527 for (p = 0; p < MAX_PLAYERS; p++)
8529 for (i = 0; i < SPR_MAX; i++)
8531 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8532 player_mapping[p][i].action = ACTION_DEFAULT;
8533 player_mapping[p][i].direction = MV_NONE;
8537 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8539 int e = em_object_mapping_list[i].element_em;
8541 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8542 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8544 if (em_object_mapping_list[i].action != -1)
8545 object_mapping[e].action = em_object_mapping_list[i].action;
8547 if (em_object_mapping_list[i].direction != -1)
8548 object_mapping[e].direction =
8549 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8552 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8554 int a = em_player_mapping_list[i].action_em;
8555 int p = em_player_mapping_list[i].player_nr;
8557 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8559 if (em_player_mapping_list[i].action != -1)
8560 player_mapping[p][a].action = em_player_mapping_list[i].action;
8562 if (em_player_mapping_list[i].direction != -1)
8563 player_mapping[p][a].direction =
8564 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8567 for (i = 0; i < TILE_MAX; i++)
8569 int element = object_mapping[i].element_rnd;
8570 int action = object_mapping[i].action;
8571 int direction = object_mapping[i].direction;
8572 boolean is_backside = object_mapping[i].is_backside;
8573 boolean action_exploding = ((action == ACTION_EXPLODING ||
8574 action == ACTION_SMASHED_BY_ROCK ||
8575 action == ACTION_SMASHED_BY_SPRING) &&
8576 element != EL_DIAMOND);
8577 boolean action_active = (action == ACTION_ACTIVE);
8578 boolean action_other = (action == ACTION_OTHER);
8580 for (j = 0; j < 8; j++)
8582 int effective_element = get_effective_element_EM(i, j);
8583 int effective_action = (j < 7 ? action :
8584 i == Xdrip_stretch ? action :
8585 i == Xdrip_stretchB ? action :
8586 i == Ydrip_s1 ? action :
8587 i == Ydrip_s1B ? action :
8588 i == Xball_1B ? action :
8589 i == Xball_2 ? action :
8590 i == Xball_2B ? action :
8591 i == Yball_eat ? action :
8592 i == Ykey_1_eat ? action :
8593 i == Ykey_2_eat ? action :
8594 i == Ykey_3_eat ? action :
8595 i == Ykey_4_eat ? action :
8596 i == Ykey_5_eat ? action :
8597 i == Ykey_6_eat ? action :
8598 i == Ykey_7_eat ? action :
8599 i == Ykey_8_eat ? action :
8600 i == Ylenses_eat ? action :
8601 i == Ymagnify_eat ? action :
8602 i == Ygrass_eat ? action :
8603 i == Ydirt_eat ? action :
8604 i == Xsand_stonein_1 ? action :
8605 i == Xsand_stonein_2 ? action :
8606 i == Xsand_stonein_3 ? action :
8607 i == Xsand_stonein_4 ? action :
8608 i == Xsand_stoneout_1 ? action :
8609 i == Xsand_stoneout_2 ? action :
8610 i == Xboom_android ? ACTION_EXPLODING :
8611 action_exploding ? ACTION_EXPLODING :
8612 action_active ? action :
8613 action_other ? action :
8615 int graphic = (el_act_dir2img(effective_element, effective_action,
8617 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8619 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8620 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8621 boolean has_action_graphics = (graphic != base_graphic);
8622 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8623 struct GraphicInfo *g = &graphic_info[graphic];
8624 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8627 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8628 boolean special_animation = (action != ACTION_DEFAULT &&
8629 g->anim_frames == 3 &&
8630 g->anim_delay == 2 &&
8631 g->anim_mode & ANIM_LINEAR);
8632 int sync_frame = (i == Xdrip_stretch ? 7 :
8633 i == Xdrip_stretchB ? 7 :
8634 i == Ydrip_s2 ? j + 8 :
8635 i == Ydrip_s2B ? j + 8 :
8644 i == Xfake_acid_1 ? 0 :
8645 i == Xfake_acid_2 ? 10 :
8646 i == Xfake_acid_3 ? 20 :
8647 i == Xfake_acid_4 ? 30 :
8648 i == Xfake_acid_5 ? 40 :
8649 i == Xfake_acid_6 ? 50 :
8650 i == Xfake_acid_7 ? 60 :
8651 i == Xfake_acid_8 ? 70 :
8653 i == Xball_2B ? j + 8 :
8654 i == Yball_eat ? j + 1 :
8655 i == Ykey_1_eat ? j + 1 :
8656 i == Ykey_2_eat ? j + 1 :
8657 i == Ykey_3_eat ? j + 1 :
8658 i == Ykey_4_eat ? j + 1 :
8659 i == Ykey_5_eat ? j + 1 :
8660 i == Ykey_6_eat ? j + 1 :
8661 i == Ykey_7_eat ? j + 1 :
8662 i == Ykey_8_eat ? j + 1 :
8663 i == Ylenses_eat ? j + 1 :
8664 i == Ymagnify_eat ? j + 1 :
8665 i == Ygrass_eat ? j + 1 :
8666 i == Ydirt_eat ? j + 1 :
8667 i == Xamoeba_1 ? 0 :
8668 i == Xamoeba_2 ? 1 :
8669 i == Xamoeba_3 ? 2 :
8670 i == Xamoeba_4 ? 3 :
8671 i == Xamoeba_5 ? 0 :
8672 i == Xamoeba_6 ? 1 :
8673 i == Xamoeba_7 ? 2 :
8674 i == Xamoeba_8 ? 3 :
8675 i == Xexit_2 ? j + 8 :
8676 i == Xexit_3 ? j + 16 :
8677 i == Xdynamite_1 ? 0 :
8678 i == Xdynamite_2 ? 8 :
8679 i == Xdynamite_3 ? 16 :
8680 i == Xdynamite_4 ? 24 :
8681 i == Xsand_stonein_1 ? j + 1 :
8682 i == Xsand_stonein_2 ? j + 9 :
8683 i == Xsand_stonein_3 ? j + 17 :
8684 i == Xsand_stonein_4 ? j + 25 :
8685 i == Xsand_stoneout_1 && j == 0 ? 0 :
8686 i == Xsand_stoneout_1 && j == 1 ? 0 :
8687 i == Xsand_stoneout_1 && j == 2 ? 1 :
8688 i == Xsand_stoneout_1 && j == 3 ? 2 :
8689 i == Xsand_stoneout_1 && j == 4 ? 2 :
8690 i == Xsand_stoneout_1 && j == 5 ? 3 :
8691 i == Xsand_stoneout_1 && j == 6 ? 4 :
8692 i == Xsand_stoneout_1 && j == 7 ? 4 :
8693 i == Xsand_stoneout_2 && j == 0 ? 5 :
8694 i == Xsand_stoneout_2 && j == 1 ? 6 :
8695 i == Xsand_stoneout_2 && j == 2 ? 7 :
8696 i == Xsand_stoneout_2 && j == 3 ? 8 :
8697 i == Xsand_stoneout_2 && j == 4 ? 9 :
8698 i == Xsand_stoneout_2 && j == 5 ? 11 :
8699 i == Xsand_stoneout_2 && j == 6 ? 13 :
8700 i == Xsand_stoneout_2 && j == 7 ? 15 :
8701 i == Xboom_bug && j == 1 ? 2 :
8702 i == Xboom_bug && j == 2 ? 2 :
8703 i == Xboom_bug && j == 3 ? 4 :
8704 i == Xboom_bug && j == 4 ? 4 :
8705 i == Xboom_bug && j == 5 ? 2 :
8706 i == Xboom_bug && j == 6 ? 2 :
8707 i == Xboom_bug && j == 7 ? 0 :
8708 i == Xboom_bomb && j == 1 ? 2 :
8709 i == Xboom_bomb && j == 2 ? 2 :
8710 i == Xboom_bomb && j == 3 ? 4 :
8711 i == Xboom_bomb && j == 4 ? 4 :
8712 i == Xboom_bomb && j == 5 ? 2 :
8713 i == Xboom_bomb && j == 6 ? 2 :
8714 i == Xboom_bomb && j == 7 ? 0 :
8715 i == Xboom_android && j == 7 ? 6 :
8716 i == Xboom_1 && j == 1 ? 2 :
8717 i == Xboom_1 && j == 2 ? 2 :
8718 i == Xboom_1 && j == 3 ? 4 :
8719 i == Xboom_1 && j == 4 ? 4 :
8720 i == Xboom_1 && j == 5 ? 6 :
8721 i == Xboom_1 && j == 6 ? 6 :
8722 i == Xboom_1 && j == 7 ? 8 :
8723 i == Xboom_2 && j == 0 ? 8 :
8724 i == Xboom_2 && j == 1 ? 8 :
8725 i == Xboom_2 && j == 2 ? 10 :
8726 i == Xboom_2 && j == 3 ? 10 :
8727 i == Xboom_2 && j == 4 ? 10 :
8728 i == Xboom_2 && j == 5 ? 12 :
8729 i == Xboom_2 && j == 6 ? 12 :
8730 i == Xboom_2 && j == 7 ? 12 :
8731 special_animation && j == 4 ? 3 :
8732 effective_action != action ? 0 :
8736 Bitmap *debug_bitmap = g_em->bitmap;
8737 int debug_src_x = g_em->src_x;
8738 int debug_src_y = g_em->src_y;
8741 int frame = getAnimationFrame(g->anim_frames,
8744 g->anim_start_frame,
8747 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8748 g->double_movement && is_backside);
8750 g_em->bitmap = src_bitmap;
8751 g_em->src_x = src_x;
8752 g_em->src_y = src_y;
8753 g_em->src_offset_x = 0;
8754 g_em->src_offset_y = 0;
8755 g_em->dst_offset_x = 0;
8756 g_em->dst_offset_y = 0;
8757 g_em->width = TILEX;
8758 g_em->height = TILEY;
8760 g_em->preserve_background = FALSE;
8762 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8765 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8766 effective_action == ACTION_MOVING ||
8767 effective_action == ACTION_PUSHING ||
8768 effective_action == ACTION_EATING)) ||
8769 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8770 effective_action == ACTION_EMPTYING)))
8773 (effective_action == ACTION_FALLING ||
8774 effective_action == ACTION_FILLING ||
8775 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8776 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8777 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8778 int num_steps = (i == Ydrip_s1 ? 16 :
8779 i == Ydrip_s1B ? 16 :
8780 i == Ydrip_s2 ? 16 :
8781 i == Ydrip_s2B ? 16 :
8782 i == Xsand_stonein_1 ? 32 :
8783 i == Xsand_stonein_2 ? 32 :
8784 i == Xsand_stonein_3 ? 32 :
8785 i == Xsand_stonein_4 ? 32 :
8786 i == Xsand_stoneout_1 ? 16 :
8787 i == Xsand_stoneout_2 ? 16 : 8);
8788 int cx = ABS(dx) * (TILEX / num_steps);
8789 int cy = ABS(dy) * (TILEY / num_steps);
8790 int step_frame = (i == Ydrip_s2 ? j + 8 :
8791 i == Ydrip_s2B ? j + 8 :
8792 i == Xsand_stonein_2 ? j + 8 :
8793 i == Xsand_stonein_3 ? j + 16 :
8794 i == Xsand_stonein_4 ? j + 24 :
8795 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8796 int step = (is_backside ? step_frame : num_steps - step_frame);
8798 if (is_backside) // tile where movement starts
8800 if (dx < 0 || dy < 0)
8802 g_em->src_offset_x = cx * step;
8803 g_em->src_offset_y = cy * step;
8807 g_em->dst_offset_x = cx * step;
8808 g_em->dst_offset_y = cy * step;
8811 else // tile where movement ends
8813 if (dx < 0 || dy < 0)
8815 g_em->dst_offset_x = cx * step;
8816 g_em->dst_offset_y = cy * step;
8820 g_em->src_offset_x = cx * step;
8821 g_em->src_offset_y = cy * step;
8825 g_em->width = TILEX - cx * step;
8826 g_em->height = TILEY - cy * step;
8829 // create unique graphic identifier to decide if tile must be redrawn
8830 /* bit 31 - 16 (16 bit): EM style graphic
8831 bit 15 - 12 ( 4 bit): EM style frame
8832 bit 11 - 6 ( 6 bit): graphic width
8833 bit 5 - 0 ( 6 bit): graphic height */
8834 g_em->unique_identifier =
8835 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8839 // skip check for EMC elements not contained in original EMC artwork
8840 if (element == EL_EMC_FAKE_ACID)
8843 if (g_em->bitmap != debug_bitmap ||
8844 g_em->src_x != debug_src_x ||
8845 g_em->src_y != debug_src_y ||
8846 g_em->src_offset_x != 0 ||
8847 g_em->src_offset_y != 0 ||
8848 g_em->dst_offset_x != 0 ||
8849 g_em->dst_offset_y != 0 ||
8850 g_em->width != TILEX ||
8851 g_em->height != TILEY)
8853 static int last_i = -1;
8861 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8862 i, element, element_info[element].token_name,
8863 element_action_info[effective_action].suffix, direction);
8865 if (element != effective_element)
8866 printf(" [%d ('%s')]",
8868 element_info[effective_element].token_name);
8872 if (g_em->bitmap != debug_bitmap)
8873 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8874 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8876 if (g_em->src_x != debug_src_x ||
8877 g_em->src_y != debug_src_y)
8878 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8879 j, (is_backside ? 'B' : 'F'),
8880 g_em->src_x, g_em->src_y,
8881 g_em->src_x / 32, g_em->src_y / 32,
8882 debug_src_x, debug_src_y,
8883 debug_src_x / 32, debug_src_y / 32);
8885 if (g_em->src_offset_x != 0 ||
8886 g_em->src_offset_y != 0 ||
8887 g_em->dst_offset_x != 0 ||
8888 g_em->dst_offset_y != 0)
8889 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8891 g_em->src_offset_x, g_em->src_offset_y,
8892 g_em->dst_offset_x, g_em->dst_offset_y);
8894 if (g_em->width != TILEX ||
8895 g_em->height != TILEY)
8896 printf(" %d (%d): size %d,%d should be %d,%d\n",
8898 g_em->width, g_em->height, TILEX, TILEY);
8900 num_em_gfx_errors++;
8907 for (i = 0; i < TILE_MAX; i++)
8909 for (j = 0; j < 8; j++)
8911 int element = object_mapping[i].element_rnd;
8912 int action = object_mapping[i].action;
8913 int direction = object_mapping[i].direction;
8914 boolean is_backside = object_mapping[i].is_backside;
8915 int graphic_action = el_act_dir2img(element, action, direction);
8916 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8918 if ((action == ACTION_SMASHED_BY_ROCK ||
8919 action == ACTION_SMASHED_BY_SPRING ||
8920 action == ACTION_EATING) &&
8921 graphic_action == graphic_default)
8923 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8924 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8925 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8926 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8929 // no separate animation for "smashed by rock" -- use rock instead
8930 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8931 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8933 g_em->bitmap = g_xx->bitmap;
8934 g_em->src_x = g_xx->src_x;
8935 g_em->src_y = g_xx->src_y;
8936 g_em->src_offset_x = g_xx->src_offset_x;
8937 g_em->src_offset_y = g_xx->src_offset_y;
8938 g_em->dst_offset_x = g_xx->dst_offset_x;
8939 g_em->dst_offset_y = g_xx->dst_offset_y;
8940 g_em->width = g_xx->width;
8941 g_em->height = g_xx->height;
8942 g_em->unique_identifier = g_xx->unique_identifier;
8945 g_em->preserve_background = TRUE;
8950 for (p = 0; p < MAX_PLAYERS; p++)
8952 for (i = 0; i < SPR_MAX; i++)
8954 int element = player_mapping[p][i].element_rnd;
8955 int action = player_mapping[p][i].action;
8956 int direction = player_mapping[p][i].direction;
8958 for (j = 0; j < 8; j++)
8960 int effective_element = element;
8961 int effective_action = action;
8962 int graphic = (direction == MV_NONE ?
8963 el_act2img(effective_element, effective_action) :
8964 el_act_dir2img(effective_element, effective_action,
8966 struct GraphicInfo *g = &graphic_info[graphic];
8967 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8973 Bitmap *debug_bitmap = g_em->bitmap;
8974 int debug_src_x = g_em->src_x;
8975 int debug_src_y = g_em->src_y;
8978 int frame = getAnimationFrame(g->anim_frames,
8981 g->anim_start_frame,
8984 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8986 g_em->bitmap = src_bitmap;
8987 g_em->src_x = src_x;
8988 g_em->src_y = src_y;
8989 g_em->src_offset_x = 0;
8990 g_em->src_offset_y = 0;
8991 g_em->dst_offset_x = 0;
8992 g_em->dst_offset_y = 0;
8993 g_em->width = TILEX;
8994 g_em->height = TILEY;
8998 // skip check for EMC elements not contained in original EMC artwork
8999 if (element == EL_PLAYER_3 ||
9000 element == EL_PLAYER_4)
9003 if (g_em->bitmap != debug_bitmap ||
9004 g_em->src_x != debug_src_x ||
9005 g_em->src_y != debug_src_y)
9007 static int last_i = -1;
9015 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9016 p, i, element, element_info[element].token_name,
9017 element_action_info[effective_action].suffix, direction);
9019 if (element != effective_element)
9020 printf(" [%d ('%s')]",
9022 element_info[effective_element].token_name);
9026 if (g_em->bitmap != debug_bitmap)
9027 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9028 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9030 if (g_em->src_x != debug_src_x ||
9031 g_em->src_y != debug_src_y)
9032 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9034 g_em->src_x, g_em->src_y,
9035 g_em->src_x / 32, g_em->src_y / 32,
9036 debug_src_x, debug_src_y,
9037 debug_src_x / 32, debug_src_y / 32);
9039 num_em_gfx_errors++;
9049 printf("::: [%d errors found]\n", num_em_gfx_errors);
9055 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9056 boolean any_player_moving,
9057 boolean any_player_snapping,
9058 boolean any_player_dropping)
9060 if (frame == 0 && !any_player_dropping)
9062 if (!local_player->was_waiting)
9064 if (!CheckSaveEngineSnapshotToList())
9067 local_player->was_waiting = TRUE;
9070 else if (any_player_moving || any_player_snapping || any_player_dropping)
9072 local_player->was_waiting = FALSE;
9076 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9077 boolean murphy_is_dropping)
9079 if (murphy_is_waiting)
9081 if (!local_player->was_waiting)
9083 if (!CheckSaveEngineSnapshotToList())
9086 local_player->was_waiting = TRUE;
9091 local_player->was_waiting = FALSE;
9095 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9096 boolean button_released)
9098 if (button_released)
9100 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9101 CheckSaveEngineSnapshotToList();
9103 else if (element_clicked)
9105 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9106 CheckSaveEngineSnapshotToList();
9108 game.snapshot.changed_action = TRUE;
9112 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9113 boolean any_player_moving,
9114 boolean any_player_snapping,
9115 boolean any_player_dropping)
9117 if (tape.single_step && tape.recording && !tape.pausing)
9118 if (frame == 0 && !any_player_dropping)
9119 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9121 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9122 any_player_snapping, any_player_dropping);
9125 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9126 boolean murphy_is_dropping)
9128 boolean murphy_starts_dropping = FALSE;
9131 for (i = 0; i < MAX_PLAYERS; i++)
9132 if (stored_player[i].force_dropping)
9133 murphy_starts_dropping = TRUE;
9135 if (tape.single_step && tape.recording && !tape.pausing)
9136 if (murphy_is_waiting && !murphy_starts_dropping)
9137 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9139 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9142 void CheckSingleStepMode_MM(boolean element_clicked,
9143 boolean button_released)
9145 if (tape.single_step && tape.recording && !tape.pausing)
9146 if (button_released)
9147 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9149 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9152 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9153 int graphic, int sync_frame, int x, int y)
9155 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9157 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9160 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9162 return (IS_NEXT_FRAME(sync_frame, graphic));
9165 int getGraphicInfo_Delay(int graphic)
9167 return graphic_info[graphic].anim_delay;
9170 void PlayMenuSoundExt(int sound)
9172 if (sound == SND_UNDEFINED)
9175 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9176 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9179 if (IS_LOOP_SOUND(sound))
9180 PlaySoundLoop(sound);
9185 void PlayMenuSound(void)
9187 PlayMenuSoundExt(menu.sound[game_status]);
9190 void PlayMenuSoundStereo(int sound, int stereo_position)
9192 if (sound == SND_UNDEFINED)
9195 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9196 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9199 if (IS_LOOP_SOUND(sound))
9200 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9202 PlaySoundStereo(sound, stereo_position);
9205 void PlayMenuSoundIfLoopExt(int sound)
9207 if (sound == SND_UNDEFINED)
9210 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9211 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9214 if (IS_LOOP_SOUND(sound))
9215 PlaySoundLoop(sound);
9218 void PlayMenuSoundIfLoop(void)
9220 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9223 void PlayMenuMusicExt(int music)
9225 if (music == MUS_UNDEFINED)
9228 if (!setup.sound_music)
9231 if (IS_LOOP_MUSIC(music))
9232 PlayMusicLoop(music);
9237 void PlayMenuMusic(void)
9239 char *curr_music = getCurrentlyPlayingMusicFilename();
9240 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9242 if (!strEqual(curr_music, next_music))
9243 PlayMenuMusicExt(menu.music[game_status]);
9246 void PlayMenuSoundsAndMusic(void)
9252 static void FadeMenuSounds(void)
9257 static void FadeMenuMusic(void)
9259 char *curr_music = getCurrentlyPlayingMusicFilename();
9260 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9262 if (!strEqual(curr_music, next_music))
9266 void FadeMenuSoundsAndMusic(void)
9272 void PlaySoundActivating(void)
9275 PlaySound(SND_MENU_ITEM_ACTIVATING);
9279 void PlaySoundSelecting(void)
9282 PlaySound(SND_MENU_ITEM_SELECTING);
9286 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9288 boolean change_fullscreen = (setup.fullscreen !=
9289 video.fullscreen_enabled);
9290 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9291 setup.window_scaling_percent !=
9292 video.window_scaling_percent);
9294 if (change_window_scaling_percent && video.fullscreen_enabled)
9297 if (!change_window_scaling_percent && !video.fullscreen_available)
9300 if (change_window_scaling_percent)
9302 SDLSetWindowScaling(setup.window_scaling_percent);
9306 else if (change_fullscreen)
9308 SDLSetWindowFullscreen(setup.fullscreen);
9310 // set setup value according to successfully changed fullscreen mode
9311 setup.fullscreen = video.fullscreen_enabled;
9316 if (change_fullscreen ||
9317 change_window_scaling_percent)
9319 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9321 // save backbuffer content which gets lost when toggling fullscreen mode
9322 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9324 if (change_window_scaling_percent)
9326 // keep window mode, but change window scaling
9327 video.fullscreen_enabled = TRUE; // force new window scaling
9330 // toggle fullscreen
9331 ChangeVideoModeIfNeeded(setup.fullscreen);
9333 // set setup value according to successfully changed fullscreen mode
9334 setup.fullscreen = video.fullscreen_enabled;
9336 // restore backbuffer content from temporary backbuffer backup bitmap
9337 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9339 FreeBitmap(tmp_backbuffer);
9341 // update visible window/screen
9342 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9346 static void JoinRectangles(int *x, int *y, int *width, int *height,
9347 int x2, int y2, int width2, int height2)
9349 // do not join with "off-screen" rectangle
9350 if (x2 == -1 || y2 == -1)
9355 *width = MAX(*width, width2);
9356 *height = MAX(*height, height2);
9359 void SetAnimStatus(int anim_status_new)
9361 if (anim_status_new == GAME_MODE_MAIN)
9362 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9363 else if (anim_status_new == GAME_MODE_SCORES)
9364 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9366 global.anim_status_next = anim_status_new;
9368 // directly set screen modes that are entered without fading
9369 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9370 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9371 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9372 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9373 global.anim_status = global.anim_status_next;
9376 void SetGameStatus(int game_status_new)
9378 if (game_status_new != game_status)
9379 game_status_last_screen = game_status;
9381 game_status = game_status_new;
9383 SetAnimStatus(game_status_new);
9386 void SetFontStatus(int game_status_new)
9388 static int last_game_status = -1;
9390 if (game_status_new != -1)
9392 // set game status for font use after storing last game status
9393 last_game_status = game_status;
9394 game_status = game_status_new;
9398 // reset game status after font use from last stored game status
9399 game_status = last_game_status;
9403 void ResetFontStatus(void)
9408 void SetLevelSetInfo(char *identifier, int level_nr)
9410 setString(&levelset.identifier, identifier);
9412 levelset.level_nr = level_nr;
9415 boolean CheckIfAllViewportsHaveChanged(void)
9417 // if game status has not changed, viewports have not changed either
9418 if (game_status == game_status_last)
9421 // check if all viewports have changed with current game status
9423 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9424 struct RectWithBorder *vp_door_1 = &viewport.door_1[game_status];
9425 struct RectWithBorder *vp_door_2 = &viewport.door_2[game_status];
9426 int new_real_sx = vp_playfield->x;
9427 int new_real_sy = vp_playfield->y;
9428 int new_full_sxsize = vp_playfield->width;
9429 int new_full_sysize = vp_playfield->height;
9430 int new_dx = vp_door_1->x;
9431 int new_dy = vp_door_1->y;
9432 int new_dxsize = vp_door_1->width;
9433 int new_dysize = vp_door_1->height;
9434 int new_vx = vp_door_2->x;
9435 int new_vy = vp_door_2->y;
9436 int new_vxsize = vp_door_2->width;
9437 int new_vysize = vp_door_2->height;
9439 boolean playfield_viewport_has_changed =
9440 (new_real_sx != REAL_SX ||
9441 new_real_sy != REAL_SY ||
9442 new_full_sxsize != FULL_SXSIZE ||
9443 new_full_sysize != FULL_SYSIZE);
9445 boolean door_1_viewport_has_changed =
9448 new_dxsize != DXSIZE ||
9449 new_dysize != DYSIZE);
9451 boolean door_2_viewport_has_changed =
9454 new_vxsize != VXSIZE ||
9455 new_vysize != VYSIZE ||
9456 game_status_last == GAME_MODE_EDITOR);
9458 return (playfield_viewport_has_changed &&
9459 door_1_viewport_has_changed &&
9460 door_2_viewport_has_changed);
9463 boolean CheckFadeAll(void)
9465 return (CheckIfGlobalBorderHasChanged() ||
9466 CheckIfAllViewportsHaveChanged());
9469 void ChangeViewportPropertiesIfNeeded(void)
9471 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9472 FALSE : setup.small_game_graphics);
9473 int gfx_game_mode = game_status;
9474 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9476 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9477 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9478 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9479 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9480 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9481 int new_win_xsize = vp_window->width;
9482 int new_win_ysize = vp_window->height;
9483 int border_left = vp_playfield->border_left;
9484 int border_right = vp_playfield->border_right;
9485 int border_top = vp_playfield->border_top;
9486 int border_bottom = vp_playfield->border_bottom;
9487 int new_sx = vp_playfield->x + border_left;
9488 int new_sy = vp_playfield->y + border_top;
9489 int new_sxsize = vp_playfield->width - border_left - border_right;
9490 int new_sysize = vp_playfield->height - border_top - border_bottom;
9491 int new_real_sx = vp_playfield->x;
9492 int new_real_sy = vp_playfield->y;
9493 int new_full_sxsize = vp_playfield->width;
9494 int new_full_sysize = vp_playfield->height;
9495 int new_dx = vp_door_1->x;
9496 int new_dy = vp_door_1->y;
9497 int new_dxsize = vp_door_1->width;
9498 int new_dysize = vp_door_1->height;
9499 int new_vx = vp_door_2->x;
9500 int new_vy = vp_door_2->y;
9501 int new_vxsize = vp_door_2->width;
9502 int new_vysize = vp_door_2->height;
9503 int new_ex = vp_door_3->x;
9504 int new_ey = vp_door_3->y;
9505 int new_exsize = vp_door_3->width;
9506 int new_eysize = vp_door_3->height;
9507 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9508 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9509 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9510 int new_scr_fieldx = new_sxsize / tilesize;
9511 int new_scr_fieldy = new_sysize / tilesize;
9512 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9513 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9514 boolean init_gfx_buffers = FALSE;
9515 boolean init_video_buffer = FALSE;
9516 boolean init_gadgets_and_anims = FALSE;
9517 boolean init_em_graphics = FALSE;
9519 if (new_win_xsize != WIN_XSIZE ||
9520 new_win_ysize != WIN_YSIZE)
9522 WIN_XSIZE = new_win_xsize;
9523 WIN_YSIZE = new_win_ysize;
9525 init_video_buffer = TRUE;
9526 init_gfx_buffers = TRUE;
9527 init_gadgets_and_anims = TRUE;
9529 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9532 if (new_scr_fieldx != SCR_FIELDX ||
9533 new_scr_fieldy != SCR_FIELDY)
9535 // this always toggles between MAIN and GAME when using small tile size
9537 SCR_FIELDX = new_scr_fieldx;
9538 SCR_FIELDY = new_scr_fieldy;
9540 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9551 new_sxsize != SXSIZE ||
9552 new_sysize != SYSIZE ||
9553 new_dxsize != DXSIZE ||
9554 new_dysize != DYSIZE ||
9555 new_vxsize != VXSIZE ||
9556 new_vysize != VYSIZE ||
9557 new_exsize != EXSIZE ||
9558 new_eysize != EYSIZE ||
9559 new_real_sx != REAL_SX ||
9560 new_real_sy != REAL_SY ||
9561 new_full_sxsize != FULL_SXSIZE ||
9562 new_full_sysize != FULL_SYSIZE ||
9563 new_tilesize_var != TILESIZE_VAR
9566 // ------------------------------------------------------------------------
9567 // determine next fading area for changed viewport definitions
9568 // ------------------------------------------------------------------------
9570 // start with current playfield area (default fading area)
9573 FADE_SXSIZE = FULL_SXSIZE;
9574 FADE_SYSIZE = FULL_SYSIZE;
9576 // add new playfield area if position or size has changed
9577 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9578 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9580 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9581 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9584 // add current and new door 1 area if position or size has changed
9585 if (new_dx != DX || new_dy != DY ||
9586 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9588 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9589 DX, DY, DXSIZE, DYSIZE);
9590 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9591 new_dx, new_dy, new_dxsize, new_dysize);
9594 // add current and new door 2 area if position or size has changed
9595 if (new_vx != VX || new_vy != VY ||
9596 new_vxsize != VXSIZE || new_vysize != VYSIZE)
9598 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9599 VX, VY, VXSIZE, VYSIZE);
9600 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9601 new_vx, new_vy, new_vxsize, new_vysize);
9604 // ------------------------------------------------------------------------
9605 // handle changed tile size
9606 // ------------------------------------------------------------------------
9608 if (new_tilesize_var != TILESIZE_VAR)
9610 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9612 // changing tile size invalidates scroll values of engine snapshots
9613 FreeEngineSnapshotSingle();
9615 // changing tile size requires update of graphic mapping for EM engine
9616 init_em_graphics = TRUE;
9627 SXSIZE = new_sxsize;
9628 SYSIZE = new_sysize;
9629 DXSIZE = new_dxsize;
9630 DYSIZE = new_dysize;
9631 VXSIZE = new_vxsize;
9632 VYSIZE = new_vysize;
9633 EXSIZE = new_exsize;
9634 EYSIZE = new_eysize;
9635 REAL_SX = new_real_sx;
9636 REAL_SY = new_real_sy;
9637 FULL_SXSIZE = new_full_sxsize;
9638 FULL_SYSIZE = new_full_sysize;
9639 TILESIZE_VAR = new_tilesize_var;
9641 init_gfx_buffers = TRUE;
9642 init_gadgets_and_anims = TRUE;
9644 // printf("::: viewports: init_gfx_buffers\n");
9645 // printf("::: viewports: init_gadgets_and_anims\n");
9648 if (init_gfx_buffers)
9650 // printf("::: init_gfx_buffers\n");
9652 SCR_FIELDX = new_scr_fieldx_buffers;
9653 SCR_FIELDY = new_scr_fieldy_buffers;
9657 SCR_FIELDX = new_scr_fieldx;
9658 SCR_FIELDY = new_scr_fieldy;
9660 SetDrawDeactivationMask(REDRAW_NONE);
9661 SetDrawBackgroundMask(REDRAW_FIELD);
9664 if (init_video_buffer)
9666 // printf("::: init_video_buffer\n");
9668 FreeAllImageTextures(); // needs old renderer to free the textures
9670 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9671 InitImageTextures();
9674 if (init_gadgets_and_anims)
9676 // printf("::: init_gadgets_and_anims\n");
9679 InitGlobalAnimations();
9682 if (init_em_graphics)
9684 InitGraphicInfo_EM();