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();
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()
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()
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()
484 if (game.envelope_active)
487 DrawLevel(REDRAW_ALL);
491 void RedrawPlayfield()
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()
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 void DrawFramesPerSecond()
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()
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);
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 */
958 else /* REDRAW_ALL */
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;
988 static void SetScreenStates_BeforeFadingIn()
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()
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()
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()
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()
1091 fading = menu.enter_menu;
1093 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1096 void FadeSetLeaveMenu()
1098 fading = menu.leave_menu;
1100 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1103 void FadeSetEnterScreen()
1105 fading = menu.enter_screen[game_status];
1107 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1110 void FadeSetNextScreen()
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()
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()
1135 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1137 fading = fading_none;
1140 void FadeSkipNextFadeIn()
1142 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1145 void FadeSkipNextFadeOut()
1147 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1150 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 Bitmap *getBackgroundBitmap(int graphic)
1162 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1165 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()
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()
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 boolean CheckIfGlobalBorderRedrawIsNeeded()
1287 // if game status has not changed, nothing has to be redrawn
1288 if (game_status == game_status_last)
1291 // redraw if last screen was title screen
1292 if (game_status_last == GAME_MODE_TITLE)
1295 // redraw if global screen border has changed
1296 if (CheckIfGlobalBorderHasChanged())
1299 // redraw if position or size of playfield area has changed
1300 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1301 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1304 // redraw if position or size of door area has changed
1305 if (dx_last != DX || dy_last != DY ||
1306 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1309 // redraw if position or size of tape area has changed
1310 if (vx_last != VX || vy_last != VY ||
1311 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1314 // redraw if position or size of editor area has changed
1315 if (ex_last != EX || ey_last != EY ||
1316 exsize_last != EXSIZE || eysize_last != EYSIZE)
1322 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1325 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1327 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1330 void RedrawGlobalBorder()
1332 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1334 RedrawGlobalBorderFromBitmap(bitmap);
1336 redraw_mask = REDRAW_ALL;
1339 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1341 static void RedrawGlobalBorderIfNeeded()
1343 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1344 if (game_status == game_status_last)
1348 // copy current draw buffer to later copy back areas that have not changed
1349 if (game_status_last != GAME_MODE_TITLE)
1350 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1352 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1353 if (CheckIfGlobalBorderRedrawIsNeeded())
1356 // redraw global screen border (or clear, if defined to be empty)
1357 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1359 if (game_status == GAME_MODE_EDITOR)
1360 DrawSpecialEditorDoor();
1362 // copy previous playfield and door areas, if they are defined on both
1363 // previous and current screen and if they still have the same size
1365 if (real_sx_last != -1 && real_sy_last != -1 &&
1366 REAL_SX != -1 && REAL_SY != -1 &&
1367 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1368 BlitBitmap(bitmap_db_store_1, backbuffer,
1369 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1372 if (dx_last != -1 && dy_last != -1 &&
1373 DX != -1 && DY != -1 &&
1374 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1375 BlitBitmap(bitmap_db_store_1, backbuffer,
1376 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1378 if (game_status != GAME_MODE_EDITOR)
1380 if (vx_last != -1 && vy_last != -1 &&
1381 VX != -1 && VY != -1 &&
1382 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1383 BlitBitmap(bitmap_db_store_1, backbuffer,
1384 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1388 if (ex_last != -1 && ey_last != -1 &&
1389 EX != -1 && EY != -1 &&
1390 exsize_last == EXSIZE && eysize_last == EYSIZE)
1391 BlitBitmap(bitmap_db_store_1, backbuffer,
1392 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1395 redraw_mask = REDRAW_ALL;
1398 game_status_last = game_status;
1400 global_border_bitmap_last = global_border_bitmap;
1402 real_sx_last = REAL_SX;
1403 real_sy_last = REAL_SY;
1404 full_sxsize_last = FULL_SXSIZE;
1405 full_sysize_last = FULL_SYSIZE;
1408 dxsize_last = DXSIZE;
1409 dysize_last = DYSIZE;
1412 vxsize_last = VXSIZE;
1413 vysize_last = VYSIZE;
1416 exsize_last = EXSIZE;
1417 eysize_last = EYSIZE;
1422 RedrawGlobalBorderIfNeeded();
1424 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1425 /* (when entering hall of fame after playing) */
1426 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1428 /* !!! maybe this should be done before clearing the background !!! */
1429 if (game_status == GAME_MODE_PLAYING)
1431 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1432 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1436 SetDrawtoField(DRAW_TO_BACKBUFFER);
1440 void MarkTileDirty(int x, int y)
1442 redraw_mask |= REDRAW_FIELD;
1445 void SetBorderElement()
1449 BorderElement = EL_EMPTY;
1451 /* the MM game engine does not use a visible border element */
1452 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1455 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1457 for (x = 0; x < lev_fieldx; x++)
1459 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1460 BorderElement = EL_STEELWALL;
1462 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1468 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1469 int max_array_fieldx, int max_array_fieldy,
1470 short field[max_array_fieldx][max_array_fieldy],
1471 int max_fieldx, int max_fieldy)
1475 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1476 static int safety = 0;
1478 /* check if starting field still has the desired content */
1479 if (field[from_x][from_y] == fill_element)
1484 if (safety > max_fieldx * max_fieldy)
1485 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1487 old_element = field[from_x][from_y];
1488 field[from_x][from_y] = fill_element;
1490 for (i = 0; i < 4; i++)
1492 x = from_x + check[i][0];
1493 y = from_y + check[i][1];
1495 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1496 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1497 field, max_fieldx, max_fieldy);
1503 void FloodFillLevel(int from_x, int from_y, int fill_element,
1504 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1505 int max_fieldx, int max_fieldy)
1507 FloodFillLevelExt(from_x, from_y, fill_element,
1508 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1509 max_fieldx, max_fieldy);
1512 void SetRandomAnimationValue(int x, int y)
1514 gfx.anim_random_frame = GfxRandom[x][y];
1517 int getGraphicAnimationFrame(int graphic, int sync_frame)
1519 /* animation synchronized with global frame counter, not move position */
1520 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1521 sync_frame = FrameCounter;
1523 return getAnimationFrame(graphic_info[graphic].anim_frames,
1524 graphic_info[graphic].anim_delay,
1525 graphic_info[graphic].anim_mode,
1526 graphic_info[graphic].anim_start_frame,
1530 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1532 struct GraphicInfo *g = &graphic_info[graphic];
1533 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1535 if (tilesize == gfx.standard_tile_size)
1536 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1537 else if (tilesize == game.tile_size)
1538 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1540 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1543 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1544 boolean get_backside)
1546 struct GraphicInfo *g = &graphic_info[graphic];
1547 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1548 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1550 if (g->offset_y == 0) /* frames are ordered horizontally */
1552 int max_width = g->anim_frames_per_line * g->width;
1553 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1555 *x = pos % max_width;
1556 *y = src_y % g->height + pos / max_width * g->height;
1558 else if (g->offset_x == 0) /* frames are ordered vertically */
1560 int max_height = g->anim_frames_per_line * g->height;
1561 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1563 *x = src_x % g->width + pos / max_height * g->width;
1564 *y = pos % max_height;
1566 else /* frames are ordered diagonally */
1568 *x = src_x + frame * g->offset_x;
1569 *y = src_y + frame * g->offset_y;
1573 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1574 Bitmap **bitmap, int *x, int *y,
1575 boolean get_backside)
1577 struct GraphicInfo *g = &graphic_info[graphic];
1579 // if no graphics defined at all, use fallback graphics
1580 if (g->bitmaps == NULL)
1581 *g = graphic_info[IMG_CHAR_EXCLAM];
1583 // if no in-game graphics defined, always use standard graphic size
1584 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1585 tilesize = TILESIZE;
1587 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1588 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1590 *x = *x * tilesize / g->tile_size;
1591 *y = *y * tilesize / g->tile_size;
1594 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1595 Bitmap **bitmap, int *x, int *y)
1597 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1600 void getFixedGraphicSource(int graphic, int frame,
1601 Bitmap **bitmap, int *x, int *y)
1603 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1606 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1608 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1611 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1612 int *x, int *y, boolean get_backside)
1614 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1618 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1620 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1623 void DrawGraphic(int x, int y, int graphic, int frame)
1626 if (!IN_SCR_FIELD(x, y))
1628 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1629 printf("DrawGraphic(): This should never happen!\n");
1634 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1637 MarkTileDirty(x, y);
1640 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1643 if (!IN_SCR_FIELD(x, y))
1645 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1646 printf("DrawGraphic(): This should never happen!\n");
1651 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1653 MarkTileDirty(x, y);
1656 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1662 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1664 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1667 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1673 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1674 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1677 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1680 if (!IN_SCR_FIELD(x, y))
1682 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1683 printf("DrawGraphicThruMask(): This should never happen!\n");
1688 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1691 MarkTileDirty(x, y);
1694 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1697 if (!IN_SCR_FIELD(x, y))
1699 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1700 printf("DrawGraphicThruMask(): This should never happen!\n");
1705 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1707 MarkTileDirty(x, y);
1710 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1716 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1718 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1722 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1723 int graphic, int frame)
1728 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1730 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1734 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1736 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1738 MarkTileDirty(x / tilesize, y / tilesize);
1741 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1744 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1745 graphic, frame, tilesize);
1746 MarkTileDirty(x / tilesize, y / tilesize);
1749 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1755 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1756 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1759 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1760 int frame, int tilesize)
1765 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1766 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1769 void DrawMiniGraphic(int x, int y, int graphic)
1771 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1772 MarkTileDirty(x / 2, y / 2);
1775 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1780 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1781 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1784 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1785 int graphic, int frame,
1786 int cut_mode, int mask_mode)
1791 int width = TILEX, height = TILEY;
1794 if (dx || dy) /* shifted graphic */
1796 if (x < BX1) /* object enters playfield from the left */
1803 else if (x > BX2) /* object enters playfield from the right */
1809 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1815 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1817 else if (dx) /* general horizontal movement */
1818 MarkTileDirty(x + SIGN(dx), y);
1820 if (y < BY1) /* object enters playfield from the top */
1822 if (cut_mode == CUT_BELOW) /* object completely above top border */
1830 else if (y > BY2) /* object enters playfield from the bottom */
1836 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1842 else if (dy > 0 && cut_mode == CUT_ABOVE)
1844 if (y == BY2) /* object completely above bottom border */
1850 MarkTileDirty(x, y + 1);
1851 } /* object leaves playfield to the bottom */
1852 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1854 else if (dy) /* general vertical movement */
1855 MarkTileDirty(x, y + SIGN(dy));
1859 if (!IN_SCR_FIELD(x, y))
1861 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1862 printf("DrawGraphicShifted(): This should never happen!\n");
1867 width = width * TILESIZE_VAR / TILESIZE;
1868 height = height * TILESIZE_VAR / TILESIZE;
1869 cx = cx * TILESIZE_VAR / TILESIZE;
1870 cy = cy * TILESIZE_VAR / TILESIZE;
1871 dx = dx * TILESIZE_VAR / TILESIZE;
1872 dy = dy * TILESIZE_VAR / TILESIZE;
1874 if (width > 0 && height > 0)
1876 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1881 dst_x = FX + x * TILEX_VAR + dx;
1882 dst_y = FY + y * TILEY_VAR + dy;
1884 if (mask_mode == USE_MASKING)
1885 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1888 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1891 MarkTileDirty(x, y);
1895 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1896 int graphic, int frame,
1897 int cut_mode, int mask_mode)
1902 int width = TILEX_VAR, height = TILEY_VAR;
1905 int x2 = x + SIGN(dx);
1906 int y2 = y + SIGN(dy);
1908 /* movement with two-tile animations must be sync'ed with movement position,
1909 not with current GfxFrame (which can be higher when using slow movement) */
1910 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1911 int anim_frames = graphic_info[graphic].anim_frames;
1913 /* (we also need anim_delay here for movement animations with less frames) */
1914 int anim_delay = graphic_info[graphic].anim_delay;
1915 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1917 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1918 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1920 /* re-calculate animation frame for two-tile movement animation */
1921 frame = getGraphicAnimationFrame(graphic, sync_frame);
1923 /* check if movement start graphic inside screen area and should be drawn */
1924 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1926 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1928 dst_x = FX + x1 * TILEX_VAR;
1929 dst_y = FY + y1 * TILEY_VAR;
1931 if (mask_mode == USE_MASKING)
1932 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1935 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1938 MarkTileDirty(x1, y1);
1941 /* check if movement end graphic inside screen area and should be drawn */
1942 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1944 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1946 dst_x = FX + x2 * TILEX_VAR;
1947 dst_y = FY + y2 * TILEY_VAR;
1949 if (mask_mode == USE_MASKING)
1950 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1953 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1956 MarkTileDirty(x2, y2);
1960 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1961 int graphic, int frame,
1962 int cut_mode, int mask_mode)
1966 DrawGraphic(x, y, graphic, frame);
1971 if (graphic_info[graphic].double_movement) /* EM style movement images */
1972 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1974 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1977 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1978 int frame, int cut_mode)
1980 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1983 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1984 int cut_mode, int mask_mode)
1986 int lx = LEVELX(x), ly = LEVELY(y);
1990 if (IN_LEV_FIELD(lx, ly))
1992 SetRandomAnimationValue(lx, ly);
1994 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1995 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1997 /* do not use double (EM style) movement graphic when not moving */
1998 if (graphic_info[graphic].double_movement && !dx && !dy)
2000 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2001 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2004 else /* border element */
2006 graphic = el2img(element);
2007 frame = getGraphicAnimationFrame(graphic, -1);
2010 if (element == EL_EXPANDABLE_WALL)
2012 boolean left_stopped = FALSE, right_stopped = FALSE;
2014 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2015 left_stopped = TRUE;
2016 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2017 right_stopped = TRUE;
2019 if (left_stopped && right_stopped)
2021 else if (left_stopped)
2023 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2024 frame = graphic_info[graphic].anim_frames - 1;
2026 else if (right_stopped)
2028 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2029 frame = graphic_info[graphic].anim_frames - 1;
2034 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2035 else if (mask_mode == USE_MASKING)
2036 DrawGraphicThruMask(x, y, graphic, frame);
2038 DrawGraphic(x, y, graphic, frame);
2041 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2042 int cut_mode, int mask_mode)
2044 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2045 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2046 cut_mode, mask_mode);
2049 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2052 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2055 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2058 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2061 void DrawLevelElementThruMask(int x, int y, int element)
2063 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2066 void DrawLevelFieldThruMask(int x, int y)
2068 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2071 /* !!! implementation of quicksand is totally broken !!! */
2072 #define IS_CRUMBLED_TILE(x, y, e) \
2073 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2074 !IS_MOVING(x, y) || \
2075 (e) == EL_QUICKSAND_EMPTYING || \
2076 (e) == EL_QUICKSAND_FAST_EMPTYING))
2078 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2083 int width, height, cx, cy;
2084 int sx = SCREENX(x), sy = SCREENY(y);
2085 int crumbled_border_size = graphic_info[graphic].border_size;
2086 int crumbled_tile_size = graphic_info[graphic].tile_size;
2087 int crumbled_border_size_var =
2088 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2091 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2093 for (i = 1; i < 4; i++)
2095 int dxx = (i & 1 ? dx : 0);
2096 int dyy = (i & 2 ? dy : 0);
2099 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2102 /* check if neighbour field is of same crumble type */
2103 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2104 graphic_info[graphic].class ==
2105 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2107 /* return if check prevents inner corner */
2108 if (same == (dxx == dx && dyy == dy))
2112 /* if we reach this point, we have an inner corner */
2114 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2116 width = crumbled_border_size_var;
2117 height = crumbled_border_size_var;
2118 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2119 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2121 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2122 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2125 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2130 int width, height, bx, by, cx, cy;
2131 int sx = SCREENX(x), sy = SCREENY(y);
2132 int crumbled_border_size = graphic_info[graphic].border_size;
2133 int crumbled_tile_size = graphic_info[graphic].tile_size;
2134 int crumbled_border_size_var =
2135 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2136 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2139 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2141 /* draw simple, sloppy, non-corner-accurate crumbled border */
2143 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2144 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2145 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2146 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2148 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2149 FX + sx * TILEX_VAR + cx,
2150 FY + sy * TILEY_VAR + cy);
2152 /* (remaining middle border part must be at least as big as corner part) */
2153 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2154 crumbled_border_size_var >= TILESIZE_VAR / 3)
2157 /* correct corners of crumbled border, if needed */
2159 for (i = -1; i <= 1; i += 2)
2161 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2162 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2163 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2166 /* check if neighbour field is of same crumble type */
2167 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2168 graphic_info[graphic].class ==
2169 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2171 /* no crumbled corner, but continued crumbled border */
2173 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2174 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2175 int b1 = (i == 1 ? crumbled_border_size_var :
2176 TILESIZE_VAR - 2 * crumbled_border_size_var);
2178 width = crumbled_border_size_var;
2179 height = crumbled_border_size_var;
2181 if (dir == 1 || dir == 2)
2196 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2198 FX + sx * TILEX_VAR + cx,
2199 FY + sy * TILEY_VAR + cy);
2204 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2206 int sx = SCREENX(x), sy = SCREENY(y);
2209 static int xy[4][2] =
2217 if (!IN_LEV_FIELD(x, y))
2220 element = TILE_GFX_ELEMENT(x, y);
2222 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2224 if (!IN_SCR_FIELD(sx, sy))
2227 /* crumble field borders towards direct neighbour fields */
2228 for (i = 0; i < 4; i++)
2230 int xx = x + xy[i][0];
2231 int yy = y + xy[i][1];
2233 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2236 /* check if neighbour field is of same crumble type */
2237 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2238 graphic_info[graphic].class ==
2239 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2242 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2245 /* crumble inner field corners towards corner neighbour fields */
2246 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2247 graphic_info[graphic].anim_frames == 2)
2249 for (i = 0; i < 4; i++)
2251 int dx = (i & 1 ? +1 : -1);
2252 int dy = (i & 2 ? +1 : -1);
2254 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2258 MarkTileDirty(sx, sy);
2260 else /* center field is not crumbled -- crumble neighbour fields */
2262 /* crumble field borders of direct neighbour fields */
2263 for (i = 0; i < 4; i++)
2265 int xx = x + xy[i][0];
2266 int yy = y + xy[i][1];
2267 int sxx = sx + xy[i][0];
2268 int syy = sy + xy[i][1];
2270 if (!IN_LEV_FIELD(xx, yy) ||
2271 !IN_SCR_FIELD(sxx, syy))
2274 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2277 element = TILE_GFX_ELEMENT(xx, yy);
2279 if (!IS_CRUMBLED_TILE(xx, yy, element))
2282 graphic = el_act2crm(element, ACTION_DEFAULT);
2284 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2286 MarkTileDirty(sxx, syy);
2289 /* crumble inner field corners of corner neighbour fields */
2290 for (i = 0; i < 4; i++)
2292 int dx = (i & 1 ? +1 : -1);
2293 int dy = (i & 2 ? +1 : -1);
2299 if (!IN_LEV_FIELD(xx, yy) ||
2300 !IN_SCR_FIELD(sxx, syy))
2303 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2306 element = TILE_GFX_ELEMENT(xx, yy);
2308 if (!IS_CRUMBLED_TILE(xx, yy, element))
2311 graphic = el_act2crm(element, ACTION_DEFAULT);
2313 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2314 graphic_info[graphic].anim_frames == 2)
2315 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2317 MarkTileDirty(sxx, syy);
2322 void DrawLevelFieldCrumbled(int x, int y)
2326 if (!IN_LEV_FIELD(x, y))
2329 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2330 GfxElement[x][y] != EL_UNDEFINED &&
2331 GFX_CRUMBLED(GfxElement[x][y]))
2333 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2338 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2340 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2343 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2346 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2347 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2348 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2349 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2350 int sx = SCREENX(x), sy = SCREENY(y);
2352 DrawGraphic(sx, sy, graphic1, frame1);
2353 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2356 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2358 int sx = SCREENX(x), sy = SCREENY(y);
2359 static int xy[4][2] =
2368 /* crumble direct neighbour fields (required for field borders) */
2369 for (i = 0; i < 4; i++)
2371 int xx = x + xy[i][0];
2372 int yy = y + xy[i][1];
2373 int sxx = sx + xy[i][0];
2374 int syy = sy + xy[i][1];
2376 if (!IN_LEV_FIELD(xx, yy) ||
2377 !IN_SCR_FIELD(sxx, syy) ||
2378 !GFX_CRUMBLED(Feld[xx][yy]) ||
2382 DrawLevelField(xx, yy);
2385 /* crumble corner neighbour fields (required for inner field corners) */
2386 for (i = 0; i < 4; i++)
2388 int dx = (i & 1 ? +1 : -1);
2389 int dy = (i & 2 ? +1 : -1);
2395 if (!IN_LEV_FIELD(xx, yy) ||
2396 !IN_SCR_FIELD(sxx, syy) ||
2397 !GFX_CRUMBLED(Feld[xx][yy]) ||
2401 int element = TILE_GFX_ELEMENT(xx, yy);
2402 int graphic = el_act2crm(element, ACTION_DEFAULT);
2404 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2405 graphic_info[graphic].anim_frames == 2)
2406 DrawLevelField(xx, yy);
2410 static int getBorderElement(int x, int y)
2414 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2415 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2416 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2417 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2418 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2419 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2420 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2422 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2423 int steel_position = (x == -1 && y == -1 ? 0 :
2424 x == lev_fieldx && y == -1 ? 1 :
2425 x == -1 && y == lev_fieldy ? 2 :
2426 x == lev_fieldx && y == lev_fieldy ? 3 :
2427 x == -1 || x == lev_fieldx ? 4 :
2428 y == -1 || y == lev_fieldy ? 5 : 6);
2430 return border[steel_position][steel_type];
2433 void DrawScreenElement(int x, int y, int element)
2435 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2436 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2439 void DrawLevelElement(int x, int y, int element)
2441 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2442 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2445 void DrawScreenField(int x, int y)
2447 int lx = LEVELX(x), ly = LEVELY(y);
2448 int element, content;
2450 if (!IN_LEV_FIELD(lx, ly))
2452 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2455 element = getBorderElement(lx, ly);
2457 DrawScreenElement(x, y, element);
2462 element = Feld[lx][ly];
2463 content = Store[lx][ly];
2465 if (IS_MOVING(lx, ly))
2467 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2468 boolean cut_mode = NO_CUTTING;
2470 if (element == EL_QUICKSAND_EMPTYING ||
2471 element == EL_QUICKSAND_FAST_EMPTYING ||
2472 element == EL_MAGIC_WALL_EMPTYING ||
2473 element == EL_BD_MAGIC_WALL_EMPTYING ||
2474 element == EL_DC_MAGIC_WALL_EMPTYING ||
2475 element == EL_AMOEBA_DROPPING)
2476 cut_mode = CUT_ABOVE;
2477 else if (element == EL_QUICKSAND_FILLING ||
2478 element == EL_QUICKSAND_FAST_FILLING ||
2479 element == EL_MAGIC_WALL_FILLING ||
2480 element == EL_BD_MAGIC_WALL_FILLING ||
2481 element == EL_DC_MAGIC_WALL_FILLING)
2482 cut_mode = CUT_BELOW;
2484 if (cut_mode == CUT_ABOVE)
2485 DrawScreenElement(x, y, element);
2487 DrawScreenElement(x, y, EL_EMPTY);
2490 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2491 else if (cut_mode == NO_CUTTING)
2492 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2495 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2497 if (cut_mode == CUT_BELOW &&
2498 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2499 DrawLevelElement(lx, ly + 1, element);
2502 if (content == EL_ACID)
2504 int dir = MovDir[lx][ly];
2505 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2506 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2508 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2510 // prevent target field from being drawn again (but without masking)
2511 // (this would happen if target field is scanned after moving element)
2512 Stop[newlx][newly] = TRUE;
2515 else if (IS_BLOCKED(lx, ly))
2520 boolean cut_mode = NO_CUTTING;
2521 int element_old, content_old;
2523 Blocked2Moving(lx, ly, &oldx, &oldy);
2526 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2527 MovDir[oldx][oldy] == MV_RIGHT);
2529 element_old = Feld[oldx][oldy];
2530 content_old = Store[oldx][oldy];
2532 if (element_old == EL_QUICKSAND_EMPTYING ||
2533 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2534 element_old == EL_MAGIC_WALL_EMPTYING ||
2535 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2536 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2537 element_old == EL_AMOEBA_DROPPING)
2538 cut_mode = CUT_ABOVE;
2540 DrawScreenElement(x, y, EL_EMPTY);
2543 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2545 else if (cut_mode == NO_CUTTING)
2546 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2549 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2552 else if (IS_DRAWABLE(element))
2553 DrawScreenElement(x, y, element);
2555 DrawScreenElement(x, y, EL_EMPTY);
2558 void DrawLevelField(int x, int y)
2560 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2561 DrawScreenField(SCREENX(x), SCREENY(y));
2562 else if (IS_MOVING(x, y))
2566 Moving2Blocked(x, y, &newx, &newy);
2567 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2568 DrawScreenField(SCREENX(newx), SCREENY(newy));
2570 else if (IS_BLOCKED(x, y))
2574 Blocked2Moving(x, y, &oldx, &oldy);
2575 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2576 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2580 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2581 int (*el2img_function)(int), boolean masked,
2582 int element_bits_draw)
2584 int element_base = map_mm_wall_element(element);
2585 int element_bits = (IS_DF_WALL(element) ?
2586 element - EL_DF_WALL_START :
2587 IS_MM_WALL(element) ?
2588 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2589 int graphic = el2img_function(element_base);
2590 int tilesize_draw = tilesize / 2;
2595 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2597 for (i = 0; i < 4; i++)
2599 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2600 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2602 if (!(element_bits_draw & (1 << i)))
2605 if (element_bits & (1 << i))
2608 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2609 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2611 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2612 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2617 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2618 tilesize_draw, tilesize_draw);
2623 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2624 boolean masked, int element_bits_draw)
2626 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2627 element, tilesize, el2edimg, masked, element_bits_draw);
2630 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2631 int (*el2img_function)(int))
2633 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2637 void DrawSizedElementExt(int x, int y, int element, int tilesize,
2640 if (IS_MM_WALL(element))
2642 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2643 element, tilesize, el2edimg, masked, 0x000f);
2647 int graphic = el2edimg(element);
2650 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2652 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2656 void DrawSizedElement(int x, int y, int element, int tilesize)
2658 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2661 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2663 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2666 void DrawMiniElement(int x, int y, int element)
2670 graphic = el2edimg(element);
2671 DrawMiniGraphic(x, y, graphic);
2674 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2677 int x = sx + scroll_x, y = sy + scroll_y;
2679 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2680 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2681 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2682 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2684 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2687 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2689 int x = sx + scroll_x, y = sy + scroll_y;
2691 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2692 DrawMiniElement(sx, sy, EL_EMPTY);
2693 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2694 DrawMiniElement(sx, sy, Feld[x][y]);
2696 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2699 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2700 int x, int y, int xsize, int ysize,
2701 int tile_width, int tile_height)
2705 int dst_x = startx + x * tile_width;
2706 int dst_y = starty + y * tile_height;
2707 int width = graphic_info[graphic].width;
2708 int height = graphic_info[graphic].height;
2709 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2710 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2711 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2712 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2713 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2714 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2715 boolean draw_masked = graphic_info[graphic].draw_masked;
2717 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2719 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2721 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2725 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2726 inner_sx + (x - 1) * tile_width % inner_width);
2727 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2728 inner_sy + (y - 1) * tile_height % inner_height);
2731 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2734 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2738 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2739 int x, int y, int xsize, int ysize, int font_nr)
2741 int font_width = getFontWidth(font_nr);
2742 int font_height = getFontHeight(font_nr);
2744 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2745 font_width, font_height);
2748 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2750 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2751 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2752 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2753 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2754 boolean no_delay = (tape.warp_forward);
2755 unsigned int anim_delay = 0;
2756 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2757 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2758 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2759 int font_width = getFontWidth(font_nr);
2760 int font_height = getFontHeight(font_nr);
2761 int max_xsize = level.envelope[envelope_nr].xsize;
2762 int max_ysize = level.envelope[envelope_nr].ysize;
2763 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2764 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2765 int xend = max_xsize;
2766 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2767 int xstep = (xstart < xend ? 1 : 0);
2768 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2770 int end = MAX(xend - xstart, yend - ystart);
2773 for (i = start; i <= end; i++)
2775 int last_frame = end; // last frame of this "for" loop
2776 int x = xstart + i * xstep;
2777 int y = ystart + i * ystep;
2778 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2779 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2780 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2781 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2784 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2786 BlitScreenToBitmap(backbuffer);
2788 SetDrawtoField(DRAW_TO_BACKBUFFER);
2790 for (yy = 0; yy < ysize; yy++)
2791 for (xx = 0; xx < xsize; xx++)
2792 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2794 DrawTextBuffer(sx + font_width, sy + font_height,
2795 level.envelope[envelope_nr].text, font_nr, max_xsize,
2796 xsize - 2, ysize - 2, 0, mask_mode,
2797 level.envelope[envelope_nr].autowrap,
2798 level.envelope[envelope_nr].centered, FALSE);
2800 redraw_mask |= REDRAW_FIELD;
2803 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2809 void ShowEnvelope(int envelope_nr)
2811 int element = EL_ENVELOPE_1 + envelope_nr;
2812 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2813 int sound_opening = element_info[element].sound[ACTION_OPENING];
2814 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2815 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2816 boolean no_delay = (tape.warp_forward);
2817 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2818 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2819 int anim_mode = graphic_info[graphic].anim_mode;
2820 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2821 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2823 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2825 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2827 if (anim_mode == ANIM_DEFAULT)
2828 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2830 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2833 Delay(wait_delay_value);
2835 WaitForEventToContinue();
2837 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2839 if (anim_mode != ANIM_NONE)
2840 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2842 if (anim_mode == ANIM_DEFAULT)
2843 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2845 game.envelope_active = FALSE;
2847 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2849 redraw_mask |= REDRAW_FIELD;
2853 static void setRequestBasePosition(int *x, int *y)
2855 int sx_base, sy_base;
2857 if (request.x != -1)
2858 sx_base = request.x;
2859 else if (request.align == ALIGN_LEFT)
2861 else if (request.align == ALIGN_RIGHT)
2862 sx_base = SX + SXSIZE;
2864 sx_base = SX + SXSIZE / 2;
2866 if (request.y != -1)
2867 sy_base = request.y;
2868 else if (request.valign == VALIGN_TOP)
2870 else if (request.valign == VALIGN_BOTTOM)
2871 sy_base = SY + SYSIZE;
2873 sy_base = SY + SYSIZE / 2;
2879 static void setRequestPositionExt(int *x, int *y, int width, int height,
2880 boolean add_border_size)
2882 int border_size = request.border_size;
2883 int sx_base, sy_base;
2886 setRequestBasePosition(&sx_base, &sy_base);
2888 if (request.align == ALIGN_LEFT)
2890 else if (request.align == ALIGN_RIGHT)
2891 sx = sx_base - width;
2893 sx = sx_base - width / 2;
2895 if (request.valign == VALIGN_TOP)
2897 else if (request.valign == VALIGN_BOTTOM)
2898 sy = sy_base - height;
2900 sy = sy_base - height / 2;
2902 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2903 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2905 if (add_border_size)
2915 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2917 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2920 void DrawEnvelopeRequest(char *text)
2922 char *text_final = text;
2923 char *text_door_style = NULL;
2924 int graphic = IMG_BACKGROUND_REQUEST;
2925 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2926 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2927 int font_nr = FONT_REQUEST;
2928 int font_width = getFontWidth(font_nr);
2929 int font_height = getFontHeight(font_nr);
2930 int border_size = request.border_size;
2931 int line_spacing = request.line_spacing;
2932 int line_height = font_height + line_spacing;
2933 int max_text_width = request.width - 2 * border_size;
2934 int max_text_height = request.height - 2 * border_size;
2935 int line_length = max_text_width / font_width;
2936 int max_lines = max_text_height / line_height;
2937 int text_width = line_length * font_width;
2938 int width = request.width;
2939 int height = request.height;
2940 int tile_size = MAX(request.step_offset, 1);
2941 int x_steps = width / tile_size;
2942 int y_steps = height / tile_size;
2943 int sx_offset = border_size;
2944 int sy_offset = border_size;
2948 if (request.centered)
2949 sx_offset = (request.width - text_width) / 2;
2951 if (request.wrap_single_words && !request.autowrap)
2953 char *src_text_ptr, *dst_text_ptr;
2955 text_door_style = checked_malloc(2 * strlen(text) + 1);
2957 src_text_ptr = text;
2958 dst_text_ptr = text_door_style;
2960 while (*src_text_ptr)
2962 if (*src_text_ptr == ' ' ||
2963 *src_text_ptr == '?' ||
2964 *src_text_ptr == '!')
2965 *dst_text_ptr++ = '\n';
2967 if (*src_text_ptr != ' ')
2968 *dst_text_ptr++ = *src_text_ptr;
2973 *dst_text_ptr = '\0';
2975 text_final = text_door_style;
2978 setRequestPosition(&sx, &sy, FALSE);
2980 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2982 for (y = 0; y < y_steps; y++)
2983 for (x = 0; x < x_steps; x++)
2984 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2985 x, y, x_steps, y_steps,
2986 tile_size, tile_size);
2988 /* force DOOR font inside door area */
2989 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2991 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2992 line_length, -1, max_lines, line_spacing, mask_mode,
2993 request.autowrap, request.centered, FALSE);
2997 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2998 RedrawGadget(tool_gadget[i]);
3000 // store readily prepared envelope request for later use when animating
3001 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3003 if (text_door_style)
3004 free(text_door_style);
3007 void AnimateEnvelopeRequest(int anim_mode, int action)
3009 int graphic = IMG_BACKGROUND_REQUEST;
3010 boolean draw_masked = graphic_info[graphic].draw_masked;
3011 int delay_value_normal = request.step_delay;
3012 int delay_value_fast = delay_value_normal / 2;
3013 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3014 boolean no_delay = (tape.warp_forward);
3015 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3016 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3017 unsigned int anim_delay = 0;
3019 int tile_size = MAX(request.step_offset, 1);
3020 int max_xsize = request.width / tile_size;
3021 int max_ysize = request.height / tile_size;
3022 int max_xsize_inner = max_xsize - 2;
3023 int max_ysize_inner = max_ysize - 2;
3025 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3026 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3027 int xend = max_xsize_inner;
3028 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3029 int xstep = (xstart < xend ? 1 : 0);
3030 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3032 int end = MAX(xend - xstart, yend - ystart);
3035 if (setup.quick_doors)
3042 for (i = start; i <= end; i++)
3044 int last_frame = end; // last frame of this "for" loop
3045 int x = xstart + i * xstep;
3046 int y = ystart + i * ystep;
3047 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3048 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3049 int xsize_size_left = (xsize - 1) * tile_size;
3050 int ysize_size_top = (ysize - 1) * tile_size;
3051 int max_xsize_pos = (max_xsize - 1) * tile_size;
3052 int max_ysize_pos = (max_ysize - 1) * tile_size;
3053 int width = xsize * tile_size;
3054 int height = ysize * tile_size;
3059 setRequestPosition(&src_x, &src_y, FALSE);
3060 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3062 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3064 for (yy = 0; yy < 2; yy++)
3066 for (xx = 0; xx < 2; xx++)
3068 int src_xx = src_x + xx * max_xsize_pos;
3069 int src_yy = src_y + yy * max_ysize_pos;
3070 int dst_xx = dst_x + xx * xsize_size_left;
3071 int dst_yy = dst_y + yy * ysize_size_top;
3072 int xx_size = (xx ? tile_size : xsize_size_left);
3073 int yy_size = (yy ? tile_size : ysize_size_top);
3076 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3077 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3079 BlitBitmap(bitmap_db_store_2, backbuffer,
3080 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3084 redraw_mask |= REDRAW_FIELD;
3088 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3094 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3096 int graphic = IMG_BACKGROUND_REQUEST;
3097 int sound_opening = SND_REQUEST_OPENING;
3098 int sound_closing = SND_REQUEST_CLOSING;
3099 int anim_mode_1 = request.anim_mode; /* (higher priority) */
3100 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
3101 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3102 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3103 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3105 if (game_status == GAME_MODE_PLAYING)
3106 BlitScreenToBitmap(backbuffer);
3108 SetDrawtoField(DRAW_TO_BACKBUFFER);
3110 // SetDrawBackgroundMask(REDRAW_NONE);
3112 if (action == ACTION_OPENING)
3114 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3116 if (req_state & REQ_ASK)
3118 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3119 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3121 else if (req_state & REQ_CONFIRM)
3123 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3125 else if (req_state & REQ_PLAYER)
3127 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3128 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3129 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3130 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3133 DrawEnvelopeRequest(text);
3136 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
3138 if (action == ACTION_OPENING)
3140 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3142 if (anim_mode == ANIM_DEFAULT)
3143 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3145 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3149 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3151 if (anim_mode != ANIM_NONE)
3152 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3154 if (anim_mode == ANIM_DEFAULT)
3155 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3158 game.envelope_active = FALSE;
3160 if (action == ACTION_CLOSING)
3161 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3163 // SetDrawBackgroundMask(last_draw_background_mask);
3165 redraw_mask |= REDRAW_FIELD;
3169 if (action == ACTION_CLOSING &&
3170 game_status == GAME_MODE_PLAYING &&
3171 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3172 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3175 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3177 if (IS_MM_WALL(element))
3179 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3185 int graphic = el2preimg(element);
3187 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3188 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3193 void DrawLevel(int draw_background_mask)
3197 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3198 SetDrawBackgroundMask(draw_background_mask);
3202 for (x = BX1; x <= BX2; x++)
3203 for (y = BY1; y <= BY2; y++)
3204 DrawScreenField(x, y);
3206 redraw_mask |= REDRAW_FIELD;
3209 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3214 for (x = 0; x < size_x; x++)
3215 for (y = 0; y < size_y; y++)
3216 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3218 redraw_mask |= REDRAW_FIELD;
3221 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3225 for (x = 0; x < size_x; x++)
3226 for (y = 0; y < size_y; y++)
3227 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3229 redraw_mask |= REDRAW_FIELD;
3232 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3234 boolean show_level_border = (BorderElement != EL_EMPTY);
3235 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3236 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3237 int tile_size = preview.tile_size;
3238 int preview_width = preview.xsize * tile_size;
3239 int preview_height = preview.ysize * tile_size;
3240 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3241 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3242 int real_preview_width = real_preview_xsize * tile_size;
3243 int real_preview_height = real_preview_ysize * tile_size;
3244 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3245 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3248 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3251 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3253 dst_x += (preview_width - real_preview_width) / 2;
3254 dst_y += (preview_height - real_preview_height) / 2;
3256 for (x = 0; x < real_preview_xsize; x++)
3258 for (y = 0; y < real_preview_ysize; y++)
3260 int lx = from_x + x + (show_level_border ? -1 : 0);
3261 int ly = from_y + y + (show_level_border ? -1 : 0);
3262 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3263 getBorderElement(lx, ly));
3265 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3266 element, tile_size);
3270 redraw_mask |= REDRAW_FIELD;
3273 #define MICROLABEL_EMPTY 0
3274 #define MICROLABEL_LEVEL_NAME 1
3275 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3276 #define MICROLABEL_LEVEL_AUTHOR 3
3277 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3278 #define MICROLABEL_IMPORTED_FROM 5
3279 #define MICROLABEL_IMPORTED_BY_HEAD 6
3280 #define MICROLABEL_IMPORTED_BY 7
3282 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3284 int max_text_width = SXSIZE;
3285 int font_width = getFontWidth(font_nr);
3287 if (pos->align == ALIGN_CENTER)
3288 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3289 else if (pos->align == ALIGN_RIGHT)
3290 max_text_width = pos->x;
3292 max_text_width = SXSIZE - pos->x;
3294 return max_text_width / font_width;
3297 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3299 char label_text[MAX_OUTPUT_LINESIZE + 1];
3300 int max_len_label_text;
3301 int font_nr = pos->font;
3304 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3307 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3308 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3309 mode == MICROLABEL_IMPORTED_BY_HEAD)
3310 font_nr = pos->font_alt;
3312 max_len_label_text = getMaxTextLength(pos, font_nr);
3314 if (pos->size != -1)
3315 max_len_label_text = pos->size;
3317 for (i = 0; i < max_len_label_text; i++)
3318 label_text[i] = ' ';
3319 label_text[max_len_label_text] = '\0';
3321 if (strlen(label_text) > 0)
3322 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3325 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3326 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3327 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3328 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3329 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3330 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3331 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3332 max_len_label_text);
3333 label_text[max_len_label_text] = '\0';
3335 if (strlen(label_text) > 0)
3336 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3338 redraw_mask |= REDRAW_FIELD;
3341 static void DrawPreviewLevelLabel(int mode)
3343 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3346 static void DrawPreviewLevelInfo(int mode)
3348 if (mode == MICROLABEL_LEVEL_NAME)
3349 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3350 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3351 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3354 static void DrawPreviewLevelExt(boolean restart)
3356 static unsigned int scroll_delay = 0;
3357 static unsigned int label_delay = 0;
3358 static int from_x, from_y, scroll_direction;
3359 static int label_state, label_counter;
3360 unsigned int scroll_delay_value = preview.step_delay;
3361 boolean show_level_border = (BorderElement != EL_EMPTY);
3362 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3363 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3370 if (preview.anim_mode == ANIM_CENTERED)
3372 if (level_xsize > preview.xsize)
3373 from_x = (level_xsize - preview.xsize) / 2;
3374 if (level_ysize > preview.ysize)
3375 from_y = (level_ysize - preview.ysize) / 2;
3378 from_x += preview.xoffset;
3379 from_y += preview.yoffset;
3381 scroll_direction = MV_RIGHT;
3385 DrawPreviewLevelPlayfield(from_x, from_y);
3386 DrawPreviewLevelLabel(label_state);
3388 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3389 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3391 /* initialize delay counters */
3392 DelayReached(&scroll_delay, 0);
3393 DelayReached(&label_delay, 0);
3395 if (leveldir_current->name)
3397 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3398 char label_text[MAX_OUTPUT_LINESIZE + 1];
3399 int font_nr = pos->font;
3400 int max_len_label_text = getMaxTextLength(pos, font_nr);
3402 if (pos->size != -1)
3403 max_len_label_text = pos->size;
3405 strncpy(label_text, leveldir_current->name, max_len_label_text);
3406 label_text[max_len_label_text] = '\0';
3408 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3409 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3415 /* scroll preview level, if needed */
3416 if (preview.anim_mode != ANIM_NONE &&
3417 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3418 DelayReached(&scroll_delay, scroll_delay_value))
3420 switch (scroll_direction)
3425 from_x -= preview.step_offset;
3426 from_x = (from_x < 0 ? 0 : from_x);
3429 scroll_direction = MV_UP;
3433 if (from_x < level_xsize - preview.xsize)
3435 from_x += preview.step_offset;
3436 from_x = (from_x > level_xsize - preview.xsize ?
3437 level_xsize - preview.xsize : from_x);
3440 scroll_direction = MV_DOWN;
3446 from_y -= preview.step_offset;
3447 from_y = (from_y < 0 ? 0 : from_y);
3450 scroll_direction = MV_RIGHT;
3454 if (from_y < level_ysize - preview.ysize)
3456 from_y += preview.step_offset;
3457 from_y = (from_y > level_ysize - preview.ysize ?
3458 level_ysize - preview.ysize : from_y);
3461 scroll_direction = MV_LEFT;
3468 DrawPreviewLevelPlayfield(from_x, from_y);
3471 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3472 /* redraw micro level label, if needed */
3473 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3474 !strEqual(level.author, ANONYMOUS_NAME) &&
3475 !strEqual(level.author, leveldir_current->name) &&
3476 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3478 int max_label_counter = 23;
3480 if (leveldir_current->imported_from != NULL &&
3481 strlen(leveldir_current->imported_from) > 0)
3482 max_label_counter += 14;
3483 if (leveldir_current->imported_by != NULL &&
3484 strlen(leveldir_current->imported_by) > 0)
3485 max_label_counter += 14;
3487 label_counter = (label_counter + 1) % max_label_counter;
3488 label_state = (label_counter >= 0 && label_counter <= 7 ?
3489 MICROLABEL_LEVEL_NAME :
3490 label_counter >= 9 && label_counter <= 12 ?
3491 MICROLABEL_LEVEL_AUTHOR_HEAD :
3492 label_counter >= 14 && label_counter <= 21 ?
3493 MICROLABEL_LEVEL_AUTHOR :
3494 label_counter >= 23 && label_counter <= 26 ?
3495 MICROLABEL_IMPORTED_FROM_HEAD :
3496 label_counter >= 28 && label_counter <= 35 ?
3497 MICROLABEL_IMPORTED_FROM :
3498 label_counter >= 37 && label_counter <= 40 ?
3499 MICROLABEL_IMPORTED_BY_HEAD :
3500 label_counter >= 42 && label_counter <= 49 ?
3501 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3503 if (leveldir_current->imported_from == NULL &&
3504 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3505 label_state == MICROLABEL_IMPORTED_FROM))
3506 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3507 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3509 DrawPreviewLevelLabel(label_state);
3513 void DrawPreviewPlayers()
3515 if (game_status != GAME_MODE_MAIN)
3518 if (!network.enabled && !setup.team_mode)
3521 boolean player_found[MAX_PLAYERS];
3522 int num_players = 0;
3525 for (i = 0; i < MAX_PLAYERS; i++)
3526 player_found[i] = FALSE;
3528 /* check which players can be found in the level (simple approach) */
3529 for (x = 0; x < lev_fieldx; x++)
3531 for (y = 0; y < lev_fieldy; y++)
3533 int element = level.field[x][y];
3535 if (ELEM_IS_PLAYER(element))
3537 int player_nr = GET_PLAYER_NR(element);
3539 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3541 if (!player_found[player_nr])
3544 player_found[player_nr] = TRUE;
3549 struct TextPosInfo *pos = &menu.main.preview_players;
3550 int tile_size = pos->tile_size;
3551 int border_size = pos->border_size;
3552 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3553 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3554 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3555 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3556 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3557 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3558 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3559 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3560 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3561 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3562 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3563 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3565 /* clear area in which the players will be drawn */
3566 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3567 max_players_width, max_players_height);
3569 /* only draw players if level is suited for team mode */
3570 if (num_players < 2)
3573 /* draw all players that were found in the level */
3574 for (i = 0; i < MAX_PLAYERS; i++)
3576 if (player_found[i])
3578 int graphic = el2img(EL_PLAYER_1 + i);
3580 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3582 xpos += player_xoffset;
3583 ypos += player_yoffset;
3588 void DrawPreviewLevelInitial()
3590 DrawPreviewLevelExt(TRUE);
3591 DrawPreviewPlayers();
3594 void DrawPreviewLevelAnimation()
3596 DrawPreviewLevelExt(FALSE);
3599 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3600 int border_size, int font_nr)
3602 int graphic = el2img(EL_PLAYER_1 + player_nr);
3603 int font_height = getFontHeight(font_nr);
3604 int player_height = MAX(tile_size, font_height);
3605 int xoffset_text = tile_size + border_size;
3606 int yoffset_text = (player_height - font_height) / 2;
3607 int yoffset_graphic = (player_height - tile_size) / 2;
3608 char *player_name = getNetworkPlayerName(player_nr + 1);
3610 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3612 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3615 void DrawNetworkPlayersExt(boolean force)
3617 if (game_status != GAME_MODE_MAIN)
3620 if (!network.connected && !force)
3623 int num_players = 0;
3626 for (i = 0; i < MAX_PLAYERS; i++)
3627 if (stored_player[i].connected_network)
3630 struct TextPosInfo *pos = &menu.main.network_players;
3631 int tile_size = pos->tile_size;
3632 int border_size = pos->border_size;
3633 int xoffset_text = tile_size + border_size;
3634 int font_nr = pos->font;
3635 int font_width = getFontWidth(font_nr);
3636 int font_height = getFontHeight(font_nr);
3637 int player_height = MAX(tile_size, font_height);
3638 int player_yoffset = player_height + border_size;
3639 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3640 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3641 int all_players_height = num_players * player_yoffset - border_size;
3642 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3643 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3644 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3646 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3647 max_players_width, max_players_height);
3649 /* first draw local network player ... */
3650 for (i = 0; i < MAX_PLAYERS; i++)
3652 if (stored_player[i].connected_network &&
3653 stored_player[i].connected_locally)
3655 char *player_name = getNetworkPlayerName(i + 1);
3656 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3657 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3659 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3661 ypos += player_yoffset;
3665 /* ... then draw all other network players */
3666 for (i = 0; i < MAX_PLAYERS; i++)
3668 if (stored_player[i].connected_network &&
3669 !stored_player[i].connected_locally)
3671 char *player_name = getNetworkPlayerName(i + 1);
3672 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3673 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3675 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3677 ypos += player_yoffset;
3682 void DrawNetworkPlayers()
3684 DrawNetworkPlayersExt(FALSE);
3687 void ClearNetworkPlayers()
3689 DrawNetworkPlayersExt(TRUE);
3692 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3693 int graphic, int sync_frame,
3696 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3698 if (mask_mode == USE_MASKING)
3699 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3701 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3704 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3705 int graphic, int sync_frame, int mask_mode)
3707 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3709 if (mask_mode == USE_MASKING)
3710 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3712 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3715 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3717 int lx = LEVELX(x), ly = LEVELY(y);
3719 if (!IN_SCR_FIELD(x, y))
3722 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3723 graphic, GfxFrame[lx][ly], NO_MASKING);
3725 MarkTileDirty(x, y);
3728 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3730 int lx = LEVELX(x), ly = LEVELY(y);
3732 if (!IN_SCR_FIELD(x, y))
3735 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3736 graphic, GfxFrame[lx][ly], NO_MASKING);
3737 MarkTileDirty(x, y);
3740 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3742 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3745 void DrawLevelElementAnimation(int x, int y, int element)
3747 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3749 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3752 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3754 int sx = SCREENX(x), sy = SCREENY(y);
3756 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3759 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3762 DrawGraphicAnimation(sx, sy, graphic);
3765 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3766 DrawLevelFieldCrumbled(x, y);
3768 if (GFX_CRUMBLED(Feld[x][y]))
3769 DrawLevelFieldCrumbled(x, y);
3773 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3775 int sx = SCREENX(x), sy = SCREENY(y);
3778 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3781 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3783 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3786 DrawGraphicAnimation(sx, sy, graphic);
3788 if (GFX_CRUMBLED(element))
3789 DrawLevelFieldCrumbled(x, y);
3792 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3794 if (player->use_murphy)
3796 /* this works only because currently only one player can be "murphy" ... */
3797 static int last_horizontal_dir = MV_LEFT;
3798 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3800 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3801 last_horizontal_dir = move_dir;
3803 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3805 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3807 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3813 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3816 static boolean equalGraphics(int graphic1, int graphic2)
3818 struct GraphicInfo *g1 = &graphic_info[graphic1];
3819 struct GraphicInfo *g2 = &graphic_info[graphic2];
3821 return (g1->bitmap == g2->bitmap &&
3822 g1->src_x == g2->src_x &&
3823 g1->src_y == g2->src_y &&
3824 g1->anim_frames == g2->anim_frames &&
3825 g1->anim_delay == g2->anim_delay &&
3826 g1->anim_mode == g2->anim_mode);
3829 void DrawAllPlayers()
3833 for (i = 0; i < MAX_PLAYERS; i++)
3834 if (stored_player[i].active)
3835 DrawPlayer(&stored_player[i]);
3838 void DrawPlayerField(int x, int y)
3840 if (!IS_PLAYER(x, y))
3843 DrawPlayer(PLAYERINFO(x, y));
3846 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3848 void DrawPlayer(struct PlayerInfo *player)
3850 int jx = player->jx;
3851 int jy = player->jy;
3852 int move_dir = player->MovDir;
3853 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3854 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3855 int last_jx = (player->is_moving ? jx - dx : jx);
3856 int last_jy = (player->is_moving ? jy - dy : jy);
3857 int next_jx = jx + dx;
3858 int next_jy = jy + dy;
3859 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3860 boolean player_is_opaque = FALSE;
3861 int sx = SCREENX(jx), sy = SCREENY(jy);
3862 int sxx = 0, syy = 0;
3863 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3865 int action = ACTION_DEFAULT;
3866 int last_player_graphic = getPlayerGraphic(player, move_dir);
3867 int last_player_frame = player->Frame;
3870 /* GfxElement[][] is set to the element the player is digging or collecting;
3871 remove also for off-screen player if the player is not moving anymore */
3872 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3873 GfxElement[jx][jy] = EL_UNDEFINED;
3875 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3879 if (!IN_LEV_FIELD(jx, jy))
3881 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3882 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3883 printf("DrawPlayerField(): This should never happen!\n");
3888 if (element == EL_EXPLOSION)
3891 action = (player->is_pushing ? ACTION_PUSHING :
3892 player->is_digging ? ACTION_DIGGING :
3893 player->is_collecting ? ACTION_COLLECTING :
3894 player->is_moving ? ACTION_MOVING :
3895 player->is_snapping ? ACTION_SNAPPING :
3896 player->is_dropping ? ACTION_DROPPING :
3897 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3899 if (player->is_waiting)
3900 move_dir = player->dir_waiting;
3902 InitPlayerGfxAnimation(player, action, move_dir);
3904 /* ----------------------------------------------------------------------- */
3905 /* draw things in the field the player is leaving, if needed */
3906 /* ----------------------------------------------------------------------- */
3908 if (player->is_moving)
3910 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3912 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3914 if (last_element == EL_DYNAMITE_ACTIVE ||
3915 last_element == EL_EM_DYNAMITE_ACTIVE ||
3916 last_element == EL_SP_DISK_RED_ACTIVE)
3917 DrawDynamite(last_jx, last_jy);
3919 DrawLevelFieldThruMask(last_jx, last_jy);
3921 else if (last_element == EL_DYNAMITE_ACTIVE ||
3922 last_element == EL_EM_DYNAMITE_ACTIVE ||
3923 last_element == EL_SP_DISK_RED_ACTIVE)
3924 DrawDynamite(last_jx, last_jy);
3926 /* !!! this is not enough to prevent flickering of players which are
3927 moving next to each others without a free tile between them -- this
3928 can only be solved by drawing all players layer by layer (first the
3929 background, then the foreground etc.) !!! => TODO */
3930 else if (!IS_PLAYER(last_jx, last_jy))
3931 DrawLevelField(last_jx, last_jy);
3934 DrawLevelField(last_jx, last_jy);
3937 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3938 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3941 if (!IN_SCR_FIELD(sx, sy))
3944 /* ----------------------------------------------------------------------- */
3945 /* draw things behind the player, if needed */
3946 /* ----------------------------------------------------------------------- */
3949 DrawLevelElement(jx, jy, Back[jx][jy]);
3950 else if (IS_ACTIVE_BOMB(element))
3951 DrawLevelElement(jx, jy, EL_EMPTY);
3954 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3956 int old_element = GfxElement[jx][jy];
3957 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3958 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3960 if (GFX_CRUMBLED(old_element))
3961 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3963 DrawGraphic(sx, sy, old_graphic, frame);
3965 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3966 player_is_opaque = TRUE;
3970 GfxElement[jx][jy] = EL_UNDEFINED;
3972 /* make sure that pushed elements are drawn with correct frame rate */
3973 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3975 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3976 GfxFrame[jx][jy] = player->StepFrame;
3978 DrawLevelField(jx, jy);
3982 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3983 /* ----------------------------------------------------------------------- */
3984 /* draw player himself */
3985 /* ----------------------------------------------------------------------- */
3987 graphic = getPlayerGraphic(player, move_dir);
3989 /* in the case of changed player action or direction, prevent the current
3990 animation frame from being restarted for identical animations */
3991 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3992 player->Frame = last_player_frame;
3994 frame = getGraphicAnimationFrame(graphic, player->Frame);
3998 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3999 sxx = player->GfxPos;
4001 syy = player->GfxPos;
4004 if (player_is_opaque)
4005 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4007 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4009 if (SHIELD_ON(player))
4011 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4012 IMG_SHIELD_NORMAL_ACTIVE);
4013 int frame = getGraphicAnimationFrame(graphic, -1);
4015 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4019 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4022 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4023 sxx = player->GfxPos;
4025 syy = player->GfxPos;
4029 /* ----------------------------------------------------------------------- */
4030 /* draw things the player is pushing, if needed */
4031 /* ----------------------------------------------------------------------- */
4033 if (player->is_pushing && player->is_moving)
4035 int px = SCREENX(jx), py = SCREENY(jy);
4036 int pxx = (TILEX - ABS(sxx)) * dx;
4037 int pyy = (TILEY - ABS(syy)) * dy;
4038 int gfx_frame = GfxFrame[jx][jy];
4044 if (!IS_MOVING(jx, jy)) /* push movement already finished */
4046 element = Feld[next_jx][next_jy];
4047 gfx_frame = GfxFrame[next_jx][next_jy];
4050 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4052 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4053 frame = getGraphicAnimationFrame(graphic, sync_frame);
4055 /* draw background element under pushed element (like the Sokoban field) */
4056 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4058 /* this allows transparent pushing animation over non-black background */
4061 DrawLevelElement(jx, jy, Back[jx][jy]);
4063 DrawLevelElement(jx, jy, EL_EMPTY);
4065 if (Back[next_jx][next_jy])
4066 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4068 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4070 else if (Back[next_jx][next_jy])
4071 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4074 /* do not draw (EM style) pushing animation when pushing is finished */
4075 /* (two-tile animations usually do not contain start and end frame) */
4076 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4077 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4079 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4081 /* masked drawing is needed for EMC style (double) movement graphics */
4082 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
4083 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4087 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4088 /* ----------------------------------------------------------------------- */
4089 /* draw player himself */
4090 /* ----------------------------------------------------------------------- */
4092 graphic = getPlayerGraphic(player, move_dir);
4094 /* in the case of changed player action or direction, prevent the current
4095 animation frame from being restarted for identical animations */
4096 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4097 player->Frame = last_player_frame;
4099 frame = getGraphicAnimationFrame(graphic, player->Frame);
4103 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4104 sxx = player->GfxPos;
4106 syy = player->GfxPos;
4109 if (player_is_opaque)
4110 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4112 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4114 if (SHIELD_ON(player))
4116 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4117 IMG_SHIELD_NORMAL_ACTIVE);
4118 int frame = getGraphicAnimationFrame(graphic, -1);
4120 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4124 /* ----------------------------------------------------------------------- */
4125 /* draw things in front of player (active dynamite or dynabombs) */
4126 /* ----------------------------------------------------------------------- */
4128 if (IS_ACTIVE_BOMB(element))
4130 graphic = el2img(element);
4131 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4133 if (game.emulation == EMU_SUPAPLEX)
4134 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4136 DrawGraphicThruMask(sx, sy, graphic, frame);
4139 if (player_is_moving && last_element == EL_EXPLOSION)
4141 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4142 GfxElement[last_jx][last_jy] : EL_EMPTY);
4143 int graphic = el_act2img(element, ACTION_EXPLODING);
4144 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4145 int phase = ExplodePhase[last_jx][last_jy] - 1;
4146 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4149 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4152 /* ----------------------------------------------------------------------- */
4153 /* draw elements the player is just walking/passing through/under */
4154 /* ----------------------------------------------------------------------- */
4156 if (player_is_moving)
4158 /* handle the field the player is leaving ... */
4159 if (IS_ACCESSIBLE_INSIDE(last_element))
4160 DrawLevelField(last_jx, last_jy);
4161 else if (IS_ACCESSIBLE_UNDER(last_element))
4162 DrawLevelFieldThruMask(last_jx, last_jy);
4165 /* do not redraw accessible elements if the player is just pushing them */
4166 if (!player_is_moving || !player->is_pushing)
4168 /* ... and the field the player is entering */
4169 if (IS_ACCESSIBLE_INSIDE(element))
4170 DrawLevelField(jx, jy);
4171 else if (IS_ACCESSIBLE_UNDER(element))
4172 DrawLevelFieldThruMask(jx, jy);
4175 MarkTileDirty(sx, sy);
4178 /* ------------------------------------------------------------------------- */
4180 void WaitForEventToContinue()
4182 boolean still_wait = TRUE;
4184 if (program.headless)
4187 /* simulate releasing mouse button over last gadget, if still pressed */
4189 HandleGadgets(-1, -1, 0);
4191 button_status = MB_RELEASED;
4199 if (NextValidEvent(&event))
4203 case EVENT_BUTTONPRESS:
4204 case EVENT_KEYPRESS:
4205 #if defined(TARGET_SDL2)
4206 case SDL_CONTROLLERBUTTONDOWN:
4208 case SDL_JOYBUTTONDOWN:
4212 case EVENT_KEYRELEASE:
4213 ClearPlayerAction();
4217 HandleOtherEvents(&event);
4221 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4230 #define MAX_REQUEST_LINES 13
4231 #define MAX_REQUEST_LINE_FONT1_LEN 7
4232 #define MAX_REQUEST_LINE_FONT2_LEN 10
4234 static int RequestHandleEvents(unsigned int req_state)
4236 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
4237 local_player->LevelSolved_GameEnd);
4238 int width = request.width;
4239 int height = request.height;
4243 setRequestPosition(&sx, &sy, FALSE);
4245 button_status = MB_RELEASED;
4247 request_gadget_id = -1;
4254 /* the MM game engine does not use a special (scrollable) field buffer */
4255 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4256 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4258 HandleGameActions();
4260 SetDrawtoField(DRAW_TO_BACKBUFFER);
4262 if (global.use_envelope_request)
4264 /* copy current state of request area to middle of playfield area */
4265 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4273 while (NextValidEvent(&event))
4277 case EVENT_BUTTONPRESS:
4278 case EVENT_BUTTONRELEASE:
4279 case EVENT_MOTIONNOTIFY:
4283 if (event.type == EVENT_MOTIONNOTIFY)
4288 motion_status = TRUE;
4289 mx = ((MotionEvent *) &event)->x;
4290 my = ((MotionEvent *) &event)->y;
4294 motion_status = FALSE;
4295 mx = ((ButtonEvent *) &event)->x;
4296 my = ((ButtonEvent *) &event)->y;
4297 if (event.type == EVENT_BUTTONPRESS)
4298 button_status = ((ButtonEvent *) &event)->button;
4300 button_status = MB_RELEASED;
4303 /* this sets 'request_gadget_id' */
4304 HandleGadgets(mx, my, button_status);
4306 switch (request_gadget_id)
4308 case TOOL_CTRL_ID_YES:
4311 case TOOL_CTRL_ID_NO:
4314 case TOOL_CTRL_ID_CONFIRM:
4315 result = TRUE | FALSE;
4318 case TOOL_CTRL_ID_PLAYER_1:
4321 case TOOL_CTRL_ID_PLAYER_2:
4324 case TOOL_CTRL_ID_PLAYER_3:
4327 case TOOL_CTRL_ID_PLAYER_4:
4338 #if defined(TARGET_SDL2)
4339 case SDL_WINDOWEVENT:
4340 HandleWindowEvent((WindowEvent *) &event);
4343 case SDL_APP_WILLENTERBACKGROUND:
4344 case SDL_APP_DIDENTERBACKGROUND:
4345 case SDL_APP_WILLENTERFOREGROUND:
4346 case SDL_APP_DIDENTERFOREGROUND:
4347 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4351 case EVENT_KEYPRESS:
4353 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4358 if (req_state & REQ_CONFIRM)
4364 #if defined(TARGET_SDL2)
4368 #if defined(KSYM_Rewind)
4369 case KSYM_Rewind: /* for Amazon Fire TV remote */
4377 #if defined(TARGET_SDL2)
4380 #if defined(KSYM_FastForward)
4381 case KSYM_FastForward: /* for Amazon Fire TV remote */
4388 HandleKeysDebug(key);
4392 if (req_state & REQ_PLAYER)
4394 int old_player_nr = setup.network_player_nr;
4397 result = old_player_nr + 1;
4402 result = old_player_nr + 1;
4433 case EVENT_KEYRELEASE:
4434 ClearPlayerAction();
4437 #if defined(TARGET_SDL2)
4438 case SDL_CONTROLLERBUTTONDOWN:
4439 switch (event.cbutton.button)
4441 case SDL_CONTROLLER_BUTTON_A:
4442 case SDL_CONTROLLER_BUTTON_X:
4443 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4444 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4448 case SDL_CONTROLLER_BUTTON_B:
4449 case SDL_CONTROLLER_BUTTON_Y:
4450 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4451 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4452 case SDL_CONTROLLER_BUTTON_BACK:
4457 if (req_state & REQ_PLAYER)
4459 int old_player_nr = setup.network_player_nr;
4462 result = old_player_nr + 1;
4464 switch (event.cbutton.button)
4466 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4467 case SDL_CONTROLLER_BUTTON_Y:
4471 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4472 case SDL_CONTROLLER_BUTTON_B:
4476 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4477 case SDL_CONTROLLER_BUTTON_A:
4481 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4482 case SDL_CONTROLLER_BUTTON_X:
4493 case SDL_CONTROLLERBUTTONUP:
4494 HandleJoystickEvent(&event);
4495 ClearPlayerAction();
4500 HandleOtherEvents(&event);
4505 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4507 int joy = AnyJoystick();
4509 if (joy & JOY_BUTTON_1)
4511 else if (joy & JOY_BUTTON_2)
4514 else if (AnyJoystick())
4516 int joy = AnyJoystick();
4518 if (req_state & REQ_PLAYER)
4522 else if (joy & JOY_RIGHT)
4524 else if (joy & JOY_DOWN)
4526 else if (joy & JOY_LEFT)
4533 if (global.use_envelope_request)
4535 /* copy back current state of pressed buttons inside request area */
4536 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4546 static boolean RequestDoor(char *text, unsigned int req_state)
4548 unsigned int old_door_state;
4549 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4550 int font_nr = FONT_TEXT_2;
4555 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4557 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4558 font_nr = FONT_TEXT_1;
4561 if (game_status == GAME_MODE_PLAYING)
4562 BlitScreenToBitmap(backbuffer);
4564 /* disable deactivated drawing when quick-loading level tape recording */
4565 if (tape.playing && tape.deactivate_display)
4566 TapeDeactivateDisplayOff(TRUE);
4568 SetMouseCursor(CURSOR_DEFAULT);
4570 /* pause network game while waiting for request to answer */
4571 if (network.enabled &&
4572 game_status == GAME_MODE_PLAYING &&
4574 req_state & REQUEST_WAIT_FOR_INPUT)
4575 SendToServer_PausePlaying();
4577 old_door_state = GetDoorState();
4579 /* simulate releasing mouse button over last gadget, if still pressed */
4581 HandleGadgets(-1, -1, 0);
4585 /* draw released gadget before proceeding */
4588 if (old_door_state & DOOR_OPEN_1)
4590 CloseDoor(DOOR_CLOSE_1);
4592 /* save old door content */
4593 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4594 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4597 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4598 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4600 /* clear door drawing field */
4601 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4603 /* force DOOR font inside door area */
4604 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4606 /* write text for request */
4607 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4609 char text_line[max_request_line_len + 1];
4615 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4617 tc = *(text_ptr + tx);
4618 // if (!tc || tc == ' ')
4619 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4623 if ((tc == '?' || tc == '!') && tl == 0)
4633 strncpy(text_line, text_ptr, tl);
4636 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4637 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4638 text_line, font_nr);
4640 text_ptr += tl + (tc == ' ' ? 1 : 0);
4641 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4646 if (req_state & REQ_ASK)
4648 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4649 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4651 else if (req_state & REQ_CONFIRM)
4653 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4655 else if (req_state & REQ_PLAYER)
4657 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4658 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4659 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4660 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4663 /* copy request gadgets to door backbuffer */
4664 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4666 OpenDoor(DOOR_OPEN_1);
4668 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4670 if (game_status == GAME_MODE_PLAYING)
4672 SetPanelBackground();
4673 SetDrawBackgroundMask(REDRAW_DOOR_1);
4677 SetDrawBackgroundMask(REDRAW_FIELD);
4683 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4685 // ---------- handle request buttons ----------
4686 result = RequestHandleEvents(req_state);
4690 if (!(req_state & REQ_STAY_OPEN))
4692 CloseDoor(DOOR_CLOSE_1);
4694 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4695 (req_state & REQ_REOPEN))
4696 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4701 if (game_status == GAME_MODE_PLAYING)
4703 SetPanelBackground();
4704 SetDrawBackgroundMask(REDRAW_DOOR_1);
4708 SetDrawBackgroundMask(REDRAW_FIELD);
4711 /* continue network game after request */
4712 if (network.enabled &&
4713 game_status == GAME_MODE_PLAYING &&
4715 req_state & REQUEST_WAIT_FOR_INPUT)
4716 SendToServer_ContinuePlaying();
4718 /* restore deactivated drawing when quick-loading level tape recording */
4719 if (tape.playing && tape.deactivate_display)
4720 TapeDeactivateDisplayOn();
4725 static boolean RequestEnvelope(char *text, unsigned int req_state)
4729 if (game_status == GAME_MODE_PLAYING)
4730 BlitScreenToBitmap(backbuffer);
4732 /* disable deactivated drawing when quick-loading level tape recording */
4733 if (tape.playing && tape.deactivate_display)
4734 TapeDeactivateDisplayOff(TRUE);
4736 SetMouseCursor(CURSOR_DEFAULT);
4738 /* pause network game while waiting for request to answer */
4739 if (network.enabled &&
4740 game_status == GAME_MODE_PLAYING &&
4742 req_state & REQUEST_WAIT_FOR_INPUT)
4743 SendToServer_PausePlaying();
4745 /* simulate releasing mouse button over last gadget, if still pressed */
4747 HandleGadgets(-1, -1, 0);
4751 // (replace with setting corresponding request background)
4752 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4753 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4755 /* clear door drawing field */
4756 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4758 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4760 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4762 if (game_status == GAME_MODE_PLAYING)
4764 SetPanelBackground();
4765 SetDrawBackgroundMask(REDRAW_DOOR_1);
4769 SetDrawBackgroundMask(REDRAW_FIELD);
4775 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4777 // ---------- handle request buttons ----------
4778 result = RequestHandleEvents(req_state);
4782 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4786 if (game_status == GAME_MODE_PLAYING)
4788 SetPanelBackground();
4789 SetDrawBackgroundMask(REDRAW_DOOR_1);
4793 SetDrawBackgroundMask(REDRAW_FIELD);
4796 /* continue network game after request */
4797 if (network.enabled &&
4798 game_status == GAME_MODE_PLAYING &&
4800 req_state & REQUEST_WAIT_FOR_INPUT)
4801 SendToServer_ContinuePlaying();
4803 /* restore deactivated drawing when quick-loading level tape recording */
4804 if (tape.playing && tape.deactivate_display)
4805 TapeDeactivateDisplayOn();
4810 boolean Request(char *text, unsigned int req_state)
4812 boolean overlay_active = GetOverlayActive();
4815 SetOverlayActive(FALSE);
4817 if (global.use_envelope_request)
4818 result = RequestEnvelope(text, req_state);
4820 result = RequestDoor(text, req_state);
4822 SetOverlayActive(overlay_active);
4827 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4829 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4830 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4833 if (dpo1->sort_priority != dpo2->sort_priority)
4834 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4836 compare_result = dpo1->nr - dpo2->nr;
4838 return compare_result;
4841 void InitGraphicCompatibilityInfo_Doors()
4847 struct DoorInfo *door;
4851 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4852 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4854 { -1, -1, -1, NULL }
4856 struct Rect door_rect_list[] =
4858 { DX, DY, DXSIZE, DYSIZE },
4859 { VX, VY, VXSIZE, VYSIZE }
4863 for (i = 0; doors[i].door_token != -1; i++)
4865 int door_token = doors[i].door_token;
4866 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4867 int part_1 = doors[i].part_1;
4868 int part_8 = doors[i].part_8;
4869 int part_2 = part_1 + 1;
4870 int part_3 = part_1 + 2;
4871 struct DoorInfo *door = doors[i].door;
4872 struct Rect *door_rect = &door_rect_list[door_index];
4873 boolean door_gfx_redefined = FALSE;
4875 /* check if any door part graphic definitions have been redefined */
4877 for (j = 0; door_part_controls[j].door_token != -1; j++)
4879 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4880 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4882 if (dpc->door_token == door_token && fi->redefined)
4883 door_gfx_redefined = TRUE;
4886 /* check for old-style door graphic/animation modifications */
4888 if (!door_gfx_redefined)
4890 if (door->anim_mode & ANIM_STATIC_PANEL)
4892 door->panel.step_xoffset = 0;
4893 door->panel.step_yoffset = 0;
4896 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4898 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4899 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4900 int num_door_steps, num_panel_steps;
4902 /* remove door part graphics other than the two default wings */
4904 for (j = 0; door_part_controls[j].door_token != -1; j++)
4906 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4907 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4909 if (dpc->graphic >= part_3 &&
4910 dpc->graphic <= part_8)
4914 /* set graphics and screen positions of the default wings */
4916 g_part_1->width = door_rect->width;
4917 g_part_1->height = door_rect->height;
4918 g_part_2->width = door_rect->width;
4919 g_part_2->height = door_rect->height;
4920 g_part_2->src_x = door_rect->width;
4921 g_part_2->src_y = g_part_1->src_y;
4923 door->part_2.x = door->part_1.x;
4924 door->part_2.y = door->part_1.y;
4926 if (door->width != -1)
4928 g_part_1->width = door->width;
4929 g_part_2->width = door->width;
4931 // special treatment for graphics and screen position of right wing
4932 g_part_2->src_x += door_rect->width - door->width;
4933 door->part_2.x += door_rect->width - door->width;
4936 if (door->height != -1)
4938 g_part_1->height = door->height;
4939 g_part_2->height = door->height;
4941 // special treatment for graphics and screen position of bottom wing
4942 g_part_2->src_y += door_rect->height - door->height;
4943 door->part_2.y += door_rect->height - door->height;
4946 /* set animation delays for the default wings and panels */
4948 door->part_1.step_delay = door->step_delay;
4949 door->part_2.step_delay = door->step_delay;
4950 door->panel.step_delay = door->step_delay;
4952 /* set animation draw order for the default wings */
4954 door->part_1.sort_priority = 2; /* draw left wing over ... */
4955 door->part_2.sort_priority = 1; /* ... right wing */
4957 /* set animation draw offset for the default wings */
4959 if (door->anim_mode & ANIM_HORIZONTAL)
4961 door->part_1.step_xoffset = door->step_offset;
4962 door->part_1.step_yoffset = 0;
4963 door->part_2.step_xoffset = door->step_offset * -1;
4964 door->part_2.step_yoffset = 0;
4966 num_door_steps = g_part_1->width / door->step_offset;
4968 else // ANIM_VERTICAL
4970 door->part_1.step_xoffset = 0;
4971 door->part_1.step_yoffset = door->step_offset;
4972 door->part_2.step_xoffset = 0;
4973 door->part_2.step_yoffset = door->step_offset * -1;
4975 num_door_steps = g_part_1->height / door->step_offset;
4978 /* set animation draw offset for the default panels */
4980 if (door->step_offset > 1)
4982 num_panel_steps = 2 * door_rect->height / door->step_offset;
4983 door->panel.start_step = num_panel_steps - num_door_steps;
4984 door->panel.start_step_closing = door->panel.start_step;
4988 num_panel_steps = door_rect->height / door->step_offset;
4989 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4990 door->panel.start_step_closing = door->panel.start_step;
4991 door->panel.step_delay *= 2;
5002 for (i = 0; door_part_controls[i].door_token != -1; i++)
5004 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5005 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5007 /* initialize "start_step_opening" and "start_step_closing", if needed */
5008 if (dpc->pos->start_step_opening == 0 &&
5009 dpc->pos->start_step_closing == 0)
5011 // dpc->pos->start_step_opening = dpc->pos->start_step;
5012 dpc->pos->start_step_closing = dpc->pos->start_step;
5015 /* fill structure for door part draw order (sorted below) */
5017 dpo->sort_priority = dpc->pos->sort_priority;
5020 /* sort door part controls according to sort_priority and graphic number */
5021 qsort(door_part_order, MAX_DOOR_PARTS,
5022 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5025 unsigned int OpenDoor(unsigned int door_state)
5027 if (door_state & DOOR_COPY_BACK)
5029 if (door_state & DOOR_OPEN_1)
5030 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5031 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5033 if (door_state & DOOR_OPEN_2)
5034 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5035 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5037 door_state &= ~DOOR_COPY_BACK;
5040 return MoveDoor(door_state);
5043 unsigned int CloseDoor(unsigned int door_state)
5045 unsigned int old_door_state = GetDoorState();
5047 if (!(door_state & DOOR_NO_COPY_BACK))
5049 if (old_door_state & DOOR_OPEN_1)
5050 BlitBitmap(backbuffer, bitmap_db_door_1,
5051 DX, DY, DXSIZE, DYSIZE, 0, 0);
5053 if (old_door_state & DOOR_OPEN_2)
5054 BlitBitmap(backbuffer, bitmap_db_door_2,
5055 VX, VY, VXSIZE, VYSIZE, 0, 0);
5057 door_state &= ~DOOR_NO_COPY_BACK;
5060 return MoveDoor(door_state);
5063 unsigned int GetDoorState()
5065 return MoveDoor(DOOR_GET_STATE);
5068 unsigned int SetDoorState(unsigned int door_state)
5070 return MoveDoor(door_state | DOOR_SET_STATE);
5073 int euclid(int a, int b)
5075 return (b ? euclid(b, a % b) : a);
5078 unsigned int MoveDoor(unsigned int door_state)
5080 struct Rect door_rect_list[] =
5082 { DX, DY, DXSIZE, DYSIZE },
5083 { VX, VY, VXSIZE, VYSIZE }
5085 static int door1 = DOOR_CLOSE_1;
5086 static int door2 = DOOR_CLOSE_2;
5087 unsigned int door_delay = 0;
5088 unsigned int door_delay_value;
5091 if (door_state == DOOR_GET_STATE)
5092 return (door1 | door2);
5094 if (door_state & DOOR_SET_STATE)
5096 if (door_state & DOOR_ACTION_1)
5097 door1 = door_state & DOOR_ACTION_1;
5098 if (door_state & DOOR_ACTION_2)
5099 door2 = door_state & DOOR_ACTION_2;
5101 return (door1 | door2);
5104 if (!(door_state & DOOR_FORCE_REDRAW))
5106 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5107 door_state &= ~DOOR_OPEN_1;
5108 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5109 door_state &= ~DOOR_CLOSE_1;
5110 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5111 door_state &= ~DOOR_OPEN_2;
5112 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5113 door_state &= ~DOOR_CLOSE_2;
5116 if (global.autoplay_leveldir)
5118 door_state |= DOOR_NO_DELAY;
5119 door_state &= ~DOOR_CLOSE_ALL;
5122 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5123 door_state |= DOOR_NO_DELAY;
5125 if (door_state & DOOR_ACTION)
5127 boolean door_panel_drawn[NUM_DOORS];
5128 boolean panel_has_doors[NUM_DOORS];
5129 boolean door_part_skip[MAX_DOOR_PARTS];
5130 boolean door_part_done[MAX_DOOR_PARTS];
5131 boolean door_part_done_all;
5132 int num_steps[MAX_DOOR_PARTS];
5133 int max_move_delay = 0; // delay for complete animations of all doors
5134 int max_step_delay = 0; // delay (ms) between two animation frames
5135 int num_move_steps = 0; // number of animation steps for all doors
5136 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5137 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5138 int current_move_delay = 0;
5142 for (i = 0; i < NUM_DOORS; i++)
5143 panel_has_doors[i] = FALSE;
5145 for (i = 0; i < MAX_DOOR_PARTS; i++)
5147 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5148 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5149 int door_token = dpc->door_token;
5151 door_part_done[i] = FALSE;
5152 door_part_skip[i] = (!(door_state & door_token) ||
5156 for (i = 0; i < MAX_DOOR_PARTS; i++)
5158 int nr = door_part_order[i].nr;
5159 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5160 struct DoorPartPosInfo *pos = dpc->pos;
5161 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5162 int door_token = dpc->door_token;
5163 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5164 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5165 int step_xoffset = ABS(pos->step_xoffset);
5166 int step_yoffset = ABS(pos->step_yoffset);
5167 int step_delay = pos->step_delay;
5168 int current_door_state = door_state & door_token;
5169 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5170 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5171 boolean part_opening = (is_panel ? door_closing : door_opening);
5172 int start_step = (part_opening ? pos->start_step_opening :
5173 pos->start_step_closing);
5174 float move_xsize = (step_xoffset ? g->width : 0);
5175 float move_ysize = (step_yoffset ? g->height : 0);
5176 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5177 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5178 int move_steps = (move_xsteps && move_ysteps ?
5179 MIN(move_xsteps, move_ysteps) :
5180 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5181 int move_delay = move_steps * step_delay;
5183 if (door_part_skip[nr])
5186 max_move_delay = MAX(max_move_delay, move_delay);
5187 max_step_delay = (max_step_delay == 0 ? step_delay :
5188 euclid(max_step_delay, step_delay));
5189 num_steps[nr] = move_steps;
5193 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5195 panel_has_doors[door_index] = TRUE;
5199 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5201 num_move_steps = max_move_delay / max_step_delay;
5202 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5204 door_delay_value = max_step_delay;
5206 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5208 start = num_move_steps - 1;
5212 /* opening door sound has priority over simultaneously closing door */
5213 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5215 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5217 if (door_state & DOOR_OPEN_1)
5218 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5219 if (door_state & DOOR_OPEN_2)
5220 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5222 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5224 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5226 if (door_state & DOOR_CLOSE_1)
5227 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5228 if (door_state & DOOR_CLOSE_2)
5229 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5233 for (k = start; k < num_move_steps; k++)
5235 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5237 door_part_done_all = TRUE;
5239 for (i = 0; i < NUM_DOORS; i++)
5240 door_panel_drawn[i] = FALSE;
5242 for (i = 0; i < MAX_DOOR_PARTS; i++)
5244 int nr = door_part_order[i].nr;
5245 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5246 struct DoorPartPosInfo *pos = dpc->pos;
5247 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5248 int door_token = dpc->door_token;
5249 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5250 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5251 boolean is_panel_and_door_has_closed = FALSE;
5252 struct Rect *door_rect = &door_rect_list[door_index];
5253 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5255 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5256 int current_door_state = door_state & door_token;
5257 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5258 boolean door_closing = !door_opening;
5259 boolean part_opening = (is_panel ? door_closing : door_opening);
5260 boolean part_closing = !part_opening;
5261 int start_step = (part_opening ? pos->start_step_opening :
5262 pos->start_step_closing);
5263 int step_delay = pos->step_delay;
5264 int step_factor = step_delay / max_step_delay;
5265 int k1 = (step_factor ? k / step_factor + 1 : k);
5266 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5267 int kk = MAX(0, k2);
5270 int src_x, src_y, src_xx, src_yy;
5271 int dst_x, dst_y, dst_xx, dst_yy;
5274 if (door_part_skip[nr])
5277 if (!(door_state & door_token))
5285 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5286 int kk_door = MAX(0, k2_door);
5287 int sync_frame = kk_door * door_delay_value;
5288 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5290 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5291 &g_src_x, &g_src_y);
5296 if (!door_panel_drawn[door_index])
5298 ClearRectangle(drawto, door_rect->x, door_rect->y,
5299 door_rect->width, door_rect->height);
5301 door_panel_drawn[door_index] = TRUE;
5304 // draw opening or closing door parts
5306 if (pos->step_xoffset < 0) // door part on right side
5309 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5312 if (dst_xx + width > door_rect->width)
5313 width = door_rect->width - dst_xx;
5315 else // door part on left side
5318 dst_xx = pos->x - kk * pos->step_xoffset;
5322 src_xx = ABS(dst_xx);
5326 width = g->width - src_xx;
5328 if (width > door_rect->width)
5329 width = door_rect->width;
5331 // printf("::: k == %d [%d] \n", k, start_step);
5334 if (pos->step_yoffset < 0) // door part on bottom side
5337 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5340 if (dst_yy + height > door_rect->height)
5341 height = door_rect->height - dst_yy;
5343 else // door part on top side
5346 dst_yy = pos->y - kk * pos->step_yoffset;
5350 src_yy = ABS(dst_yy);
5354 height = g->height - src_yy;
5357 src_x = g_src_x + src_xx;
5358 src_y = g_src_y + src_yy;
5360 dst_x = door_rect->x + dst_xx;
5361 dst_y = door_rect->y + dst_yy;
5363 is_panel_and_door_has_closed =
5366 panel_has_doors[door_index] &&
5367 k >= num_move_steps_doors_only - 1);
5369 if (width >= 0 && width <= g->width &&
5370 height >= 0 && height <= g->height &&
5371 !is_panel_and_door_has_closed)
5373 if (is_panel || !pos->draw_masked)
5374 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5377 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5381 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5383 if ((part_opening && (width < 0 || height < 0)) ||
5384 (part_closing && (width >= g->width && height >= g->height)))
5385 door_part_done[nr] = TRUE;
5387 // continue door part animations, but not panel after door has closed
5388 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5389 door_part_done_all = FALSE;
5392 if (!(door_state & DOOR_NO_DELAY))
5396 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5398 current_move_delay += max_step_delay;
5400 /* prevent OS (Windows) from complaining about program not responding */
5404 if (door_part_done_all)
5408 if (!(door_state & DOOR_NO_DELAY))
5410 /* wait for specified door action post delay */
5411 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5412 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5413 else if (door_state & DOOR_ACTION_1)
5414 door_delay_value = door_1.post_delay;
5415 else if (door_state & DOOR_ACTION_2)
5416 door_delay_value = door_2.post_delay;
5418 while (!DelayReached(&door_delay, door_delay_value))
5423 if (door_state & DOOR_ACTION_1)
5424 door1 = door_state & DOOR_ACTION_1;
5425 if (door_state & DOOR_ACTION_2)
5426 door2 = door_state & DOOR_ACTION_2;
5428 // draw masked border over door area
5429 DrawMaskedBorder(REDRAW_DOOR_1);
5430 DrawMaskedBorder(REDRAW_DOOR_2);
5434 return (door1 | door2);
5437 static boolean useSpecialEditorDoor()
5439 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5440 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5442 // do not draw special editor door if editor border defined or redefined
5443 if (graphic_info[graphic].bitmap != NULL || redefined)
5446 // do not draw special editor door if global border defined to be empty
5447 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5450 // do not draw special editor door if viewport definitions do not match
5454 EY + EYSIZE != VY + VYSIZE)
5460 void DrawSpecialEditorDoor()
5462 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5463 int top_border_width = gfx1->width;
5464 int top_border_height = gfx1->height;
5465 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5466 int ex = EX - outer_border;
5467 int ey = EY - outer_border;
5468 int vy = VY - outer_border;
5469 int exsize = EXSIZE + 2 * outer_border;
5471 if (!useSpecialEditorDoor())
5474 /* draw bigger level editor toolbox window */
5475 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5476 top_border_width, top_border_height, ex, ey - top_border_height);
5477 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5478 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5480 redraw_mask |= REDRAW_ALL;
5483 void UndrawSpecialEditorDoor()
5485 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5486 int top_border_width = gfx1->width;
5487 int top_border_height = gfx1->height;
5488 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5489 int ex = EX - outer_border;
5490 int ey = EY - outer_border;
5491 int ey_top = ey - top_border_height;
5492 int exsize = EXSIZE + 2 * outer_border;
5493 int eysize = EYSIZE + 2 * outer_border;
5495 if (!useSpecialEditorDoor())
5498 /* draw normal tape recorder window */
5499 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5501 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5502 ex, ey_top, top_border_width, top_border_height,
5504 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5505 ex, ey, exsize, eysize, ex, ey);
5509 // if screen background is set to "[NONE]", clear editor toolbox window
5510 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5511 ClearRectangle(drawto, ex, ey, exsize, eysize);
5514 redraw_mask |= REDRAW_ALL;
5518 /* ---------- new tool button stuff ---------------------------------------- */
5523 struct TextPosInfo *pos;
5526 } toolbutton_info[NUM_TOOL_BUTTONS] =
5529 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5530 TOOL_CTRL_ID_YES, "yes"
5533 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5534 TOOL_CTRL_ID_NO, "no"
5537 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5538 TOOL_CTRL_ID_CONFIRM, "confirm"
5541 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5542 TOOL_CTRL_ID_PLAYER_1, "player 1"
5545 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5546 TOOL_CTRL_ID_PLAYER_2, "player 2"
5549 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5550 TOOL_CTRL_ID_PLAYER_3, "player 3"
5553 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5554 TOOL_CTRL_ID_PLAYER_4, "player 4"
5558 void CreateToolButtons()
5562 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5564 int graphic = toolbutton_info[i].graphic;
5565 struct GraphicInfo *gfx = &graphic_info[graphic];
5566 struct TextPosInfo *pos = toolbutton_info[i].pos;
5567 struct GadgetInfo *gi;
5568 Bitmap *deco_bitmap = None;
5569 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5570 unsigned int event_mask = GD_EVENT_RELEASED;
5573 int gd_x = gfx->src_x;
5574 int gd_y = gfx->src_y;
5575 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5576 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5581 if (global.use_envelope_request)
5583 setRequestPosition(&dx, &dy, TRUE);
5585 // check if request buttons are outside of envelope and fix, if needed
5586 if (x < 0 || x + gfx->width > request.width ||
5587 y < 0 || y + gfx->height > request.height)
5589 if (id == TOOL_CTRL_ID_YES)
5592 y = request.height - 2 * request.border_size - gfx->height;
5594 else if (id == TOOL_CTRL_ID_NO)
5596 x = request.width - 2 * request.border_size - gfx->width;
5597 y = request.height - 2 * request.border_size - gfx->height;
5599 else if (id == TOOL_CTRL_ID_CONFIRM)
5601 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5602 y = request.height - 2 * request.border_size - gfx->height;
5604 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5606 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5608 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5609 y = request.height - 2 * request.border_size - gfx->height * 2;
5611 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5612 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5617 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5619 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5621 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5622 pos->size, &deco_bitmap, &deco_x, &deco_y);
5623 deco_xpos = (gfx->width - pos->size) / 2;
5624 deco_ypos = (gfx->height - pos->size) / 2;
5627 gi = CreateGadget(GDI_CUSTOM_ID, id,
5628 GDI_IMAGE_ID, graphic,
5629 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5632 GDI_WIDTH, gfx->width,
5633 GDI_HEIGHT, gfx->height,
5634 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5635 GDI_STATE, GD_BUTTON_UNPRESSED,
5636 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5637 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5638 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5639 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5640 GDI_DECORATION_SIZE, pos->size, pos->size,
5641 GDI_DECORATION_SHIFTING, 1, 1,
5642 GDI_DIRECT_DRAW, FALSE,
5643 GDI_EVENT_MASK, event_mask,
5644 GDI_CALLBACK_ACTION, HandleToolButtons,
5648 Error(ERR_EXIT, "cannot create gadget");
5650 tool_gadget[id] = gi;
5654 void FreeToolButtons()
5658 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5659 FreeGadget(tool_gadget[i]);
5662 static void UnmapToolButtons()
5666 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5667 UnmapGadget(tool_gadget[i]);
5670 static void HandleToolButtons(struct GadgetInfo *gi)
5672 request_gadget_id = gi->custom_id;
5675 static struct Mapping_EM_to_RND_object
5678 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5679 boolean is_backside; /* backside of moving element */
5685 em_object_mapping_list[] =
5688 Xblank, TRUE, FALSE,
5692 Yacid_splash_eB, FALSE, FALSE,
5693 EL_ACID_SPLASH_RIGHT, -1, -1
5696 Yacid_splash_wB, FALSE, FALSE,
5697 EL_ACID_SPLASH_LEFT, -1, -1
5700 #ifdef EM_ENGINE_BAD_ROLL
5702 Xstone_force_e, FALSE, FALSE,
5703 EL_ROCK, -1, MV_BIT_RIGHT
5706 Xstone_force_w, FALSE, FALSE,
5707 EL_ROCK, -1, MV_BIT_LEFT
5710 Xnut_force_e, FALSE, FALSE,
5711 EL_NUT, -1, MV_BIT_RIGHT
5714 Xnut_force_w, FALSE, FALSE,
5715 EL_NUT, -1, MV_BIT_LEFT
5718 Xspring_force_e, FALSE, FALSE,
5719 EL_SPRING, -1, MV_BIT_RIGHT
5722 Xspring_force_w, FALSE, FALSE,
5723 EL_SPRING, -1, MV_BIT_LEFT
5726 Xemerald_force_e, FALSE, FALSE,
5727 EL_EMERALD, -1, MV_BIT_RIGHT
5730 Xemerald_force_w, FALSE, FALSE,
5731 EL_EMERALD, -1, MV_BIT_LEFT
5734 Xdiamond_force_e, FALSE, FALSE,
5735 EL_DIAMOND, -1, MV_BIT_RIGHT
5738 Xdiamond_force_w, FALSE, FALSE,
5739 EL_DIAMOND, -1, MV_BIT_LEFT
5742 Xbomb_force_e, FALSE, FALSE,
5743 EL_BOMB, -1, MV_BIT_RIGHT
5746 Xbomb_force_w, FALSE, FALSE,
5747 EL_BOMB, -1, MV_BIT_LEFT
5749 #endif /* EM_ENGINE_BAD_ROLL */
5752 Xstone, TRUE, FALSE,
5756 Xstone_pause, FALSE, FALSE,
5760 Xstone_fall, FALSE, FALSE,
5764 Ystone_s, FALSE, FALSE,
5765 EL_ROCK, ACTION_FALLING, -1
5768 Ystone_sB, FALSE, TRUE,
5769 EL_ROCK, ACTION_FALLING, -1
5772 Ystone_e, FALSE, FALSE,
5773 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5776 Ystone_eB, FALSE, TRUE,
5777 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5780 Ystone_w, FALSE, FALSE,
5781 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5784 Ystone_wB, FALSE, TRUE,
5785 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5792 Xnut_pause, FALSE, FALSE,
5796 Xnut_fall, FALSE, FALSE,
5800 Ynut_s, FALSE, FALSE,
5801 EL_NUT, ACTION_FALLING, -1
5804 Ynut_sB, FALSE, TRUE,
5805 EL_NUT, ACTION_FALLING, -1
5808 Ynut_e, FALSE, FALSE,
5809 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5812 Ynut_eB, FALSE, TRUE,
5813 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5816 Ynut_w, FALSE, FALSE,
5817 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5820 Ynut_wB, FALSE, TRUE,
5821 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5824 Xbug_n, TRUE, FALSE,
5828 Xbug_e, TRUE, FALSE,
5829 EL_BUG_RIGHT, -1, -1
5832 Xbug_s, TRUE, FALSE,
5836 Xbug_w, TRUE, FALSE,
5840 Xbug_gon, FALSE, FALSE,
5844 Xbug_goe, FALSE, FALSE,
5845 EL_BUG_RIGHT, -1, -1
5848 Xbug_gos, FALSE, FALSE,
5852 Xbug_gow, FALSE, FALSE,
5856 Ybug_n, FALSE, FALSE,
5857 EL_BUG, ACTION_MOVING, MV_BIT_UP
5860 Ybug_nB, FALSE, TRUE,
5861 EL_BUG, ACTION_MOVING, MV_BIT_UP
5864 Ybug_e, FALSE, FALSE,
5865 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5868 Ybug_eB, FALSE, TRUE,
5869 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5872 Ybug_s, FALSE, FALSE,
5873 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5876 Ybug_sB, FALSE, TRUE,
5877 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5880 Ybug_w, FALSE, FALSE,
5881 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5884 Ybug_wB, FALSE, TRUE,
5885 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5888 Ybug_w_n, FALSE, FALSE,
5889 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5892 Ybug_n_e, FALSE, FALSE,
5893 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5896 Ybug_e_s, FALSE, FALSE,
5897 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5900 Ybug_s_w, FALSE, FALSE,
5901 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5904 Ybug_e_n, FALSE, FALSE,
5905 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5908 Ybug_s_e, FALSE, FALSE,
5909 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5912 Ybug_w_s, FALSE, FALSE,
5913 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5916 Ybug_n_w, FALSE, FALSE,
5917 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5920 Ybug_stone, FALSE, FALSE,
5921 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5924 Ybug_spring, FALSE, FALSE,
5925 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5928 Xtank_n, TRUE, FALSE,
5929 EL_SPACESHIP_UP, -1, -1
5932 Xtank_e, TRUE, FALSE,
5933 EL_SPACESHIP_RIGHT, -1, -1
5936 Xtank_s, TRUE, FALSE,
5937 EL_SPACESHIP_DOWN, -1, -1
5940 Xtank_w, TRUE, FALSE,
5941 EL_SPACESHIP_LEFT, -1, -1
5944 Xtank_gon, FALSE, FALSE,
5945 EL_SPACESHIP_UP, -1, -1
5948 Xtank_goe, FALSE, FALSE,
5949 EL_SPACESHIP_RIGHT, -1, -1
5952 Xtank_gos, FALSE, FALSE,
5953 EL_SPACESHIP_DOWN, -1, -1
5956 Xtank_gow, FALSE, FALSE,
5957 EL_SPACESHIP_LEFT, -1, -1
5960 Ytank_n, FALSE, FALSE,
5961 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5964 Ytank_nB, FALSE, TRUE,
5965 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5968 Ytank_e, FALSE, FALSE,
5969 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5972 Ytank_eB, FALSE, TRUE,
5973 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5976 Ytank_s, FALSE, FALSE,
5977 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5980 Ytank_sB, FALSE, TRUE,
5981 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5984 Ytank_w, FALSE, FALSE,
5985 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5988 Ytank_wB, FALSE, TRUE,
5989 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5992 Ytank_w_n, FALSE, FALSE,
5993 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5996 Ytank_n_e, FALSE, FALSE,
5997 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6000 Ytank_e_s, FALSE, FALSE,
6001 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6004 Ytank_s_w, FALSE, FALSE,
6005 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6008 Ytank_e_n, FALSE, FALSE,
6009 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6012 Ytank_s_e, FALSE, FALSE,
6013 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6016 Ytank_w_s, FALSE, FALSE,
6017 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6020 Ytank_n_w, FALSE, FALSE,
6021 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6024 Ytank_stone, FALSE, FALSE,
6025 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6028 Ytank_spring, FALSE, FALSE,
6029 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6032 Xandroid, TRUE, FALSE,
6033 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6036 Xandroid_1_n, FALSE, FALSE,
6037 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6040 Xandroid_2_n, FALSE, FALSE,
6041 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6044 Xandroid_1_e, FALSE, FALSE,
6045 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6048 Xandroid_2_e, FALSE, FALSE,
6049 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6052 Xandroid_1_w, FALSE, FALSE,
6053 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6056 Xandroid_2_w, FALSE, FALSE,
6057 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6060 Xandroid_1_s, FALSE, FALSE,
6061 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6064 Xandroid_2_s, FALSE, FALSE,
6065 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6068 Yandroid_n, FALSE, FALSE,
6069 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6072 Yandroid_nB, FALSE, TRUE,
6073 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6076 Yandroid_ne, FALSE, FALSE,
6077 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6080 Yandroid_neB, FALSE, TRUE,
6081 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6084 Yandroid_e, FALSE, FALSE,
6085 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6088 Yandroid_eB, FALSE, TRUE,
6089 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6092 Yandroid_se, FALSE, FALSE,
6093 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6096 Yandroid_seB, FALSE, TRUE,
6097 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6100 Yandroid_s, FALSE, FALSE,
6101 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6104 Yandroid_sB, FALSE, TRUE,
6105 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6108 Yandroid_sw, FALSE, FALSE,
6109 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6112 Yandroid_swB, FALSE, TRUE,
6113 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6116 Yandroid_w, FALSE, FALSE,
6117 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6120 Yandroid_wB, FALSE, TRUE,
6121 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6124 Yandroid_nw, FALSE, FALSE,
6125 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6128 Yandroid_nwB, FALSE, TRUE,
6129 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6132 Xspring, TRUE, FALSE,
6136 Xspring_pause, FALSE, FALSE,
6140 Xspring_e, FALSE, FALSE,
6144 Xspring_w, FALSE, FALSE,
6148 Xspring_fall, FALSE, FALSE,
6152 Yspring_s, FALSE, FALSE,
6153 EL_SPRING, ACTION_FALLING, -1
6156 Yspring_sB, FALSE, TRUE,
6157 EL_SPRING, ACTION_FALLING, -1
6160 Yspring_e, FALSE, FALSE,
6161 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6164 Yspring_eB, FALSE, TRUE,
6165 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6168 Yspring_w, FALSE, FALSE,
6169 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6172 Yspring_wB, FALSE, TRUE,
6173 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6176 Yspring_kill_e, FALSE, FALSE,
6177 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6180 Yspring_kill_eB, FALSE, TRUE,
6181 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6184 Yspring_kill_w, FALSE, FALSE,
6185 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6188 Yspring_kill_wB, FALSE, TRUE,
6189 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6192 Xeater_n, TRUE, FALSE,
6193 EL_YAMYAM_UP, -1, -1
6196 Xeater_e, TRUE, FALSE,
6197 EL_YAMYAM_RIGHT, -1, -1
6200 Xeater_w, TRUE, FALSE,
6201 EL_YAMYAM_LEFT, -1, -1
6204 Xeater_s, TRUE, FALSE,
6205 EL_YAMYAM_DOWN, -1, -1
6208 Yeater_n, FALSE, FALSE,
6209 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6212 Yeater_nB, FALSE, TRUE,
6213 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6216 Yeater_e, FALSE, FALSE,
6217 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6220 Yeater_eB, FALSE, TRUE,
6221 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6224 Yeater_s, FALSE, FALSE,
6225 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6228 Yeater_sB, FALSE, TRUE,
6229 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6232 Yeater_w, FALSE, FALSE,
6233 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6236 Yeater_wB, FALSE, TRUE,
6237 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6240 Yeater_stone, FALSE, FALSE,
6241 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6244 Yeater_spring, FALSE, FALSE,
6245 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6248 Xalien, TRUE, FALSE,
6252 Xalien_pause, FALSE, FALSE,
6256 Yalien_n, FALSE, FALSE,
6257 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6260 Yalien_nB, FALSE, TRUE,
6261 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6264 Yalien_e, FALSE, FALSE,
6265 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6268 Yalien_eB, FALSE, TRUE,
6269 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6272 Yalien_s, FALSE, FALSE,
6273 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6276 Yalien_sB, FALSE, TRUE,
6277 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6280 Yalien_w, FALSE, FALSE,
6281 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6284 Yalien_wB, FALSE, TRUE,
6285 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6288 Yalien_stone, FALSE, FALSE,
6289 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6292 Yalien_spring, FALSE, FALSE,
6293 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6296 Xemerald, TRUE, FALSE,
6300 Xemerald_pause, FALSE, FALSE,
6304 Xemerald_fall, FALSE, FALSE,
6308 Xemerald_shine, FALSE, FALSE,
6309 EL_EMERALD, ACTION_TWINKLING, -1
6312 Yemerald_s, FALSE, FALSE,
6313 EL_EMERALD, ACTION_FALLING, -1
6316 Yemerald_sB, FALSE, TRUE,
6317 EL_EMERALD, ACTION_FALLING, -1
6320 Yemerald_e, FALSE, FALSE,
6321 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6324 Yemerald_eB, FALSE, TRUE,
6325 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6328 Yemerald_w, FALSE, FALSE,
6329 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6332 Yemerald_wB, FALSE, TRUE,
6333 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6336 Yemerald_eat, FALSE, FALSE,
6337 EL_EMERALD, ACTION_COLLECTING, -1
6340 Yemerald_stone, FALSE, FALSE,
6341 EL_NUT, ACTION_BREAKING, -1
6344 Xdiamond, TRUE, FALSE,
6348 Xdiamond_pause, FALSE, FALSE,
6352 Xdiamond_fall, FALSE, FALSE,
6356 Xdiamond_shine, FALSE, FALSE,
6357 EL_DIAMOND, ACTION_TWINKLING, -1
6360 Ydiamond_s, FALSE, FALSE,
6361 EL_DIAMOND, ACTION_FALLING, -1
6364 Ydiamond_sB, FALSE, TRUE,
6365 EL_DIAMOND, ACTION_FALLING, -1
6368 Ydiamond_e, FALSE, FALSE,
6369 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6372 Ydiamond_eB, FALSE, TRUE,
6373 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6376 Ydiamond_w, FALSE, FALSE,
6377 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6380 Ydiamond_wB, FALSE, TRUE,
6381 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6384 Ydiamond_eat, FALSE, FALSE,
6385 EL_DIAMOND, ACTION_COLLECTING, -1
6388 Ydiamond_stone, FALSE, FALSE,
6389 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6392 Xdrip_fall, TRUE, FALSE,
6393 EL_AMOEBA_DROP, -1, -1
6396 Xdrip_stretch, FALSE, FALSE,
6397 EL_AMOEBA_DROP, ACTION_FALLING, -1
6400 Xdrip_stretchB, FALSE, TRUE,
6401 EL_AMOEBA_DROP, ACTION_FALLING, -1
6404 Xdrip_eat, FALSE, FALSE,
6405 EL_AMOEBA_DROP, ACTION_GROWING, -1
6408 Ydrip_s1, FALSE, FALSE,
6409 EL_AMOEBA_DROP, ACTION_FALLING, -1
6412 Ydrip_s1B, FALSE, TRUE,
6413 EL_AMOEBA_DROP, ACTION_FALLING, -1
6416 Ydrip_s2, FALSE, FALSE,
6417 EL_AMOEBA_DROP, ACTION_FALLING, -1
6420 Ydrip_s2B, FALSE, TRUE,
6421 EL_AMOEBA_DROP, ACTION_FALLING, -1
6428 Xbomb_pause, FALSE, FALSE,
6432 Xbomb_fall, FALSE, FALSE,
6436 Ybomb_s, FALSE, FALSE,
6437 EL_BOMB, ACTION_FALLING, -1
6440 Ybomb_sB, FALSE, TRUE,
6441 EL_BOMB, ACTION_FALLING, -1
6444 Ybomb_e, FALSE, FALSE,
6445 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6448 Ybomb_eB, FALSE, TRUE,
6449 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6452 Ybomb_w, FALSE, FALSE,
6453 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6456 Ybomb_wB, FALSE, TRUE,
6457 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6460 Ybomb_eat, FALSE, FALSE,
6461 EL_BOMB, ACTION_ACTIVATING, -1
6464 Xballoon, TRUE, FALSE,
6468 Yballoon_n, FALSE, FALSE,
6469 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6472 Yballoon_nB, FALSE, TRUE,
6473 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6476 Yballoon_e, FALSE, FALSE,
6477 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6480 Yballoon_eB, FALSE, TRUE,
6481 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6484 Yballoon_s, FALSE, FALSE,
6485 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6488 Yballoon_sB, FALSE, TRUE,
6489 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6492 Yballoon_w, FALSE, FALSE,
6493 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6496 Yballoon_wB, FALSE, TRUE,
6497 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6500 Xgrass, TRUE, FALSE,
6501 EL_EMC_GRASS, -1, -1
6504 Ygrass_nB, FALSE, FALSE,
6505 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6508 Ygrass_eB, FALSE, FALSE,
6509 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6512 Ygrass_sB, FALSE, FALSE,
6513 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6516 Ygrass_wB, FALSE, FALSE,
6517 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6524 Ydirt_nB, FALSE, FALSE,
6525 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6528 Ydirt_eB, FALSE, FALSE,
6529 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6532 Ydirt_sB, FALSE, FALSE,
6533 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6536 Ydirt_wB, FALSE, FALSE,
6537 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6540 Xacid_ne, TRUE, FALSE,
6541 EL_ACID_POOL_TOPRIGHT, -1, -1
6544 Xacid_se, TRUE, FALSE,
6545 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6548 Xacid_s, TRUE, FALSE,
6549 EL_ACID_POOL_BOTTOM, -1, -1
6552 Xacid_sw, TRUE, FALSE,
6553 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6556 Xacid_nw, TRUE, FALSE,
6557 EL_ACID_POOL_TOPLEFT, -1, -1
6560 Xacid_1, TRUE, FALSE,
6564 Xacid_2, FALSE, FALSE,
6568 Xacid_3, FALSE, FALSE,
6572 Xacid_4, FALSE, FALSE,
6576 Xacid_5, FALSE, FALSE,
6580 Xacid_6, FALSE, FALSE,
6584 Xacid_7, FALSE, FALSE,
6588 Xacid_8, FALSE, FALSE,
6592 Xball_1, TRUE, FALSE,
6593 EL_EMC_MAGIC_BALL, -1, -1
6596 Xball_1B, FALSE, FALSE,
6597 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6600 Xball_2, FALSE, FALSE,
6601 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6604 Xball_2B, FALSE, FALSE,
6605 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6608 Yball_eat, FALSE, FALSE,
6609 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6612 Ykey_1_eat, FALSE, FALSE,
6613 EL_EM_KEY_1, ACTION_COLLECTING, -1
6616 Ykey_2_eat, FALSE, FALSE,
6617 EL_EM_KEY_2, ACTION_COLLECTING, -1
6620 Ykey_3_eat, FALSE, FALSE,
6621 EL_EM_KEY_3, ACTION_COLLECTING, -1
6624 Ykey_4_eat, FALSE, FALSE,
6625 EL_EM_KEY_4, ACTION_COLLECTING, -1
6628 Ykey_5_eat, FALSE, FALSE,
6629 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6632 Ykey_6_eat, FALSE, FALSE,
6633 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6636 Ykey_7_eat, FALSE, FALSE,
6637 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6640 Ykey_8_eat, FALSE, FALSE,
6641 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6644 Ylenses_eat, FALSE, FALSE,
6645 EL_EMC_LENSES, ACTION_COLLECTING, -1
6648 Ymagnify_eat, FALSE, FALSE,
6649 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6652 Ygrass_eat, FALSE, FALSE,
6653 EL_EMC_GRASS, ACTION_SNAPPING, -1
6656 Ydirt_eat, FALSE, FALSE,
6657 EL_SAND, ACTION_SNAPPING, -1
6660 Xgrow_ns, TRUE, FALSE,
6661 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6664 Ygrow_ns_eat, FALSE, FALSE,
6665 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6668 Xgrow_ew, TRUE, FALSE,
6669 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6672 Ygrow_ew_eat, FALSE, FALSE,
6673 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6676 Xwonderwall, TRUE, FALSE,
6677 EL_MAGIC_WALL, -1, -1
6680 XwonderwallB, FALSE, FALSE,
6681 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6684 Xamoeba_1, TRUE, FALSE,
6685 EL_AMOEBA_DRY, ACTION_OTHER, -1
6688 Xamoeba_2, FALSE, FALSE,
6689 EL_AMOEBA_DRY, ACTION_OTHER, -1
6692 Xamoeba_3, FALSE, FALSE,
6693 EL_AMOEBA_DRY, ACTION_OTHER, -1
6696 Xamoeba_4, FALSE, FALSE,
6697 EL_AMOEBA_DRY, ACTION_OTHER, -1
6700 Xamoeba_5, TRUE, FALSE,
6701 EL_AMOEBA_WET, ACTION_OTHER, -1
6704 Xamoeba_6, FALSE, FALSE,
6705 EL_AMOEBA_WET, ACTION_OTHER, -1
6708 Xamoeba_7, FALSE, FALSE,
6709 EL_AMOEBA_WET, ACTION_OTHER, -1
6712 Xamoeba_8, FALSE, FALSE,
6713 EL_AMOEBA_WET, ACTION_OTHER, -1
6716 Xdoor_1, TRUE, FALSE,
6717 EL_EM_GATE_1, -1, -1
6720 Xdoor_2, TRUE, FALSE,
6721 EL_EM_GATE_2, -1, -1
6724 Xdoor_3, TRUE, FALSE,
6725 EL_EM_GATE_3, -1, -1
6728 Xdoor_4, TRUE, FALSE,
6729 EL_EM_GATE_4, -1, -1
6732 Xdoor_5, TRUE, FALSE,
6733 EL_EMC_GATE_5, -1, -1
6736 Xdoor_6, TRUE, FALSE,
6737 EL_EMC_GATE_6, -1, -1
6740 Xdoor_7, TRUE, FALSE,
6741 EL_EMC_GATE_7, -1, -1
6744 Xdoor_8, TRUE, FALSE,
6745 EL_EMC_GATE_8, -1, -1
6748 Xkey_1, TRUE, FALSE,
6752 Xkey_2, TRUE, FALSE,
6756 Xkey_3, TRUE, FALSE,
6760 Xkey_4, TRUE, FALSE,
6764 Xkey_5, TRUE, FALSE,
6765 EL_EMC_KEY_5, -1, -1
6768 Xkey_6, TRUE, FALSE,
6769 EL_EMC_KEY_6, -1, -1
6772 Xkey_7, TRUE, FALSE,
6773 EL_EMC_KEY_7, -1, -1
6776 Xkey_8, TRUE, FALSE,
6777 EL_EMC_KEY_8, -1, -1
6780 Xwind_n, TRUE, FALSE,
6781 EL_BALLOON_SWITCH_UP, -1, -1
6784 Xwind_e, TRUE, FALSE,
6785 EL_BALLOON_SWITCH_RIGHT, -1, -1
6788 Xwind_s, TRUE, FALSE,
6789 EL_BALLOON_SWITCH_DOWN, -1, -1
6792 Xwind_w, TRUE, FALSE,
6793 EL_BALLOON_SWITCH_LEFT, -1, -1
6796 Xwind_nesw, TRUE, FALSE,
6797 EL_BALLOON_SWITCH_ANY, -1, -1
6800 Xwind_stop, TRUE, FALSE,
6801 EL_BALLOON_SWITCH_NONE, -1, -1
6805 EL_EM_EXIT_CLOSED, -1, -1
6808 Xexit_1, TRUE, FALSE,
6809 EL_EM_EXIT_OPEN, -1, -1
6812 Xexit_2, FALSE, FALSE,
6813 EL_EM_EXIT_OPEN, -1, -1
6816 Xexit_3, FALSE, FALSE,
6817 EL_EM_EXIT_OPEN, -1, -1
6820 Xdynamite, TRUE, FALSE,
6821 EL_EM_DYNAMITE, -1, -1
6824 Ydynamite_eat, FALSE, FALSE,
6825 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6828 Xdynamite_1, TRUE, FALSE,
6829 EL_EM_DYNAMITE_ACTIVE, -1, -1
6832 Xdynamite_2, FALSE, FALSE,
6833 EL_EM_DYNAMITE_ACTIVE, -1, -1
6836 Xdynamite_3, FALSE, FALSE,
6837 EL_EM_DYNAMITE_ACTIVE, -1, -1
6840 Xdynamite_4, FALSE, FALSE,
6841 EL_EM_DYNAMITE_ACTIVE, -1, -1
6844 Xbumper, TRUE, FALSE,
6845 EL_EMC_SPRING_BUMPER, -1, -1
6848 XbumperB, FALSE, FALSE,
6849 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6852 Xwheel, TRUE, FALSE,
6853 EL_ROBOT_WHEEL, -1, -1
6856 XwheelB, FALSE, FALSE,
6857 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6860 Xswitch, TRUE, FALSE,
6861 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6864 XswitchB, FALSE, FALSE,
6865 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6869 EL_QUICKSAND_EMPTY, -1, -1
6872 Xsand_stone, TRUE, FALSE,
6873 EL_QUICKSAND_FULL, -1, -1
6876 Xsand_stonein_1, FALSE, TRUE,
6877 EL_ROCK, ACTION_FILLING, -1
6880 Xsand_stonein_2, FALSE, TRUE,
6881 EL_ROCK, ACTION_FILLING, -1
6884 Xsand_stonein_3, FALSE, TRUE,
6885 EL_ROCK, ACTION_FILLING, -1
6888 Xsand_stonein_4, FALSE, TRUE,
6889 EL_ROCK, ACTION_FILLING, -1
6892 Xsand_stonesand_1, FALSE, FALSE,
6893 EL_QUICKSAND_EMPTYING, -1, -1
6896 Xsand_stonesand_2, FALSE, FALSE,
6897 EL_QUICKSAND_EMPTYING, -1, -1
6900 Xsand_stonesand_3, FALSE, FALSE,
6901 EL_QUICKSAND_EMPTYING, -1, -1
6904 Xsand_stonesand_4, FALSE, FALSE,
6905 EL_QUICKSAND_EMPTYING, -1, -1
6908 Xsand_stonesand_quickout_1, FALSE, FALSE,
6909 EL_QUICKSAND_EMPTYING, -1, -1
6912 Xsand_stonesand_quickout_2, FALSE, FALSE,
6913 EL_QUICKSAND_EMPTYING, -1, -1
6916 Xsand_stoneout_1, FALSE, FALSE,
6917 EL_ROCK, ACTION_EMPTYING, -1
6920 Xsand_stoneout_2, FALSE, FALSE,
6921 EL_ROCK, ACTION_EMPTYING, -1
6924 Xsand_sandstone_1, FALSE, FALSE,
6925 EL_QUICKSAND_FILLING, -1, -1
6928 Xsand_sandstone_2, FALSE, FALSE,
6929 EL_QUICKSAND_FILLING, -1, -1
6932 Xsand_sandstone_3, FALSE, FALSE,
6933 EL_QUICKSAND_FILLING, -1, -1
6936 Xsand_sandstone_4, FALSE, FALSE,
6937 EL_QUICKSAND_FILLING, -1, -1
6940 Xplant, TRUE, FALSE,
6941 EL_EMC_PLANT, -1, -1
6944 Yplant, FALSE, FALSE,
6945 EL_EMC_PLANT, -1, -1
6948 Xlenses, TRUE, FALSE,
6949 EL_EMC_LENSES, -1, -1
6952 Xmagnify, TRUE, FALSE,
6953 EL_EMC_MAGNIFIER, -1, -1
6956 Xdripper, TRUE, FALSE,
6957 EL_EMC_DRIPPER, -1, -1
6960 XdripperB, FALSE, FALSE,
6961 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6964 Xfake_blank, TRUE, FALSE,
6965 EL_INVISIBLE_WALL, -1, -1
6968 Xfake_blankB, FALSE, FALSE,
6969 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6972 Xfake_grass, TRUE, FALSE,
6973 EL_EMC_FAKE_GRASS, -1, -1
6976 Xfake_grassB, FALSE, FALSE,
6977 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6980 Xfake_door_1, TRUE, FALSE,
6981 EL_EM_GATE_1_GRAY, -1, -1
6984 Xfake_door_2, TRUE, FALSE,
6985 EL_EM_GATE_2_GRAY, -1, -1
6988 Xfake_door_3, TRUE, FALSE,
6989 EL_EM_GATE_3_GRAY, -1, -1
6992 Xfake_door_4, TRUE, FALSE,
6993 EL_EM_GATE_4_GRAY, -1, -1
6996 Xfake_door_5, TRUE, FALSE,
6997 EL_EMC_GATE_5_GRAY, -1, -1
7000 Xfake_door_6, TRUE, FALSE,
7001 EL_EMC_GATE_6_GRAY, -1, -1
7004 Xfake_door_7, TRUE, FALSE,
7005 EL_EMC_GATE_7_GRAY, -1, -1
7008 Xfake_door_8, TRUE, FALSE,
7009 EL_EMC_GATE_8_GRAY, -1, -1
7012 Xfake_acid_1, TRUE, FALSE,
7013 EL_EMC_FAKE_ACID, -1, -1
7016 Xfake_acid_2, FALSE, FALSE,
7017 EL_EMC_FAKE_ACID, -1, -1
7020 Xfake_acid_3, FALSE, FALSE,
7021 EL_EMC_FAKE_ACID, -1, -1
7024 Xfake_acid_4, FALSE, FALSE,
7025 EL_EMC_FAKE_ACID, -1, -1
7028 Xfake_acid_5, FALSE, FALSE,
7029 EL_EMC_FAKE_ACID, -1, -1
7032 Xfake_acid_6, FALSE, FALSE,
7033 EL_EMC_FAKE_ACID, -1, -1
7036 Xfake_acid_7, FALSE, FALSE,
7037 EL_EMC_FAKE_ACID, -1, -1
7040 Xfake_acid_8, FALSE, FALSE,
7041 EL_EMC_FAKE_ACID, -1, -1
7044 Xsteel_1, TRUE, FALSE,
7045 EL_STEELWALL, -1, -1
7048 Xsteel_2, TRUE, FALSE,
7049 EL_EMC_STEELWALL_2, -1, -1
7052 Xsteel_3, TRUE, FALSE,
7053 EL_EMC_STEELWALL_3, -1, -1
7056 Xsteel_4, TRUE, FALSE,
7057 EL_EMC_STEELWALL_4, -1, -1
7060 Xwall_1, TRUE, FALSE,
7064 Xwall_2, TRUE, FALSE,
7065 EL_EMC_WALL_14, -1, -1
7068 Xwall_3, TRUE, FALSE,
7069 EL_EMC_WALL_15, -1, -1
7072 Xwall_4, TRUE, FALSE,
7073 EL_EMC_WALL_16, -1, -1
7076 Xround_wall_1, TRUE, FALSE,
7077 EL_WALL_SLIPPERY, -1, -1
7080 Xround_wall_2, TRUE, FALSE,
7081 EL_EMC_WALL_SLIPPERY_2, -1, -1
7084 Xround_wall_3, TRUE, FALSE,
7085 EL_EMC_WALL_SLIPPERY_3, -1, -1
7088 Xround_wall_4, TRUE, FALSE,
7089 EL_EMC_WALL_SLIPPERY_4, -1, -1
7092 Xdecor_1, TRUE, FALSE,
7093 EL_EMC_WALL_8, -1, -1
7096 Xdecor_2, TRUE, FALSE,
7097 EL_EMC_WALL_6, -1, -1
7100 Xdecor_3, TRUE, FALSE,
7101 EL_EMC_WALL_4, -1, -1
7104 Xdecor_4, TRUE, FALSE,
7105 EL_EMC_WALL_7, -1, -1
7108 Xdecor_5, TRUE, FALSE,
7109 EL_EMC_WALL_5, -1, -1
7112 Xdecor_6, TRUE, FALSE,
7113 EL_EMC_WALL_9, -1, -1
7116 Xdecor_7, TRUE, FALSE,
7117 EL_EMC_WALL_10, -1, -1
7120 Xdecor_8, TRUE, FALSE,
7121 EL_EMC_WALL_1, -1, -1
7124 Xdecor_9, TRUE, FALSE,
7125 EL_EMC_WALL_2, -1, -1
7128 Xdecor_10, TRUE, FALSE,
7129 EL_EMC_WALL_3, -1, -1
7132 Xdecor_11, TRUE, FALSE,
7133 EL_EMC_WALL_11, -1, -1
7136 Xdecor_12, TRUE, FALSE,
7137 EL_EMC_WALL_12, -1, -1
7140 Xalpha_0, TRUE, FALSE,
7141 EL_CHAR('0'), -1, -1
7144 Xalpha_1, TRUE, FALSE,
7145 EL_CHAR('1'), -1, -1
7148 Xalpha_2, TRUE, FALSE,
7149 EL_CHAR('2'), -1, -1
7152 Xalpha_3, TRUE, FALSE,
7153 EL_CHAR('3'), -1, -1
7156 Xalpha_4, TRUE, FALSE,
7157 EL_CHAR('4'), -1, -1
7160 Xalpha_5, TRUE, FALSE,
7161 EL_CHAR('5'), -1, -1
7164 Xalpha_6, TRUE, FALSE,
7165 EL_CHAR('6'), -1, -1
7168 Xalpha_7, TRUE, FALSE,
7169 EL_CHAR('7'), -1, -1
7172 Xalpha_8, TRUE, FALSE,
7173 EL_CHAR('8'), -1, -1
7176 Xalpha_9, TRUE, FALSE,
7177 EL_CHAR('9'), -1, -1
7180 Xalpha_excla, TRUE, FALSE,
7181 EL_CHAR('!'), -1, -1
7184 Xalpha_quote, TRUE, FALSE,
7185 EL_CHAR('"'), -1, -1
7188 Xalpha_comma, TRUE, FALSE,
7189 EL_CHAR(','), -1, -1
7192 Xalpha_minus, TRUE, FALSE,
7193 EL_CHAR('-'), -1, -1
7196 Xalpha_perio, TRUE, FALSE,
7197 EL_CHAR('.'), -1, -1
7200 Xalpha_colon, TRUE, FALSE,
7201 EL_CHAR(':'), -1, -1
7204 Xalpha_quest, TRUE, FALSE,
7205 EL_CHAR('?'), -1, -1
7208 Xalpha_a, TRUE, FALSE,
7209 EL_CHAR('A'), -1, -1
7212 Xalpha_b, TRUE, FALSE,
7213 EL_CHAR('B'), -1, -1
7216 Xalpha_c, TRUE, FALSE,
7217 EL_CHAR('C'), -1, -1
7220 Xalpha_d, TRUE, FALSE,
7221 EL_CHAR('D'), -1, -1
7224 Xalpha_e, TRUE, FALSE,
7225 EL_CHAR('E'), -1, -1
7228 Xalpha_f, TRUE, FALSE,
7229 EL_CHAR('F'), -1, -1
7232 Xalpha_g, TRUE, FALSE,
7233 EL_CHAR('G'), -1, -1
7236 Xalpha_h, TRUE, FALSE,
7237 EL_CHAR('H'), -1, -1
7240 Xalpha_i, TRUE, FALSE,
7241 EL_CHAR('I'), -1, -1
7244 Xalpha_j, TRUE, FALSE,
7245 EL_CHAR('J'), -1, -1
7248 Xalpha_k, TRUE, FALSE,
7249 EL_CHAR('K'), -1, -1
7252 Xalpha_l, TRUE, FALSE,
7253 EL_CHAR('L'), -1, -1
7256 Xalpha_m, TRUE, FALSE,
7257 EL_CHAR('M'), -1, -1
7260 Xalpha_n, TRUE, FALSE,
7261 EL_CHAR('N'), -1, -1
7264 Xalpha_o, TRUE, FALSE,
7265 EL_CHAR('O'), -1, -1
7268 Xalpha_p, TRUE, FALSE,
7269 EL_CHAR('P'), -1, -1
7272 Xalpha_q, TRUE, FALSE,
7273 EL_CHAR('Q'), -1, -1
7276 Xalpha_r, TRUE, FALSE,
7277 EL_CHAR('R'), -1, -1
7280 Xalpha_s, TRUE, FALSE,
7281 EL_CHAR('S'), -1, -1
7284 Xalpha_t, TRUE, FALSE,
7285 EL_CHAR('T'), -1, -1
7288 Xalpha_u, TRUE, FALSE,
7289 EL_CHAR('U'), -1, -1
7292 Xalpha_v, TRUE, FALSE,
7293 EL_CHAR('V'), -1, -1
7296 Xalpha_w, TRUE, FALSE,
7297 EL_CHAR('W'), -1, -1
7300 Xalpha_x, TRUE, FALSE,
7301 EL_CHAR('X'), -1, -1
7304 Xalpha_y, TRUE, FALSE,
7305 EL_CHAR('Y'), -1, -1
7308 Xalpha_z, TRUE, FALSE,
7309 EL_CHAR('Z'), -1, -1
7312 Xalpha_arrow_e, TRUE, FALSE,
7313 EL_CHAR('>'), -1, -1
7316 Xalpha_arrow_w, TRUE, FALSE,
7317 EL_CHAR('<'), -1, -1
7320 Xalpha_copyr, TRUE, FALSE,
7321 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7325 Xboom_bug, FALSE, FALSE,
7326 EL_BUG, ACTION_EXPLODING, -1
7329 Xboom_bomb, FALSE, FALSE,
7330 EL_BOMB, ACTION_EXPLODING, -1
7333 Xboom_android, FALSE, FALSE,
7334 EL_EMC_ANDROID, ACTION_OTHER, -1
7337 Xboom_1, FALSE, FALSE,
7338 EL_DEFAULT, ACTION_EXPLODING, -1
7341 Xboom_2, FALSE, FALSE,
7342 EL_DEFAULT, ACTION_EXPLODING, -1
7345 Znormal, FALSE, FALSE,
7349 Zdynamite, FALSE, FALSE,
7353 Zplayer, FALSE, FALSE,
7357 ZBORDER, FALSE, FALSE,
7367 static struct Mapping_EM_to_RND_player
7376 em_player_mapping_list[] =
7380 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7384 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7388 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7392 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7396 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7400 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7404 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7408 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7412 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7416 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7420 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7424 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7428 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7432 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7436 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7440 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7444 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7448 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7452 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7456 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7460 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7464 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7468 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7472 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7476 EL_PLAYER_1, ACTION_DEFAULT, -1,
7480 EL_PLAYER_2, ACTION_DEFAULT, -1,
7484 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7488 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7492 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7496 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7500 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7504 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7508 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7512 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7516 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7520 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7524 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7528 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7532 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7536 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7540 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7544 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7548 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7552 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7556 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7560 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7564 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7568 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7572 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7576 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7580 EL_PLAYER_3, ACTION_DEFAULT, -1,
7584 EL_PLAYER_4, ACTION_DEFAULT, -1,
7593 int map_element_RND_to_EM(int element_rnd)
7595 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7596 static boolean mapping_initialized = FALSE;
7598 if (!mapping_initialized)
7602 /* return "Xalpha_quest" for all undefined elements in mapping array */
7603 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7604 mapping_RND_to_EM[i] = Xalpha_quest;
7606 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7607 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7608 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7609 em_object_mapping_list[i].element_em;
7611 mapping_initialized = TRUE;
7614 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7615 return mapping_RND_to_EM[element_rnd];
7617 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7622 int map_element_EM_to_RND(int element_em)
7624 static unsigned short mapping_EM_to_RND[TILE_MAX];
7625 static boolean mapping_initialized = FALSE;
7627 if (!mapping_initialized)
7631 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7632 for (i = 0; i < TILE_MAX; i++)
7633 mapping_EM_to_RND[i] = EL_UNKNOWN;
7635 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7636 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7637 em_object_mapping_list[i].element_rnd;
7639 mapping_initialized = TRUE;
7642 if (element_em >= 0 && element_em < TILE_MAX)
7643 return mapping_EM_to_RND[element_em];
7645 Error(ERR_WARN, "invalid EM level element %d", element_em);
7650 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7652 struct LevelInfo_EM *level_em = level->native_em_level;
7653 struct LEVEL *lev = level_em->lev;
7656 for (i = 0; i < TILE_MAX; i++)
7657 lev->android_array[i] = Xblank;
7659 for (i = 0; i < level->num_android_clone_elements; i++)
7661 int element_rnd = level->android_clone_element[i];
7662 int element_em = map_element_RND_to_EM(element_rnd);
7664 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7665 if (em_object_mapping_list[j].element_rnd == element_rnd)
7666 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7670 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7672 struct LevelInfo_EM *level_em = level->native_em_level;
7673 struct LEVEL *lev = level_em->lev;
7676 level->num_android_clone_elements = 0;
7678 for (i = 0; i < TILE_MAX; i++)
7680 int element_em = lev->android_array[i];
7682 boolean element_found = FALSE;
7684 if (element_em == Xblank)
7687 element_rnd = map_element_EM_to_RND(element_em);
7689 for (j = 0; j < level->num_android_clone_elements; j++)
7690 if (level->android_clone_element[j] == element_rnd)
7691 element_found = TRUE;
7695 level->android_clone_element[level->num_android_clone_elements++] =
7698 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7703 if (level->num_android_clone_elements == 0)
7705 level->num_android_clone_elements = 1;
7706 level->android_clone_element[0] = EL_EMPTY;
7710 int map_direction_RND_to_EM(int direction)
7712 return (direction == MV_UP ? 0 :
7713 direction == MV_RIGHT ? 1 :
7714 direction == MV_DOWN ? 2 :
7715 direction == MV_LEFT ? 3 :
7719 int map_direction_EM_to_RND(int direction)
7721 return (direction == 0 ? MV_UP :
7722 direction == 1 ? MV_RIGHT :
7723 direction == 2 ? MV_DOWN :
7724 direction == 3 ? MV_LEFT :
7728 int map_element_RND_to_SP(int element_rnd)
7730 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7732 if (element_rnd >= EL_SP_START &&
7733 element_rnd <= EL_SP_END)
7734 element_sp = element_rnd - EL_SP_START;
7735 else if (element_rnd == EL_EMPTY_SPACE)
7737 else if (element_rnd == EL_INVISIBLE_WALL)
7743 int map_element_SP_to_RND(int element_sp)
7745 int element_rnd = EL_UNKNOWN;
7747 if (element_sp >= 0x00 &&
7749 element_rnd = EL_SP_START + element_sp;
7750 else if (element_sp == 0x28)
7751 element_rnd = EL_INVISIBLE_WALL;
7756 int map_action_SP_to_RND(int action_sp)
7760 case actActive: return ACTION_ACTIVE;
7761 case actImpact: return ACTION_IMPACT;
7762 case actExploding: return ACTION_EXPLODING;
7763 case actDigging: return ACTION_DIGGING;
7764 case actSnapping: return ACTION_SNAPPING;
7765 case actCollecting: return ACTION_COLLECTING;
7766 case actPassing: return ACTION_PASSING;
7767 case actPushing: return ACTION_PUSHING;
7768 case actDropping: return ACTION_DROPPING;
7770 default: return ACTION_DEFAULT;
7774 int map_element_RND_to_MM(int element_rnd)
7776 return (element_rnd >= EL_MM_START_1 &&
7777 element_rnd <= EL_MM_END_1 ?
7778 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7780 element_rnd >= EL_MM_START_2 &&
7781 element_rnd <= EL_MM_END_2 ?
7782 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7784 element_rnd >= EL_CHAR_START &&
7785 element_rnd <= EL_CHAR_END ?
7786 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7788 element_rnd >= EL_MM_RUNTIME_START &&
7789 element_rnd <= EL_MM_RUNTIME_END ?
7790 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7792 element_rnd >= EL_MM_DUMMY_START &&
7793 element_rnd <= EL_MM_DUMMY_END ?
7794 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7796 EL_MM_EMPTY_NATIVE);
7799 int map_element_MM_to_RND(int element_mm)
7801 return (element_mm == EL_MM_EMPTY_NATIVE ||
7802 element_mm == EL_DF_EMPTY_NATIVE ?
7805 element_mm >= EL_MM_START_1_NATIVE &&
7806 element_mm <= EL_MM_END_1_NATIVE ?
7807 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7809 element_mm >= EL_MM_START_2_NATIVE &&
7810 element_mm <= EL_MM_END_2_NATIVE ?
7811 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7813 element_mm >= EL_MM_CHAR_START_NATIVE &&
7814 element_mm <= EL_MM_CHAR_END_NATIVE ?
7815 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7817 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7818 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7819 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7821 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7822 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7823 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7828 int map_action_MM_to_RND(int action_mm)
7830 /* all MM actions are defined to exactly match their RND counterparts */
7834 int map_sound_MM_to_RND(int sound_mm)
7838 case SND_MM_GAME_LEVELTIME_CHARGING:
7839 return SND_GAME_LEVELTIME_CHARGING;
7841 case SND_MM_GAME_HEALTH_CHARGING:
7842 return SND_GAME_HEALTH_CHARGING;
7845 return SND_UNDEFINED;
7849 int map_mm_wall_element(int element)
7851 return (element >= EL_MM_STEEL_WALL_START &&
7852 element <= EL_MM_STEEL_WALL_END ?
7855 element >= EL_MM_WOODEN_WALL_START &&
7856 element <= EL_MM_WOODEN_WALL_END ?
7859 element >= EL_MM_ICE_WALL_START &&
7860 element <= EL_MM_ICE_WALL_END ?
7863 element >= EL_MM_AMOEBA_WALL_START &&
7864 element <= EL_MM_AMOEBA_WALL_END ?
7867 element >= EL_DF_STEEL_WALL_START &&
7868 element <= EL_DF_STEEL_WALL_END ?
7871 element >= EL_DF_WOODEN_WALL_START &&
7872 element <= EL_DF_WOODEN_WALL_END ?
7878 int map_mm_wall_element_editor(int element)
7882 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7883 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7884 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7885 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7886 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7887 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7889 default: return element;
7893 int get_next_element(int element)
7897 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7898 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7899 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7900 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7901 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7902 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7903 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7904 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7905 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7906 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7907 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7909 default: return element;
7913 int el2img_mm(int element_mm)
7915 return el2img(map_element_MM_to_RND(element_mm));
7918 int el_act_dir2img(int element, int action, int direction)
7920 element = GFX_ELEMENT(element);
7921 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7923 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7924 return element_info[element].direction_graphic[action][direction];
7927 static int el_act_dir2crm(int element, int action, int direction)
7929 element = GFX_ELEMENT(element);
7930 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7932 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7933 return element_info[element].direction_crumbled[action][direction];
7936 int el_act2img(int element, int action)
7938 element = GFX_ELEMENT(element);
7940 return element_info[element].graphic[action];
7943 int el_act2crm(int element, int action)
7945 element = GFX_ELEMENT(element);
7947 return element_info[element].crumbled[action];
7950 int el_dir2img(int element, int direction)
7952 element = GFX_ELEMENT(element);
7954 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7957 int el2baseimg(int element)
7959 return element_info[element].graphic[ACTION_DEFAULT];
7962 int el2img(int element)
7964 element = GFX_ELEMENT(element);
7966 return element_info[element].graphic[ACTION_DEFAULT];
7969 int el2edimg(int element)
7971 element = GFX_ELEMENT(element);
7973 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7976 int el2preimg(int element)
7978 element = GFX_ELEMENT(element);
7980 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7983 int el2panelimg(int element)
7985 element = GFX_ELEMENT(element);
7987 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7990 int font2baseimg(int font_nr)
7992 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7995 int getBeltNrFromBeltElement(int element)
7997 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7998 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7999 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8002 int getBeltNrFromBeltActiveElement(int element)
8004 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8005 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8006 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8009 int getBeltNrFromBeltSwitchElement(int element)
8011 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8012 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8013 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8016 int getBeltDirNrFromBeltElement(int element)
8018 static int belt_base_element[4] =
8020 EL_CONVEYOR_BELT_1_LEFT,
8021 EL_CONVEYOR_BELT_2_LEFT,
8022 EL_CONVEYOR_BELT_3_LEFT,
8023 EL_CONVEYOR_BELT_4_LEFT
8026 int belt_nr = getBeltNrFromBeltElement(element);
8027 int belt_dir_nr = element - belt_base_element[belt_nr];
8029 return (belt_dir_nr % 3);
8032 int getBeltDirNrFromBeltSwitchElement(int element)
8034 static int belt_base_element[4] =
8036 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8037 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8038 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8039 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8042 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8043 int belt_dir_nr = element - belt_base_element[belt_nr];
8045 return (belt_dir_nr % 3);
8048 int getBeltDirFromBeltElement(int element)
8050 static int belt_move_dir[3] =
8057 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8059 return belt_move_dir[belt_dir_nr];
8062 int getBeltDirFromBeltSwitchElement(int element)
8064 static int belt_move_dir[3] =
8071 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8073 return belt_move_dir[belt_dir_nr];
8076 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8078 static int belt_base_element[4] =
8080 EL_CONVEYOR_BELT_1_LEFT,
8081 EL_CONVEYOR_BELT_2_LEFT,
8082 EL_CONVEYOR_BELT_3_LEFT,
8083 EL_CONVEYOR_BELT_4_LEFT
8086 return belt_base_element[belt_nr] + belt_dir_nr;
8089 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8091 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8093 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8096 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8098 static int belt_base_element[4] =
8100 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8101 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8102 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8103 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8106 return belt_base_element[belt_nr] + belt_dir_nr;
8109 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8111 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8113 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8116 boolean getTeamMode_EM()
8118 return game.team_mode;
8121 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8123 int game_frame_delay_value;
8125 game_frame_delay_value =
8126 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8127 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8130 if (tape.playing && tape.warp_forward && !tape.pausing)
8131 game_frame_delay_value = 0;
8133 return game_frame_delay_value;
8136 unsigned int InitRND(int seed)
8138 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8139 return InitEngineRandom_EM(seed);
8140 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8141 return InitEngineRandom_SP(seed);
8142 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8143 return InitEngineRandom_MM(seed);
8145 return InitEngineRandom_RND(seed);
8148 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8149 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8151 inline static int get_effective_element_EM(int tile, int frame_em)
8153 int element = object_mapping[tile].element_rnd;
8154 int action = object_mapping[tile].action;
8155 boolean is_backside = object_mapping[tile].is_backside;
8156 boolean action_removing = (action == ACTION_DIGGING ||
8157 action == ACTION_SNAPPING ||
8158 action == ACTION_COLLECTING);
8164 case Yacid_splash_eB:
8165 case Yacid_splash_wB:
8166 return (frame_em > 5 ? EL_EMPTY : element);
8172 else /* frame_em == 7 */
8176 case Yacid_splash_eB:
8177 case Yacid_splash_wB:
8180 case Yemerald_stone:
8183 case Ydiamond_stone:
8187 case Xdrip_stretchB:
8206 case Xsand_stonein_1:
8207 case Xsand_stonein_2:
8208 case Xsand_stonein_3:
8209 case Xsand_stonein_4:
8213 return (is_backside || action_removing ? EL_EMPTY : element);
8218 inline static boolean check_linear_animation_EM(int tile)
8222 case Xsand_stonesand_1:
8223 case Xsand_stonesand_quickout_1:
8224 case Xsand_sandstone_1:
8225 case Xsand_stonein_1:
8226 case Xsand_stoneout_1:
8245 case Yacid_splash_eB:
8246 case Yacid_splash_wB:
8247 case Yemerald_stone:
8254 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8255 boolean has_crumbled_graphics,
8256 int crumbled, int sync_frame)
8258 /* if element can be crumbled, but certain action graphics are just empty
8259 space (like instantly snapping sand to empty space in 1 frame), do not
8260 treat these empty space graphics as crumbled graphics in EMC engine */
8261 if (crumbled == IMG_EMPTY_SPACE)
8262 has_crumbled_graphics = FALSE;
8264 if (has_crumbled_graphics)
8266 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8267 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8268 g_crumbled->anim_delay,
8269 g_crumbled->anim_mode,
8270 g_crumbled->anim_start_frame,
8273 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8274 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8276 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8277 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8279 g_em->has_crumbled_graphics = TRUE;
8283 g_em->crumbled_bitmap = NULL;
8284 g_em->crumbled_src_x = 0;
8285 g_em->crumbled_src_y = 0;
8286 g_em->crumbled_border_size = 0;
8287 g_em->crumbled_tile_size = 0;
8289 g_em->has_crumbled_graphics = FALSE;
8293 void ResetGfxAnimation_EM(int x, int y, int tile)
8298 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8299 int tile, int frame_em, int x, int y)
8301 int action = object_mapping[tile].action;
8302 int direction = object_mapping[tile].direction;
8303 int effective_element = get_effective_element_EM(tile, frame_em);
8304 int graphic = (direction == MV_NONE ?
8305 el_act2img(effective_element, action) :
8306 el_act_dir2img(effective_element, action, direction));
8307 struct GraphicInfo *g = &graphic_info[graphic];
8309 boolean action_removing = (action == ACTION_DIGGING ||
8310 action == ACTION_SNAPPING ||
8311 action == ACTION_COLLECTING);
8312 boolean action_moving = (action == ACTION_FALLING ||
8313 action == ACTION_MOVING ||
8314 action == ACTION_PUSHING ||
8315 action == ACTION_EATING ||
8316 action == ACTION_FILLING ||
8317 action == ACTION_EMPTYING);
8318 boolean action_falling = (action == ACTION_FALLING ||
8319 action == ACTION_FILLING ||
8320 action == ACTION_EMPTYING);
8322 /* special case: graphic uses "2nd movement tile" and has defined
8323 7 frames for movement animation (or less) => use default graphic
8324 for last (8th) frame which ends the movement animation */
8325 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8327 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
8328 graphic = (direction == MV_NONE ?
8329 el_act2img(effective_element, action) :
8330 el_act_dir2img(effective_element, action, direction));
8332 g = &graphic_info[graphic];
8335 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8339 else if (action_moving)
8341 boolean is_backside = object_mapping[tile].is_backside;
8345 int direction = object_mapping[tile].direction;
8346 int move_dir = (action_falling ? MV_DOWN : direction);
8351 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
8352 if (g->double_movement && frame_em == 0)
8356 if (move_dir == MV_LEFT)
8357 GfxFrame[x - 1][y] = GfxFrame[x][y];
8358 else if (move_dir == MV_RIGHT)
8359 GfxFrame[x + 1][y] = GfxFrame[x][y];
8360 else if (move_dir == MV_UP)
8361 GfxFrame[x][y - 1] = GfxFrame[x][y];
8362 else if (move_dir == MV_DOWN)
8363 GfxFrame[x][y + 1] = GfxFrame[x][y];
8370 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
8371 if (tile == Xsand_stonesand_quickout_1 ||
8372 tile == Xsand_stonesand_quickout_2)
8376 if (graphic_info[graphic].anim_global_sync)
8377 sync_frame = FrameCounter;
8378 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8379 sync_frame = GfxFrame[x][y];
8381 sync_frame = 0; /* playfield border (pseudo steel) */
8383 SetRandomAnimationValue(x, y);
8385 int frame = getAnimationFrame(g->anim_frames,
8388 g->anim_start_frame,
8391 g_em->unique_identifier =
8392 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8395 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8396 int tile, int frame_em, int x, int y)
8398 int action = object_mapping[tile].action;
8399 int direction = object_mapping[tile].direction;
8400 boolean is_backside = object_mapping[tile].is_backside;
8401 int effective_element = get_effective_element_EM(tile, frame_em);
8402 int effective_action = action;
8403 int graphic = (direction == MV_NONE ?
8404 el_act2img(effective_element, effective_action) :
8405 el_act_dir2img(effective_element, effective_action,
8407 int crumbled = (direction == MV_NONE ?
8408 el_act2crm(effective_element, effective_action) :
8409 el_act_dir2crm(effective_element, effective_action,
8411 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8412 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8413 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8414 struct GraphicInfo *g = &graphic_info[graphic];
8417 /* special case: graphic uses "2nd movement tile" and has defined
8418 7 frames for movement animation (or less) => use default graphic
8419 for last (8th) frame which ends the movement animation */
8420 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8422 effective_action = ACTION_DEFAULT;
8423 graphic = (direction == MV_NONE ?
8424 el_act2img(effective_element, effective_action) :
8425 el_act_dir2img(effective_element, effective_action,
8427 crumbled = (direction == MV_NONE ?
8428 el_act2crm(effective_element, effective_action) :
8429 el_act_dir2crm(effective_element, effective_action,
8432 g = &graphic_info[graphic];
8435 if (graphic_info[graphic].anim_global_sync)
8436 sync_frame = FrameCounter;
8437 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8438 sync_frame = GfxFrame[x][y];
8440 sync_frame = 0; /* playfield border (pseudo steel) */
8442 SetRandomAnimationValue(x, y);
8444 int frame = getAnimationFrame(g->anim_frames,
8447 g->anim_start_frame,
8450 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8451 g->double_movement && is_backside);
8453 /* (updating the "crumbled" graphic definitions is probably not really needed,
8454 as animations for crumbled graphics can't be longer than one EMC cycle) */
8455 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8459 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8460 int player_nr, int anim, int frame_em)
8462 int element = player_mapping[player_nr][anim].element_rnd;
8463 int action = player_mapping[player_nr][anim].action;
8464 int direction = player_mapping[player_nr][anim].direction;
8465 int graphic = (direction == MV_NONE ?
8466 el_act2img(element, action) :
8467 el_act_dir2img(element, action, direction));
8468 struct GraphicInfo *g = &graphic_info[graphic];
8471 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8473 stored_player[player_nr].StepFrame = frame_em;
8475 sync_frame = stored_player[player_nr].Frame;
8477 int frame = getAnimationFrame(g->anim_frames,
8480 g->anim_start_frame,
8483 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8484 &g_em->src_x, &g_em->src_y, FALSE);
8487 void InitGraphicInfo_EM(void)
8492 int num_em_gfx_errors = 0;
8494 if (graphic_info_em_object[0][0].bitmap == NULL)
8496 /* EM graphics not yet initialized in em_open_all() */
8501 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8504 /* always start with reliable default values */
8505 for (i = 0; i < TILE_MAX; i++)
8507 object_mapping[i].element_rnd = EL_UNKNOWN;
8508 object_mapping[i].is_backside = FALSE;
8509 object_mapping[i].action = ACTION_DEFAULT;
8510 object_mapping[i].direction = MV_NONE;
8513 /* always start with reliable default values */
8514 for (p = 0; p < MAX_PLAYERS; p++)
8516 for (i = 0; i < SPR_MAX; i++)
8518 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8519 player_mapping[p][i].action = ACTION_DEFAULT;
8520 player_mapping[p][i].direction = MV_NONE;
8524 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8526 int e = em_object_mapping_list[i].element_em;
8528 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8529 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8531 if (em_object_mapping_list[i].action != -1)
8532 object_mapping[e].action = em_object_mapping_list[i].action;
8534 if (em_object_mapping_list[i].direction != -1)
8535 object_mapping[e].direction =
8536 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8539 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8541 int a = em_player_mapping_list[i].action_em;
8542 int p = em_player_mapping_list[i].player_nr;
8544 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8546 if (em_player_mapping_list[i].action != -1)
8547 player_mapping[p][a].action = em_player_mapping_list[i].action;
8549 if (em_player_mapping_list[i].direction != -1)
8550 player_mapping[p][a].direction =
8551 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8554 for (i = 0; i < TILE_MAX; i++)
8556 int element = object_mapping[i].element_rnd;
8557 int action = object_mapping[i].action;
8558 int direction = object_mapping[i].direction;
8559 boolean is_backside = object_mapping[i].is_backside;
8560 boolean action_exploding = ((action == ACTION_EXPLODING ||
8561 action == ACTION_SMASHED_BY_ROCK ||
8562 action == ACTION_SMASHED_BY_SPRING) &&
8563 element != EL_DIAMOND);
8564 boolean action_active = (action == ACTION_ACTIVE);
8565 boolean action_other = (action == ACTION_OTHER);
8567 for (j = 0; j < 8; j++)
8569 int effective_element = get_effective_element_EM(i, j);
8570 int effective_action = (j < 7 ? action :
8571 i == Xdrip_stretch ? action :
8572 i == Xdrip_stretchB ? action :
8573 i == Ydrip_s1 ? action :
8574 i == Ydrip_s1B ? action :
8575 i == Xball_1B ? action :
8576 i == Xball_2 ? action :
8577 i == Xball_2B ? action :
8578 i == Yball_eat ? action :
8579 i == Ykey_1_eat ? action :
8580 i == Ykey_2_eat ? action :
8581 i == Ykey_3_eat ? action :
8582 i == Ykey_4_eat ? action :
8583 i == Ykey_5_eat ? action :
8584 i == Ykey_6_eat ? action :
8585 i == Ykey_7_eat ? action :
8586 i == Ykey_8_eat ? action :
8587 i == Ylenses_eat ? action :
8588 i == Ymagnify_eat ? action :
8589 i == Ygrass_eat ? action :
8590 i == Ydirt_eat ? action :
8591 i == Xsand_stonein_1 ? action :
8592 i == Xsand_stonein_2 ? action :
8593 i == Xsand_stonein_3 ? action :
8594 i == Xsand_stonein_4 ? action :
8595 i == Xsand_stoneout_1 ? action :
8596 i == Xsand_stoneout_2 ? action :
8597 i == Xboom_android ? ACTION_EXPLODING :
8598 action_exploding ? ACTION_EXPLODING :
8599 action_active ? action :
8600 action_other ? action :
8602 int graphic = (el_act_dir2img(effective_element, effective_action,
8604 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8606 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8607 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8608 boolean has_action_graphics = (graphic != base_graphic);
8609 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8610 struct GraphicInfo *g = &graphic_info[graphic];
8611 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8614 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8615 boolean special_animation = (action != ACTION_DEFAULT &&
8616 g->anim_frames == 3 &&
8617 g->anim_delay == 2 &&
8618 g->anim_mode & ANIM_LINEAR);
8619 int sync_frame = (i == Xdrip_stretch ? 7 :
8620 i == Xdrip_stretchB ? 7 :
8621 i == Ydrip_s2 ? j + 8 :
8622 i == Ydrip_s2B ? j + 8 :
8631 i == Xfake_acid_1 ? 0 :
8632 i == Xfake_acid_2 ? 10 :
8633 i == Xfake_acid_3 ? 20 :
8634 i == Xfake_acid_4 ? 30 :
8635 i == Xfake_acid_5 ? 40 :
8636 i == Xfake_acid_6 ? 50 :
8637 i == Xfake_acid_7 ? 60 :
8638 i == Xfake_acid_8 ? 70 :
8640 i == Xball_2B ? j + 8 :
8641 i == Yball_eat ? j + 1 :
8642 i == Ykey_1_eat ? j + 1 :
8643 i == Ykey_2_eat ? j + 1 :
8644 i == Ykey_3_eat ? j + 1 :
8645 i == Ykey_4_eat ? j + 1 :
8646 i == Ykey_5_eat ? j + 1 :
8647 i == Ykey_6_eat ? j + 1 :
8648 i == Ykey_7_eat ? j + 1 :
8649 i == Ykey_8_eat ? j + 1 :
8650 i == Ylenses_eat ? j + 1 :
8651 i == Ymagnify_eat ? j + 1 :
8652 i == Ygrass_eat ? j + 1 :
8653 i == Ydirt_eat ? j + 1 :
8654 i == Xamoeba_1 ? 0 :
8655 i == Xamoeba_2 ? 1 :
8656 i == Xamoeba_3 ? 2 :
8657 i == Xamoeba_4 ? 3 :
8658 i == Xamoeba_5 ? 0 :
8659 i == Xamoeba_6 ? 1 :
8660 i == Xamoeba_7 ? 2 :
8661 i == Xamoeba_8 ? 3 :
8662 i == Xexit_2 ? j + 8 :
8663 i == Xexit_3 ? j + 16 :
8664 i == Xdynamite_1 ? 0 :
8665 i == Xdynamite_2 ? 8 :
8666 i == Xdynamite_3 ? 16 :
8667 i == Xdynamite_4 ? 24 :
8668 i == Xsand_stonein_1 ? j + 1 :
8669 i == Xsand_stonein_2 ? j + 9 :
8670 i == Xsand_stonein_3 ? j + 17 :
8671 i == Xsand_stonein_4 ? j + 25 :
8672 i == Xsand_stoneout_1 && j == 0 ? 0 :
8673 i == Xsand_stoneout_1 && j == 1 ? 0 :
8674 i == Xsand_stoneout_1 && j == 2 ? 1 :
8675 i == Xsand_stoneout_1 && j == 3 ? 2 :
8676 i == Xsand_stoneout_1 && j == 4 ? 2 :
8677 i == Xsand_stoneout_1 && j == 5 ? 3 :
8678 i == Xsand_stoneout_1 && j == 6 ? 4 :
8679 i == Xsand_stoneout_1 && j == 7 ? 4 :
8680 i == Xsand_stoneout_2 && j == 0 ? 5 :
8681 i == Xsand_stoneout_2 && j == 1 ? 6 :
8682 i == Xsand_stoneout_2 && j == 2 ? 7 :
8683 i == Xsand_stoneout_2 && j == 3 ? 8 :
8684 i == Xsand_stoneout_2 && j == 4 ? 9 :
8685 i == Xsand_stoneout_2 && j == 5 ? 11 :
8686 i == Xsand_stoneout_2 && j == 6 ? 13 :
8687 i == Xsand_stoneout_2 && j == 7 ? 15 :
8688 i == Xboom_bug && j == 1 ? 2 :
8689 i == Xboom_bug && j == 2 ? 2 :
8690 i == Xboom_bug && j == 3 ? 4 :
8691 i == Xboom_bug && j == 4 ? 4 :
8692 i == Xboom_bug && j == 5 ? 2 :
8693 i == Xboom_bug && j == 6 ? 2 :
8694 i == Xboom_bug && j == 7 ? 0 :
8695 i == Xboom_bomb && j == 1 ? 2 :
8696 i == Xboom_bomb && j == 2 ? 2 :
8697 i == Xboom_bomb && j == 3 ? 4 :
8698 i == Xboom_bomb && j == 4 ? 4 :
8699 i == Xboom_bomb && j == 5 ? 2 :
8700 i == Xboom_bomb && j == 6 ? 2 :
8701 i == Xboom_bomb && j == 7 ? 0 :
8702 i == Xboom_android && j == 7 ? 6 :
8703 i == Xboom_1 && j == 1 ? 2 :
8704 i == Xboom_1 && j == 2 ? 2 :
8705 i == Xboom_1 && j == 3 ? 4 :
8706 i == Xboom_1 && j == 4 ? 4 :
8707 i == Xboom_1 && j == 5 ? 6 :
8708 i == Xboom_1 && j == 6 ? 6 :
8709 i == Xboom_1 && j == 7 ? 8 :
8710 i == Xboom_2 && j == 0 ? 8 :
8711 i == Xboom_2 && j == 1 ? 8 :
8712 i == Xboom_2 && j == 2 ? 10 :
8713 i == Xboom_2 && j == 3 ? 10 :
8714 i == Xboom_2 && j == 4 ? 10 :
8715 i == Xboom_2 && j == 5 ? 12 :
8716 i == Xboom_2 && j == 6 ? 12 :
8717 i == Xboom_2 && j == 7 ? 12 :
8718 special_animation && j == 4 ? 3 :
8719 effective_action != action ? 0 :
8723 Bitmap *debug_bitmap = g_em->bitmap;
8724 int debug_src_x = g_em->src_x;
8725 int debug_src_y = g_em->src_y;
8728 int frame = getAnimationFrame(g->anim_frames,
8731 g->anim_start_frame,
8734 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8735 g->double_movement && is_backside);
8737 g_em->bitmap = src_bitmap;
8738 g_em->src_x = src_x;
8739 g_em->src_y = src_y;
8740 g_em->src_offset_x = 0;
8741 g_em->src_offset_y = 0;
8742 g_em->dst_offset_x = 0;
8743 g_em->dst_offset_y = 0;
8744 g_em->width = TILEX;
8745 g_em->height = TILEY;
8747 g_em->preserve_background = FALSE;
8749 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8752 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8753 effective_action == ACTION_MOVING ||
8754 effective_action == ACTION_PUSHING ||
8755 effective_action == ACTION_EATING)) ||
8756 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8757 effective_action == ACTION_EMPTYING)))
8760 (effective_action == ACTION_FALLING ||
8761 effective_action == ACTION_FILLING ||
8762 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8763 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8764 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8765 int num_steps = (i == Ydrip_s1 ? 16 :
8766 i == Ydrip_s1B ? 16 :
8767 i == Ydrip_s2 ? 16 :
8768 i == Ydrip_s2B ? 16 :
8769 i == Xsand_stonein_1 ? 32 :
8770 i == Xsand_stonein_2 ? 32 :
8771 i == Xsand_stonein_3 ? 32 :
8772 i == Xsand_stonein_4 ? 32 :
8773 i == Xsand_stoneout_1 ? 16 :
8774 i == Xsand_stoneout_2 ? 16 : 8);
8775 int cx = ABS(dx) * (TILEX / num_steps);
8776 int cy = ABS(dy) * (TILEY / num_steps);
8777 int step_frame = (i == Ydrip_s2 ? j + 8 :
8778 i == Ydrip_s2B ? j + 8 :
8779 i == Xsand_stonein_2 ? j + 8 :
8780 i == Xsand_stonein_3 ? j + 16 :
8781 i == Xsand_stonein_4 ? j + 24 :
8782 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8783 int step = (is_backside ? step_frame : num_steps - step_frame);
8785 if (is_backside) /* tile where movement starts */
8787 if (dx < 0 || dy < 0)
8789 g_em->src_offset_x = cx * step;
8790 g_em->src_offset_y = cy * step;
8794 g_em->dst_offset_x = cx * step;
8795 g_em->dst_offset_y = cy * step;
8798 else /* tile where movement ends */
8800 if (dx < 0 || dy < 0)
8802 g_em->dst_offset_x = cx * step;
8803 g_em->dst_offset_y = cy * step;
8807 g_em->src_offset_x = cx * step;
8808 g_em->src_offset_y = cy * step;
8812 g_em->width = TILEX - cx * step;
8813 g_em->height = TILEY - cy * step;
8816 /* create unique graphic identifier to decide if tile must be redrawn */
8817 /* bit 31 - 16 (16 bit): EM style graphic
8818 bit 15 - 12 ( 4 bit): EM style frame
8819 bit 11 - 6 ( 6 bit): graphic width
8820 bit 5 - 0 ( 6 bit): graphic height */
8821 g_em->unique_identifier =
8822 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8826 /* skip check for EMC elements not contained in original EMC artwork */
8827 if (element == EL_EMC_FAKE_ACID)
8830 if (g_em->bitmap != debug_bitmap ||
8831 g_em->src_x != debug_src_x ||
8832 g_em->src_y != debug_src_y ||
8833 g_em->src_offset_x != 0 ||
8834 g_em->src_offset_y != 0 ||
8835 g_em->dst_offset_x != 0 ||
8836 g_em->dst_offset_y != 0 ||
8837 g_em->width != TILEX ||
8838 g_em->height != TILEY)
8840 static int last_i = -1;
8848 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8849 i, element, element_info[element].token_name,
8850 element_action_info[effective_action].suffix, direction);
8852 if (element != effective_element)
8853 printf(" [%d ('%s')]",
8855 element_info[effective_element].token_name);
8859 if (g_em->bitmap != debug_bitmap)
8860 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8861 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8863 if (g_em->src_x != debug_src_x ||
8864 g_em->src_y != debug_src_y)
8865 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8866 j, (is_backside ? 'B' : 'F'),
8867 g_em->src_x, g_em->src_y,
8868 g_em->src_x / 32, g_em->src_y / 32,
8869 debug_src_x, debug_src_y,
8870 debug_src_x / 32, debug_src_y / 32);
8872 if (g_em->src_offset_x != 0 ||
8873 g_em->src_offset_y != 0 ||
8874 g_em->dst_offset_x != 0 ||
8875 g_em->dst_offset_y != 0)
8876 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8878 g_em->src_offset_x, g_em->src_offset_y,
8879 g_em->dst_offset_x, g_em->dst_offset_y);
8881 if (g_em->width != TILEX ||
8882 g_em->height != TILEY)
8883 printf(" %d (%d): size %d,%d should be %d,%d\n",
8885 g_em->width, g_em->height, TILEX, TILEY);
8887 num_em_gfx_errors++;
8894 for (i = 0; i < TILE_MAX; i++)
8896 for (j = 0; j < 8; j++)
8898 int element = object_mapping[i].element_rnd;
8899 int action = object_mapping[i].action;
8900 int direction = object_mapping[i].direction;
8901 boolean is_backside = object_mapping[i].is_backside;
8902 int graphic_action = el_act_dir2img(element, action, direction);
8903 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8905 if ((action == ACTION_SMASHED_BY_ROCK ||
8906 action == ACTION_SMASHED_BY_SPRING ||
8907 action == ACTION_EATING) &&
8908 graphic_action == graphic_default)
8910 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8911 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8912 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8913 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8916 /* no separate animation for "smashed by rock" -- use rock instead */
8917 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8918 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8920 g_em->bitmap = g_xx->bitmap;
8921 g_em->src_x = g_xx->src_x;
8922 g_em->src_y = g_xx->src_y;
8923 g_em->src_offset_x = g_xx->src_offset_x;
8924 g_em->src_offset_y = g_xx->src_offset_y;
8925 g_em->dst_offset_x = g_xx->dst_offset_x;
8926 g_em->dst_offset_y = g_xx->dst_offset_y;
8927 g_em->width = g_xx->width;
8928 g_em->height = g_xx->height;
8929 g_em->unique_identifier = g_xx->unique_identifier;
8932 g_em->preserve_background = TRUE;
8937 for (p = 0; p < MAX_PLAYERS; p++)
8939 for (i = 0; i < SPR_MAX; i++)
8941 int element = player_mapping[p][i].element_rnd;
8942 int action = player_mapping[p][i].action;
8943 int direction = player_mapping[p][i].direction;
8945 for (j = 0; j < 8; j++)
8947 int effective_element = element;
8948 int effective_action = action;
8949 int graphic = (direction == MV_NONE ?
8950 el_act2img(effective_element, effective_action) :
8951 el_act_dir2img(effective_element, effective_action,
8953 struct GraphicInfo *g = &graphic_info[graphic];
8954 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8960 Bitmap *debug_bitmap = g_em->bitmap;
8961 int debug_src_x = g_em->src_x;
8962 int debug_src_y = g_em->src_y;
8965 int frame = getAnimationFrame(g->anim_frames,
8968 g->anim_start_frame,
8971 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8973 g_em->bitmap = src_bitmap;
8974 g_em->src_x = src_x;
8975 g_em->src_y = src_y;
8976 g_em->src_offset_x = 0;
8977 g_em->src_offset_y = 0;
8978 g_em->dst_offset_x = 0;
8979 g_em->dst_offset_y = 0;
8980 g_em->width = TILEX;
8981 g_em->height = TILEY;
8985 /* skip check for EMC elements not contained in original EMC artwork */
8986 if (element == EL_PLAYER_3 ||
8987 element == EL_PLAYER_4)
8990 if (g_em->bitmap != debug_bitmap ||
8991 g_em->src_x != debug_src_x ||
8992 g_em->src_y != debug_src_y)
8994 static int last_i = -1;
9002 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9003 p, i, element, element_info[element].token_name,
9004 element_action_info[effective_action].suffix, direction);
9006 if (element != effective_element)
9007 printf(" [%d ('%s')]",
9009 element_info[effective_element].token_name);
9013 if (g_em->bitmap != debug_bitmap)
9014 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9015 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9017 if (g_em->src_x != debug_src_x ||
9018 g_em->src_y != debug_src_y)
9019 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9021 g_em->src_x, g_em->src_y,
9022 g_em->src_x / 32, g_em->src_y / 32,
9023 debug_src_x, debug_src_y,
9024 debug_src_x / 32, debug_src_y / 32);
9026 num_em_gfx_errors++;
9036 printf("::: [%d errors found]\n", num_em_gfx_errors);
9042 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9043 boolean any_player_moving,
9044 boolean any_player_snapping,
9045 boolean any_player_dropping)
9047 if (frame == 0 && !any_player_dropping)
9049 if (!local_player->was_waiting)
9051 if (!CheckSaveEngineSnapshotToList())
9054 local_player->was_waiting = TRUE;
9057 else if (any_player_moving || any_player_snapping || any_player_dropping)
9059 local_player->was_waiting = FALSE;
9063 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9064 boolean murphy_is_dropping)
9066 if (murphy_is_waiting)
9068 if (!local_player->was_waiting)
9070 if (!CheckSaveEngineSnapshotToList())
9073 local_player->was_waiting = TRUE;
9078 local_player->was_waiting = FALSE;
9082 void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9083 boolean button_released)
9085 if (button_released)
9087 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9088 CheckSaveEngineSnapshotToList();
9090 else if (element_clicked)
9092 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9093 CheckSaveEngineSnapshotToList();
9095 game.snapshot.changed_action = TRUE;
9099 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9100 boolean any_player_moving,
9101 boolean any_player_snapping,
9102 boolean any_player_dropping)
9104 if (tape.single_step && tape.recording && !tape.pausing)
9105 if (frame == 0 && !any_player_dropping)
9106 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9108 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9109 any_player_snapping, any_player_dropping);
9112 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9113 boolean murphy_is_dropping)
9115 boolean murphy_starts_dropping = FALSE;
9118 for (i = 0; i < MAX_PLAYERS; i++)
9119 if (stored_player[i].force_dropping)
9120 murphy_starts_dropping = TRUE;
9122 if (tape.single_step && tape.recording && !tape.pausing)
9123 if (murphy_is_waiting && !murphy_starts_dropping)
9124 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9126 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9129 void CheckSingleStepMode_MM(boolean element_clicked,
9130 boolean button_released)
9132 if (tape.single_step && tape.recording && !tape.pausing)
9133 if (button_released)
9134 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9136 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9139 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9140 int graphic, int sync_frame, int x, int y)
9142 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9144 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9147 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9149 return (IS_NEXT_FRAME(sync_frame, graphic));
9152 int getGraphicInfo_Delay(int graphic)
9154 return graphic_info[graphic].anim_delay;
9157 void PlayMenuSoundExt(int sound)
9159 if (sound == SND_UNDEFINED)
9162 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9163 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9166 if (IS_LOOP_SOUND(sound))
9167 PlaySoundLoop(sound);
9172 void PlayMenuSound()
9174 PlayMenuSoundExt(menu.sound[game_status]);
9177 void PlayMenuSoundStereo(int sound, int stereo_position)
9179 if (sound == SND_UNDEFINED)
9182 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9183 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9186 if (IS_LOOP_SOUND(sound))
9187 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9189 PlaySoundStereo(sound, stereo_position);
9192 void PlayMenuSoundIfLoopExt(int sound)
9194 if (sound == SND_UNDEFINED)
9197 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9198 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9201 if (IS_LOOP_SOUND(sound))
9202 PlaySoundLoop(sound);
9205 void PlayMenuSoundIfLoop()
9207 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9210 void PlayMenuMusicExt(int music)
9212 if (music == MUS_UNDEFINED)
9215 if (!setup.sound_music)
9221 void PlayMenuMusic()
9223 char *curr_music = getCurrentlyPlayingMusicFilename();
9224 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9226 if (!strEqual(curr_music, next_music))
9227 PlayMenuMusicExt(menu.music[game_status]);
9230 void PlayMenuSoundsAndMusic()
9236 static void FadeMenuSounds()
9241 static void FadeMenuMusic()
9243 char *curr_music = getCurrentlyPlayingMusicFilename();
9244 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9246 if (!strEqual(curr_music, next_music))
9250 void FadeMenuSoundsAndMusic()
9256 void PlaySoundActivating()
9259 PlaySound(SND_MENU_ITEM_ACTIVATING);
9263 void PlaySoundSelecting()
9266 PlaySound(SND_MENU_ITEM_SELECTING);
9270 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
9272 boolean change_fullscreen = (setup.fullscreen !=
9273 video.fullscreen_enabled);
9274 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9275 setup.window_scaling_percent !=
9276 video.window_scaling_percent);
9278 if (change_window_scaling_percent && video.fullscreen_enabled)
9281 if (!change_window_scaling_percent && !video.fullscreen_available)
9284 #if defined(TARGET_SDL2)
9285 if (change_window_scaling_percent)
9287 SDLSetWindowScaling(setup.window_scaling_percent);
9291 else if (change_fullscreen)
9293 SDLSetWindowFullscreen(setup.fullscreen);
9295 /* set setup value according to successfully changed fullscreen mode */
9296 setup.fullscreen = video.fullscreen_enabled;
9302 if (change_fullscreen ||
9303 change_window_scaling_percent)
9305 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9307 /* save backbuffer content which gets lost when toggling fullscreen mode */
9308 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9310 if (change_window_scaling_percent)
9312 /* keep window mode, but change window scaling */
9313 video.fullscreen_enabled = TRUE; /* force new window scaling */
9316 /* toggle fullscreen */
9317 ChangeVideoModeIfNeeded(setup.fullscreen);
9319 /* set setup value according to successfully changed fullscreen mode */
9320 setup.fullscreen = video.fullscreen_enabled;
9322 /* restore backbuffer content from temporary backbuffer backup bitmap */
9323 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9325 FreeBitmap(tmp_backbuffer);
9327 /* update visible window/screen */
9328 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9332 void JoinRectangles(int *x, int *y, int *width, int *height,
9333 int x2, int y2, int width2, int height2)
9335 // do not join with "off-screen" rectangle
9336 if (x2 == -1 || y2 == -1)
9341 *width = MAX(*width, width2);
9342 *height = MAX(*height, height2);
9345 void SetAnimStatus(int anim_status_new)
9347 if (anim_status_new == GAME_MODE_MAIN)
9348 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9349 else if (anim_status_new == GAME_MODE_SCORES)
9350 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9352 global.anim_status_next = anim_status_new;
9354 // directly set screen modes that are entered without fading
9355 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9356 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9357 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9358 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9359 global.anim_status = global.anim_status_next;
9362 void SetGameStatus(int game_status_new)
9364 if (game_status_new != game_status)
9365 game_status_last_screen = game_status;
9367 game_status = game_status_new;
9369 SetAnimStatus(game_status_new);
9372 void SetFontStatus(int game_status_new)
9374 static int last_game_status = -1;
9376 if (game_status_new != -1)
9378 // set game status for font use after storing last game status
9379 last_game_status = game_status;
9380 game_status = game_status_new;
9384 // reset game status after font use from last stored game status
9385 game_status = last_game_status;
9389 void ResetFontStatus()
9394 boolean CheckIfPlayfieldViewportHasChanged()
9396 // if game status has not changed, playfield viewport has not changed either
9397 if (game_status == game_status_last)
9400 // check if playfield viewport has changed with current game status
9401 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9402 int new_real_sx = vp_playfield->x;
9403 int new_real_sy = vp_playfield->y;
9404 int new_full_sxsize = vp_playfield->width;
9405 int new_full_sysize = vp_playfield->height;
9407 return (new_real_sx != REAL_SX ||
9408 new_real_sy != REAL_SY ||
9409 new_full_sxsize != FULL_SXSIZE ||
9410 new_full_sysize != FULL_SYSIZE);
9413 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
9415 return (CheckIfGlobalBorderHasChanged() ||
9416 CheckIfPlayfieldViewportHasChanged());
9419 void ChangeViewportPropertiesIfNeeded()
9421 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9422 FALSE : setup.small_game_graphics);
9423 int gfx_game_mode = game_status;
9424 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9426 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9427 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9428 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9429 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9430 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9431 int new_win_xsize = vp_window->width;
9432 int new_win_ysize = vp_window->height;
9433 int border_size = vp_playfield->border_size;
9434 int new_sx = vp_playfield->x + border_size;
9435 int new_sy = vp_playfield->y + border_size;
9436 int new_sxsize = vp_playfield->width - 2 * border_size;
9437 int new_sysize = vp_playfield->height - 2 * border_size;
9438 int new_real_sx = vp_playfield->x;
9439 int new_real_sy = vp_playfield->y;
9440 int new_full_sxsize = vp_playfield->width;
9441 int new_full_sysize = vp_playfield->height;
9442 int new_dx = vp_door_1->x;
9443 int new_dy = vp_door_1->y;
9444 int new_dxsize = vp_door_1->width;
9445 int new_dysize = vp_door_1->height;
9446 int new_vx = vp_door_2->x;
9447 int new_vy = vp_door_2->y;
9448 int new_vxsize = vp_door_2->width;
9449 int new_vysize = vp_door_2->height;
9450 int new_ex = vp_door_3->x;
9451 int new_ey = vp_door_3->y;
9452 int new_exsize = vp_door_3->width;
9453 int new_eysize = vp_door_3->height;
9454 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9455 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9456 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9457 int new_scr_fieldx = new_sxsize / tilesize;
9458 int new_scr_fieldy = new_sysize / tilesize;
9459 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9460 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9461 boolean init_gfx_buffers = FALSE;
9462 boolean init_video_buffer = FALSE;
9463 boolean init_gadgets_and_anims = FALSE;
9464 boolean init_em_graphics = FALSE;
9466 if (new_win_xsize != WIN_XSIZE ||
9467 new_win_ysize != WIN_YSIZE)
9469 WIN_XSIZE = new_win_xsize;
9470 WIN_YSIZE = new_win_ysize;
9472 init_video_buffer = TRUE;
9473 init_gfx_buffers = TRUE;
9474 init_gadgets_and_anims = TRUE;
9476 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9479 if (new_scr_fieldx != SCR_FIELDX ||
9480 new_scr_fieldy != SCR_FIELDY)
9482 /* this always toggles between MAIN and GAME when using small tile size */
9484 SCR_FIELDX = new_scr_fieldx;
9485 SCR_FIELDY = new_scr_fieldy;
9487 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9498 new_sxsize != SXSIZE ||
9499 new_sysize != SYSIZE ||
9500 new_dxsize != DXSIZE ||
9501 new_dysize != DYSIZE ||
9502 new_vxsize != VXSIZE ||
9503 new_vysize != VYSIZE ||
9504 new_exsize != EXSIZE ||
9505 new_eysize != EYSIZE ||
9506 new_real_sx != REAL_SX ||
9507 new_real_sy != REAL_SY ||
9508 new_full_sxsize != FULL_SXSIZE ||
9509 new_full_sysize != FULL_SYSIZE ||
9510 new_tilesize_var != TILESIZE_VAR
9513 // ------------------------------------------------------------------------
9514 // determine next fading area for changed viewport definitions
9515 // ------------------------------------------------------------------------
9517 // start with current playfield area (default fading area)
9520 FADE_SXSIZE = FULL_SXSIZE;
9521 FADE_SYSIZE = FULL_SYSIZE;
9523 // add new playfield area if position or size has changed
9524 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9525 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9527 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9528 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9531 // add current and new door 1 area if position or size has changed
9532 if (new_dx != DX || new_dy != DY ||
9533 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9535 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9536 DX, DY, DXSIZE, DYSIZE);
9537 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9538 new_dx, new_dy, new_dxsize, new_dysize);
9541 // add current and new door 2 area if position or size has changed
9542 if (new_dx != VX || new_dy != VY ||
9543 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9545 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9546 VX, VY, VXSIZE, VYSIZE);
9547 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9548 new_vx, new_vy, new_vxsize, new_vysize);
9551 // ------------------------------------------------------------------------
9552 // handle changed tile size
9553 // ------------------------------------------------------------------------
9555 if (new_tilesize_var != TILESIZE_VAR)
9557 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9559 // changing tile size invalidates scroll values of engine snapshots
9560 FreeEngineSnapshotSingle();
9562 // changing tile size requires update of graphic mapping for EM engine
9563 init_em_graphics = TRUE;
9574 SXSIZE = new_sxsize;
9575 SYSIZE = new_sysize;
9576 DXSIZE = new_dxsize;
9577 DYSIZE = new_dysize;
9578 VXSIZE = new_vxsize;
9579 VYSIZE = new_vysize;
9580 EXSIZE = new_exsize;
9581 EYSIZE = new_eysize;
9582 REAL_SX = new_real_sx;
9583 REAL_SY = new_real_sy;
9584 FULL_SXSIZE = new_full_sxsize;
9585 FULL_SYSIZE = new_full_sysize;
9586 TILESIZE_VAR = new_tilesize_var;
9588 init_gfx_buffers = TRUE;
9589 init_gadgets_and_anims = TRUE;
9591 // printf("::: viewports: init_gfx_buffers\n");
9592 // printf("::: viewports: init_gadgets_and_anims\n");
9595 if (init_gfx_buffers)
9597 // printf("::: init_gfx_buffers\n");
9599 SCR_FIELDX = new_scr_fieldx_buffers;
9600 SCR_FIELDY = new_scr_fieldy_buffers;
9604 SCR_FIELDX = new_scr_fieldx;
9605 SCR_FIELDY = new_scr_fieldy;
9607 SetDrawDeactivationMask(REDRAW_NONE);
9608 SetDrawBackgroundMask(REDRAW_FIELD);
9611 if (init_video_buffer)
9613 // printf("::: init_video_buffer\n");
9615 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9616 InitImageTextures();
9619 if (init_gadgets_and_anims)
9621 // printf("::: init_gadgets_and_anims\n");
9624 InitGlobalAnimations();
9627 if (init_em_graphics)
9629 InitGraphicInfo_EM();