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 if (game_status == GAME_MODE_PLAYING &&
1046 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1047 SetOverlayActive(TRUE);
1049 SetScreenStates_AfterFadingIn();
1051 // force update of global animation status in case of rapid screen changes
1052 redraw_mask = REDRAW_ALL;
1056 void FadeOut(int fade_mask)
1058 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1059 if (!equalRedrawMasks(fade_mask, redraw_mask))
1062 SetScreenStates_BeforeFadingOut();
1064 SetTileCursorActive(FALSE);
1065 SetOverlayActive(FALSE);
1068 DrawMaskedBorder(REDRAW_ALL);
1071 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1072 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1074 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1076 SetScreenStates_AfterFadingOut();
1079 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1081 static struct TitleFadingInfo fading_leave_stored;
1084 fading_leave_stored = fading_leave;
1086 fading = fading_leave_stored;
1089 void FadeSetEnterMenu(void)
1091 fading = menu.enter_menu;
1093 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1096 void FadeSetLeaveMenu(void)
1098 fading = menu.leave_menu;
1100 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1103 void FadeSetEnterScreen(void)
1105 fading = menu.enter_screen[game_status];
1107 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1110 void FadeSetNextScreen(void)
1112 fading = menu.next_screen[game_status];
1114 // (do not overwrite fade mode set by FadeSetEnterScreen)
1115 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1118 void FadeSetLeaveScreen(void)
1120 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1123 void FadeSetFromType(int type)
1125 if (type & TYPE_ENTER_SCREEN)
1126 FadeSetEnterScreen();
1127 else if (type & TYPE_ENTER)
1129 else if (type & TYPE_LEAVE)
1133 void FadeSetDisabled(void)
1135 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1137 fading = fading_none;
1140 void FadeSkipNextFadeIn(void)
1142 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1145 void FadeSkipNextFadeOut(void)
1147 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1150 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1152 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1154 return (graphic == IMG_UNDEFINED ? NULL :
1155 graphic_info[graphic].bitmap != NULL || redefined ?
1156 graphic_info[graphic].bitmap :
1157 graphic_info[default_graphic].bitmap);
1160 static Bitmap *getBackgroundBitmap(int graphic)
1162 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1165 static Bitmap *getGlobalBorderBitmap(int graphic)
1167 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1170 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1173 (status == GAME_MODE_MAIN ||
1174 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1175 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1176 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1177 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1180 return getGlobalBorderBitmap(graphic);
1183 void SetWindowBackgroundImageIfDefined(int graphic)
1185 if (graphic_info[graphic].bitmap)
1186 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1189 void SetMainBackgroundImageIfDefined(int graphic)
1191 if (graphic_info[graphic].bitmap)
1192 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1195 void SetDoorBackgroundImageIfDefined(int graphic)
1197 if (graphic_info[graphic].bitmap)
1198 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1201 void SetWindowBackgroundImage(int graphic)
1203 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1206 void SetMainBackgroundImage(int graphic)
1208 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1211 void SetDoorBackgroundImage(int graphic)
1213 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1216 void SetPanelBackground(void)
1218 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1220 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1221 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1223 SetDoorBackgroundBitmap(bitmap_db_panel);
1226 void DrawBackground(int x, int y, int width, int height)
1228 // "drawto" might still point to playfield buffer here (hall of fame)
1229 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1231 if (IN_GFX_FIELD_FULL(x, y))
1232 redraw_mask |= REDRAW_FIELD;
1233 else if (IN_GFX_DOOR_1(x, y))
1234 redraw_mask |= REDRAW_DOOR_1;
1235 else if (IN_GFX_DOOR_2(x, y))
1236 redraw_mask |= REDRAW_DOOR_2;
1237 else if (IN_GFX_DOOR_3(x, y))
1238 redraw_mask |= REDRAW_DOOR_3;
1241 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1243 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1245 if (font->bitmap == NULL)
1248 DrawBackground(x, y, width, height);
1251 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1253 struct GraphicInfo *g = &graphic_info[graphic];
1255 if (g->bitmap == NULL)
1258 DrawBackground(x, y, width, height);
1261 static int game_status_last = -1;
1262 static Bitmap *global_border_bitmap_last = NULL;
1263 static Bitmap *global_border_bitmap = NULL;
1264 static int real_sx_last = -1, real_sy_last = -1;
1265 static int full_sxsize_last = -1, full_sysize_last = -1;
1266 static int dx_last = -1, dy_last = -1;
1267 static int dxsize_last = -1, dysize_last = -1;
1268 static int vx_last = -1, vy_last = -1;
1269 static int vxsize_last = -1, vysize_last = -1;
1270 static int ex_last = -1, ey_last = -1;
1271 static int exsize_last = -1, eysize_last = -1;
1273 boolean CheckIfGlobalBorderHasChanged(void)
1275 // if game status has not changed, global border has not changed either
1276 if (game_status == game_status_last)
1279 // determine and store new global border bitmap for current game status
1280 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1282 return (global_border_bitmap_last != global_border_bitmap);
1285 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1287 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1288 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1290 // if game status has not changed, nothing has to be redrawn
1291 if (game_status == game_status_last)
1294 // redraw if last screen was title screen
1295 if (game_status_last == GAME_MODE_TITLE)
1298 // redraw if global screen border has changed
1299 if (CheckIfGlobalBorderHasChanged())
1302 // redraw if position or size of playfield area has changed
1303 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1304 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1307 // redraw if position or size of door area has changed
1308 if (dx_last != DX || dy_last != DY ||
1309 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1312 // redraw if position or size of tape area has changed
1313 if (vx_last != VX || vy_last != VY ||
1314 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1317 // redraw if position or size of editor area has changed
1318 if (ex_last != EX || ey_last != EY ||
1319 exsize_last != EXSIZE || eysize_last != EYSIZE)
1326 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1329 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1331 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1334 void RedrawGlobalBorder(void)
1336 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1338 RedrawGlobalBorderFromBitmap(bitmap);
1340 redraw_mask = REDRAW_ALL;
1343 static void RedrawGlobalBorderIfNeeded(void)
1345 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1346 if (game_status == game_status_last)
1350 // copy current draw buffer to later copy back areas that have not changed
1351 if (game_status_last != GAME_MODE_TITLE)
1352 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1354 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1355 if (CheckIfGlobalBorderRedrawIsNeeded())
1358 // redraw global screen border (or clear, if defined to be empty)
1359 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1361 if (game_status == GAME_MODE_EDITOR)
1362 DrawSpecialEditorDoor();
1364 // copy previous playfield and door areas, if they are defined on both
1365 // previous and current screen and if they still have the same size
1367 if (real_sx_last != -1 && real_sy_last != -1 &&
1368 REAL_SX != -1 && REAL_SY != -1 &&
1369 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1370 BlitBitmap(bitmap_db_store_1, backbuffer,
1371 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1374 if (dx_last != -1 && dy_last != -1 &&
1375 DX != -1 && DY != -1 &&
1376 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1377 BlitBitmap(bitmap_db_store_1, backbuffer,
1378 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1380 if (game_status != GAME_MODE_EDITOR)
1382 if (vx_last != -1 && vy_last != -1 &&
1383 VX != -1 && VY != -1 &&
1384 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1385 BlitBitmap(bitmap_db_store_1, backbuffer,
1386 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1390 if (ex_last != -1 && ey_last != -1 &&
1391 EX != -1 && EY != -1 &&
1392 exsize_last == EXSIZE && eysize_last == EYSIZE)
1393 BlitBitmap(bitmap_db_store_1, backbuffer,
1394 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1397 redraw_mask = REDRAW_ALL;
1400 game_status_last = game_status;
1402 global_border_bitmap_last = global_border_bitmap;
1404 real_sx_last = REAL_SX;
1405 real_sy_last = REAL_SY;
1406 full_sxsize_last = FULL_SXSIZE;
1407 full_sysize_last = FULL_SYSIZE;
1410 dxsize_last = DXSIZE;
1411 dysize_last = DYSIZE;
1414 vxsize_last = VXSIZE;
1415 vysize_last = VYSIZE;
1418 exsize_last = EXSIZE;
1419 eysize_last = EYSIZE;
1422 void ClearField(void)
1424 RedrawGlobalBorderIfNeeded();
1426 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1427 // (when entering hall of fame after playing)
1428 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1430 // !!! maybe this should be done before clearing the background !!!
1431 if (game_status == GAME_MODE_PLAYING)
1433 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1434 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1438 SetDrawtoField(DRAW_TO_BACKBUFFER);
1442 void MarkTileDirty(int x, int y)
1444 redraw_mask |= REDRAW_FIELD;
1447 void SetBorderElement(void)
1451 BorderElement = EL_EMPTY;
1453 // the MM game engine does not use a visible border element
1454 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1457 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1459 for (x = 0; x < lev_fieldx; x++)
1461 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1462 BorderElement = EL_STEELWALL;
1464 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1470 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1471 int max_array_fieldx, int max_array_fieldy,
1472 short field[max_array_fieldx][max_array_fieldy],
1473 int max_fieldx, int max_fieldy)
1477 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1478 static int safety = 0;
1480 // check if starting field still has the desired content
1481 if (field[from_x][from_y] == fill_element)
1486 if (safety > max_fieldx * max_fieldy)
1487 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1489 old_element = field[from_x][from_y];
1490 field[from_x][from_y] = fill_element;
1492 for (i = 0; i < 4; i++)
1494 x = from_x + check[i][0];
1495 y = from_y + check[i][1];
1497 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1498 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1499 field, max_fieldx, max_fieldy);
1505 void FloodFillLevel(int from_x, int from_y, int fill_element,
1506 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1507 int max_fieldx, int max_fieldy)
1509 FloodFillLevelExt(from_x, from_y, fill_element,
1510 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1511 max_fieldx, max_fieldy);
1514 void SetRandomAnimationValue(int x, int y)
1516 gfx.anim_random_frame = GfxRandom[x][y];
1519 int getGraphicAnimationFrame(int graphic, int sync_frame)
1521 // animation synchronized with global frame counter, not move position
1522 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1523 sync_frame = FrameCounter;
1525 return getAnimationFrame(graphic_info[graphic].anim_frames,
1526 graphic_info[graphic].anim_delay,
1527 graphic_info[graphic].anim_mode,
1528 graphic_info[graphic].anim_start_frame,
1532 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1534 struct GraphicInfo *g = &graphic_info[graphic];
1535 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1537 if (tilesize == gfx.standard_tile_size)
1538 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1539 else if (tilesize == game.tile_size)
1540 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1542 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1545 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1546 boolean get_backside)
1548 struct GraphicInfo *g = &graphic_info[graphic];
1549 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1550 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1552 if (g->offset_y == 0) // frames are ordered horizontally
1554 int max_width = g->anim_frames_per_line * g->width;
1555 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1557 *x = pos % max_width;
1558 *y = src_y % g->height + pos / max_width * g->height;
1560 else if (g->offset_x == 0) // frames are ordered vertically
1562 int max_height = g->anim_frames_per_line * g->height;
1563 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1565 *x = src_x % g->width + pos / max_height * g->width;
1566 *y = pos % max_height;
1568 else // frames are ordered diagonally
1570 *x = src_x + frame * g->offset_x;
1571 *y = src_y + frame * g->offset_y;
1575 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1576 Bitmap **bitmap, int *x, int *y,
1577 boolean get_backside)
1579 struct GraphicInfo *g = &graphic_info[graphic];
1581 // if no graphics defined at all, use fallback graphics
1582 if (g->bitmaps == NULL)
1583 *g = graphic_info[IMG_CHAR_EXCLAM];
1585 // if no in-game graphics defined, always use standard graphic size
1586 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1587 tilesize = TILESIZE;
1589 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1590 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1592 *x = *x * tilesize / g->tile_size;
1593 *y = *y * tilesize / g->tile_size;
1596 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1597 Bitmap **bitmap, int *x, int *y)
1599 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1602 void getFixedGraphicSource(int graphic, int frame,
1603 Bitmap **bitmap, int *x, int *y)
1605 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1608 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1610 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1613 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1614 int *x, int *y, boolean get_backside)
1616 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1620 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1622 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1625 void DrawGraphic(int x, int y, int graphic, int frame)
1628 if (!IN_SCR_FIELD(x, y))
1630 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1631 printf("DrawGraphic(): This should never happen!\n");
1636 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1639 MarkTileDirty(x, y);
1642 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1645 if (!IN_SCR_FIELD(x, y))
1647 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1648 printf("DrawGraphic(): This should never happen!\n");
1653 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1655 MarkTileDirty(x, y);
1658 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1664 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1666 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1669 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1675 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1676 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1679 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1682 if (!IN_SCR_FIELD(x, y))
1684 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1685 printf("DrawGraphicThruMask(): This should never happen!\n");
1690 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1693 MarkTileDirty(x, y);
1696 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1699 if (!IN_SCR_FIELD(x, y))
1701 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1702 printf("DrawGraphicThruMask(): This should never happen!\n");
1707 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1709 MarkTileDirty(x, y);
1712 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1718 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1720 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1724 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1725 int graphic, int frame)
1730 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1732 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1736 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1738 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1740 MarkTileDirty(x / tilesize, y / tilesize);
1743 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1746 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1747 graphic, frame, tilesize);
1748 MarkTileDirty(x / tilesize, y / tilesize);
1751 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1757 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1758 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1761 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1762 int frame, int tilesize)
1767 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1768 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1771 void DrawMiniGraphic(int x, int y, int graphic)
1773 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1774 MarkTileDirty(x / 2, y / 2);
1777 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1782 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1783 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1786 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1787 int graphic, int frame,
1788 int cut_mode, int mask_mode)
1793 int width = TILEX, height = TILEY;
1796 if (dx || dy) // shifted graphic
1798 if (x < BX1) // object enters playfield from the left
1805 else if (x > BX2) // object enters playfield from the right
1811 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1817 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1819 else if (dx) // general horizontal movement
1820 MarkTileDirty(x + SIGN(dx), y);
1822 if (y < BY1) // object enters playfield from the top
1824 if (cut_mode == CUT_BELOW) // object completely above top border
1832 else if (y > BY2) // object enters playfield from the bottom
1838 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1844 else if (dy > 0 && cut_mode == CUT_ABOVE)
1846 if (y == BY2) // object completely above bottom border
1852 MarkTileDirty(x, y + 1);
1853 } // object leaves playfield to the bottom
1854 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1856 else if (dy) // general vertical movement
1857 MarkTileDirty(x, y + SIGN(dy));
1861 if (!IN_SCR_FIELD(x, y))
1863 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1864 printf("DrawGraphicShifted(): This should never happen!\n");
1869 width = width * TILESIZE_VAR / TILESIZE;
1870 height = height * TILESIZE_VAR / TILESIZE;
1871 cx = cx * TILESIZE_VAR / TILESIZE;
1872 cy = cy * TILESIZE_VAR / TILESIZE;
1873 dx = dx * TILESIZE_VAR / TILESIZE;
1874 dy = dy * TILESIZE_VAR / TILESIZE;
1876 if (width > 0 && height > 0)
1878 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1883 dst_x = FX + x * TILEX_VAR + dx;
1884 dst_y = FY + y * TILEY_VAR + dy;
1886 if (mask_mode == USE_MASKING)
1887 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1890 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1893 MarkTileDirty(x, y);
1897 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1898 int graphic, int frame,
1899 int cut_mode, int mask_mode)
1904 int width = TILEX_VAR, height = TILEY_VAR;
1907 int x2 = x + SIGN(dx);
1908 int y2 = y + SIGN(dy);
1910 // movement with two-tile animations must be sync'ed with movement position,
1911 // not with current GfxFrame (which can be higher when using slow movement)
1912 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1913 int anim_frames = graphic_info[graphic].anim_frames;
1915 // (we also need anim_delay here for movement animations with less frames)
1916 int anim_delay = graphic_info[graphic].anim_delay;
1917 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1919 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1920 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1922 // re-calculate animation frame for two-tile movement animation
1923 frame = getGraphicAnimationFrame(graphic, sync_frame);
1925 // check if movement start graphic inside screen area and should be drawn
1926 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1928 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1930 dst_x = FX + x1 * TILEX_VAR;
1931 dst_y = FY + y1 * TILEY_VAR;
1933 if (mask_mode == USE_MASKING)
1934 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1937 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1940 MarkTileDirty(x1, y1);
1943 // check if movement end graphic inside screen area and should be drawn
1944 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1946 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1948 dst_x = FX + x2 * TILEX_VAR;
1949 dst_y = FY + y2 * TILEY_VAR;
1951 if (mask_mode == USE_MASKING)
1952 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1955 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1958 MarkTileDirty(x2, y2);
1962 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1963 int graphic, int frame,
1964 int cut_mode, int mask_mode)
1968 DrawGraphic(x, y, graphic, frame);
1973 if (graphic_info[graphic].double_movement) // EM style movement images
1974 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1976 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1979 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1980 int graphic, int frame, int cut_mode)
1982 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1985 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1986 int cut_mode, int mask_mode)
1988 int lx = LEVELX(x), ly = LEVELY(y);
1992 if (IN_LEV_FIELD(lx, ly))
1994 SetRandomAnimationValue(lx, ly);
1996 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1997 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1999 // do not use double (EM style) movement graphic when not moving
2000 if (graphic_info[graphic].double_movement && !dx && !dy)
2002 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2003 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2006 else // border element
2008 graphic = el2img(element);
2009 frame = getGraphicAnimationFrame(graphic, -1);
2012 if (element == EL_EXPANDABLE_WALL)
2014 boolean left_stopped = FALSE, right_stopped = FALSE;
2016 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2017 left_stopped = TRUE;
2018 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2019 right_stopped = TRUE;
2021 if (left_stopped && right_stopped)
2023 else if (left_stopped)
2025 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2026 frame = graphic_info[graphic].anim_frames - 1;
2028 else if (right_stopped)
2030 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2031 frame = graphic_info[graphic].anim_frames - 1;
2036 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2037 else if (mask_mode == USE_MASKING)
2038 DrawGraphicThruMask(x, y, graphic, frame);
2040 DrawGraphic(x, y, graphic, frame);
2043 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2044 int cut_mode, int mask_mode)
2046 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2047 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2048 cut_mode, mask_mode);
2051 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2054 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2057 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2060 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2063 void DrawLevelElementThruMask(int x, int y, int element)
2065 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2068 void DrawLevelFieldThruMask(int x, int y)
2070 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2073 // !!! implementation of quicksand is totally broken !!!
2074 #define IS_CRUMBLED_TILE(x, y, e) \
2075 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2076 !IS_MOVING(x, y) || \
2077 (e) == EL_QUICKSAND_EMPTYING || \
2078 (e) == EL_QUICKSAND_FAST_EMPTYING))
2080 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2085 int width, height, cx, cy;
2086 int sx = SCREENX(x), sy = SCREENY(y);
2087 int crumbled_border_size = graphic_info[graphic].border_size;
2088 int crumbled_tile_size = graphic_info[graphic].tile_size;
2089 int crumbled_border_size_var =
2090 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2093 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2095 for (i = 1; i < 4; i++)
2097 int dxx = (i & 1 ? dx : 0);
2098 int dyy = (i & 2 ? dy : 0);
2101 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2104 // check if neighbour field is of same crumble type
2105 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2106 graphic_info[graphic].class ==
2107 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2109 // return if check prevents inner corner
2110 if (same == (dxx == dx && dyy == dy))
2114 // if we reach this point, we have an inner corner
2116 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2118 width = crumbled_border_size_var;
2119 height = crumbled_border_size_var;
2120 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2121 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2123 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2124 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2127 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2132 int width, height, bx, by, cx, cy;
2133 int sx = SCREENX(x), sy = SCREENY(y);
2134 int crumbled_border_size = graphic_info[graphic].border_size;
2135 int crumbled_tile_size = graphic_info[graphic].tile_size;
2136 int crumbled_border_size_var =
2137 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2138 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2141 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2143 // draw simple, sloppy, non-corner-accurate crumbled border
2145 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2146 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2147 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2148 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2150 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2151 FX + sx * TILEX_VAR + cx,
2152 FY + sy * TILEY_VAR + cy);
2154 // (remaining middle border part must be at least as big as corner part)
2155 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2156 crumbled_border_size_var >= TILESIZE_VAR / 3)
2159 // correct corners of crumbled border, if needed
2161 for (i = -1; i <= 1; i += 2)
2163 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2164 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2165 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2168 // check if neighbour field is of same crumble type
2169 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2170 graphic_info[graphic].class ==
2171 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2173 // no crumbled corner, but continued crumbled border
2175 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2176 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2177 int b1 = (i == 1 ? crumbled_border_size_var :
2178 TILESIZE_VAR - 2 * crumbled_border_size_var);
2180 width = crumbled_border_size_var;
2181 height = crumbled_border_size_var;
2183 if (dir == 1 || dir == 2)
2198 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2200 FX + sx * TILEX_VAR + cx,
2201 FY + sy * TILEY_VAR + cy);
2206 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2208 int sx = SCREENX(x), sy = SCREENY(y);
2211 static int xy[4][2] =
2219 if (!IN_LEV_FIELD(x, y))
2222 element = TILE_GFX_ELEMENT(x, y);
2224 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2226 if (!IN_SCR_FIELD(sx, sy))
2229 // crumble field borders towards direct neighbour fields
2230 for (i = 0; i < 4; i++)
2232 int xx = x + xy[i][0];
2233 int yy = y + xy[i][1];
2235 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2238 // check if neighbour field is of same crumble type
2239 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2240 graphic_info[graphic].class ==
2241 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2244 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2247 // crumble inner field corners towards corner neighbour fields
2248 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2249 graphic_info[graphic].anim_frames == 2)
2251 for (i = 0; i < 4; i++)
2253 int dx = (i & 1 ? +1 : -1);
2254 int dy = (i & 2 ? +1 : -1);
2256 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2260 MarkTileDirty(sx, sy);
2262 else // center field is not crumbled -- crumble neighbour fields
2264 // crumble field borders of direct neighbour fields
2265 for (i = 0; i < 4; i++)
2267 int xx = x + xy[i][0];
2268 int yy = y + xy[i][1];
2269 int sxx = sx + xy[i][0];
2270 int syy = sy + xy[i][1];
2272 if (!IN_LEV_FIELD(xx, yy) ||
2273 !IN_SCR_FIELD(sxx, syy))
2276 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2279 element = TILE_GFX_ELEMENT(xx, yy);
2281 if (!IS_CRUMBLED_TILE(xx, yy, element))
2284 graphic = el_act2crm(element, ACTION_DEFAULT);
2286 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2288 MarkTileDirty(sxx, syy);
2291 // crumble inner field corners of corner neighbour fields
2292 for (i = 0; i < 4; i++)
2294 int dx = (i & 1 ? +1 : -1);
2295 int dy = (i & 2 ? +1 : -1);
2301 if (!IN_LEV_FIELD(xx, yy) ||
2302 !IN_SCR_FIELD(sxx, syy))
2305 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2308 element = TILE_GFX_ELEMENT(xx, yy);
2310 if (!IS_CRUMBLED_TILE(xx, yy, element))
2313 graphic = el_act2crm(element, ACTION_DEFAULT);
2315 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2316 graphic_info[graphic].anim_frames == 2)
2317 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2319 MarkTileDirty(sxx, syy);
2324 void DrawLevelFieldCrumbled(int x, int y)
2328 if (!IN_LEV_FIELD(x, y))
2331 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2332 GfxElement[x][y] != EL_UNDEFINED &&
2333 GFX_CRUMBLED(GfxElement[x][y]))
2335 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2340 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2342 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2345 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2348 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2349 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2350 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2351 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2352 int sx = SCREENX(x), sy = SCREENY(y);
2354 DrawGraphic(sx, sy, graphic1, frame1);
2355 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2358 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2360 int sx = SCREENX(x), sy = SCREENY(y);
2361 static int xy[4][2] =
2370 // crumble direct neighbour fields (required for field borders)
2371 for (i = 0; i < 4; i++)
2373 int xx = x + xy[i][0];
2374 int yy = y + xy[i][1];
2375 int sxx = sx + xy[i][0];
2376 int syy = sy + xy[i][1];
2378 if (!IN_LEV_FIELD(xx, yy) ||
2379 !IN_SCR_FIELD(sxx, syy) ||
2380 !GFX_CRUMBLED(Feld[xx][yy]) ||
2384 DrawLevelField(xx, yy);
2387 // crumble corner neighbour fields (required for inner field corners)
2388 for (i = 0; i < 4; i++)
2390 int dx = (i & 1 ? +1 : -1);
2391 int dy = (i & 2 ? +1 : -1);
2397 if (!IN_LEV_FIELD(xx, yy) ||
2398 !IN_SCR_FIELD(sxx, syy) ||
2399 !GFX_CRUMBLED(Feld[xx][yy]) ||
2403 int element = TILE_GFX_ELEMENT(xx, yy);
2404 int graphic = el_act2crm(element, ACTION_DEFAULT);
2406 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2407 graphic_info[graphic].anim_frames == 2)
2408 DrawLevelField(xx, yy);
2412 static int getBorderElement(int x, int y)
2416 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2417 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2418 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2419 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2420 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2421 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2422 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2424 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2425 int steel_position = (x == -1 && y == -1 ? 0 :
2426 x == lev_fieldx && y == -1 ? 1 :
2427 x == -1 && y == lev_fieldy ? 2 :
2428 x == lev_fieldx && y == lev_fieldy ? 3 :
2429 x == -1 || x == lev_fieldx ? 4 :
2430 y == -1 || y == lev_fieldy ? 5 : 6);
2432 return border[steel_position][steel_type];
2435 void DrawScreenElement(int x, int y, int element)
2437 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2438 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2441 void DrawLevelElement(int x, int y, int element)
2443 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2444 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2447 void DrawScreenField(int x, int y)
2449 int lx = LEVELX(x), ly = LEVELY(y);
2450 int element, content;
2452 if (!IN_LEV_FIELD(lx, ly))
2454 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2457 element = getBorderElement(lx, ly);
2459 DrawScreenElement(x, y, element);
2464 element = Feld[lx][ly];
2465 content = Store[lx][ly];
2467 if (IS_MOVING(lx, ly))
2469 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2470 boolean cut_mode = NO_CUTTING;
2472 if (element == EL_QUICKSAND_EMPTYING ||
2473 element == EL_QUICKSAND_FAST_EMPTYING ||
2474 element == EL_MAGIC_WALL_EMPTYING ||
2475 element == EL_BD_MAGIC_WALL_EMPTYING ||
2476 element == EL_DC_MAGIC_WALL_EMPTYING ||
2477 element == EL_AMOEBA_DROPPING)
2478 cut_mode = CUT_ABOVE;
2479 else if (element == EL_QUICKSAND_FILLING ||
2480 element == EL_QUICKSAND_FAST_FILLING ||
2481 element == EL_MAGIC_WALL_FILLING ||
2482 element == EL_BD_MAGIC_WALL_FILLING ||
2483 element == EL_DC_MAGIC_WALL_FILLING)
2484 cut_mode = CUT_BELOW;
2486 if (cut_mode == CUT_ABOVE)
2487 DrawScreenElement(x, y, element);
2489 DrawScreenElement(x, y, EL_EMPTY);
2492 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2493 else if (cut_mode == NO_CUTTING)
2494 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2497 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2499 if (cut_mode == CUT_BELOW &&
2500 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2501 DrawLevelElement(lx, ly + 1, element);
2504 if (content == EL_ACID)
2506 int dir = MovDir[lx][ly];
2507 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2508 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2510 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2512 // prevent target field from being drawn again (but without masking)
2513 // (this would happen if target field is scanned after moving element)
2514 Stop[newlx][newly] = TRUE;
2517 else if (IS_BLOCKED(lx, ly))
2522 boolean cut_mode = NO_CUTTING;
2523 int element_old, content_old;
2525 Blocked2Moving(lx, ly, &oldx, &oldy);
2528 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2529 MovDir[oldx][oldy] == MV_RIGHT);
2531 element_old = Feld[oldx][oldy];
2532 content_old = Store[oldx][oldy];
2534 if (element_old == EL_QUICKSAND_EMPTYING ||
2535 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2536 element_old == EL_MAGIC_WALL_EMPTYING ||
2537 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2538 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2539 element_old == EL_AMOEBA_DROPPING)
2540 cut_mode = CUT_ABOVE;
2542 DrawScreenElement(x, y, EL_EMPTY);
2545 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2547 else if (cut_mode == NO_CUTTING)
2548 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2551 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2554 else if (IS_DRAWABLE(element))
2555 DrawScreenElement(x, y, element);
2557 DrawScreenElement(x, y, EL_EMPTY);
2560 void DrawLevelField(int x, int y)
2562 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2563 DrawScreenField(SCREENX(x), SCREENY(y));
2564 else if (IS_MOVING(x, y))
2568 Moving2Blocked(x, y, &newx, &newy);
2569 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2570 DrawScreenField(SCREENX(newx), SCREENY(newy));
2572 else if (IS_BLOCKED(x, y))
2576 Blocked2Moving(x, y, &oldx, &oldy);
2577 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2578 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2582 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2583 int (*el2img_function)(int), boolean masked,
2584 int element_bits_draw)
2586 int element_base = map_mm_wall_element(element);
2587 int element_bits = (IS_DF_WALL(element) ?
2588 element - EL_DF_WALL_START :
2589 IS_MM_WALL(element) ?
2590 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2591 int graphic = el2img_function(element_base);
2592 int tilesize_draw = tilesize / 2;
2597 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2599 for (i = 0; i < 4; i++)
2601 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2602 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2604 if (!(element_bits_draw & (1 << i)))
2607 if (element_bits & (1 << i))
2610 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2611 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2613 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2614 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2619 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2620 tilesize_draw, tilesize_draw);
2625 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2626 boolean masked, int element_bits_draw)
2628 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2629 element, tilesize, el2edimg, masked, element_bits_draw);
2632 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2633 int (*el2img_function)(int))
2635 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2639 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2642 if (IS_MM_WALL(element))
2644 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2645 element, tilesize, el2edimg, masked, 0x000f);
2649 int graphic = el2edimg(element);
2652 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2654 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2658 void DrawSizedElement(int x, int y, int element, int tilesize)
2660 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2663 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2665 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2668 void DrawMiniElement(int x, int y, int element)
2672 graphic = el2edimg(element);
2673 DrawMiniGraphic(x, y, graphic);
2676 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2679 int x = sx + scroll_x, y = sy + scroll_y;
2681 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2682 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2683 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2684 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2686 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2689 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2691 int x = sx + scroll_x, y = sy + scroll_y;
2693 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2694 DrawMiniElement(sx, sy, EL_EMPTY);
2695 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2696 DrawMiniElement(sx, sy, Feld[x][y]);
2698 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2701 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2702 int x, int y, int xsize, int ysize,
2703 int tile_width, int tile_height)
2707 int dst_x = startx + x * tile_width;
2708 int dst_y = starty + y * tile_height;
2709 int width = graphic_info[graphic].width;
2710 int height = graphic_info[graphic].height;
2711 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2712 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2713 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2714 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2715 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2716 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2717 boolean draw_masked = graphic_info[graphic].draw_masked;
2719 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2721 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2723 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2727 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2728 inner_sx + (x - 1) * tile_width % inner_width);
2729 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2730 inner_sy + (y - 1) * tile_height % inner_height);
2733 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2736 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2740 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2741 int x, int y, int xsize, int ysize,
2744 int font_width = getFontWidth(font_nr);
2745 int font_height = getFontHeight(font_nr);
2747 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2748 font_width, font_height);
2751 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2753 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2754 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2755 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2756 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2757 boolean no_delay = (tape.warp_forward);
2758 unsigned int anim_delay = 0;
2759 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2760 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2761 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2762 int font_width = getFontWidth(font_nr);
2763 int font_height = getFontHeight(font_nr);
2764 int max_xsize = level.envelope[envelope_nr].xsize;
2765 int max_ysize = level.envelope[envelope_nr].ysize;
2766 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2767 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2768 int xend = max_xsize;
2769 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2770 int xstep = (xstart < xend ? 1 : 0);
2771 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2773 int end = MAX(xend - xstart, yend - ystart);
2776 for (i = start; i <= end; i++)
2778 int last_frame = end; // last frame of this "for" loop
2779 int x = xstart + i * xstep;
2780 int y = ystart + i * ystep;
2781 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2782 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2783 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2784 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2787 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2789 BlitScreenToBitmap(backbuffer);
2791 SetDrawtoField(DRAW_TO_BACKBUFFER);
2793 for (yy = 0; yy < ysize; yy++)
2794 for (xx = 0; xx < xsize; xx++)
2795 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2797 DrawTextBuffer(sx + font_width, sy + font_height,
2798 level.envelope[envelope_nr].text, font_nr, max_xsize,
2799 xsize - 2, ysize - 2, 0, mask_mode,
2800 level.envelope[envelope_nr].autowrap,
2801 level.envelope[envelope_nr].centered, FALSE);
2803 redraw_mask |= REDRAW_FIELD;
2806 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2809 ClearAutoRepeatKeyEvents();
2812 void ShowEnvelope(int envelope_nr)
2814 int element = EL_ENVELOPE_1 + envelope_nr;
2815 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2816 int sound_opening = element_info[element].sound[ACTION_OPENING];
2817 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2818 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2819 boolean no_delay = (tape.warp_forward);
2820 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2821 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2822 int anim_mode = graphic_info[graphic].anim_mode;
2823 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2824 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2826 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2828 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2830 if (anim_mode == ANIM_DEFAULT)
2831 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2833 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2836 Delay(wait_delay_value);
2838 WaitForEventToContinue();
2840 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2842 if (anim_mode != ANIM_NONE)
2843 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2845 if (anim_mode == ANIM_DEFAULT)
2846 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2848 game.envelope_active = FALSE;
2850 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2852 redraw_mask |= REDRAW_FIELD;
2856 static void setRequestBasePosition(int *x, int *y)
2858 int sx_base, sy_base;
2860 if (request.x != -1)
2861 sx_base = request.x;
2862 else if (request.align == ALIGN_LEFT)
2864 else if (request.align == ALIGN_RIGHT)
2865 sx_base = SX + SXSIZE;
2867 sx_base = SX + SXSIZE / 2;
2869 if (request.y != -1)
2870 sy_base = request.y;
2871 else if (request.valign == VALIGN_TOP)
2873 else if (request.valign == VALIGN_BOTTOM)
2874 sy_base = SY + SYSIZE;
2876 sy_base = SY + SYSIZE / 2;
2882 static void setRequestPositionExt(int *x, int *y, int width, int height,
2883 boolean add_border_size)
2885 int border_size = request.border_size;
2886 int sx_base, sy_base;
2889 setRequestBasePosition(&sx_base, &sy_base);
2891 if (request.align == ALIGN_LEFT)
2893 else if (request.align == ALIGN_RIGHT)
2894 sx = sx_base - width;
2896 sx = sx_base - width / 2;
2898 if (request.valign == VALIGN_TOP)
2900 else if (request.valign == VALIGN_BOTTOM)
2901 sy = sy_base - height;
2903 sy = sy_base - height / 2;
2905 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2906 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2908 if (add_border_size)
2918 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2920 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2923 static void DrawEnvelopeRequest(char *text)
2925 char *text_final = text;
2926 char *text_door_style = NULL;
2927 int graphic = IMG_BACKGROUND_REQUEST;
2928 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2929 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2930 int font_nr = FONT_REQUEST;
2931 int font_width = getFontWidth(font_nr);
2932 int font_height = getFontHeight(font_nr);
2933 int border_size = request.border_size;
2934 int line_spacing = request.line_spacing;
2935 int line_height = font_height + line_spacing;
2936 int max_text_width = request.width - 2 * border_size;
2937 int max_text_height = request.height - 2 * border_size;
2938 int line_length = max_text_width / font_width;
2939 int max_lines = max_text_height / line_height;
2940 int text_width = line_length * font_width;
2941 int width = request.width;
2942 int height = request.height;
2943 int tile_size = MAX(request.step_offset, 1);
2944 int x_steps = width / tile_size;
2945 int y_steps = height / tile_size;
2946 int sx_offset = border_size;
2947 int sy_offset = border_size;
2951 if (request.centered)
2952 sx_offset = (request.width - text_width) / 2;
2954 if (request.wrap_single_words && !request.autowrap)
2956 char *src_text_ptr, *dst_text_ptr;
2958 text_door_style = checked_malloc(2 * strlen(text) + 1);
2960 src_text_ptr = text;
2961 dst_text_ptr = text_door_style;
2963 while (*src_text_ptr)
2965 if (*src_text_ptr == ' ' ||
2966 *src_text_ptr == '?' ||
2967 *src_text_ptr == '!')
2968 *dst_text_ptr++ = '\n';
2970 if (*src_text_ptr != ' ')
2971 *dst_text_ptr++ = *src_text_ptr;
2976 *dst_text_ptr = '\0';
2978 text_final = text_door_style;
2981 setRequestPosition(&sx, &sy, FALSE);
2983 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2985 for (y = 0; y < y_steps; y++)
2986 for (x = 0; x < x_steps; x++)
2987 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2988 x, y, x_steps, y_steps,
2989 tile_size, tile_size);
2991 // force DOOR font inside door area
2992 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2994 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2995 line_length, -1, max_lines, line_spacing, mask_mode,
2996 request.autowrap, request.centered, FALSE);
3000 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3001 RedrawGadget(tool_gadget[i]);
3003 // store readily prepared envelope request for later use when animating
3004 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3006 if (text_door_style)
3007 free(text_door_style);
3010 static void AnimateEnvelopeRequest(int anim_mode, int action)
3012 int graphic = IMG_BACKGROUND_REQUEST;
3013 boolean draw_masked = graphic_info[graphic].draw_masked;
3014 int delay_value_normal = request.step_delay;
3015 int delay_value_fast = delay_value_normal / 2;
3016 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3017 boolean no_delay = (tape.warp_forward);
3018 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3019 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3020 unsigned int anim_delay = 0;
3022 int tile_size = MAX(request.step_offset, 1);
3023 int max_xsize = request.width / tile_size;
3024 int max_ysize = request.height / tile_size;
3025 int max_xsize_inner = max_xsize - 2;
3026 int max_ysize_inner = max_ysize - 2;
3028 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3029 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3030 int xend = max_xsize_inner;
3031 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3032 int xstep = (xstart < xend ? 1 : 0);
3033 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3035 int end = MAX(xend - xstart, yend - ystart);
3038 if (setup.quick_doors)
3045 for (i = start; i <= end; i++)
3047 int last_frame = end; // last frame of this "for" loop
3048 int x = xstart + i * xstep;
3049 int y = ystart + i * ystep;
3050 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3051 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3052 int xsize_size_left = (xsize - 1) * tile_size;
3053 int ysize_size_top = (ysize - 1) * tile_size;
3054 int max_xsize_pos = (max_xsize - 1) * tile_size;
3055 int max_ysize_pos = (max_ysize - 1) * tile_size;
3056 int width = xsize * tile_size;
3057 int height = ysize * tile_size;
3062 setRequestPosition(&src_x, &src_y, FALSE);
3063 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3065 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3067 for (yy = 0; yy < 2; yy++)
3069 for (xx = 0; xx < 2; xx++)
3071 int src_xx = src_x + xx * max_xsize_pos;
3072 int src_yy = src_y + yy * max_ysize_pos;
3073 int dst_xx = dst_x + xx * xsize_size_left;
3074 int dst_yy = dst_y + yy * ysize_size_top;
3075 int xx_size = (xx ? tile_size : xsize_size_left);
3076 int yy_size = (yy ? tile_size : ysize_size_top);
3079 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3080 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3082 BlitBitmap(bitmap_db_store_2, backbuffer,
3083 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3087 redraw_mask |= REDRAW_FIELD;
3091 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3094 ClearAutoRepeatKeyEvents();
3097 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3099 int graphic = IMG_BACKGROUND_REQUEST;
3100 int sound_opening = SND_REQUEST_OPENING;
3101 int sound_closing = SND_REQUEST_CLOSING;
3102 int anim_mode_1 = request.anim_mode; // (higher priority)
3103 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3104 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3105 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3106 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3108 if (game_status == GAME_MODE_PLAYING)
3109 BlitScreenToBitmap(backbuffer);
3111 SetDrawtoField(DRAW_TO_BACKBUFFER);
3113 // SetDrawBackgroundMask(REDRAW_NONE);
3115 if (action == ACTION_OPENING)
3117 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3119 if (req_state & REQ_ASK)
3121 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3122 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3124 else if (req_state & REQ_CONFIRM)
3126 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3128 else if (req_state & REQ_PLAYER)
3130 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3131 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3132 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3133 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3136 DrawEnvelopeRequest(text);
3139 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3141 if (action == ACTION_OPENING)
3143 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3145 if (anim_mode == ANIM_DEFAULT)
3146 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3148 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3152 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3154 if (anim_mode != ANIM_NONE)
3155 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3157 if (anim_mode == ANIM_DEFAULT)
3158 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3161 game.envelope_active = FALSE;
3163 if (action == ACTION_CLOSING)
3164 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3166 // SetDrawBackgroundMask(last_draw_background_mask);
3168 redraw_mask |= REDRAW_FIELD;
3172 if (action == ACTION_CLOSING &&
3173 game_status == GAME_MODE_PLAYING &&
3174 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3175 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3178 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3180 if (IS_MM_WALL(element))
3182 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3188 int graphic = el2preimg(element);
3190 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3191 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3196 void DrawLevel(int draw_background_mask)
3200 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3201 SetDrawBackgroundMask(draw_background_mask);
3205 for (x = BX1; x <= BX2; x++)
3206 for (y = BY1; y <= BY2; y++)
3207 DrawScreenField(x, y);
3209 redraw_mask |= REDRAW_FIELD;
3212 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3217 for (x = 0; x < size_x; x++)
3218 for (y = 0; y < size_y; y++)
3219 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3221 redraw_mask |= REDRAW_FIELD;
3224 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3228 for (x = 0; x < size_x; x++)
3229 for (y = 0; y < size_y; y++)
3230 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3232 redraw_mask |= REDRAW_FIELD;
3235 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3237 boolean show_level_border = (BorderElement != EL_EMPTY);
3238 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3239 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3240 int tile_size = preview.tile_size;
3241 int preview_width = preview.xsize * tile_size;
3242 int preview_height = preview.ysize * tile_size;
3243 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3244 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3245 int real_preview_width = real_preview_xsize * tile_size;
3246 int real_preview_height = real_preview_ysize * tile_size;
3247 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3248 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3251 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3254 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3256 dst_x += (preview_width - real_preview_width) / 2;
3257 dst_y += (preview_height - real_preview_height) / 2;
3259 for (x = 0; x < real_preview_xsize; x++)
3261 for (y = 0; y < real_preview_ysize; y++)
3263 int lx = from_x + x + (show_level_border ? -1 : 0);
3264 int ly = from_y + y + (show_level_border ? -1 : 0);
3265 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3266 getBorderElement(lx, ly));
3268 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3269 element, tile_size);
3273 redraw_mask |= REDRAW_FIELD;
3276 #define MICROLABEL_EMPTY 0
3277 #define MICROLABEL_LEVEL_NAME 1
3278 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3279 #define MICROLABEL_LEVEL_AUTHOR 3
3280 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3281 #define MICROLABEL_IMPORTED_FROM 5
3282 #define MICROLABEL_IMPORTED_BY_HEAD 6
3283 #define MICROLABEL_IMPORTED_BY 7
3285 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3287 int max_text_width = SXSIZE;
3288 int font_width = getFontWidth(font_nr);
3290 if (pos->align == ALIGN_CENTER)
3291 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3292 else if (pos->align == ALIGN_RIGHT)
3293 max_text_width = pos->x;
3295 max_text_width = SXSIZE - pos->x;
3297 return max_text_width / font_width;
3300 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3302 char label_text[MAX_OUTPUT_LINESIZE + 1];
3303 int max_len_label_text;
3304 int font_nr = pos->font;
3307 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3310 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3311 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3312 mode == MICROLABEL_IMPORTED_BY_HEAD)
3313 font_nr = pos->font_alt;
3315 max_len_label_text = getMaxTextLength(pos, font_nr);
3317 if (pos->size != -1)
3318 max_len_label_text = pos->size;
3320 for (i = 0; i < max_len_label_text; i++)
3321 label_text[i] = ' ';
3322 label_text[max_len_label_text] = '\0';
3324 if (strlen(label_text) > 0)
3325 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3328 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3329 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3330 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3331 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3332 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3333 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3334 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3335 max_len_label_text);
3336 label_text[max_len_label_text] = '\0';
3338 if (strlen(label_text) > 0)
3339 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3341 redraw_mask |= REDRAW_FIELD;
3344 static void DrawPreviewLevelLabel(int mode)
3346 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3349 static void DrawPreviewLevelInfo(int mode)
3351 if (mode == MICROLABEL_LEVEL_NAME)
3352 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3353 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3354 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3357 static void DrawPreviewLevelExt(boolean restart)
3359 static unsigned int scroll_delay = 0;
3360 static unsigned int label_delay = 0;
3361 static int from_x, from_y, scroll_direction;
3362 static int label_state, label_counter;
3363 unsigned int scroll_delay_value = preview.step_delay;
3364 boolean show_level_border = (BorderElement != EL_EMPTY);
3365 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3366 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3373 if (preview.anim_mode == ANIM_CENTERED)
3375 if (level_xsize > preview.xsize)
3376 from_x = (level_xsize - preview.xsize) / 2;
3377 if (level_ysize > preview.ysize)
3378 from_y = (level_ysize - preview.ysize) / 2;
3381 from_x += preview.xoffset;
3382 from_y += preview.yoffset;
3384 scroll_direction = MV_RIGHT;
3388 DrawPreviewLevelPlayfield(from_x, from_y);
3389 DrawPreviewLevelLabel(label_state);
3391 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3392 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3394 // initialize delay counters
3395 DelayReached(&scroll_delay, 0);
3396 DelayReached(&label_delay, 0);
3398 if (leveldir_current->name)
3400 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3401 char label_text[MAX_OUTPUT_LINESIZE + 1];
3402 int font_nr = pos->font;
3403 int max_len_label_text = getMaxTextLength(pos, font_nr);
3405 if (pos->size != -1)
3406 max_len_label_text = pos->size;
3408 strncpy(label_text, leveldir_current->name, max_len_label_text);
3409 label_text[max_len_label_text] = '\0';
3411 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3412 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3418 // scroll preview level, if needed
3419 if (preview.anim_mode != ANIM_NONE &&
3420 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3421 DelayReached(&scroll_delay, scroll_delay_value))
3423 switch (scroll_direction)
3428 from_x -= preview.step_offset;
3429 from_x = (from_x < 0 ? 0 : from_x);
3432 scroll_direction = MV_UP;
3436 if (from_x < level_xsize - preview.xsize)
3438 from_x += preview.step_offset;
3439 from_x = (from_x > level_xsize - preview.xsize ?
3440 level_xsize - preview.xsize : from_x);
3443 scroll_direction = MV_DOWN;
3449 from_y -= preview.step_offset;
3450 from_y = (from_y < 0 ? 0 : from_y);
3453 scroll_direction = MV_RIGHT;
3457 if (from_y < level_ysize - preview.ysize)
3459 from_y += preview.step_offset;
3460 from_y = (from_y > level_ysize - preview.ysize ?
3461 level_ysize - preview.ysize : from_y);
3464 scroll_direction = MV_LEFT;
3471 DrawPreviewLevelPlayfield(from_x, from_y);
3474 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3475 // redraw micro level label, if needed
3476 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3477 !strEqual(level.author, ANONYMOUS_NAME) &&
3478 !strEqual(level.author, leveldir_current->name) &&
3479 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3481 int max_label_counter = 23;
3483 if (leveldir_current->imported_from != NULL &&
3484 strlen(leveldir_current->imported_from) > 0)
3485 max_label_counter += 14;
3486 if (leveldir_current->imported_by != NULL &&
3487 strlen(leveldir_current->imported_by) > 0)
3488 max_label_counter += 14;
3490 label_counter = (label_counter + 1) % max_label_counter;
3491 label_state = (label_counter >= 0 && label_counter <= 7 ?
3492 MICROLABEL_LEVEL_NAME :
3493 label_counter >= 9 && label_counter <= 12 ?
3494 MICROLABEL_LEVEL_AUTHOR_HEAD :
3495 label_counter >= 14 && label_counter <= 21 ?
3496 MICROLABEL_LEVEL_AUTHOR :
3497 label_counter >= 23 && label_counter <= 26 ?
3498 MICROLABEL_IMPORTED_FROM_HEAD :
3499 label_counter >= 28 && label_counter <= 35 ?
3500 MICROLABEL_IMPORTED_FROM :
3501 label_counter >= 37 && label_counter <= 40 ?
3502 MICROLABEL_IMPORTED_BY_HEAD :
3503 label_counter >= 42 && label_counter <= 49 ?
3504 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3506 if (leveldir_current->imported_from == NULL &&
3507 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3508 label_state == MICROLABEL_IMPORTED_FROM))
3509 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3510 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3512 DrawPreviewLevelLabel(label_state);
3516 static void DrawPreviewPlayers(void)
3518 if (game_status != GAME_MODE_MAIN)
3521 if (!network.enabled && !setup.team_mode)
3524 boolean player_found[MAX_PLAYERS];
3525 int num_players = 0;
3528 for (i = 0; i < MAX_PLAYERS; i++)
3529 player_found[i] = FALSE;
3531 // check which players can be found in the level (simple approach)
3532 for (x = 0; x < lev_fieldx; x++)
3534 for (y = 0; y < lev_fieldy; y++)
3536 int element = level.field[x][y];
3538 if (ELEM_IS_PLAYER(element))
3540 int player_nr = GET_PLAYER_NR(element);
3542 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3544 if (!player_found[player_nr])
3547 player_found[player_nr] = TRUE;
3552 struct TextPosInfo *pos = &menu.main.preview_players;
3553 int tile_size = pos->tile_size;
3554 int border_size = pos->border_size;
3555 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3556 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3557 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3558 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3559 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3560 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3561 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3562 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3563 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3564 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3565 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3566 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3568 // clear area in which the players will be drawn
3569 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3570 max_players_width, max_players_height);
3572 // only draw players if level is suited for team mode
3573 if (num_players < 2)
3576 // draw all players that were found in the level
3577 for (i = 0; i < MAX_PLAYERS; i++)
3579 if (player_found[i])
3581 int graphic = el2img(EL_PLAYER_1 + i);
3583 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3585 xpos += player_xoffset;
3586 ypos += player_yoffset;
3591 void DrawPreviewLevelInitial(void)
3593 DrawPreviewLevelExt(TRUE);
3594 DrawPreviewPlayers();
3597 void DrawPreviewLevelAnimation(void)
3599 DrawPreviewLevelExt(FALSE);
3602 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3603 int border_size, int font_nr)
3605 int graphic = el2img(EL_PLAYER_1 + player_nr);
3606 int font_height = getFontHeight(font_nr);
3607 int player_height = MAX(tile_size, font_height);
3608 int xoffset_text = tile_size + border_size;
3609 int yoffset_text = (player_height - font_height) / 2;
3610 int yoffset_graphic = (player_height - tile_size) / 2;
3611 char *player_name = getNetworkPlayerName(player_nr + 1);
3613 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3615 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3618 static void DrawNetworkPlayersExt(boolean force)
3620 if (game_status != GAME_MODE_MAIN)
3623 if (!network.connected && !force)
3626 int num_players = 0;
3629 for (i = 0; i < MAX_PLAYERS; i++)
3630 if (stored_player[i].connected_network)
3633 struct TextPosInfo *pos = &menu.main.network_players;
3634 int tile_size = pos->tile_size;
3635 int border_size = pos->border_size;
3636 int xoffset_text = tile_size + border_size;
3637 int font_nr = pos->font;
3638 int font_width = getFontWidth(font_nr);
3639 int font_height = getFontHeight(font_nr);
3640 int player_height = MAX(tile_size, font_height);
3641 int player_yoffset = player_height + border_size;
3642 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3643 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3644 int all_players_height = num_players * player_yoffset - border_size;
3645 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3646 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3647 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3649 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3650 max_players_width, max_players_height);
3652 // first draw local network player ...
3653 for (i = 0; i < MAX_PLAYERS; i++)
3655 if (stored_player[i].connected_network &&
3656 stored_player[i].connected_locally)
3658 char *player_name = getNetworkPlayerName(i + 1);
3659 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3660 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3662 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3664 ypos += player_yoffset;
3668 // ... then draw all other network players
3669 for (i = 0; i < MAX_PLAYERS; i++)
3671 if (stored_player[i].connected_network &&
3672 !stored_player[i].connected_locally)
3674 char *player_name = getNetworkPlayerName(i + 1);
3675 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3676 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3678 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3680 ypos += player_yoffset;
3685 void DrawNetworkPlayers(void)
3687 DrawNetworkPlayersExt(FALSE);
3690 void ClearNetworkPlayers(void)
3692 DrawNetworkPlayersExt(TRUE);
3695 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3696 int graphic, int sync_frame,
3699 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3701 if (mask_mode == USE_MASKING)
3702 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3704 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3707 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3708 int graphic, int sync_frame, int mask_mode)
3710 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3712 if (mask_mode == USE_MASKING)
3713 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3715 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3718 static void DrawGraphicAnimation(int x, int y, int graphic)
3720 int lx = LEVELX(x), ly = LEVELY(y);
3722 if (!IN_SCR_FIELD(x, y))
3725 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3726 graphic, GfxFrame[lx][ly], NO_MASKING);
3728 MarkTileDirty(x, y);
3731 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3733 int lx = LEVELX(x), ly = LEVELY(y);
3735 if (!IN_SCR_FIELD(x, y))
3738 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3739 graphic, GfxFrame[lx][ly], NO_MASKING);
3740 MarkTileDirty(x, y);
3743 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3745 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3748 void DrawLevelElementAnimation(int x, int y, int element)
3750 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3752 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3755 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3757 int sx = SCREENX(x), sy = SCREENY(y);
3759 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3762 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3765 DrawGraphicAnimation(sx, sy, graphic);
3768 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3769 DrawLevelFieldCrumbled(x, y);
3771 if (GFX_CRUMBLED(Feld[x][y]))
3772 DrawLevelFieldCrumbled(x, y);
3776 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3778 int sx = SCREENX(x), sy = SCREENY(y);
3781 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3784 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3786 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3789 DrawGraphicAnimation(sx, sy, graphic);
3791 if (GFX_CRUMBLED(element))
3792 DrawLevelFieldCrumbled(x, y);
3795 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3797 if (player->use_murphy)
3799 // this works only because currently only one player can be "murphy" ...
3800 static int last_horizontal_dir = MV_LEFT;
3801 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3803 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3804 last_horizontal_dir = move_dir;
3806 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3808 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3810 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3816 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3819 static boolean equalGraphics(int graphic1, int graphic2)
3821 struct GraphicInfo *g1 = &graphic_info[graphic1];
3822 struct GraphicInfo *g2 = &graphic_info[graphic2];
3824 return (g1->bitmap == g2->bitmap &&
3825 g1->src_x == g2->src_x &&
3826 g1->src_y == g2->src_y &&
3827 g1->anim_frames == g2->anim_frames &&
3828 g1->anim_delay == g2->anim_delay &&
3829 g1->anim_mode == g2->anim_mode);
3832 void DrawAllPlayers(void)
3836 for (i = 0; i < MAX_PLAYERS; i++)
3837 if (stored_player[i].active)
3838 DrawPlayer(&stored_player[i]);
3841 void DrawPlayerField(int x, int y)
3843 if (!IS_PLAYER(x, y))
3846 DrawPlayer(PLAYERINFO(x, y));
3849 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3851 void DrawPlayer(struct PlayerInfo *player)
3853 int jx = player->jx;
3854 int jy = player->jy;
3855 int move_dir = player->MovDir;
3856 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3857 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3858 int last_jx = (player->is_moving ? jx - dx : jx);
3859 int last_jy = (player->is_moving ? jy - dy : jy);
3860 int next_jx = jx + dx;
3861 int next_jy = jy + dy;
3862 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3863 boolean player_is_opaque = FALSE;
3864 int sx = SCREENX(jx), sy = SCREENY(jy);
3865 int sxx = 0, syy = 0;
3866 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3868 int action = ACTION_DEFAULT;
3869 int last_player_graphic = getPlayerGraphic(player, move_dir);
3870 int last_player_frame = player->Frame;
3873 // GfxElement[][] is set to the element the player is digging or collecting;
3874 // remove also for off-screen player if the player is not moving anymore
3875 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3876 GfxElement[jx][jy] = EL_UNDEFINED;
3878 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3882 if (!IN_LEV_FIELD(jx, jy))
3884 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3885 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3886 printf("DrawPlayerField(): This should never happen!\n");
3891 if (element == EL_EXPLOSION)
3894 action = (player->is_pushing ? ACTION_PUSHING :
3895 player->is_digging ? ACTION_DIGGING :
3896 player->is_collecting ? ACTION_COLLECTING :
3897 player->is_moving ? ACTION_MOVING :
3898 player->is_snapping ? ACTION_SNAPPING :
3899 player->is_dropping ? ACTION_DROPPING :
3900 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3902 if (player->is_waiting)
3903 move_dir = player->dir_waiting;
3905 InitPlayerGfxAnimation(player, action, move_dir);
3907 // --------------------------------------------------------------------------
3908 // draw things in the field the player is leaving, if needed
3909 // --------------------------------------------------------------------------
3911 if (player->is_moving)
3913 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3915 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3917 if (last_element == EL_DYNAMITE_ACTIVE ||
3918 last_element == EL_EM_DYNAMITE_ACTIVE ||
3919 last_element == EL_SP_DISK_RED_ACTIVE)
3920 DrawDynamite(last_jx, last_jy);
3922 DrawLevelFieldThruMask(last_jx, last_jy);
3924 else if (last_element == EL_DYNAMITE_ACTIVE ||
3925 last_element == EL_EM_DYNAMITE_ACTIVE ||
3926 last_element == EL_SP_DISK_RED_ACTIVE)
3927 DrawDynamite(last_jx, last_jy);
3929 /* !!! this is not enough to prevent flickering of players which are
3930 moving next to each others without a free tile between them -- this
3931 can only be solved by drawing all players layer by layer (first the
3932 background, then the foreground etc.) !!! => TODO */
3933 else if (!IS_PLAYER(last_jx, last_jy))
3934 DrawLevelField(last_jx, last_jy);
3937 DrawLevelField(last_jx, last_jy);
3940 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3941 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3944 if (!IN_SCR_FIELD(sx, sy))
3947 // --------------------------------------------------------------------------
3948 // draw things behind the player, if needed
3949 // --------------------------------------------------------------------------
3952 DrawLevelElement(jx, jy, Back[jx][jy]);
3953 else if (IS_ACTIVE_BOMB(element))
3954 DrawLevelElement(jx, jy, EL_EMPTY);
3957 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3959 int old_element = GfxElement[jx][jy];
3960 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3961 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3963 if (GFX_CRUMBLED(old_element))
3964 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3966 DrawGraphic(sx, sy, old_graphic, frame);
3968 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3969 player_is_opaque = TRUE;
3973 GfxElement[jx][jy] = EL_UNDEFINED;
3975 // make sure that pushed elements are drawn with correct frame rate
3976 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3978 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3979 GfxFrame[jx][jy] = player->StepFrame;
3981 DrawLevelField(jx, jy);
3985 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3986 // -----------------------------------------------------------------------
3987 // draw player himself
3988 // -----------------------------------------------------------------------
3990 graphic = getPlayerGraphic(player, move_dir);
3992 // in the case of changed player action or direction, prevent the current
3993 // animation frame from being restarted for identical animations
3994 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3995 player->Frame = last_player_frame;
3997 frame = getGraphicAnimationFrame(graphic, player->Frame);
4001 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4002 sxx = player->GfxPos;
4004 syy = player->GfxPos;
4007 if (player_is_opaque)
4008 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4010 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4012 if (SHIELD_ON(player))
4014 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4015 IMG_SHIELD_NORMAL_ACTIVE);
4016 int frame = getGraphicAnimationFrame(graphic, -1);
4018 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4022 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4025 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4026 sxx = player->GfxPos;
4028 syy = player->GfxPos;
4032 // --------------------------------------------------------------------------
4033 // draw things the player is pushing, if needed
4034 // --------------------------------------------------------------------------
4036 if (player->is_pushing && player->is_moving)
4038 int px = SCREENX(jx), py = SCREENY(jy);
4039 int pxx = (TILEX - ABS(sxx)) * dx;
4040 int pyy = (TILEY - ABS(syy)) * dy;
4041 int gfx_frame = GfxFrame[jx][jy];
4047 if (!IS_MOVING(jx, jy)) // push movement already finished
4049 element = Feld[next_jx][next_jy];
4050 gfx_frame = GfxFrame[next_jx][next_jy];
4053 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4055 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4056 frame = getGraphicAnimationFrame(graphic, sync_frame);
4058 // draw background element under pushed element (like the Sokoban field)
4059 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4061 // this allows transparent pushing animation over non-black background
4064 DrawLevelElement(jx, jy, Back[jx][jy]);
4066 DrawLevelElement(jx, jy, EL_EMPTY);
4068 if (Back[next_jx][next_jy])
4069 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4071 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4073 else if (Back[next_jx][next_jy])
4074 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4077 // do not draw (EM style) pushing animation when pushing is finished
4078 // (two-tile animations usually do not contain start and end frame)
4079 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4080 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4082 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4084 // masked drawing is needed for EMC style (double) movement graphics
4085 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4086 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4090 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4091 // -----------------------------------------------------------------------
4092 // draw player himself
4093 // -----------------------------------------------------------------------
4095 graphic = getPlayerGraphic(player, move_dir);
4097 // in the case of changed player action or direction, prevent the current
4098 // animation frame from being restarted for identical animations
4099 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4100 player->Frame = last_player_frame;
4102 frame = getGraphicAnimationFrame(graphic, player->Frame);
4106 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4107 sxx = player->GfxPos;
4109 syy = player->GfxPos;
4112 if (player_is_opaque)
4113 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4115 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4117 if (SHIELD_ON(player))
4119 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4120 IMG_SHIELD_NORMAL_ACTIVE);
4121 int frame = getGraphicAnimationFrame(graphic, -1);
4123 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4127 // --------------------------------------------------------------------------
4128 // draw things in front of player (active dynamite or dynabombs)
4129 // --------------------------------------------------------------------------
4131 if (IS_ACTIVE_BOMB(element))
4133 graphic = el2img(element);
4134 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4136 if (game.emulation == EMU_SUPAPLEX)
4137 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4139 DrawGraphicThruMask(sx, sy, graphic, frame);
4142 if (player_is_moving && last_element == EL_EXPLOSION)
4144 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4145 GfxElement[last_jx][last_jy] : EL_EMPTY);
4146 int graphic = el_act2img(element, ACTION_EXPLODING);
4147 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4148 int phase = ExplodePhase[last_jx][last_jy] - 1;
4149 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4152 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4155 // --------------------------------------------------------------------------
4156 // draw elements the player is just walking/passing through/under
4157 // --------------------------------------------------------------------------
4159 if (player_is_moving)
4161 // handle the field the player is leaving ...
4162 if (IS_ACCESSIBLE_INSIDE(last_element))
4163 DrawLevelField(last_jx, last_jy);
4164 else if (IS_ACCESSIBLE_UNDER(last_element))
4165 DrawLevelFieldThruMask(last_jx, last_jy);
4168 // do not redraw accessible elements if the player is just pushing them
4169 if (!player_is_moving || !player->is_pushing)
4171 // ... and the field the player is entering
4172 if (IS_ACCESSIBLE_INSIDE(element))
4173 DrawLevelField(jx, jy);
4174 else if (IS_ACCESSIBLE_UNDER(element))
4175 DrawLevelFieldThruMask(jx, jy);
4178 MarkTileDirty(sx, sy);
4181 // ----------------------------------------------------------------------------
4183 void WaitForEventToContinue(void)
4185 boolean still_wait = TRUE;
4187 if (program.headless)
4190 // simulate releasing mouse button over last gadget, if still pressed
4192 HandleGadgets(-1, -1, 0);
4194 button_status = MB_RELEASED;
4202 if (NextValidEvent(&event))
4206 case EVENT_BUTTONRELEASE:
4207 case EVENT_KEYPRESS:
4208 #if defined(TARGET_SDL2)
4209 case SDL_CONTROLLERBUTTONDOWN:
4211 case SDL_JOYBUTTONDOWN:
4215 case EVENT_KEYRELEASE:
4216 ClearPlayerAction();
4220 HandleOtherEvents(&event);
4224 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4233 #define MAX_REQUEST_LINES 13
4234 #define MAX_REQUEST_LINE_FONT1_LEN 7
4235 #define MAX_REQUEST_LINE_FONT2_LEN 10
4237 static int RequestHandleEvents(unsigned int req_state)
4239 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4241 int width = request.width;
4242 int height = request.height;
4246 // when showing request dialog after game ended, deactivate game panel
4247 if (game_just_ended)
4248 game.panel.active = FALSE;
4250 game.request_active = TRUE;
4252 setRequestPosition(&sx, &sy, FALSE);
4254 button_status = MB_RELEASED;
4256 request_gadget_id = -1;
4261 if (game_just_ended)
4263 // the MM game engine does not use a special (scrollable) field buffer
4264 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4265 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4267 HandleGameActions();
4269 SetDrawtoField(DRAW_TO_BACKBUFFER);
4271 if (global.use_envelope_request)
4273 // copy current state of request area to middle of playfield area
4274 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4282 while (NextValidEvent(&event))
4286 case EVENT_BUTTONPRESS:
4287 case EVENT_BUTTONRELEASE:
4288 case EVENT_MOTIONNOTIFY:
4292 if (event.type == EVENT_MOTIONNOTIFY)
4297 motion_status = TRUE;
4298 mx = ((MotionEvent *) &event)->x;
4299 my = ((MotionEvent *) &event)->y;
4303 motion_status = FALSE;
4304 mx = ((ButtonEvent *) &event)->x;
4305 my = ((ButtonEvent *) &event)->y;
4306 if (event.type == EVENT_BUTTONPRESS)
4307 button_status = ((ButtonEvent *) &event)->button;
4309 button_status = MB_RELEASED;
4312 // this sets 'request_gadget_id'
4313 HandleGadgets(mx, my, button_status);
4315 switch (request_gadget_id)
4317 case TOOL_CTRL_ID_YES:
4320 case TOOL_CTRL_ID_NO:
4323 case TOOL_CTRL_ID_CONFIRM:
4324 result = TRUE | FALSE;
4327 case TOOL_CTRL_ID_PLAYER_1:
4330 case TOOL_CTRL_ID_PLAYER_2:
4333 case TOOL_CTRL_ID_PLAYER_3:
4336 case TOOL_CTRL_ID_PLAYER_4:
4347 #if defined(TARGET_SDL2)
4348 case SDL_WINDOWEVENT:
4349 HandleWindowEvent((WindowEvent *) &event);
4352 case SDL_APP_WILLENTERBACKGROUND:
4353 case SDL_APP_DIDENTERBACKGROUND:
4354 case SDL_APP_WILLENTERFOREGROUND:
4355 case SDL_APP_DIDENTERFOREGROUND:
4356 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4360 case EVENT_KEYPRESS:
4362 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4367 if (req_state & REQ_CONFIRM)
4373 #if defined(TARGET_SDL2)
4377 #if defined(KSYM_Rewind)
4378 case KSYM_Rewind: // for Amazon Fire TV remote
4386 #if defined(TARGET_SDL2)
4389 #if defined(KSYM_FastForward)
4390 case KSYM_FastForward: // for Amazon Fire TV remote
4397 HandleKeysDebug(key);
4401 if (req_state & REQ_PLAYER)
4403 int old_player_nr = setup.network_player_nr;
4406 result = old_player_nr + 1;
4411 result = old_player_nr + 1;
4442 case EVENT_KEYRELEASE:
4443 ClearPlayerAction();
4446 #if defined(TARGET_SDL2)
4447 case SDL_CONTROLLERBUTTONDOWN:
4448 switch (event.cbutton.button)
4450 case SDL_CONTROLLER_BUTTON_A:
4451 case SDL_CONTROLLER_BUTTON_X:
4452 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4453 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4457 case SDL_CONTROLLER_BUTTON_B:
4458 case SDL_CONTROLLER_BUTTON_Y:
4459 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4460 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4461 case SDL_CONTROLLER_BUTTON_BACK:
4466 if (req_state & REQ_PLAYER)
4468 int old_player_nr = setup.network_player_nr;
4471 result = old_player_nr + 1;
4473 switch (event.cbutton.button)
4475 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4476 case SDL_CONTROLLER_BUTTON_Y:
4480 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4481 case SDL_CONTROLLER_BUTTON_B:
4485 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4486 case SDL_CONTROLLER_BUTTON_A:
4490 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4491 case SDL_CONTROLLER_BUTTON_X:
4502 case SDL_CONTROLLERBUTTONUP:
4503 HandleJoystickEvent(&event);
4504 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 &&
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 &&
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 &&
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 &&
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_active = GetOverlayActive();
4826 SetOverlayActive(FALSE);
4828 if (global.use_envelope_request)
4829 result = RequestEnvelope(text, req_state);
4831 result = RequestDoor(text, req_state);
4833 SetOverlayActive(overlay_active);
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 defined(TARGET_SDL2)
9301 if (change_window_scaling_percent)
9303 SDLSetWindowScaling(setup.window_scaling_percent);
9307 else if (change_fullscreen)
9309 SDLSetWindowFullscreen(setup.fullscreen);
9311 // set setup value according to successfully changed fullscreen mode
9312 setup.fullscreen = video.fullscreen_enabled;
9318 if (change_fullscreen ||
9319 change_window_scaling_percent)
9321 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9323 // save backbuffer content which gets lost when toggling fullscreen mode
9324 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9326 if (change_window_scaling_percent)
9328 // keep window mode, but change window scaling
9329 video.fullscreen_enabled = TRUE; // force new window scaling
9332 // toggle fullscreen
9333 ChangeVideoModeIfNeeded(setup.fullscreen);
9335 // set setup value according to successfully changed fullscreen mode
9336 setup.fullscreen = video.fullscreen_enabled;
9338 // restore backbuffer content from temporary backbuffer backup bitmap
9339 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9341 FreeBitmap(tmp_backbuffer);
9343 // update visible window/screen
9344 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9348 static void JoinRectangles(int *x, int *y, int *width, int *height,
9349 int x2, int y2, int width2, int height2)
9351 // do not join with "off-screen" rectangle
9352 if (x2 == -1 || y2 == -1)
9357 *width = MAX(*width, width2);
9358 *height = MAX(*height, height2);
9361 void SetAnimStatus(int anim_status_new)
9363 if (anim_status_new == GAME_MODE_MAIN)
9364 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9365 else if (anim_status_new == GAME_MODE_SCORES)
9366 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9368 global.anim_status_next = anim_status_new;
9370 // directly set screen modes that are entered without fading
9371 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9372 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9373 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9374 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9375 global.anim_status = global.anim_status_next;
9378 void SetGameStatus(int game_status_new)
9380 if (game_status_new != game_status)
9381 game_status_last_screen = game_status;
9383 game_status = game_status_new;
9385 SetAnimStatus(game_status_new);
9388 void SetFontStatus(int game_status_new)
9390 static int last_game_status = -1;
9392 if (game_status_new != -1)
9394 // set game status for font use after storing last game status
9395 last_game_status = game_status;
9396 game_status = game_status_new;
9400 // reset game status after font use from last stored game status
9401 game_status = last_game_status;
9405 void ResetFontStatus(void)
9410 void SetLevelSetInfo(char *identifier, int level_nr)
9412 setString(&levelset.identifier, identifier);
9414 levelset.level_nr = level_nr;
9417 boolean CheckIfPlayfieldViewportHasChanged(void)
9419 // if game status has not changed, playfield viewport has not changed either
9420 if (game_status == game_status_last)
9423 // check if playfield viewport has changed with current game status
9424 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9425 int new_real_sx = vp_playfield->x;
9426 int new_real_sy = vp_playfield->y;
9427 int new_full_sxsize = vp_playfield->width;
9428 int new_full_sysize = vp_playfield->height;
9430 return (new_real_sx != REAL_SX ||
9431 new_real_sy != REAL_SY ||
9432 new_full_sxsize != FULL_SXSIZE ||
9433 new_full_sysize != FULL_SYSIZE);
9436 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged(void)
9438 return (CheckIfGlobalBorderHasChanged() ||
9439 CheckIfPlayfieldViewportHasChanged());
9442 void ChangeViewportPropertiesIfNeeded(void)
9444 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9445 FALSE : setup.small_game_graphics);
9446 int gfx_game_mode = game_status;
9447 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9449 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9450 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9451 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9452 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9453 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9454 int new_win_xsize = vp_window->width;
9455 int new_win_ysize = vp_window->height;
9456 int border_size = vp_playfield->border_size;
9457 int new_sx = vp_playfield->x + border_size;
9458 int new_sy = vp_playfield->y + border_size;
9459 int new_sxsize = vp_playfield->width - 2 * border_size;
9460 int new_sysize = vp_playfield->height - 2 * border_size;
9461 int new_real_sx = vp_playfield->x;
9462 int new_real_sy = vp_playfield->y;
9463 int new_full_sxsize = vp_playfield->width;
9464 int new_full_sysize = vp_playfield->height;
9465 int new_dx = vp_door_1->x;
9466 int new_dy = vp_door_1->y;
9467 int new_dxsize = vp_door_1->width;
9468 int new_dysize = vp_door_1->height;
9469 int new_vx = vp_door_2->x;
9470 int new_vy = vp_door_2->y;
9471 int new_vxsize = vp_door_2->width;
9472 int new_vysize = vp_door_2->height;
9473 int new_ex = vp_door_3->x;
9474 int new_ey = vp_door_3->y;
9475 int new_exsize = vp_door_3->width;
9476 int new_eysize = vp_door_3->height;
9477 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9478 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9479 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9480 int new_scr_fieldx = new_sxsize / tilesize;
9481 int new_scr_fieldy = new_sysize / tilesize;
9482 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9483 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9484 boolean init_gfx_buffers = FALSE;
9485 boolean init_video_buffer = FALSE;
9486 boolean init_gadgets_and_anims = FALSE;
9487 boolean init_em_graphics = FALSE;
9489 if (new_win_xsize != WIN_XSIZE ||
9490 new_win_ysize != WIN_YSIZE)
9492 WIN_XSIZE = new_win_xsize;
9493 WIN_YSIZE = new_win_ysize;
9495 init_video_buffer = TRUE;
9496 init_gfx_buffers = TRUE;
9497 init_gadgets_and_anims = TRUE;
9499 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9502 if (new_scr_fieldx != SCR_FIELDX ||
9503 new_scr_fieldy != SCR_FIELDY)
9505 // this always toggles between MAIN and GAME when using small tile size
9507 SCR_FIELDX = new_scr_fieldx;
9508 SCR_FIELDY = new_scr_fieldy;
9510 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9521 new_sxsize != SXSIZE ||
9522 new_sysize != SYSIZE ||
9523 new_dxsize != DXSIZE ||
9524 new_dysize != DYSIZE ||
9525 new_vxsize != VXSIZE ||
9526 new_vysize != VYSIZE ||
9527 new_exsize != EXSIZE ||
9528 new_eysize != EYSIZE ||
9529 new_real_sx != REAL_SX ||
9530 new_real_sy != REAL_SY ||
9531 new_full_sxsize != FULL_SXSIZE ||
9532 new_full_sysize != FULL_SYSIZE ||
9533 new_tilesize_var != TILESIZE_VAR
9536 // ------------------------------------------------------------------------
9537 // determine next fading area for changed viewport definitions
9538 // ------------------------------------------------------------------------
9540 // start with current playfield area (default fading area)
9543 FADE_SXSIZE = FULL_SXSIZE;
9544 FADE_SYSIZE = FULL_SYSIZE;
9546 // add new playfield area if position or size has changed
9547 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9548 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9550 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9551 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9554 // add current and new door 1 area if position or size has changed
9555 if (new_dx != DX || new_dy != DY ||
9556 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9558 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9559 DX, DY, DXSIZE, DYSIZE);
9560 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9561 new_dx, new_dy, new_dxsize, new_dysize);
9564 // add current and new door 2 area if position or size has changed
9565 if (new_dx != VX || new_dy != VY ||
9566 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9568 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9569 VX, VY, VXSIZE, VYSIZE);
9570 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9571 new_vx, new_vy, new_vxsize, new_vysize);
9574 // ------------------------------------------------------------------------
9575 // handle changed tile size
9576 // ------------------------------------------------------------------------
9578 if (new_tilesize_var != TILESIZE_VAR)
9580 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9582 // changing tile size invalidates scroll values of engine snapshots
9583 FreeEngineSnapshotSingle();
9585 // changing tile size requires update of graphic mapping for EM engine
9586 init_em_graphics = TRUE;
9597 SXSIZE = new_sxsize;
9598 SYSIZE = new_sysize;
9599 DXSIZE = new_dxsize;
9600 DYSIZE = new_dysize;
9601 VXSIZE = new_vxsize;
9602 VYSIZE = new_vysize;
9603 EXSIZE = new_exsize;
9604 EYSIZE = new_eysize;
9605 REAL_SX = new_real_sx;
9606 REAL_SY = new_real_sy;
9607 FULL_SXSIZE = new_full_sxsize;
9608 FULL_SYSIZE = new_full_sysize;
9609 TILESIZE_VAR = new_tilesize_var;
9611 init_gfx_buffers = TRUE;
9612 init_gadgets_and_anims = TRUE;
9614 // printf("::: viewports: init_gfx_buffers\n");
9615 // printf("::: viewports: init_gadgets_and_anims\n");
9618 if (init_gfx_buffers)
9620 // printf("::: init_gfx_buffers\n");
9622 SCR_FIELDX = new_scr_fieldx_buffers;
9623 SCR_FIELDY = new_scr_fieldy_buffers;
9627 SCR_FIELDX = new_scr_fieldx;
9628 SCR_FIELDY = new_scr_fieldy;
9630 SetDrawDeactivationMask(REDRAW_NONE);
9631 SetDrawBackgroundMask(REDRAW_FIELD);
9634 if (init_video_buffer)
9636 // printf("::: init_video_buffer\n");
9638 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9639 InitImageTextures();
9642 if (init_gadgets_and_anims)
9644 // printf("::: init_gadgets_and_anims\n");
9647 InitGlobalAnimations();
9650 if (init_em_graphics)
9652 InitGraphicInfo_EM();