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;
986 static void SetScreenStates_BeforeFadingIn()
988 // temporarily set screen mode for animations to screen after fading in
989 global.anim_status = global.anim_status_next;
991 // store backbuffer with all animations that will be started after fading in
992 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
993 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
995 // set screen mode for animations back to fading
996 global.anim_status = GAME_MODE_PSEUDO_FADING;
999 static void SetScreenStates_AfterFadingIn()
1001 // store new source screen (to use correct masked border for fading)
1002 gfx.fade_border_source_status = global.border_status;
1004 global.anim_status = global.anim_status_next;
1007 static void SetScreenStates_BeforeFadingOut()
1009 // store new target screen (to use correct masked border for fading)
1010 gfx.fade_border_target_status = game_status;
1012 // set screen mode for animations to fading
1013 global.anim_status = GAME_MODE_PSEUDO_FADING;
1015 // store backbuffer with all animations that will be stopped for fading out
1016 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1017 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1020 static void SetScreenStates_AfterFadingOut()
1022 global.border_status = game_status;
1025 void FadeIn(int fade_mask)
1027 SetScreenStates_BeforeFadingIn();
1030 DrawMaskedBorder(REDRAW_ALL);
1033 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1034 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1036 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1040 FADE_SXSIZE = FULL_SXSIZE;
1041 FADE_SYSIZE = FULL_SYSIZE;
1043 if (game_status == GAME_MODE_PLAYING &&
1044 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1045 SetOverlayActive(TRUE);
1047 SetScreenStates_AfterFadingIn();
1049 // force update of global animation status in case of rapid screen changes
1050 redraw_mask = REDRAW_ALL;
1054 void FadeOut(int fade_mask)
1056 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1057 if (!equalRedrawMasks(fade_mask, redraw_mask))
1060 SetScreenStates_BeforeFadingOut();
1062 SetTileCursorActive(FALSE);
1063 SetOverlayActive(FALSE);
1066 DrawMaskedBorder(REDRAW_ALL);
1069 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1070 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1072 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1074 SetScreenStates_AfterFadingOut();
1077 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1079 static struct TitleFadingInfo fading_leave_stored;
1082 fading_leave_stored = fading_leave;
1084 fading = fading_leave_stored;
1087 void FadeSetEnterMenu()
1089 fading = menu.enter_menu;
1091 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1094 void FadeSetLeaveMenu()
1096 fading = menu.leave_menu;
1098 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1101 void FadeSetEnterScreen()
1103 fading = menu.enter_screen[game_status];
1105 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1108 void FadeSetNextScreen()
1110 fading = menu.next_screen[game_status];
1112 // (do not overwrite fade mode set by FadeSetEnterScreen)
1113 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1116 void FadeSetLeaveScreen()
1118 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1121 void FadeSetFromType(int type)
1123 if (type & TYPE_ENTER_SCREEN)
1124 FadeSetEnterScreen();
1125 else if (type & TYPE_ENTER)
1127 else if (type & TYPE_LEAVE)
1131 void FadeSetDisabled()
1133 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1135 fading = fading_none;
1138 void FadeSkipNextFadeIn()
1140 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1143 void FadeSkipNextFadeOut()
1145 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1148 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1150 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1152 return (graphic == IMG_UNDEFINED ? NULL :
1153 graphic_info[graphic].bitmap != NULL || redefined ?
1154 graphic_info[graphic].bitmap :
1155 graphic_info[default_graphic].bitmap);
1158 Bitmap *getBackgroundBitmap(int graphic)
1160 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1163 Bitmap *getGlobalBorderBitmap(int graphic)
1165 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1168 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1171 (status == GAME_MODE_MAIN ||
1172 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1173 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1174 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1175 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1178 return getGlobalBorderBitmap(graphic);
1181 void SetWindowBackgroundImageIfDefined(int graphic)
1183 if (graphic_info[graphic].bitmap)
1184 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1187 void SetMainBackgroundImageIfDefined(int graphic)
1189 if (graphic_info[graphic].bitmap)
1190 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1193 void SetDoorBackgroundImageIfDefined(int graphic)
1195 if (graphic_info[graphic].bitmap)
1196 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1199 void SetWindowBackgroundImage(int graphic)
1201 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1204 void SetMainBackgroundImage(int graphic)
1206 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1209 void SetDoorBackgroundImage(int graphic)
1211 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1214 void SetPanelBackground()
1216 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1218 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1219 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1221 SetDoorBackgroundBitmap(bitmap_db_panel);
1224 void DrawBackground(int x, int y, int width, int height)
1226 /* "drawto" might still point to playfield buffer here (hall of fame) */
1227 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1229 if (IN_GFX_FIELD_FULL(x, y))
1230 redraw_mask |= REDRAW_FIELD;
1231 else if (IN_GFX_DOOR_1(x, y))
1232 redraw_mask |= REDRAW_DOOR_1;
1233 else if (IN_GFX_DOOR_2(x, y))
1234 redraw_mask |= REDRAW_DOOR_2;
1235 else if (IN_GFX_DOOR_3(x, y))
1236 redraw_mask |= REDRAW_DOOR_3;
1239 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1241 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1243 if (font->bitmap == NULL)
1246 DrawBackground(x, y, width, height);
1249 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1251 struct GraphicInfo *g = &graphic_info[graphic];
1253 if (g->bitmap == NULL)
1256 DrawBackground(x, y, width, height);
1259 static int game_status_last = -1;
1260 static Bitmap *global_border_bitmap_last = NULL;
1261 static Bitmap *global_border_bitmap = NULL;
1262 static int real_sx_last = -1, real_sy_last = -1;
1263 static int full_sxsize_last = -1, full_sysize_last = -1;
1264 static int dx_last = -1, dy_last = -1;
1265 static int dxsize_last = -1, dysize_last = -1;
1266 static int vx_last = -1, vy_last = -1;
1267 static int vxsize_last = -1, vysize_last = -1;
1268 static int ex_last = -1, ey_last = -1;
1269 static int exsize_last = -1, eysize_last = -1;
1271 boolean CheckIfGlobalBorderHasChanged()
1273 // if game status has not changed, global border has not changed either
1274 if (game_status == game_status_last)
1277 // determine and store new global border bitmap for current game status
1278 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1280 return (global_border_bitmap_last != global_border_bitmap);
1283 boolean CheckIfGlobalBorderRedrawIsNeeded()
1285 // if game status has not changed, nothing has to be redrawn
1286 if (game_status == game_status_last)
1289 // redraw if last screen was title screen
1290 if (game_status_last == GAME_MODE_TITLE)
1293 // redraw if global screen border has changed
1294 if (CheckIfGlobalBorderHasChanged())
1297 // redraw if position or size of playfield area has changed
1298 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1299 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1302 // redraw if position or size of door area has changed
1303 if (dx_last != DX || dy_last != DY ||
1304 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1307 // redraw if position or size of tape area has changed
1308 if (vx_last != VX || vy_last != VY ||
1309 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1312 // redraw if position or size of editor area has changed
1313 if (ex_last != EX || ey_last != EY ||
1314 exsize_last != EXSIZE || eysize_last != EYSIZE)
1320 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1323 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1325 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1328 void RedrawGlobalBorder()
1330 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1332 RedrawGlobalBorderFromBitmap(bitmap);
1334 redraw_mask = REDRAW_ALL;
1337 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1339 static void RedrawGlobalBorderIfNeeded()
1341 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1342 if (game_status == game_status_last)
1346 // copy current draw buffer to later copy back areas that have not changed
1347 if (game_status_last != GAME_MODE_TITLE)
1348 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1350 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1351 if (CheckIfGlobalBorderRedrawIsNeeded())
1354 // redraw global screen border (or clear, if defined to be empty)
1355 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1357 if (game_status == GAME_MODE_EDITOR)
1358 DrawSpecialEditorDoor();
1360 // copy previous playfield and door areas, if they are defined on both
1361 // previous and current screen and if they still have the same size
1363 if (real_sx_last != -1 && real_sy_last != -1 &&
1364 REAL_SX != -1 && REAL_SY != -1 &&
1365 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1366 BlitBitmap(bitmap_db_store_1, backbuffer,
1367 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1370 if (dx_last != -1 && dy_last != -1 &&
1371 DX != -1 && DY != -1 &&
1372 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1373 BlitBitmap(bitmap_db_store_1, backbuffer,
1374 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1376 if (game_status != GAME_MODE_EDITOR)
1378 if (vx_last != -1 && vy_last != -1 &&
1379 VX != -1 && VY != -1 &&
1380 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1381 BlitBitmap(bitmap_db_store_1, backbuffer,
1382 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1386 if (ex_last != -1 && ey_last != -1 &&
1387 EX != -1 && EY != -1 &&
1388 exsize_last == EXSIZE && eysize_last == EYSIZE)
1389 BlitBitmap(bitmap_db_store_1, backbuffer,
1390 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1393 redraw_mask = REDRAW_ALL;
1396 game_status_last = game_status;
1398 global_border_bitmap_last = global_border_bitmap;
1400 real_sx_last = REAL_SX;
1401 real_sy_last = REAL_SY;
1402 full_sxsize_last = FULL_SXSIZE;
1403 full_sysize_last = FULL_SYSIZE;
1406 dxsize_last = DXSIZE;
1407 dysize_last = DYSIZE;
1410 vxsize_last = VXSIZE;
1411 vysize_last = VYSIZE;
1414 exsize_last = EXSIZE;
1415 eysize_last = EYSIZE;
1420 RedrawGlobalBorderIfNeeded();
1422 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1423 /* (when entering hall of fame after playing) */
1424 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1426 /* !!! maybe this should be done before clearing the background !!! */
1427 if (game_status == GAME_MODE_PLAYING)
1429 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1430 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1434 SetDrawtoField(DRAW_TO_BACKBUFFER);
1438 void MarkTileDirty(int x, int y)
1440 redraw_mask |= REDRAW_FIELD;
1443 void SetBorderElement()
1447 BorderElement = EL_EMPTY;
1449 /* the MM game engine does not use a visible border element */
1450 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1453 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1455 for (x = 0; x < lev_fieldx; x++)
1457 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1458 BorderElement = EL_STEELWALL;
1460 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1466 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1467 int max_array_fieldx, int max_array_fieldy,
1468 short field[max_array_fieldx][max_array_fieldy],
1469 int max_fieldx, int max_fieldy)
1473 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1474 static int safety = 0;
1476 /* check if starting field still has the desired content */
1477 if (field[from_x][from_y] == fill_element)
1482 if (safety > max_fieldx * max_fieldy)
1483 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1485 old_element = field[from_x][from_y];
1486 field[from_x][from_y] = fill_element;
1488 for (i = 0; i < 4; i++)
1490 x = from_x + check[i][0];
1491 y = from_y + check[i][1];
1493 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1494 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1495 field, max_fieldx, max_fieldy);
1501 void FloodFillLevel(int from_x, int from_y, int fill_element,
1502 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1503 int max_fieldx, int max_fieldy)
1505 FloodFillLevelExt(from_x, from_y, fill_element,
1506 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1507 max_fieldx, max_fieldy);
1510 void SetRandomAnimationValue(int x, int y)
1512 gfx.anim_random_frame = GfxRandom[x][y];
1515 int getGraphicAnimationFrame(int graphic, int sync_frame)
1517 /* animation synchronized with global frame counter, not move position */
1518 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1519 sync_frame = FrameCounter;
1521 return getAnimationFrame(graphic_info[graphic].anim_frames,
1522 graphic_info[graphic].anim_delay,
1523 graphic_info[graphic].anim_mode,
1524 graphic_info[graphic].anim_start_frame,
1528 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1530 struct GraphicInfo *g = &graphic_info[graphic];
1531 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1533 if (tilesize == gfx.standard_tile_size)
1534 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1535 else if (tilesize == game.tile_size)
1536 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1538 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1541 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1542 boolean get_backside)
1544 struct GraphicInfo *g = &graphic_info[graphic];
1545 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1546 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1548 if (g->offset_y == 0) /* frames are ordered horizontally */
1550 int max_width = g->anim_frames_per_line * g->width;
1551 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1553 *x = pos % max_width;
1554 *y = src_y % g->height + pos / max_width * g->height;
1556 else if (g->offset_x == 0) /* frames are ordered vertically */
1558 int max_height = g->anim_frames_per_line * g->height;
1559 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1561 *x = src_x % g->width + pos / max_height * g->width;
1562 *y = pos % max_height;
1564 else /* frames are ordered diagonally */
1566 *x = src_x + frame * g->offset_x;
1567 *y = src_y + frame * g->offset_y;
1571 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1572 Bitmap **bitmap, int *x, int *y,
1573 boolean get_backside)
1575 struct GraphicInfo *g = &graphic_info[graphic];
1577 // if no graphics defined at all, use fallback graphics
1578 if (g->bitmaps == NULL)
1579 *g = graphic_info[IMG_CHAR_EXCLAM];
1581 // if no in-game graphics defined, always use standard graphic size
1582 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1583 tilesize = TILESIZE;
1585 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1586 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1588 *x = *x * tilesize / g->tile_size;
1589 *y = *y * tilesize / g->tile_size;
1592 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1593 Bitmap **bitmap, int *x, int *y)
1595 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1598 void getFixedGraphicSource(int graphic, int frame,
1599 Bitmap **bitmap, int *x, int *y)
1601 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1604 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1606 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1609 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1610 int *x, int *y, boolean get_backside)
1612 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1616 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1618 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1621 void DrawGraphic(int x, int y, int graphic, int frame)
1624 if (!IN_SCR_FIELD(x, y))
1626 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1627 printf("DrawGraphic(): This should never happen!\n");
1632 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1635 MarkTileDirty(x, y);
1638 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1641 if (!IN_SCR_FIELD(x, y))
1643 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1644 printf("DrawGraphic(): This should never happen!\n");
1649 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1651 MarkTileDirty(x, y);
1654 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1660 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1662 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1665 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1671 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1672 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1675 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1678 if (!IN_SCR_FIELD(x, y))
1680 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1681 printf("DrawGraphicThruMask(): This should never happen!\n");
1686 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1689 MarkTileDirty(x, y);
1692 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1695 if (!IN_SCR_FIELD(x, y))
1697 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1698 printf("DrawGraphicThruMask(): This should never happen!\n");
1703 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1705 MarkTileDirty(x, y);
1708 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1714 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1716 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1720 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1721 int graphic, int frame)
1726 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1728 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1732 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1734 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1736 MarkTileDirty(x / tilesize, y / tilesize);
1739 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1742 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1743 graphic, frame, tilesize);
1744 MarkTileDirty(x / tilesize, y / tilesize);
1747 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1753 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1754 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1757 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1758 int frame, int tilesize)
1763 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1764 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1767 void DrawMiniGraphic(int x, int y, int graphic)
1769 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1770 MarkTileDirty(x / 2, y / 2);
1773 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1778 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1779 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1782 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1783 int graphic, int frame,
1784 int cut_mode, int mask_mode)
1789 int width = TILEX, height = TILEY;
1792 if (dx || dy) /* shifted graphic */
1794 if (x < BX1) /* object enters playfield from the left */
1801 else if (x > BX2) /* object enters playfield from the right */
1807 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1813 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1815 else if (dx) /* general horizontal movement */
1816 MarkTileDirty(x + SIGN(dx), y);
1818 if (y < BY1) /* object enters playfield from the top */
1820 if (cut_mode == CUT_BELOW) /* object completely above top border */
1828 else if (y > BY2) /* object enters playfield from the bottom */
1834 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1840 else if (dy > 0 && cut_mode == CUT_ABOVE)
1842 if (y == BY2) /* object completely above bottom border */
1848 MarkTileDirty(x, y + 1);
1849 } /* object leaves playfield to the bottom */
1850 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1852 else if (dy) /* general vertical movement */
1853 MarkTileDirty(x, y + SIGN(dy));
1857 if (!IN_SCR_FIELD(x, y))
1859 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1860 printf("DrawGraphicShifted(): This should never happen!\n");
1865 width = width * TILESIZE_VAR / TILESIZE;
1866 height = height * TILESIZE_VAR / TILESIZE;
1867 cx = cx * TILESIZE_VAR / TILESIZE;
1868 cy = cy * TILESIZE_VAR / TILESIZE;
1869 dx = dx * TILESIZE_VAR / TILESIZE;
1870 dy = dy * TILESIZE_VAR / TILESIZE;
1872 if (width > 0 && height > 0)
1874 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1879 dst_x = FX + x * TILEX_VAR + dx;
1880 dst_y = FY + y * TILEY_VAR + dy;
1882 if (mask_mode == USE_MASKING)
1883 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1886 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1889 MarkTileDirty(x, y);
1893 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1894 int graphic, int frame,
1895 int cut_mode, int mask_mode)
1900 int width = TILEX_VAR, height = TILEY_VAR;
1903 int x2 = x + SIGN(dx);
1904 int y2 = y + SIGN(dy);
1906 /* movement with two-tile animations must be sync'ed with movement position,
1907 not with current GfxFrame (which can be higher when using slow movement) */
1908 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1909 int anim_frames = graphic_info[graphic].anim_frames;
1911 /* (we also need anim_delay here for movement animations with less frames) */
1912 int anim_delay = graphic_info[graphic].anim_delay;
1913 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1915 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1916 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1918 /* re-calculate animation frame for two-tile movement animation */
1919 frame = getGraphicAnimationFrame(graphic, sync_frame);
1921 /* check if movement start graphic inside screen area and should be drawn */
1922 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1924 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1926 dst_x = FX + x1 * TILEX_VAR;
1927 dst_y = FY + y1 * TILEY_VAR;
1929 if (mask_mode == USE_MASKING)
1930 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1933 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1936 MarkTileDirty(x1, y1);
1939 /* check if movement end graphic inside screen area and should be drawn */
1940 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1942 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1944 dst_x = FX + x2 * TILEX_VAR;
1945 dst_y = FY + y2 * TILEY_VAR;
1947 if (mask_mode == USE_MASKING)
1948 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1951 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 MarkTileDirty(x2, y2);
1958 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1959 int graphic, int frame,
1960 int cut_mode, int mask_mode)
1964 DrawGraphic(x, y, graphic, frame);
1969 if (graphic_info[graphic].double_movement) /* EM style movement images */
1970 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1972 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1975 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1976 int frame, int cut_mode)
1978 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1981 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1982 int cut_mode, int mask_mode)
1984 int lx = LEVELX(x), ly = LEVELY(y);
1988 if (IN_LEV_FIELD(lx, ly))
1990 SetRandomAnimationValue(lx, ly);
1992 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1993 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1995 /* do not use double (EM style) movement graphic when not moving */
1996 if (graphic_info[graphic].double_movement && !dx && !dy)
1998 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1999 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2002 else /* border element */
2004 graphic = el2img(element);
2005 frame = getGraphicAnimationFrame(graphic, -1);
2008 if (element == EL_EXPANDABLE_WALL)
2010 boolean left_stopped = FALSE, right_stopped = FALSE;
2012 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2013 left_stopped = TRUE;
2014 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2015 right_stopped = TRUE;
2017 if (left_stopped && right_stopped)
2019 else if (left_stopped)
2021 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2022 frame = graphic_info[graphic].anim_frames - 1;
2024 else if (right_stopped)
2026 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2027 frame = graphic_info[graphic].anim_frames - 1;
2032 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2033 else if (mask_mode == USE_MASKING)
2034 DrawGraphicThruMask(x, y, graphic, frame);
2036 DrawGraphic(x, y, graphic, frame);
2039 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2040 int cut_mode, int mask_mode)
2042 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2043 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2044 cut_mode, mask_mode);
2047 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2050 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2053 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2056 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2059 void DrawLevelElementThruMask(int x, int y, int element)
2061 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2064 void DrawLevelFieldThruMask(int x, int y)
2066 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2069 /* !!! implementation of quicksand is totally broken !!! */
2070 #define IS_CRUMBLED_TILE(x, y, e) \
2071 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2072 !IS_MOVING(x, y) || \
2073 (e) == EL_QUICKSAND_EMPTYING || \
2074 (e) == EL_QUICKSAND_FAST_EMPTYING))
2076 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2081 int width, height, cx, cy;
2082 int sx = SCREENX(x), sy = SCREENY(y);
2083 int crumbled_border_size = graphic_info[graphic].border_size;
2084 int crumbled_tile_size = graphic_info[graphic].tile_size;
2085 int crumbled_border_size_var =
2086 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2089 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2091 for (i = 1; i < 4; i++)
2093 int dxx = (i & 1 ? dx : 0);
2094 int dyy = (i & 2 ? dy : 0);
2097 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2100 /* check if neighbour field is of same crumble type */
2101 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2102 graphic_info[graphic].class ==
2103 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2105 /* return if check prevents inner corner */
2106 if (same == (dxx == dx && dyy == dy))
2110 /* if we reach this point, we have an inner corner */
2112 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2114 width = crumbled_border_size_var;
2115 height = crumbled_border_size_var;
2116 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2117 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2119 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2120 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2123 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2128 int width, height, bx, by, cx, cy;
2129 int sx = SCREENX(x), sy = SCREENY(y);
2130 int crumbled_border_size = graphic_info[graphic].border_size;
2131 int crumbled_tile_size = graphic_info[graphic].tile_size;
2132 int crumbled_border_size_var =
2133 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2134 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2137 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2139 /* draw simple, sloppy, non-corner-accurate crumbled border */
2141 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2142 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2143 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2144 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2146 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2147 FX + sx * TILEX_VAR + cx,
2148 FY + sy * TILEY_VAR + cy);
2150 /* (remaining middle border part must be at least as big as corner part) */
2151 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2152 crumbled_border_size_var >= TILESIZE_VAR / 3)
2155 /* correct corners of crumbled border, if needed */
2157 for (i = -1; i <= 1; i += 2)
2159 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2160 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2161 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2164 /* check if neighbour field is of same crumble type */
2165 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2166 graphic_info[graphic].class ==
2167 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2169 /* no crumbled corner, but continued crumbled border */
2171 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2172 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2173 int b1 = (i == 1 ? crumbled_border_size_var :
2174 TILESIZE_VAR - 2 * crumbled_border_size_var);
2176 width = crumbled_border_size_var;
2177 height = crumbled_border_size_var;
2179 if (dir == 1 || dir == 2)
2194 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2196 FX + sx * TILEX_VAR + cx,
2197 FY + sy * TILEY_VAR + cy);
2202 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2204 int sx = SCREENX(x), sy = SCREENY(y);
2207 static int xy[4][2] =
2215 if (!IN_LEV_FIELD(x, y))
2218 element = TILE_GFX_ELEMENT(x, y);
2220 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2222 if (!IN_SCR_FIELD(sx, sy))
2225 /* crumble field borders towards direct neighbour fields */
2226 for (i = 0; i < 4; i++)
2228 int xx = x + xy[i][0];
2229 int yy = y + xy[i][1];
2231 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2234 /* check if neighbour field is of same crumble type */
2235 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2236 graphic_info[graphic].class ==
2237 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2240 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2243 /* crumble inner field corners towards corner neighbour fields */
2244 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2245 graphic_info[graphic].anim_frames == 2)
2247 for (i = 0; i < 4; i++)
2249 int dx = (i & 1 ? +1 : -1);
2250 int dy = (i & 2 ? +1 : -1);
2252 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2256 MarkTileDirty(sx, sy);
2258 else /* center field is not crumbled -- crumble neighbour fields */
2260 /* crumble field borders of direct neighbour fields */
2261 for (i = 0; i < 4; i++)
2263 int xx = x + xy[i][0];
2264 int yy = y + xy[i][1];
2265 int sxx = sx + xy[i][0];
2266 int syy = sy + xy[i][1];
2268 if (!IN_LEV_FIELD(xx, yy) ||
2269 !IN_SCR_FIELD(sxx, syy))
2272 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2275 element = TILE_GFX_ELEMENT(xx, yy);
2277 if (!IS_CRUMBLED_TILE(xx, yy, element))
2280 graphic = el_act2crm(element, ACTION_DEFAULT);
2282 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2284 MarkTileDirty(sxx, syy);
2287 /* crumble inner field corners of corner neighbour fields */
2288 for (i = 0; i < 4; i++)
2290 int dx = (i & 1 ? +1 : -1);
2291 int dy = (i & 2 ? +1 : -1);
2297 if (!IN_LEV_FIELD(xx, yy) ||
2298 !IN_SCR_FIELD(sxx, syy))
2301 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2304 element = TILE_GFX_ELEMENT(xx, yy);
2306 if (!IS_CRUMBLED_TILE(xx, yy, element))
2309 graphic = el_act2crm(element, ACTION_DEFAULT);
2311 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2312 graphic_info[graphic].anim_frames == 2)
2313 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2315 MarkTileDirty(sxx, syy);
2320 void DrawLevelFieldCrumbled(int x, int y)
2324 if (!IN_LEV_FIELD(x, y))
2327 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2328 GfxElement[x][y] != EL_UNDEFINED &&
2329 GFX_CRUMBLED(GfxElement[x][y]))
2331 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2336 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2338 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2341 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2344 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2345 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2346 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2347 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2348 int sx = SCREENX(x), sy = SCREENY(y);
2350 DrawGraphic(sx, sy, graphic1, frame1);
2351 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2354 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2356 int sx = SCREENX(x), sy = SCREENY(y);
2357 static int xy[4][2] =
2366 /* crumble direct neighbour fields (required for field borders) */
2367 for (i = 0; i < 4; i++)
2369 int xx = x + xy[i][0];
2370 int yy = y + xy[i][1];
2371 int sxx = sx + xy[i][0];
2372 int syy = sy + xy[i][1];
2374 if (!IN_LEV_FIELD(xx, yy) ||
2375 !IN_SCR_FIELD(sxx, syy) ||
2376 !GFX_CRUMBLED(Feld[xx][yy]) ||
2380 DrawLevelField(xx, yy);
2383 /* crumble corner neighbour fields (required for inner field corners) */
2384 for (i = 0; i < 4; i++)
2386 int dx = (i & 1 ? +1 : -1);
2387 int dy = (i & 2 ? +1 : -1);
2393 if (!IN_LEV_FIELD(xx, yy) ||
2394 !IN_SCR_FIELD(sxx, syy) ||
2395 !GFX_CRUMBLED(Feld[xx][yy]) ||
2399 int element = TILE_GFX_ELEMENT(xx, yy);
2400 int graphic = el_act2crm(element, ACTION_DEFAULT);
2402 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2403 graphic_info[graphic].anim_frames == 2)
2404 DrawLevelField(xx, yy);
2408 static int getBorderElement(int x, int y)
2412 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2413 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2414 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2415 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2416 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2417 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2418 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2420 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2421 int steel_position = (x == -1 && y == -1 ? 0 :
2422 x == lev_fieldx && y == -1 ? 1 :
2423 x == -1 && y == lev_fieldy ? 2 :
2424 x == lev_fieldx && y == lev_fieldy ? 3 :
2425 x == -1 || x == lev_fieldx ? 4 :
2426 y == -1 || y == lev_fieldy ? 5 : 6);
2428 return border[steel_position][steel_type];
2431 void DrawScreenElement(int x, int y, int element)
2433 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2434 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2437 void DrawLevelElement(int x, int y, int element)
2439 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2440 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2443 void DrawScreenField(int x, int y)
2445 int lx = LEVELX(x), ly = LEVELY(y);
2446 int element, content;
2448 if (!IN_LEV_FIELD(lx, ly))
2450 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2453 element = getBorderElement(lx, ly);
2455 DrawScreenElement(x, y, element);
2460 element = Feld[lx][ly];
2461 content = Store[lx][ly];
2463 if (IS_MOVING(lx, ly))
2465 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2466 boolean cut_mode = NO_CUTTING;
2468 if (element == EL_QUICKSAND_EMPTYING ||
2469 element == EL_QUICKSAND_FAST_EMPTYING ||
2470 element == EL_MAGIC_WALL_EMPTYING ||
2471 element == EL_BD_MAGIC_WALL_EMPTYING ||
2472 element == EL_DC_MAGIC_WALL_EMPTYING ||
2473 element == EL_AMOEBA_DROPPING)
2474 cut_mode = CUT_ABOVE;
2475 else if (element == EL_QUICKSAND_FILLING ||
2476 element == EL_QUICKSAND_FAST_FILLING ||
2477 element == EL_MAGIC_WALL_FILLING ||
2478 element == EL_BD_MAGIC_WALL_FILLING ||
2479 element == EL_DC_MAGIC_WALL_FILLING)
2480 cut_mode = CUT_BELOW;
2482 if (cut_mode == CUT_ABOVE)
2483 DrawScreenElement(x, y, element);
2485 DrawScreenElement(x, y, EL_EMPTY);
2488 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2489 else if (cut_mode == NO_CUTTING)
2490 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2493 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2495 if (cut_mode == CUT_BELOW &&
2496 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2497 DrawLevelElement(lx, ly + 1, element);
2500 if (content == EL_ACID)
2502 int dir = MovDir[lx][ly];
2503 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2504 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2506 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2508 // prevent target field from being drawn again (but without masking)
2509 // (this would happen if target field is scanned after moving element)
2510 Stop[newlx][newly] = TRUE;
2513 else if (IS_BLOCKED(lx, ly))
2518 boolean cut_mode = NO_CUTTING;
2519 int element_old, content_old;
2521 Blocked2Moving(lx, ly, &oldx, &oldy);
2524 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2525 MovDir[oldx][oldy] == MV_RIGHT);
2527 element_old = Feld[oldx][oldy];
2528 content_old = Store[oldx][oldy];
2530 if (element_old == EL_QUICKSAND_EMPTYING ||
2531 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2532 element_old == EL_MAGIC_WALL_EMPTYING ||
2533 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2534 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2535 element_old == EL_AMOEBA_DROPPING)
2536 cut_mode = CUT_ABOVE;
2538 DrawScreenElement(x, y, EL_EMPTY);
2541 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2543 else if (cut_mode == NO_CUTTING)
2544 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2547 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2550 else if (IS_DRAWABLE(element))
2551 DrawScreenElement(x, y, element);
2553 DrawScreenElement(x, y, EL_EMPTY);
2556 void DrawLevelField(int x, int y)
2558 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2559 DrawScreenField(SCREENX(x), SCREENY(y));
2560 else if (IS_MOVING(x, y))
2564 Moving2Blocked(x, y, &newx, &newy);
2565 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2566 DrawScreenField(SCREENX(newx), SCREENY(newy));
2568 else if (IS_BLOCKED(x, y))
2572 Blocked2Moving(x, y, &oldx, &oldy);
2573 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2574 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2578 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2579 int (*el2img_function)(int), boolean masked,
2580 int element_bits_draw)
2582 int element_base = map_mm_wall_element(element);
2583 int element_bits = (IS_DF_WALL(element) ?
2584 element - EL_DF_WALL_START :
2585 IS_MM_WALL(element) ?
2586 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2587 int graphic = el2img_function(element_base);
2588 int tilesize_draw = tilesize / 2;
2593 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2595 for (i = 0; i < 4; i++)
2597 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2598 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2600 if (!(element_bits_draw & (1 << i)))
2603 if (element_bits & (1 << i))
2606 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2607 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2609 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2610 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2615 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2616 tilesize_draw, tilesize_draw);
2621 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2622 boolean masked, int element_bits_draw)
2624 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2625 element, tilesize, el2edimg, masked, element_bits_draw);
2628 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2629 int (*el2img_function)(int))
2631 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2635 void DrawSizedElementExt(int x, int y, int element, int tilesize,
2638 if (IS_MM_WALL(element))
2640 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2641 element, tilesize, el2edimg, masked, 0x000f);
2645 int graphic = el2edimg(element);
2648 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2650 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2654 void DrawSizedElement(int x, int y, int element, int tilesize)
2656 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2659 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2661 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2664 void DrawMiniElement(int x, int y, int element)
2668 graphic = el2edimg(element);
2669 DrawMiniGraphic(x, y, graphic);
2672 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2675 int x = sx + scroll_x, y = sy + scroll_y;
2677 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2678 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2679 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2680 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2682 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2685 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2687 int x = sx + scroll_x, y = sy + scroll_y;
2689 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2690 DrawMiniElement(sx, sy, EL_EMPTY);
2691 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2692 DrawMiniElement(sx, sy, Feld[x][y]);
2694 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2697 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2698 int x, int y, int xsize, int ysize,
2699 int tile_width, int tile_height)
2703 int dst_x = startx + x * tile_width;
2704 int dst_y = starty + y * tile_height;
2705 int width = graphic_info[graphic].width;
2706 int height = graphic_info[graphic].height;
2707 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2708 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2709 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2710 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2711 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2712 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2713 boolean draw_masked = graphic_info[graphic].draw_masked;
2715 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2717 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2719 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2723 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2724 inner_sx + (x - 1) * tile_width % inner_width);
2725 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2726 inner_sy + (y - 1) * tile_height % inner_height);
2729 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2732 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2736 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2737 int x, int y, int xsize, int ysize, int font_nr)
2739 int font_width = getFontWidth(font_nr);
2740 int font_height = getFontHeight(font_nr);
2742 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2743 font_width, font_height);
2746 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2748 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2749 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2750 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2751 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2752 boolean no_delay = (tape.warp_forward);
2753 unsigned int anim_delay = 0;
2754 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2755 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2756 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2757 int font_width = getFontWidth(font_nr);
2758 int font_height = getFontHeight(font_nr);
2759 int max_xsize = level.envelope[envelope_nr].xsize;
2760 int max_ysize = level.envelope[envelope_nr].ysize;
2761 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2762 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2763 int xend = max_xsize;
2764 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2765 int xstep = (xstart < xend ? 1 : 0);
2766 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2768 int end = MAX(xend - xstart, yend - ystart);
2771 for (i = start; i <= end; i++)
2773 int last_frame = end; // last frame of this "for" loop
2774 int x = xstart + i * xstep;
2775 int y = ystart + i * ystep;
2776 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2777 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2778 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2779 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2782 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2784 BlitScreenToBitmap(backbuffer);
2786 SetDrawtoField(DRAW_TO_BACKBUFFER);
2788 for (yy = 0; yy < ysize; yy++)
2789 for (xx = 0; xx < xsize; xx++)
2790 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2792 DrawTextBuffer(sx + font_width, sy + font_height,
2793 level.envelope[envelope_nr].text, font_nr, max_xsize,
2794 xsize - 2, ysize - 2, 0, mask_mode,
2795 level.envelope[envelope_nr].autowrap,
2796 level.envelope[envelope_nr].centered, FALSE);
2798 redraw_mask |= REDRAW_FIELD;
2801 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2805 void ShowEnvelope(int envelope_nr)
2807 int element = EL_ENVELOPE_1 + envelope_nr;
2808 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2809 int sound_opening = element_info[element].sound[ACTION_OPENING];
2810 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2811 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2812 boolean no_delay = (tape.warp_forward);
2813 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2814 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2815 int anim_mode = graphic_info[graphic].anim_mode;
2816 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2817 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2819 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2821 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2823 if (anim_mode == ANIM_DEFAULT)
2824 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2826 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2829 Delay(wait_delay_value);
2831 WaitForEventToContinue();
2833 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2835 if (anim_mode != ANIM_NONE)
2836 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2838 if (anim_mode == ANIM_DEFAULT)
2839 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2841 game.envelope_active = FALSE;
2843 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2845 redraw_mask |= REDRAW_FIELD;
2849 static void setRequestBasePosition(int *x, int *y)
2851 int sx_base, sy_base;
2853 if (request.x != -1)
2854 sx_base = request.x;
2855 else if (request.align == ALIGN_LEFT)
2857 else if (request.align == ALIGN_RIGHT)
2858 sx_base = SX + SXSIZE;
2860 sx_base = SX + SXSIZE / 2;
2862 if (request.y != -1)
2863 sy_base = request.y;
2864 else if (request.valign == VALIGN_TOP)
2866 else if (request.valign == VALIGN_BOTTOM)
2867 sy_base = SY + SYSIZE;
2869 sy_base = SY + SYSIZE / 2;
2875 static void setRequestPositionExt(int *x, int *y, int width, int height,
2876 boolean add_border_size)
2878 int border_size = request.border_size;
2879 int sx_base, sy_base;
2882 setRequestBasePosition(&sx_base, &sy_base);
2884 if (request.align == ALIGN_LEFT)
2886 else if (request.align == ALIGN_RIGHT)
2887 sx = sx_base - width;
2889 sx = sx_base - width / 2;
2891 if (request.valign == VALIGN_TOP)
2893 else if (request.valign == VALIGN_BOTTOM)
2894 sy = sy_base - height;
2896 sy = sy_base - height / 2;
2898 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2899 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2901 if (add_border_size)
2911 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2913 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2916 void DrawEnvelopeRequest(char *text)
2918 char *text_final = text;
2919 char *text_door_style = NULL;
2920 int graphic = IMG_BACKGROUND_REQUEST;
2921 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2922 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2923 int font_nr = FONT_REQUEST;
2924 int font_width = getFontWidth(font_nr);
2925 int font_height = getFontHeight(font_nr);
2926 int border_size = request.border_size;
2927 int line_spacing = request.line_spacing;
2928 int line_height = font_height + line_spacing;
2929 int max_text_width = request.width - 2 * border_size;
2930 int max_text_height = request.height - 2 * border_size;
2931 int line_length = max_text_width / font_width;
2932 int max_lines = max_text_height / line_height;
2933 int text_width = line_length * font_width;
2934 int width = request.width;
2935 int height = request.height;
2936 int tile_size = MAX(request.step_offset, 1);
2937 int x_steps = width / tile_size;
2938 int y_steps = height / tile_size;
2939 int sx_offset = border_size;
2940 int sy_offset = border_size;
2944 if (request.centered)
2945 sx_offset = (request.width - text_width) / 2;
2947 if (request.wrap_single_words && !request.autowrap)
2949 char *src_text_ptr, *dst_text_ptr;
2951 text_door_style = checked_malloc(2 * strlen(text) + 1);
2953 src_text_ptr = text;
2954 dst_text_ptr = text_door_style;
2956 while (*src_text_ptr)
2958 if (*src_text_ptr == ' ' ||
2959 *src_text_ptr == '?' ||
2960 *src_text_ptr == '!')
2961 *dst_text_ptr++ = '\n';
2963 if (*src_text_ptr != ' ')
2964 *dst_text_ptr++ = *src_text_ptr;
2969 *dst_text_ptr = '\0';
2971 text_final = text_door_style;
2974 setRequestPosition(&sx, &sy, FALSE);
2976 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2978 for (y = 0; y < y_steps; y++)
2979 for (x = 0; x < x_steps; x++)
2980 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2981 x, y, x_steps, y_steps,
2982 tile_size, tile_size);
2984 /* force DOOR font inside door area */
2985 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2987 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2988 line_length, -1, max_lines, line_spacing, mask_mode,
2989 request.autowrap, request.centered, FALSE);
2993 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2994 RedrawGadget(tool_gadget[i]);
2996 // store readily prepared envelope request for later use when animating
2997 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2999 if (text_door_style)
3000 free(text_door_style);
3003 void AnimateEnvelopeRequest(int anim_mode, int action)
3005 int graphic = IMG_BACKGROUND_REQUEST;
3006 boolean draw_masked = graphic_info[graphic].draw_masked;
3007 int delay_value_normal = request.step_delay;
3008 int delay_value_fast = delay_value_normal / 2;
3009 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3010 boolean no_delay = (tape.warp_forward);
3011 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3012 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3013 unsigned int anim_delay = 0;
3015 int tile_size = MAX(request.step_offset, 1);
3016 int max_xsize = request.width / tile_size;
3017 int max_ysize = request.height / tile_size;
3018 int max_xsize_inner = max_xsize - 2;
3019 int max_ysize_inner = max_ysize - 2;
3021 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3022 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3023 int xend = max_xsize_inner;
3024 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3025 int xstep = (xstart < xend ? 1 : 0);
3026 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3028 int end = MAX(xend - xstart, yend - ystart);
3031 if (setup.quick_doors)
3038 for (i = start; i <= end; i++)
3040 int last_frame = end; // last frame of this "for" loop
3041 int x = xstart + i * xstep;
3042 int y = ystart + i * ystep;
3043 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3044 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3045 int xsize_size_left = (xsize - 1) * tile_size;
3046 int ysize_size_top = (ysize - 1) * tile_size;
3047 int max_xsize_pos = (max_xsize - 1) * tile_size;
3048 int max_ysize_pos = (max_ysize - 1) * tile_size;
3049 int width = xsize * tile_size;
3050 int height = ysize * tile_size;
3055 setRequestPosition(&src_x, &src_y, FALSE);
3056 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3058 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3060 for (yy = 0; yy < 2; yy++)
3062 for (xx = 0; xx < 2; xx++)
3064 int src_xx = src_x + xx * max_xsize_pos;
3065 int src_yy = src_y + yy * max_ysize_pos;
3066 int dst_xx = dst_x + xx * xsize_size_left;
3067 int dst_yy = dst_y + yy * ysize_size_top;
3068 int xx_size = (xx ? tile_size : xsize_size_left);
3069 int yy_size = (yy ? tile_size : ysize_size_top);
3072 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3073 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3075 BlitBitmap(bitmap_db_store_2, backbuffer,
3076 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3080 redraw_mask |= REDRAW_FIELD;
3084 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3088 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3090 int graphic = IMG_BACKGROUND_REQUEST;
3091 int sound_opening = SND_REQUEST_OPENING;
3092 int sound_closing = SND_REQUEST_CLOSING;
3093 int anim_mode_1 = request.anim_mode; /* (higher priority) */
3094 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
3095 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3096 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3097 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3099 if (game_status == GAME_MODE_PLAYING)
3100 BlitScreenToBitmap(backbuffer);
3102 SetDrawtoField(DRAW_TO_BACKBUFFER);
3104 // SetDrawBackgroundMask(REDRAW_NONE);
3106 if (action == ACTION_OPENING)
3108 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3110 if (req_state & REQ_ASK)
3112 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3113 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3115 else if (req_state & REQ_CONFIRM)
3117 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3119 else if (req_state & REQ_PLAYER)
3121 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3122 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3123 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3124 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3127 DrawEnvelopeRequest(text);
3130 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
3132 if (action == ACTION_OPENING)
3134 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3136 if (anim_mode == ANIM_DEFAULT)
3137 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3139 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3143 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3145 if (anim_mode != ANIM_NONE)
3146 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3148 if (anim_mode == ANIM_DEFAULT)
3149 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3152 game.envelope_active = FALSE;
3154 if (action == ACTION_CLOSING)
3155 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3157 // SetDrawBackgroundMask(last_draw_background_mask);
3159 redraw_mask |= REDRAW_FIELD;
3163 if (action == ACTION_CLOSING &&
3164 game_status == GAME_MODE_PLAYING &&
3165 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3166 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3169 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3171 if (IS_MM_WALL(element))
3173 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3179 int graphic = el2preimg(element);
3181 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3182 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3187 void DrawLevel(int draw_background_mask)
3191 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3192 SetDrawBackgroundMask(draw_background_mask);
3196 for (x = BX1; x <= BX2; x++)
3197 for (y = BY1; y <= BY2; y++)
3198 DrawScreenField(x, y);
3200 redraw_mask |= REDRAW_FIELD;
3203 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3208 for (x = 0; x < size_x; x++)
3209 for (y = 0; y < size_y; y++)
3210 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3212 redraw_mask |= REDRAW_FIELD;
3215 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3219 for (x = 0; x < size_x; x++)
3220 for (y = 0; y < size_y; y++)
3221 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3223 redraw_mask |= REDRAW_FIELD;
3226 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3228 boolean show_level_border = (BorderElement != EL_EMPTY);
3229 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3230 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3231 int tile_size = preview.tile_size;
3232 int preview_width = preview.xsize * tile_size;
3233 int preview_height = preview.ysize * tile_size;
3234 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3235 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3236 int real_preview_width = real_preview_xsize * tile_size;
3237 int real_preview_height = real_preview_ysize * tile_size;
3238 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3239 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3242 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3245 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3247 dst_x += (preview_width - real_preview_width) / 2;
3248 dst_y += (preview_height - real_preview_height) / 2;
3250 for (x = 0; x < real_preview_xsize; x++)
3252 for (y = 0; y < real_preview_ysize; y++)
3254 int lx = from_x + x + (show_level_border ? -1 : 0);
3255 int ly = from_y + y + (show_level_border ? -1 : 0);
3256 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3257 getBorderElement(lx, ly));
3259 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3260 element, tile_size);
3264 redraw_mask |= REDRAW_FIELD;
3267 #define MICROLABEL_EMPTY 0
3268 #define MICROLABEL_LEVEL_NAME 1
3269 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3270 #define MICROLABEL_LEVEL_AUTHOR 3
3271 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3272 #define MICROLABEL_IMPORTED_FROM 5
3273 #define MICROLABEL_IMPORTED_BY_HEAD 6
3274 #define MICROLABEL_IMPORTED_BY 7
3276 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3278 int max_text_width = SXSIZE;
3279 int font_width = getFontWidth(font_nr);
3281 if (pos->align == ALIGN_CENTER)
3282 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3283 else if (pos->align == ALIGN_RIGHT)
3284 max_text_width = pos->x;
3286 max_text_width = SXSIZE - pos->x;
3288 return max_text_width / font_width;
3291 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3293 char label_text[MAX_OUTPUT_LINESIZE + 1];
3294 int max_len_label_text;
3295 int font_nr = pos->font;
3298 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3301 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3302 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3303 mode == MICROLABEL_IMPORTED_BY_HEAD)
3304 font_nr = pos->font_alt;
3306 max_len_label_text = getMaxTextLength(pos, font_nr);
3308 if (pos->size != -1)
3309 max_len_label_text = pos->size;
3311 for (i = 0; i < max_len_label_text; i++)
3312 label_text[i] = ' ';
3313 label_text[max_len_label_text] = '\0';
3315 if (strlen(label_text) > 0)
3316 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3319 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3320 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3321 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3322 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3323 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3324 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3325 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3326 max_len_label_text);
3327 label_text[max_len_label_text] = '\0';
3329 if (strlen(label_text) > 0)
3330 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3332 redraw_mask |= REDRAW_FIELD;
3335 static void DrawPreviewLevelLabel(int mode)
3337 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3340 static void DrawPreviewLevelInfo(int mode)
3342 if (mode == MICROLABEL_LEVEL_NAME)
3343 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3344 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3345 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3348 static void DrawPreviewLevelExt(boolean restart)
3350 static unsigned int scroll_delay = 0;
3351 static unsigned int label_delay = 0;
3352 static int from_x, from_y, scroll_direction;
3353 static int label_state, label_counter;
3354 unsigned int scroll_delay_value = preview.step_delay;
3355 boolean show_level_border = (BorderElement != EL_EMPTY);
3356 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3357 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3364 if (preview.anim_mode == ANIM_CENTERED)
3366 if (level_xsize > preview.xsize)
3367 from_x = (level_xsize - preview.xsize) / 2;
3368 if (level_ysize > preview.ysize)
3369 from_y = (level_ysize - preview.ysize) / 2;
3372 from_x += preview.xoffset;
3373 from_y += preview.yoffset;
3375 scroll_direction = MV_RIGHT;
3379 DrawPreviewLevelPlayfield(from_x, from_y);
3380 DrawPreviewLevelLabel(label_state);
3382 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3383 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3385 /* initialize delay counters */
3386 DelayReached(&scroll_delay, 0);
3387 DelayReached(&label_delay, 0);
3389 if (leveldir_current->name)
3391 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3392 char label_text[MAX_OUTPUT_LINESIZE + 1];
3393 int font_nr = pos->font;
3394 int max_len_label_text = getMaxTextLength(pos, font_nr);
3396 if (pos->size != -1)
3397 max_len_label_text = pos->size;
3399 strncpy(label_text, leveldir_current->name, max_len_label_text);
3400 label_text[max_len_label_text] = '\0';
3402 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3403 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3409 /* scroll preview level, if needed */
3410 if (preview.anim_mode != ANIM_NONE &&
3411 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3412 DelayReached(&scroll_delay, scroll_delay_value))
3414 switch (scroll_direction)
3419 from_x -= preview.step_offset;
3420 from_x = (from_x < 0 ? 0 : from_x);
3423 scroll_direction = MV_UP;
3427 if (from_x < level_xsize - preview.xsize)
3429 from_x += preview.step_offset;
3430 from_x = (from_x > level_xsize - preview.xsize ?
3431 level_xsize - preview.xsize : from_x);
3434 scroll_direction = MV_DOWN;
3440 from_y -= preview.step_offset;
3441 from_y = (from_y < 0 ? 0 : from_y);
3444 scroll_direction = MV_RIGHT;
3448 if (from_y < level_ysize - preview.ysize)
3450 from_y += preview.step_offset;
3451 from_y = (from_y > level_ysize - preview.ysize ?
3452 level_ysize - preview.ysize : from_y);
3455 scroll_direction = MV_LEFT;
3462 DrawPreviewLevelPlayfield(from_x, from_y);
3465 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3466 /* redraw micro level label, if needed */
3467 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3468 !strEqual(level.author, ANONYMOUS_NAME) &&
3469 !strEqual(level.author, leveldir_current->name) &&
3470 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3472 int max_label_counter = 23;
3474 if (leveldir_current->imported_from != NULL &&
3475 strlen(leveldir_current->imported_from) > 0)
3476 max_label_counter += 14;
3477 if (leveldir_current->imported_by != NULL &&
3478 strlen(leveldir_current->imported_by) > 0)
3479 max_label_counter += 14;
3481 label_counter = (label_counter + 1) % max_label_counter;
3482 label_state = (label_counter >= 0 && label_counter <= 7 ?
3483 MICROLABEL_LEVEL_NAME :
3484 label_counter >= 9 && label_counter <= 12 ?
3485 MICROLABEL_LEVEL_AUTHOR_HEAD :
3486 label_counter >= 14 && label_counter <= 21 ?
3487 MICROLABEL_LEVEL_AUTHOR :
3488 label_counter >= 23 && label_counter <= 26 ?
3489 MICROLABEL_IMPORTED_FROM_HEAD :
3490 label_counter >= 28 && label_counter <= 35 ?
3491 MICROLABEL_IMPORTED_FROM :
3492 label_counter >= 37 && label_counter <= 40 ?
3493 MICROLABEL_IMPORTED_BY_HEAD :
3494 label_counter >= 42 && label_counter <= 49 ?
3495 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3497 if (leveldir_current->imported_from == NULL &&
3498 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3499 label_state == MICROLABEL_IMPORTED_FROM))
3500 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3501 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3503 DrawPreviewLevelLabel(label_state);
3507 void DrawPreviewLevelInitial()
3509 DrawPreviewLevelExt(TRUE);
3512 void DrawPreviewLevelAnimation()
3514 DrawPreviewLevelExt(FALSE);
3517 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3518 int border_size, int font_nr)
3520 int graphic = el2img(EL_PLAYER_1 + player_nr);
3521 int font_height = getFontHeight(font_nr);
3522 int player_height = MAX(tile_size, font_height);
3523 int xoffset_text = tile_size + border_size;
3524 int yoffset_text = (player_height - font_height) / 2;
3525 int yoffset_graphic = (player_height - tile_size) / 2;
3526 char *player_name = getNetworkPlayerName(player_nr + 1);
3528 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3530 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3533 void DrawNetworkPlayersExt(boolean force)
3535 if (game_status != GAME_MODE_MAIN)
3538 if (!network.connected && !force)
3541 int num_players = 0;
3544 for (i = 0; i < MAX_PLAYERS; i++)
3545 if (stored_player[i].connected_network)
3548 struct TextPosInfo *pos = &menu.main.network_players;
3549 int tile_size = pos->tile_size;
3550 int border_size = 2;
3551 int xoffset_text = tile_size + border_size;
3552 int font_nr = pos->font;
3553 int font_width = getFontWidth(font_nr);
3554 int font_height = getFontHeight(font_nr);
3555 int player_height = MAX(tile_size, font_height);
3556 int player_yoffset = player_height + border_size;
3557 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3558 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3559 int all_players_height = num_players * player_yoffset - border_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 ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3564 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3565 max_players_width, max_players_height);
3567 /* first draw local network player ... */
3568 for (i = 0; i < MAX_PLAYERS; i++)
3570 if (stored_player[i].connected_network &&
3571 stored_player[i].connected_locally)
3573 char *player_name = getNetworkPlayerName(i + 1);
3574 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3575 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3577 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3579 ypos += player_yoffset;
3583 /* ... then draw all other network players */
3584 for (i = 0; i < MAX_PLAYERS; i++)
3586 if (stored_player[i].connected_network &&
3587 !stored_player[i].connected_locally)
3589 char *player_name = getNetworkPlayerName(i + 1);
3590 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3591 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3593 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3595 ypos += player_yoffset;
3600 void DrawNetworkPlayers()
3602 DrawNetworkPlayersExt(FALSE);
3605 void ClearNetworkPlayers()
3607 DrawNetworkPlayersExt(TRUE);
3610 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3611 int graphic, int sync_frame,
3614 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3616 if (mask_mode == USE_MASKING)
3617 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3619 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3622 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3623 int graphic, int sync_frame, int mask_mode)
3625 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3627 if (mask_mode == USE_MASKING)
3628 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3630 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3633 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3635 int lx = LEVELX(x), ly = LEVELY(y);
3637 if (!IN_SCR_FIELD(x, y))
3640 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3641 graphic, GfxFrame[lx][ly], NO_MASKING);
3643 MarkTileDirty(x, y);
3646 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3648 int lx = LEVELX(x), ly = LEVELY(y);
3650 if (!IN_SCR_FIELD(x, y))
3653 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3654 graphic, GfxFrame[lx][ly], NO_MASKING);
3655 MarkTileDirty(x, y);
3658 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3660 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3663 void DrawLevelElementAnimation(int x, int y, int element)
3665 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3667 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3670 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3672 int sx = SCREENX(x), sy = SCREENY(y);
3674 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3677 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3680 DrawGraphicAnimation(sx, sy, graphic);
3683 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3684 DrawLevelFieldCrumbled(x, y);
3686 if (GFX_CRUMBLED(Feld[x][y]))
3687 DrawLevelFieldCrumbled(x, y);
3691 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3693 int sx = SCREENX(x), sy = SCREENY(y);
3696 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3699 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3701 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3704 DrawGraphicAnimation(sx, sy, graphic);
3706 if (GFX_CRUMBLED(element))
3707 DrawLevelFieldCrumbled(x, y);
3710 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3712 if (player->use_murphy)
3714 /* this works only because currently only one player can be "murphy" ... */
3715 static int last_horizontal_dir = MV_LEFT;
3716 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3718 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3719 last_horizontal_dir = move_dir;
3721 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3723 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3725 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3731 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3734 static boolean equalGraphics(int graphic1, int graphic2)
3736 struct GraphicInfo *g1 = &graphic_info[graphic1];
3737 struct GraphicInfo *g2 = &graphic_info[graphic2];
3739 return (g1->bitmap == g2->bitmap &&
3740 g1->src_x == g2->src_x &&
3741 g1->src_y == g2->src_y &&
3742 g1->anim_frames == g2->anim_frames &&
3743 g1->anim_delay == g2->anim_delay &&
3744 g1->anim_mode == g2->anim_mode);
3747 void DrawAllPlayers()
3751 for (i = 0; i < MAX_PLAYERS; i++)
3752 if (stored_player[i].active)
3753 DrawPlayer(&stored_player[i]);
3756 void DrawPlayerField(int x, int y)
3758 if (!IS_PLAYER(x, y))
3761 DrawPlayer(PLAYERINFO(x, y));
3764 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3766 void DrawPlayer(struct PlayerInfo *player)
3768 int jx = player->jx;
3769 int jy = player->jy;
3770 int move_dir = player->MovDir;
3771 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3772 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3773 int last_jx = (player->is_moving ? jx - dx : jx);
3774 int last_jy = (player->is_moving ? jy - dy : jy);
3775 int next_jx = jx + dx;
3776 int next_jy = jy + dy;
3777 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3778 boolean player_is_opaque = FALSE;
3779 int sx = SCREENX(jx), sy = SCREENY(jy);
3780 int sxx = 0, syy = 0;
3781 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3783 int action = ACTION_DEFAULT;
3784 int last_player_graphic = getPlayerGraphic(player, move_dir);
3785 int last_player_frame = player->Frame;
3788 /* GfxElement[][] is set to the element the player is digging or collecting;
3789 remove also for off-screen player if the player is not moving anymore */
3790 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3791 GfxElement[jx][jy] = EL_UNDEFINED;
3793 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3797 if (!IN_LEV_FIELD(jx, jy))
3799 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3800 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3801 printf("DrawPlayerField(): This should never happen!\n");
3806 if (element == EL_EXPLOSION)
3809 action = (player->is_pushing ? ACTION_PUSHING :
3810 player->is_digging ? ACTION_DIGGING :
3811 player->is_collecting ? ACTION_COLLECTING :
3812 player->is_moving ? ACTION_MOVING :
3813 player->is_snapping ? ACTION_SNAPPING :
3814 player->is_dropping ? ACTION_DROPPING :
3815 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3817 if (player->is_waiting)
3818 move_dir = player->dir_waiting;
3820 InitPlayerGfxAnimation(player, action, move_dir);
3822 /* ----------------------------------------------------------------------- */
3823 /* draw things in the field the player is leaving, if needed */
3824 /* ----------------------------------------------------------------------- */
3826 if (player->is_moving)
3828 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3830 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3832 if (last_element == EL_DYNAMITE_ACTIVE ||
3833 last_element == EL_EM_DYNAMITE_ACTIVE ||
3834 last_element == EL_SP_DISK_RED_ACTIVE)
3835 DrawDynamite(last_jx, last_jy);
3837 DrawLevelFieldThruMask(last_jx, last_jy);
3839 else if (last_element == EL_DYNAMITE_ACTIVE ||
3840 last_element == EL_EM_DYNAMITE_ACTIVE ||
3841 last_element == EL_SP_DISK_RED_ACTIVE)
3842 DrawDynamite(last_jx, last_jy);
3844 /* !!! this is not enough to prevent flickering of players which are
3845 moving next to each others without a free tile between them -- this
3846 can only be solved by drawing all players layer by layer (first the
3847 background, then the foreground etc.) !!! => TODO */
3848 else if (!IS_PLAYER(last_jx, last_jy))
3849 DrawLevelField(last_jx, last_jy);
3852 DrawLevelField(last_jx, last_jy);
3855 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3856 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3859 if (!IN_SCR_FIELD(sx, sy))
3862 /* ----------------------------------------------------------------------- */
3863 /* draw things behind the player, if needed */
3864 /* ----------------------------------------------------------------------- */
3867 DrawLevelElement(jx, jy, Back[jx][jy]);
3868 else if (IS_ACTIVE_BOMB(element))
3869 DrawLevelElement(jx, jy, EL_EMPTY);
3872 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3874 int old_element = GfxElement[jx][jy];
3875 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3876 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3878 if (GFX_CRUMBLED(old_element))
3879 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3881 DrawGraphic(sx, sy, old_graphic, frame);
3883 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3884 player_is_opaque = TRUE;
3888 GfxElement[jx][jy] = EL_UNDEFINED;
3890 /* make sure that pushed elements are drawn with correct frame rate */
3891 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3893 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3894 GfxFrame[jx][jy] = player->StepFrame;
3896 DrawLevelField(jx, jy);
3900 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3901 /* ----------------------------------------------------------------------- */
3902 /* draw player himself */
3903 /* ----------------------------------------------------------------------- */
3905 graphic = getPlayerGraphic(player, move_dir);
3907 /* in the case of changed player action or direction, prevent the current
3908 animation frame from being restarted for identical animations */
3909 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3910 player->Frame = last_player_frame;
3912 frame = getGraphicAnimationFrame(graphic, player->Frame);
3916 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3917 sxx = player->GfxPos;
3919 syy = player->GfxPos;
3922 if (player_is_opaque)
3923 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3925 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3927 if (SHIELD_ON(player))
3929 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3930 IMG_SHIELD_NORMAL_ACTIVE);
3931 int frame = getGraphicAnimationFrame(graphic, -1);
3933 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3937 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3940 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3941 sxx = player->GfxPos;
3943 syy = player->GfxPos;
3947 /* ----------------------------------------------------------------------- */
3948 /* draw things the player is pushing, if needed */
3949 /* ----------------------------------------------------------------------- */
3951 if (player->is_pushing && player->is_moving)
3953 int px = SCREENX(jx), py = SCREENY(jy);
3954 int pxx = (TILEX - ABS(sxx)) * dx;
3955 int pyy = (TILEY - ABS(syy)) * dy;
3956 int gfx_frame = GfxFrame[jx][jy];
3962 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3964 element = Feld[next_jx][next_jy];
3965 gfx_frame = GfxFrame[next_jx][next_jy];
3968 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3970 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3971 frame = getGraphicAnimationFrame(graphic, sync_frame);
3973 /* draw background element under pushed element (like the Sokoban field) */
3974 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3976 /* this allows transparent pushing animation over non-black background */
3979 DrawLevelElement(jx, jy, Back[jx][jy]);
3981 DrawLevelElement(jx, jy, EL_EMPTY);
3983 if (Back[next_jx][next_jy])
3984 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3986 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3988 else if (Back[next_jx][next_jy])
3989 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3992 /* do not draw (EM style) pushing animation when pushing is finished */
3993 /* (two-tile animations usually do not contain start and end frame) */
3994 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3995 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3997 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3999 /* masked drawing is needed for EMC style (double) movement graphics */
4000 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
4001 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4005 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4006 /* ----------------------------------------------------------------------- */
4007 /* draw player himself */
4008 /* ----------------------------------------------------------------------- */
4010 graphic = getPlayerGraphic(player, move_dir);
4012 /* in the case of changed player action or direction, prevent the current
4013 animation frame from being restarted for identical animations */
4014 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4015 player->Frame = last_player_frame;
4017 frame = getGraphicAnimationFrame(graphic, player->Frame);
4021 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4022 sxx = player->GfxPos;
4024 syy = player->GfxPos;
4027 if (player_is_opaque)
4028 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4030 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4032 if (SHIELD_ON(player))
4034 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4035 IMG_SHIELD_NORMAL_ACTIVE);
4036 int frame = getGraphicAnimationFrame(graphic, -1);
4038 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4042 /* ----------------------------------------------------------------------- */
4043 /* draw things in front of player (active dynamite or dynabombs) */
4044 /* ----------------------------------------------------------------------- */
4046 if (IS_ACTIVE_BOMB(element))
4048 graphic = el2img(element);
4049 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4051 if (game.emulation == EMU_SUPAPLEX)
4052 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4054 DrawGraphicThruMask(sx, sy, graphic, frame);
4057 if (player_is_moving && last_element == EL_EXPLOSION)
4059 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4060 GfxElement[last_jx][last_jy] : EL_EMPTY);
4061 int graphic = el_act2img(element, ACTION_EXPLODING);
4062 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4063 int phase = ExplodePhase[last_jx][last_jy] - 1;
4064 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4067 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4070 /* ----------------------------------------------------------------------- */
4071 /* draw elements the player is just walking/passing through/under */
4072 /* ----------------------------------------------------------------------- */
4074 if (player_is_moving)
4076 /* handle the field the player is leaving ... */
4077 if (IS_ACCESSIBLE_INSIDE(last_element))
4078 DrawLevelField(last_jx, last_jy);
4079 else if (IS_ACCESSIBLE_UNDER(last_element))
4080 DrawLevelFieldThruMask(last_jx, last_jy);
4083 /* do not redraw accessible elements if the player is just pushing them */
4084 if (!player_is_moving || !player->is_pushing)
4086 /* ... and the field the player is entering */
4087 if (IS_ACCESSIBLE_INSIDE(element))
4088 DrawLevelField(jx, jy);
4089 else if (IS_ACCESSIBLE_UNDER(element))
4090 DrawLevelFieldThruMask(jx, jy);
4093 MarkTileDirty(sx, sy);
4096 /* ------------------------------------------------------------------------- */
4098 void WaitForEventToContinue()
4100 boolean still_wait = TRUE;
4102 if (program.headless)
4105 /* simulate releasing mouse button over last gadget, if still pressed */
4107 HandleGadgets(-1, -1, 0);
4109 button_status = MB_RELEASED;
4117 if (NextValidEvent(&event))
4121 case EVENT_BUTTONPRESS:
4122 case EVENT_KEYPRESS:
4123 #if defined(TARGET_SDL2)
4124 case SDL_CONTROLLERBUTTONDOWN:
4126 case SDL_JOYBUTTONDOWN:
4130 case EVENT_KEYRELEASE:
4131 ClearPlayerAction();
4135 HandleOtherEvents(&event);
4139 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4148 #define MAX_REQUEST_LINES 13
4149 #define MAX_REQUEST_LINE_FONT1_LEN 7
4150 #define MAX_REQUEST_LINE_FONT2_LEN 10
4152 static int RequestHandleEvents(unsigned int req_state)
4154 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
4155 local_player->LevelSolved_GameEnd);
4156 int width = request.width;
4157 int height = request.height;
4161 setRequestPosition(&sx, &sy, FALSE);
4163 button_status = MB_RELEASED;
4165 request_gadget_id = -1;
4172 /* the MM game engine does not use a special (scrollable) field buffer */
4173 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4174 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4176 HandleGameActions();
4178 SetDrawtoField(DRAW_TO_BACKBUFFER);
4180 if (global.use_envelope_request)
4182 /* copy current state of request area to middle of playfield area */
4183 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4191 while (NextValidEvent(&event))
4195 case EVENT_BUTTONPRESS:
4196 case EVENT_BUTTONRELEASE:
4197 case EVENT_MOTIONNOTIFY:
4201 if (event.type == EVENT_MOTIONNOTIFY)
4206 motion_status = TRUE;
4207 mx = ((MotionEvent *) &event)->x;
4208 my = ((MotionEvent *) &event)->y;
4212 motion_status = FALSE;
4213 mx = ((ButtonEvent *) &event)->x;
4214 my = ((ButtonEvent *) &event)->y;
4215 if (event.type == EVENT_BUTTONPRESS)
4216 button_status = ((ButtonEvent *) &event)->button;
4218 button_status = MB_RELEASED;
4221 /* this sets 'request_gadget_id' */
4222 HandleGadgets(mx, my, button_status);
4224 switch (request_gadget_id)
4226 case TOOL_CTRL_ID_YES:
4229 case TOOL_CTRL_ID_NO:
4232 case TOOL_CTRL_ID_CONFIRM:
4233 result = TRUE | FALSE;
4236 case TOOL_CTRL_ID_PLAYER_1:
4239 case TOOL_CTRL_ID_PLAYER_2:
4242 case TOOL_CTRL_ID_PLAYER_3:
4245 case TOOL_CTRL_ID_PLAYER_4:
4256 #if defined(TARGET_SDL2)
4257 case SDL_WINDOWEVENT:
4258 HandleWindowEvent((WindowEvent *) &event);
4261 case SDL_APP_WILLENTERBACKGROUND:
4262 case SDL_APP_DIDENTERBACKGROUND:
4263 case SDL_APP_WILLENTERFOREGROUND:
4264 case SDL_APP_DIDENTERFOREGROUND:
4265 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4269 case EVENT_KEYPRESS:
4271 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4276 if (req_state & REQ_CONFIRM)
4282 #if defined(TARGET_SDL2)
4286 #if defined(KSYM_Rewind)
4287 case KSYM_Rewind: /* for Amazon Fire TV remote */
4295 #if defined(TARGET_SDL2)
4298 #if defined(KSYM_FastForward)
4299 case KSYM_FastForward: /* for Amazon Fire TV remote */
4306 HandleKeysDebug(key);
4310 if (req_state & REQ_PLAYER)
4312 int old_player_nr = setup.network_player_nr;
4315 result = old_player_nr + 1;
4320 result = old_player_nr + 1;
4351 case EVENT_KEYRELEASE:
4352 ClearPlayerAction();
4355 #if defined(TARGET_SDL2)
4356 case SDL_CONTROLLERBUTTONDOWN:
4357 switch (event.cbutton.button)
4359 case SDL_CONTROLLER_BUTTON_A:
4360 case SDL_CONTROLLER_BUTTON_X:
4361 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4362 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4366 case SDL_CONTROLLER_BUTTON_B:
4367 case SDL_CONTROLLER_BUTTON_Y:
4368 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4369 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4370 case SDL_CONTROLLER_BUTTON_BACK:
4375 if (req_state & REQ_PLAYER)
4377 int old_player_nr = setup.network_player_nr;
4380 result = old_player_nr + 1;
4382 switch (event.cbutton.button)
4384 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4385 case SDL_CONTROLLER_BUTTON_Y:
4389 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4390 case SDL_CONTROLLER_BUTTON_B:
4394 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4395 case SDL_CONTROLLER_BUTTON_A:
4399 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4400 case SDL_CONTROLLER_BUTTON_X:
4411 case SDL_CONTROLLERBUTTONUP:
4412 HandleJoystickEvent(&event);
4413 ClearPlayerAction();
4418 HandleOtherEvents(&event);
4423 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4425 int joy = AnyJoystick();
4427 if (joy & JOY_BUTTON_1)
4429 else if (joy & JOY_BUTTON_2)
4432 else if (AnyJoystick())
4434 int joy = AnyJoystick();
4436 if (req_state & REQ_PLAYER)
4440 else if (joy & JOY_RIGHT)
4442 else if (joy & JOY_DOWN)
4444 else if (joy & JOY_LEFT)
4451 if (global.use_envelope_request)
4453 /* copy back current state of pressed buttons inside request area */
4454 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4464 static boolean RequestDoor(char *text, unsigned int req_state)
4466 unsigned int old_door_state;
4467 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4468 int font_nr = FONT_TEXT_2;
4473 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4475 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4476 font_nr = FONT_TEXT_1;
4479 if (game_status == GAME_MODE_PLAYING)
4480 BlitScreenToBitmap(backbuffer);
4482 /* disable deactivated drawing when quick-loading level tape recording */
4483 if (tape.playing && tape.deactivate_display)
4484 TapeDeactivateDisplayOff(TRUE);
4486 SetMouseCursor(CURSOR_DEFAULT);
4488 /* pause network game while waiting for request to answer */
4489 if (network.enabled &&
4490 game_status == GAME_MODE_PLAYING &&
4491 req_state & REQUEST_WAIT_FOR_INPUT)
4492 SendToServer_PausePlaying();
4494 old_door_state = GetDoorState();
4496 /* simulate releasing mouse button over last gadget, if still pressed */
4498 HandleGadgets(-1, -1, 0);
4502 /* draw released gadget before proceeding */
4505 if (old_door_state & DOOR_OPEN_1)
4507 CloseDoor(DOOR_CLOSE_1);
4509 /* save old door content */
4510 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4511 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4514 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4515 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4517 /* clear door drawing field */
4518 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4520 /* force DOOR font inside door area */
4521 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4523 /* write text for request */
4524 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4526 char text_line[max_request_line_len + 1];
4532 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4534 tc = *(text_ptr + tx);
4535 // if (!tc || tc == ' ')
4536 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4540 if ((tc == '?' || tc == '!') && tl == 0)
4550 strncpy(text_line, text_ptr, tl);
4553 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4554 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4555 text_line, font_nr);
4557 text_ptr += tl + (tc == ' ' ? 1 : 0);
4558 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4563 if (req_state & REQ_ASK)
4565 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4566 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4568 else if (req_state & REQ_CONFIRM)
4570 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4572 else if (req_state & REQ_PLAYER)
4574 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4575 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4576 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4577 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4580 /* copy request gadgets to door backbuffer */
4581 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4583 OpenDoor(DOOR_OPEN_1);
4585 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4587 if (game_status == GAME_MODE_PLAYING)
4589 SetPanelBackground();
4590 SetDrawBackgroundMask(REDRAW_DOOR_1);
4594 SetDrawBackgroundMask(REDRAW_FIELD);
4600 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4602 // ---------- handle request buttons ----------
4603 result = RequestHandleEvents(req_state);
4607 if (!(req_state & REQ_STAY_OPEN))
4609 CloseDoor(DOOR_CLOSE_1);
4611 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4612 (req_state & REQ_REOPEN))
4613 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4618 if (game_status == GAME_MODE_PLAYING)
4620 SetPanelBackground();
4621 SetDrawBackgroundMask(REDRAW_DOOR_1);
4625 SetDrawBackgroundMask(REDRAW_FIELD);
4628 /* continue network game after request */
4629 if (network.enabled &&
4630 game_status == GAME_MODE_PLAYING &&
4631 req_state & REQUEST_WAIT_FOR_INPUT)
4632 SendToServer_ContinuePlaying();
4634 /* restore deactivated drawing when quick-loading level tape recording */
4635 if (tape.playing && tape.deactivate_display)
4636 TapeDeactivateDisplayOn();
4641 static boolean RequestEnvelope(char *text, unsigned int req_state)
4645 if (game_status == GAME_MODE_PLAYING)
4646 BlitScreenToBitmap(backbuffer);
4648 /* disable deactivated drawing when quick-loading level tape recording */
4649 if (tape.playing && tape.deactivate_display)
4650 TapeDeactivateDisplayOff(TRUE);
4652 SetMouseCursor(CURSOR_DEFAULT);
4654 /* pause network game while waiting for request to answer */
4655 if (network.enabled &&
4656 game_status == GAME_MODE_PLAYING &&
4657 req_state & REQUEST_WAIT_FOR_INPUT)
4658 SendToServer_PausePlaying();
4660 /* simulate releasing mouse button over last gadget, if still pressed */
4662 HandleGadgets(-1, -1, 0);
4666 // (replace with setting corresponding request background)
4667 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4668 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4670 /* clear door drawing field */
4671 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4673 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4675 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4677 if (game_status == GAME_MODE_PLAYING)
4679 SetPanelBackground();
4680 SetDrawBackgroundMask(REDRAW_DOOR_1);
4684 SetDrawBackgroundMask(REDRAW_FIELD);
4690 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4692 // ---------- handle request buttons ----------
4693 result = RequestHandleEvents(req_state);
4697 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
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 &&
4714 req_state & REQUEST_WAIT_FOR_INPUT)
4715 SendToServer_ContinuePlaying();
4717 /* restore deactivated drawing when quick-loading level tape recording */
4718 if (tape.playing && tape.deactivate_display)
4719 TapeDeactivateDisplayOn();
4724 boolean Request(char *text, unsigned int req_state)
4726 boolean overlay_active = GetOverlayActive();
4729 SetOverlayActive(FALSE);
4731 if (global.use_envelope_request)
4732 result = RequestEnvelope(text, req_state);
4734 result = RequestDoor(text, req_state);
4736 SetOverlayActive(overlay_active);
4741 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4743 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4744 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4747 if (dpo1->sort_priority != dpo2->sort_priority)
4748 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4750 compare_result = dpo1->nr - dpo2->nr;
4752 return compare_result;
4755 void InitGraphicCompatibilityInfo_Doors()
4761 struct DoorInfo *door;
4765 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4766 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4768 { -1, -1, -1, NULL }
4770 struct Rect door_rect_list[] =
4772 { DX, DY, DXSIZE, DYSIZE },
4773 { VX, VY, VXSIZE, VYSIZE }
4777 for (i = 0; doors[i].door_token != -1; i++)
4779 int door_token = doors[i].door_token;
4780 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4781 int part_1 = doors[i].part_1;
4782 int part_8 = doors[i].part_8;
4783 int part_2 = part_1 + 1;
4784 int part_3 = part_1 + 2;
4785 struct DoorInfo *door = doors[i].door;
4786 struct Rect *door_rect = &door_rect_list[door_index];
4787 boolean door_gfx_redefined = FALSE;
4789 /* check if any door part graphic definitions have been redefined */
4791 for (j = 0; door_part_controls[j].door_token != -1; j++)
4793 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4794 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4796 if (dpc->door_token == door_token && fi->redefined)
4797 door_gfx_redefined = TRUE;
4800 /* check for old-style door graphic/animation modifications */
4802 if (!door_gfx_redefined)
4804 if (door->anim_mode & ANIM_STATIC_PANEL)
4806 door->panel.step_xoffset = 0;
4807 door->panel.step_yoffset = 0;
4810 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4812 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4813 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4814 int num_door_steps, num_panel_steps;
4816 /* remove door part graphics other than the two default wings */
4818 for (j = 0; door_part_controls[j].door_token != -1; j++)
4820 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4821 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4823 if (dpc->graphic >= part_3 &&
4824 dpc->graphic <= part_8)
4828 /* set graphics and screen positions of the default wings */
4830 g_part_1->width = door_rect->width;
4831 g_part_1->height = door_rect->height;
4832 g_part_2->width = door_rect->width;
4833 g_part_2->height = door_rect->height;
4834 g_part_2->src_x = door_rect->width;
4835 g_part_2->src_y = g_part_1->src_y;
4837 door->part_2.x = door->part_1.x;
4838 door->part_2.y = door->part_1.y;
4840 if (door->width != -1)
4842 g_part_1->width = door->width;
4843 g_part_2->width = door->width;
4845 // special treatment for graphics and screen position of right wing
4846 g_part_2->src_x += door_rect->width - door->width;
4847 door->part_2.x += door_rect->width - door->width;
4850 if (door->height != -1)
4852 g_part_1->height = door->height;
4853 g_part_2->height = door->height;
4855 // special treatment for graphics and screen position of bottom wing
4856 g_part_2->src_y += door_rect->height - door->height;
4857 door->part_2.y += door_rect->height - door->height;
4860 /* set animation delays for the default wings and panels */
4862 door->part_1.step_delay = door->step_delay;
4863 door->part_2.step_delay = door->step_delay;
4864 door->panel.step_delay = door->step_delay;
4866 /* set animation draw order for the default wings */
4868 door->part_1.sort_priority = 2; /* draw left wing over ... */
4869 door->part_2.sort_priority = 1; /* ... right wing */
4871 /* set animation draw offset for the default wings */
4873 if (door->anim_mode & ANIM_HORIZONTAL)
4875 door->part_1.step_xoffset = door->step_offset;
4876 door->part_1.step_yoffset = 0;
4877 door->part_2.step_xoffset = door->step_offset * -1;
4878 door->part_2.step_yoffset = 0;
4880 num_door_steps = g_part_1->width / door->step_offset;
4882 else // ANIM_VERTICAL
4884 door->part_1.step_xoffset = 0;
4885 door->part_1.step_yoffset = door->step_offset;
4886 door->part_2.step_xoffset = 0;
4887 door->part_2.step_yoffset = door->step_offset * -1;
4889 num_door_steps = g_part_1->height / door->step_offset;
4892 /* set animation draw offset for the default panels */
4894 if (door->step_offset > 1)
4896 num_panel_steps = 2 * door_rect->height / door->step_offset;
4897 door->panel.start_step = num_panel_steps - num_door_steps;
4898 door->panel.start_step_closing = door->panel.start_step;
4902 num_panel_steps = door_rect->height / door->step_offset;
4903 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4904 door->panel.start_step_closing = door->panel.start_step;
4905 door->panel.step_delay *= 2;
4916 for (i = 0; door_part_controls[i].door_token != -1; i++)
4918 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4919 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4921 /* initialize "start_step_opening" and "start_step_closing", if needed */
4922 if (dpc->pos->start_step_opening == 0 &&
4923 dpc->pos->start_step_closing == 0)
4925 // dpc->pos->start_step_opening = dpc->pos->start_step;
4926 dpc->pos->start_step_closing = dpc->pos->start_step;
4929 /* fill structure for door part draw order (sorted below) */
4931 dpo->sort_priority = dpc->pos->sort_priority;
4934 /* sort door part controls according to sort_priority and graphic number */
4935 qsort(door_part_order, MAX_DOOR_PARTS,
4936 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4939 unsigned int OpenDoor(unsigned int door_state)
4941 if (door_state & DOOR_COPY_BACK)
4943 if (door_state & DOOR_OPEN_1)
4944 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4945 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4947 if (door_state & DOOR_OPEN_2)
4948 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4949 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4951 door_state &= ~DOOR_COPY_BACK;
4954 return MoveDoor(door_state);
4957 unsigned int CloseDoor(unsigned int door_state)
4959 unsigned int old_door_state = GetDoorState();
4961 if (!(door_state & DOOR_NO_COPY_BACK))
4963 if (old_door_state & DOOR_OPEN_1)
4964 BlitBitmap(backbuffer, bitmap_db_door_1,
4965 DX, DY, DXSIZE, DYSIZE, 0, 0);
4967 if (old_door_state & DOOR_OPEN_2)
4968 BlitBitmap(backbuffer, bitmap_db_door_2,
4969 VX, VY, VXSIZE, VYSIZE, 0, 0);
4971 door_state &= ~DOOR_NO_COPY_BACK;
4974 return MoveDoor(door_state);
4977 unsigned int GetDoorState()
4979 return MoveDoor(DOOR_GET_STATE);
4982 unsigned int SetDoorState(unsigned int door_state)
4984 return MoveDoor(door_state | DOOR_SET_STATE);
4987 int euclid(int a, int b)
4989 return (b ? euclid(b, a % b) : a);
4992 unsigned int MoveDoor(unsigned int door_state)
4994 struct Rect door_rect_list[] =
4996 { DX, DY, DXSIZE, DYSIZE },
4997 { VX, VY, VXSIZE, VYSIZE }
4999 static int door1 = DOOR_CLOSE_1;
5000 static int door2 = DOOR_CLOSE_2;
5001 unsigned int door_delay = 0;
5002 unsigned int door_delay_value;
5005 if (door_state == DOOR_GET_STATE)
5006 return (door1 | door2);
5008 if (door_state & DOOR_SET_STATE)
5010 if (door_state & DOOR_ACTION_1)
5011 door1 = door_state & DOOR_ACTION_1;
5012 if (door_state & DOOR_ACTION_2)
5013 door2 = door_state & DOOR_ACTION_2;
5015 return (door1 | door2);
5018 if (!(door_state & DOOR_FORCE_REDRAW))
5020 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5021 door_state &= ~DOOR_OPEN_1;
5022 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5023 door_state &= ~DOOR_CLOSE_1;
5024 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5025 door_state &= ~DOOR_OPEN_2;
5026 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5027 door_state &= ~DOOR_CLOSE_2;
5030 if (global.autoplay_leveldir)
5032 door_state |= DOOR_NO_DELAY;
5033 door_state &= ~DOOR_CLOSE_ALL;
5036 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5037 door_state |= DOOR_NO_DELAY;
5039 if (door_state & DOOR_ACTION)
5041 boolean door_panel_drawn[NUM_DOORS];
5042 boolean panel_has_doors[NUM_DOORS];
5043 boolean door_part_skip[MAX_DOOR_PARTS];
5044 boolean door_part_done[MAX_DOOR_PARTS];
5045 boolean door_part_done_all;
5046 int num_steps[MAX_DOOR_PARTS];
5047 int max_move_delay = 0; // delay for complete animations of all doors
5048 int max_step_delay = 0; // delay (ms) between two animation frames
5049 int num_move_steps = 0; // number of animation steps for all doors
5050 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5051 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5052 int current_move_delay = 0;
5056 for (i = 0; i < NUM_DOORS; i++)
5057 panel_has_doors[i] = FALSE;
5059 for (i = 0; i < MAX_DOOR_PARTS; i++)
5061 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5062 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5063 int door_token = dpc->door_token;
5065 door_part_done[i] = FALSE;
5066 door_part_skip[i] = (!(door_state & door_token) ||
5070 for (i = 0; i < MAX_DOOR_PARTS; i++)
5072 int nr = door_part_order[i].nr;
5073 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5074 struct DoorPartPosInfo *pos = dpc->pos;
5075 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5076 int door_token = dpc->door_token;
5077 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5078 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5079 int step_xoffset = ABS(pos->step_xoffset);
5080 int step_yoffset = ABS(pos->step_yoffset);
5081 int step_delay = pos->step_delay;
5082 int current_door_state = door_state & door_token;
5083 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5084 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5085 boolean part_opening = (is_panel ? door_closing : door_opening);
5086 int start_step = (part_opening ? pos->start_step_opening :
5087 pos->start_step_closing);
5088 float move_xsize = (step_xoffset ? g->width : 0);
5089 float move_ysize = (step_yoffset ? g->height : 0);
5090 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5091 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5092 int move_steps = (move_xsteps && move_ysteps ?
5093 MIN(move_xsteps, move_ysteps) :
5094 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5095 int move_delay = move_steps * step_delay;
5097 if (door_part_skip[nr])
5100 max_move_delay = MAX(max_move_delay, move_delay);
5101 max_step_delay = (max_step_delay == 0 ? step_delay :
5102 euclid(max_step_delay, step_delay));
5103 num_steps[nr] = move_steps;
5107 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5109 panel_has_doors[door_index] = TRUE;
5113 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5115 num_move_steps = max_move_delay / max_step_delay;
5116 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5118 door_delay_value = max_step_delay;
5120 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5122 start = num_move_steps - 1;
5126 /* opening door sound has priority over simultaneously closing door */
5127 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5129 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5131 if (door_state & DOOR_OPEN_1)
5132 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5133 if (door_state & DOOR_OPEN_2)
5134 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5136 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5138 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5140 if (door_state & DOOR_CLOSE_1)
5141 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5142 if (door_state & DOOR_CLOSE_2)
5143 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5147 for (k = start; k < num_move_steps; k++)
5149 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5151 door_part_done_all = TRUE;
5153 for (i = 0; i < NUM_DOORS; i++)
5154 door_panel_drawn[i] = FALSE;
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 boolean is_panel_and_door_has_closed = FALSE;
5166 struct Rect *door_rect = &door_rect_list[door_index];
5167 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5169 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5170 int current_door_state = door_state & door_token;
5171 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5172 boolean door_closing = !door_opening;
5173 boolean part_opening = (is_panel ? door_closing : door_opening);
5174 boolean part_closing = !part_opening;
5175 int start_step = (part_opening ? pos->start_step_opening :
5176 pos->start_step_closing);
5177 int step_delay = pos->step_delay;
5178 int step_factor = step_delay / max_step_delay;
5179 int k1 = (step_factor ? k / step_factor + 1 : k);
5180 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5181 int kk = MAX(0, k2);
5184 int src_x, src_y, src_xx, src_yy;
5185 int dst_x, dst_y, dst_xx, dst_yy;
5188 if (door_part_skip[nr])
5191 if (!(door_state & door_token))
5199 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5200 int kk_door = MAX(0, k2_door);
5201 int sync_frame = kk_door * door_delay_value;
5202 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5204 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5205 &g_src_x, &g_src_y);
5210 if (!door_panel_drawn[door_index])
5212 ClearRectangle(drawto, door_rect->x, door_rect->y,
5213 door_rect->width, door_rect->height);
5215 door_panel_drawn[door_index] = TRUE;
5218 // draw opening or closing door parts
5220 if (pos->step_xoffset < 0) // door part on right side
5223 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5226 if (dst_xx + width > door_rect->width)
5227 width = door_rect->width - dst_xx;
5229 else // door part on left side
5232 dst_xx = pos->x - kk * pos->step_xoffset;
5236 src_xx = ABS(dst_xx);
5240 width = g->width - src_xx;
5242 if (width > door_rect->width)
5243 width = door_rect->width;
5245 // printf("::: k == %d [%d] \n", k, start_step);
5248 if (pos->step_yoffset < 0) // door part on bottom side
5251 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5254 if (dst_yy + height > door_rect->height)
5255 height = door_rect->height - dst_yy;
5257 else // door part on top side
5260 dst_yy = pos->y - kk * pos->step_yoffset;
5264 src_yy = ABS(dst_yy);
5268 height = g->height - src_yy;
5271 src_x = g_src_x + src_xx;
5272 src_y = g_src_y + src_yy;
5274 dst_x = door_rect->x + dst_xx;
5275 dst_y = door_rect->y + dst_yy;
5277 is_panel_and_door_has_closed =
5280 panel_has_doors[door_index] &&
5281 k >= num_move_steps_doors_only - 1);
5283 if (width >= 0 && width <= g->width &&
5284 height >= 0 && height <= g->height &&
5285 !is_panel_and_door_has_closed)
5287 if (is_panel || !pos->draw_masked)
5288 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5291 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5295 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5297 if ((part_opening && (width < 0 || height < 0)) ||
5298 (part_closing && (width >= g->width && height >= g->height)))
5299 door_part_done[nr] = TRUE;
5301 // continue door part animations, but not panel after door has closed
5302 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5303 door_part_done_all = FALSE;
5306 if (!(door_state & DOOR_NO_DELAY))
5310 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5312 current_move_delay += max_step_delay;
5314 /* prevent OS (Windows) from complaining about program not responding */
5318 if (door_part_done_all)
5322 if (!(door_state & DOOR_NO_DELAY))
5324 /* wait for specified door action post delay */
5325 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5326 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5327 else if (door_state & DOOR_ACTION_1)
5328 door_delay_value = door_1.post_delay;
5329 else if (door_state & DOOR_ACTION_2)
5330 door_delay_value = door_2.post_delay;
5332 while (!DelayReached(&door_delay, door_delay_value))
5337 if (door_state & DOOR_ACTION_1)
5338 door1 = door_state & DOOR_ACTION_1;
5339 if (door_state & DOOR_ACTION_2)
5340 door2 = door_state & DOOR_ACTION_2;
5342 // draw masked border over door area
5343 DrawMaskedBorder(REDRAW_DOOR_1);
5344 DrawMaskedBorder(REDRAW_DOOR_2);
5346 return (door1 | door2);
5349 static boolean useSpecialEditorDoor()
5351 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5352 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5354 // do not draw special editor door if editor border defined or redefined
5355 if (graphic_info[graphic].bitmap != NULL || redefined)
5358 // do not draw special editor door if global border defined to be empty
5359 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5362 // do not draw special editor door if viewport definitions do not match
5366 EY + EYSIZE != VY + VYSIZE)
5372 void DrawSpecialEditorDoor()
5374 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5375 int top_border_width = gfx1->width;
5376 int top_border_height = gfx1->height;
5377 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5378 int ex = EX - outer_border;
5379 int ey = EY - outer_border;
5380 int vy = VY - outer_border;
5381 int exsize = EXSIZE + 2 * outer_border;
5383 if (!useSpecialEditorDoor())
5386 /* draw bigger level editor toolbox window */
5387 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5388 top_border_width, top_border_height, ex, ey - top_border_height);
5389 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5390 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5392 redraw_mask |= REDRAW_ALL;
5395 void UndrawSpecialEditorDoor()
5397 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5398 int top_border_width = gfx1->width;
5399 int top_border_height = gfx1->height;
5400 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5401 int ex = EX - outer_border;
5402 int ey = EY - outer_border;
5403 int ey_top = ey - top_border_height;
5404 int exsize = EXSIZE + 2 * outer_border;
5405 int eysize = EYSIZE + 2 * outer_border;
5407 if (!useSpecialEditorDoor())
5410 /* draw normal tape recorder window */
5411 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5413 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5414 ex, ey_top, top_border_width, top_border_height,
5416 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5417 ex, ey, exsize, eysize, ex, ey);
5421 // if screen background is set to "[NONE]", clear editor toolbox window
5422 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5423 ClearRectangle(drawto, ex, ey, exsize, eysize);
5426 redraw_mask |= REDRAW_ALL;
5430 /* ---------- new tool button stuff ---------------------------------------- */
5435 struct TextPosInfo *pos;
5438 } toolbutton_info[NUM_TOOL_BUTTONS] =
5441 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5442 TOOL_CTRL_ID_YES, "yes"
5445 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5446 TOOL_CTRL_ID_NO, "no"
5449 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5450 TOOL_CTRL_ID_CONFIRM, "confirm"
5453 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5454 TOOL_CTRL_ID_PLAYER_1, "player 1"
5457 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5458 TOOL_CTRL_ID_PLAYER_2, "player 2"
5461 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5462 TOOL_CTRL_ID_PLAYER_3, "player 3"
5465 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5466 TOOL_CTRL_ID_PLAYER_4, "player 4"
5470 void CreateToolButtons()
5474 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5476 int graphic = toolbutton_info[i].graphic;
5477 struct GraphicInfo *gfx = &graphic_info[graphic];
5478 struct TextPosInfo *pos = toolbutton_info[i].pos;
5479 struct GadgetInfo *gi;
5480 Bitmap *deco_bitmap = None;
5481 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5482 unsigned int event_mask = GD_EVENT_RELEASED;
5485 int gd_x = gfx->src_x;
5486 int gd_y = gfx->src_y;
5487 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5488 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5493 if (global.use_envelope_request)
5495 setRequestPosition(&dx, &dy, TRUE);
5497 // check if request buttons are outside of envelope and fix, if needed
5498 if (x < 0 || x + gfx->width > request.width ||
5499 y < 0 || y + gfx->height > request.height)
5501 if (id == TOOL_CTRL_ID_YES)
5504 y = request.height - 2 * request.border_size - gfx->height;
5506 else if (id == TOOL_CTRL_ID_NO)
5508 x = request.width - 2 * request.border_size - gfx->width;
5509 y = request.height - 2 * request.border_size - gfx->height;
5511 else if (id == TOOL_CTRL_ID_CONFIRM)
5513 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5514 y = request.height - 2 * request.border_size - gfx->height;
5516 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5518 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5520 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5521 y = request.height - 2 * request.border_size - gfx->height * 2;
5523 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5524 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5529 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5531 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5533 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5534 pos->size, &deco_bitmap, &deco_x, &deco_y);
5535 deco_xpos = (gfx->width - pos->size) / 2;
5536 deco_ypos = (gfx->height - pos->size) / 2;
5539 gi = CreateGadget(GDI_CUSTOM_ID, id,
5540 GDI_IMAGE_ID, graphic,
5541 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5544 GDI_WIDTH, gfx->width,
5545 GDI_HEIGHT, gfx->height,
5546 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5547 GDI_STATE, GD_BUTTON_UNPRESSED,
5548 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5549 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5550 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5551 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5552 GDI_DECORATION_SIZE, pos->size, pos->size,
5553 GDI_DECORATION_SHIFTING, 1, 1,
5554 GDI_DIRECT_DRAW, FALSE,
5555 GDI_EVENT_MASK, event_mask,
5556 GDI_CALLBACK_ACTION, HandleToolButtons,
5560 Error(ERR_EXIT, "cannot create gadget");
5562 tool_gadget[id] = gi;
5566 void FreeToolButtons()
5570 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5571 FreeGadget(tool_gadget[i]);
5574 static void UnmapToolButtons()
5578 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5579 UnmapGadget(tool_gadget[i]);
5582 static void HandleToolButtons(struct GadgetInfo *gi)
5584 request_gadget_id = gi->custom_id;
5587 static struct Mapping_EM_to_RND_object
5590 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5591 boolean is_backside; /* backside of moving element */
5597 em_object_mapping_list[] =
5600 Xblank, TRUE, FALSE,
5604 Yacid_splash_eB, FALSE, FALSE,
5605 EL_ACID_SPLASH_RIGHT, -1, -1
5608 Yacid_splash_wB, FALSE, FALSE,
5609 EL_ACID_SPLASH_LEFT, -1, -1
5612 #ifdef EM_ENGINE_BAD_ROLL
5614 Xstone_force_e, FALSE, FALSE,
5615 EL_ROCK, -1, MV_BIT_RIGHT
5618 Xstone_force_w, FALSE, FALSE,
5619 EL_ROCK, -1, MV_BIT_LEFT
5622 Xnut_force_e, FALSE, FALSE,
5623 EL_NUT, -1, MV_BIT_RIGHT
5626 Xnut_force_w, FALSE, FALSE,
5627 EL_NUT, -1, MV_BIT_LEFT
5630 Xspring_force_e, FALSE, FALSE,
5631 EL_SPRING, -1, MV_BIT_RIGHT
5634 Xspring_force_w, FALSE, FALSE,
5635 EL_SPRING, -1, MV_BIT_LEFT
5638 Xemerald_force_e, FALSE, FALSE,
5639 EL_EMERALD, -1, MV_BIT_RIGHT
5642 Xemerald_force_w, FALSE, FALSE,
5643 EL_EMERALD, -1, MV_BIT_LEFT
5646 Xdiamond_force_e, FALSE, FALSE,
5647 EL_DIAMOND, -1, MV_BIT_RIGHT
5650 Xdiamond_force_w, FALSE, FALSE,
5651 EL_DIAMOND, -1, MV_BIT_LEFT
5654 Xbomb_force_e, FALSE, FALSE,
5655 EL_BOMB, -1, MV_BIT_RIGHT
5658 Xbomb_force_w, FALSE, FALSE,
5659 EL_BOMB, -1, MV_BIT_LEFT
5661 #endif /* EM_ENGINE_BAD_ROLL */
5664 Xstone, TRUE, FALSE,
5668 Xstone_pause, FALSE, FALSE,
5672 Xstone_fall, FALSE, FALSE,
5676 Ystone_s, FALSE, FALSE,
5677 EL_ROCK, ACTION_FALLING, -1
5680 Ystone_sB, FALSE, TRUE,
5681 EL_ROCK, ACTION_FALLING, -1
5684 Ystone_e, FALSE, FALSE,
5685 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5688 Ystone_eB, FALSE, TRUE,
5689 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5692 Ystone_w, FALSE, FALSE,
5693 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5696 Ystone_wB, FALSE, TRUE,
5697 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5704 Xnut_pause, FALSE, FALSE,
5708 Xnut_fall, FALSE, FALSE,
5712 Ynut_s, FALSE, FALSE,
5713 EL_NUT, ACTION_FALLING, -1
5716 Ynut_sB, FALSE, TRUE,
5717 EL_NUT, ACTION_FALLING, -1
5720 Ynut_e, FALSE, FALSE,
5721 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5724 Ynut_eB, FALSE, TRUE,
5725 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5728 Ynut_w, FALSE, FALSE,
5729 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5732 Ynut_wB, FALSE, TRUE,
5733 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5736 Xbug_n, TRUE, FALSE,
5740 Xbug_e, TRUE, FALSE,
5741 EL_BUG_RIGHT, -1, -1
5744 Xbug_s, TRUE, FALSE,
5748 Xbug_w, TRUE, FALSE,
5752 Xbug_gon, FALSE, FALSE,
5756 Xbug_goe, FALSE, FALSE,
5757 EL_BUG_RIGHT, -1, -1
5760 Xbug_gos, FALSE, FALSE,
5764 Xbug_gow, FALSE, FALSE,
5768 Ybug_n, FALSE, FALSE,
5769 EL_BUG, ACTION_MOVING, MV_BIT_UP
5772 Ybug_nB, FALSE, TRUE,
5773 EL_BUG, ACTION_MOVING, MV_BIT_UP
5776 Ybug_e, FALSE, FALSE,
5777 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5780 Ybug_eB, FALSE, TRUE,
5781 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5784 Ybug_s, FALSE, FALSE,
5785 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5788 Ybug_sB, FALSE, TRUE,
5789 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5792 Ybug_w, FALSE, FALSE,
5793 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5796 Ybug_wB, FALSE, TRUE,
5797 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5800 Ybug_w_n, FALSE, FALSE,
5801 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5804 Ybug_n_e, FALSE, FALSE,
5805 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5808 Ybug_e_s, FALSE, FALSE,
5809 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5812 Ybug_s_w, FALSE, FALSE,
5813 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5816 Ybug_e_n, FALSE, FALSE,
5817 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5820 Ybug_s_e, FALSE, FALSE,
5821 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5824 Ybug_w_s, FALSE, FALSE,
5825 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5828 Ybug_n_w, FALSE, FALSE,
5829 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5832 Ybug_stone, FALSE, FALSE,
5833 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5836 Ybug_spring, FALSE, FALSE,
5837 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5840 Xtank_n, TRUE, FALSE,
5841 EL_SPACESHIP_UP, -1, -1
5844 Xtank_e, TRUE, FALSE,
5845 EL_SPACESHIP_RIGHT, -1, -1
5848 Xtank_s, TRUE, FALSE,
5849 EL_SPACESHIP_DOWN, -1, -1
5852 Xtank_w, TRUE, FALSE,
5853 EL_SPACESHIP_LEFT, -1, -1
5856 Xtank_gon, FALSE, FALSE,
5857 EL_SPACESHIP_UP, -1, -1
5860 Xtank_goe, FALSE, FALSE,
5861 EL_SPACESHIP_RIGHT, -1, -1
5864 Xtank_gos, FALSE, FALSE,
5865 EL_SPACESHIP_DOWN, -1, -1
5868 Xtank_gow, FALSE, FALSE,
5869 EL_SPACESHIP_LEFT, -1, -1
5872 Ytank_n, FALSE, FALSE,
5873 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5876 Ytank_nB, FALSE, TRUE,
5877 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5880 Ytank_e, FALSE, FALSE,
5881 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5884 Ytank_eB, FALSE, TRUE,
5885 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5888 Ytank_s, FALSE, FALSE,
5889 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5892 Ytank_sB, FALSE, TRUE,
5893 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5896 Ytank_w, FALSE, FALSE,
5897 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5900 Ytank_wB, FALSE, TRUE,
5901 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5904 Ytank_w_n, FALSE, FALSE,
5905 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5908 Ytank_n_e, FALSE, FALSE,
5909 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5912 Ytank_e_s, FALSE, FALSE,
5913 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5916 Ytank_s_w, FALSE, FALSE,
5917 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5920 Ytank_e_n, FALSE, FALSE,
5921 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5924 Ytank_s_e, FALSE, FALSE,
5925 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5928 Ytank_w_s, FALSE, FALSE,
5929 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5932 Ytank_n_w, FALSE, FALSE,
5933 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5936 Ytank_stone, FALSE, FALSE,
5937 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5940 Ytank_spring, FALSE, FALSE,
5941 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5944 Xandroid, TRUE, FALSE,
5945 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5948 Xandroid_1_n, FALSE, FALSE,
5949 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5952 Xandroid_2_n, FALSE, FALSE,
5953 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5956 Xandroid_1_e, FALSE, FALSE,
5957 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5960 Xandroid_2_e, FALSE, FALSE,
5961 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5964 Xandroid_1_w, FALSE, FALSE,
5965 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5968 Xandroid_2_w, FALSE, FALSE,
5969 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5972 Xandroid_1_s, FALSE, FALSE,
5973 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5976 Xandroid_2_s, FALSE, FALSE,
5977 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5980 Yandroid_n, FALSE, FALSE,
5981 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5984 Yandroid_nB, FALSE, TRUE,
5985 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5988 Yandroid_ne, FALSE, FALSE,
5989 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5992 Yandroid_neB, FALSE, TRUE,
5993 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5996 Yandroid_e, FALSE, FALSE,
5997 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6000 Yandroid_eB, FALSE, TRUE,
6001 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6004 Yandroid_se, FALSE, FALSE,
6005 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6008 Yandroid_seB, FALSE, TRUE,
6009 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6012 Yandroid_s, FALSE, FALSE,
6013 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6016 Yandroid_sB, FALSE, TRUE,
6017 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6020 Yandroid_sw, FALSE, FALSE,
6021 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6024 Yandroid_swB, FALSE, TRUE,
6025 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6028 Yandroid_w, FALSE, FALSE,
6029 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6032 Yandroid_wB, FALSE, TRUE,
6033 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6036 Yandroid_nw, FALSE, FALSE,
6037 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6040 Yandroid_nwB, FALSE, TRUE,
6041 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6044 Xspring, TRUE, FALSE,
6048 Xspring_pause, FALSE, FALSE,
6052 Xspring_e, FALSE, FALSE,
6056 Xspring_w, FALSE, FALSE,
6060 Xspring_fall, FALSE, FALSE,
6064 Yspring_s, FALSE, FALSE,
6065 EL_SPRING, ACTION_FALLING, -1
6068 Yspring_sB, FALSE, TRUE,
6069 EL_SPRING, ACTION_FALLING, -1
6072 Yspring_e, FALSE, FALSE,
6073 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6076 Yspring_eB, FALSE, TRUE,
6077 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6080 Yspring_w, FALSE, FALSE,
6081 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6084 Yspring_wB, FALSE, TRUE,
6085 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6088 Yspring_kill_e, FALSE, FALSE,
6089 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6092 Yspring_kill_eB, FALSE, TRUE,
6093 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6096 Yspring_kill_w, FALSE, FALSE,
6097 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6100 Yspring_kill_wB, FALSE, TRUE,
6101 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6104 Xeater_n, TRUE, FALSE,
6105 EL_YAMYAM_UP, -1, -1
6108 Xeater_e, TRUE, FALSE,
6109 EL_YAMYAM_RIGHT, -1, -1
6112 Xeater_w, TRUE, FALSE,
6113 EL_YAMYAM_LEFT, -1, -1
6116 Xeater_s, TRUE, FALSE,
6117 EL_YAMYAM_DOWN, -1, -1
6120 Yeater_n, FALSE, FALSE,
6121 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6124 Yeater_nB, FALSE, TRUE,
6125 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6128 Yeater_e, FALSE, FALSE,
6129 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6132 Yeater_eB, FALSE, TRUE,
6133 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6136 Yeater_s, FALSE, FALSE,
6137 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6140 Yeater_sB, FALSE, TRUE,
6141 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6144 Yeater_w, FALSE, FALSE,
6145 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6148 Yeater_wB, FALSE, TRUE,
6149 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6152 Yeater_stone, FALSE, FALSE,
6153 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6156 Yeater_spring, FALSE, FALSE,
6157 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6160 Xalien, TRUE, FALSE,
6164 Xalien_pause, FALSE, FALSE,
6168 Yalien_n, FALSE, FALSE,
6169 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6172 Yalien_nB, FALSE, TRUE,
6173 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6176 Yalien_e, FALSE, FALSE,
6177 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6180 Yalien_eB, FALSE, TRUE,
6181 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6184 Yalien_s, FALSE, FALSE,
6185 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6188 Yalien_sB, FALSE, TRUE,
6189 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6192 Yalien_w, FALSE, FALSE,
6193 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6196 Yalien_wB, FALSE, TRUE,
6197 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6200 Yalien_stone, FALSE, FALSE,
6201 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6204 Yalien_spring, FALSE, FALSE,
6205 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6208 Xemerald, TRUE, FALSE,
6212 Xemerald_pause, FALSE, FALSE,
6216 Xemerald_fall, FALSE, FALSE,
6220 Xemerald_shine, FALSE, FALSE,
6221 EL_EMERALD, ACTION_TWINKLING, -1
6224 Yemerald_s, FALSE, FALSE,
6225 EL_EMERALD, ACTION_FALLING, -1
6228 Yemerald_sB, FALSE, TRUE,
6229 EL_EMERALD, ACTION_FALLING, -1
6232 Yemerald_e, FALSE, FALSE,
6233 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6236 Yemerald_eB, FALSE, TRUE,
6237 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6240 Yemerald_w, FALSE, FALSE,
6241 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6244 Yemerald_wB, FALSE, TRUE,
6245 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6248 Yemerald_eat, FALSE, FALSE,
6249 EL_EMERALD, ACTION_COLLECTING, -1
6252 Yemerald_stone, FALSE, FALSE,
6253 EL_NUT, ACTION_BREAKING, -1
6256 Xdiamond, TRUE, FALSE,
6260 Xdiamond_pause, FALSE, FALSE,
6264 Xdiamond_fall, FALSE, FALSE,
6268 Xdiamond_shine, FALSE, FALSE,
6269 EL_DIAMOND, ACTION_TWINKLING, -1
6272 Ydiamond_s, FALSE, FALSE,
6273 EL_DIAMOND, ACTION_FALLING, -1
6276 Ydiamond_sB, FALSE, TRUE,
6277 EL_DIAMOND, ACTION_FALLING, -1
6280 Ydiamond_e, FALSE, FALSE,
6281 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6284 Ydiamond_eB, FALSE, TRUE,
6285 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6288 Ydiamond_w, FALSE, FALSE,
6289 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6292 Ydiamond_wB, FALSE, TRUE,
6293 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6296 Ydiamond_eat, FALSE, FALSE,
6297 EL_DIAMOND, ACTION_COLLECTING, -1
6300 Ydiamond_stone, FALSE, FALSE,
6301 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6304 Xdrip_fall, TRUE, FALSE,
6305 EL_AMOEBA_DROP, -1, -1
6308 Xdrip_stretch, FALSE, FALSE,
6309 EL_AMOEBA_DROP, ACTION_FALLING, -1
6312 Xdrip_stretchB, FALSE, TRUE,
6313 EL_AMOEBA_DROP, ACTION_FALLING, -1
6316 Xdrip_eat, FALSE, FALSE,
6317 EL_AMOEBA_DROP, ACTION_GROWING, -1
6320 Ydrip_s1, FALSE, FALSE,
6321 EL_AMOEBA_DROP, ACTION_FALLING, -1
6324 Ydrip_s1B, FALSE, TRUE,
6325 EL_AMOEBA_DROP, ACTION_FALLING, -1
6328 Ydrip_s2, FALSE, FALSE,
6329 EL_AMOEBA_DROP, ACTION_FALLING, -1
6332 Ydrip_s2B, FALSE, TRUE,
6333 EL_AMOEBA_DROP, ACTION_FALLING, -1
6340 Xbomb_pause, FALSE, FALSE,
6344 Xbomb_fall, FALSE, FALSE,
6348 Ybomb_s, FALSE, FALSE,
6349 EL_BOMB, ACTION_FALLING, -1
6352 Ybomb_sB, FALSE, TRUE,
6353 EL_BOMB, ACTION_FALLING, -1
6356 Ybomb_e, FALSE, FALSE,
6357 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6360 Ybomb_eB, FALSE, TRUE,
6361 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6364 Ybomb_w, FALSE, FALSE,
6365 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6368 Ybomb_wB, FALSE, TRUE,
6369 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6372 Ybomb_eat, FALSE, FALSE,
6373 EL_BOMB, ACTION_ACTIVATING, -1
6376 Xballoon, TRUE, FALSE,
6380 Yballoon_n, FALSE, FALSE,
6381 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6384 Yballoon_nB, FALSE, TRUE,
6385 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6388 Yballoon_e, FALSE, FALSE,
6389 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6392 Yballoon_eB, FALSE, TRUE,
6393 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6396 Yballoon_s, FALSE, FALSE,
6397 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6400 Yballoon_sB, FALSE, TRUE,
6401 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6404 Yballoon_w, FALSE, FALSE,
6405 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6408 Yballoon_wB, FALSE, TRUE,
6409 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6412 Xgrass, TRUE, FALSE,
6413 EL_EMC_GRASS, -1, -1
6416 Ygrass_nB, FALSE, FALSE,
6417 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6420 Ygrass_eB, FALSE, FALSE,
6421 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6424 Ygrass_sB, FALSE, FALSE,
6425 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6428 Ygrass_wB, FALSE, FALSE,
6429 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6436 Ydirt_nB, FALSE, FALSE,
6437 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6440 Ydirt_eB, FALSE, FALSE,
6441 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6444 Ydirt_sB, FALSE, FALSE,
6445 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6448 Ydirt_wB, FALSE, FALSE,
6449 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6452 Xacid_ne, TRUE, FALSE,
6453 EL_ACID_POOL_TOPRIGHT, -1, -1
6456 Xacid_se, TRUE, FALSE,
6457 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6460 Xacid_s, TRUE, FALSE,
6461 EL_ACID_POOL_BOTTOM, -1, -1
6464 Xacid_sw, TRUE, FALSE,
6465 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6468 Xacid_nw, TRUE, FALSE,
6469 EL_ACID_POOL_TOPLEFT, -1, -1
6472 Xacid_1, TRUE, FALSE,
6476 Xacid_2, FALSE, FALSE,
6480 Xacid_3, FALSE, FALSE,
6484 Xacid_4, FALSE, FALSE,
6488 Xacid_5, FALSE, FALSE,
6492 Xacid_6, FALSE, FALSE,
6496 Xacid_7, FALSE, FALSE,
6500 Xacid_8, FALSE, FALSE,
6504 Xball_1, TRUE, FALSE,
6505 EL_EMC_MAGIC_BALL, -1, -1
6508 Xball_1B, FALSE, FALSE,
6509 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6512 Xball_2, FALSE, FALSE,
6513 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6516 Xball_2B, FALSE, FALSE,
6517 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6520 Yball_eat, FALSE, FALSE,
6521 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6524 Ykey_1_eat, FALSE, FALSE,
6525 EL_EM_KEY_1, ACTION_COLLECTING, -1
6528 Ykey_2_eat, FALSE, FALSE,
6529 EL_EM_KEY_2, ACTION_COLLECTING, -1
6532 Ykey_3_eat, FALSE, FALSE,
6533 EL_EM_KEY_3, ACTION_COLLECTING, -1
6536 Ykey_4_eat, FALSE, FALSE,
6537 EL_EM_KEY_4, ACTION_COLLECTING, -1
6540 Ykey_5_eat, FALSE, FALSE,
6541 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6544 Ykey_6_eat, FALSE, FALSE,
6545 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6548 Ykey_7_eat, FALSE, FALSE,
6549 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6552 Ykey_8_eat, FALSE, FALSE,
6553 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6556 Ylenses_eat, FALSE, FALSE,
6557 EL_EMC_LENSES, ACTION_COLLECTING, -1
6560 Ymagnify_eat, FALSE, FALSE,
6561 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6564 Ygrass_eat, FALSE, FALSE,
6565 EL_EMC_GRASS, ACTION_SNAPPING, -1
6568 Ydirt_eat, FALSE, FALSE,
6569 EL_SAND, ACTION_SNAPPING, -1
6572 Xgrow_ns, TRUE, FALSE,
6573 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6576 Ygrow_ns_eat, FALSE, FALSE,
6577 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6580 Xgrow_ew, TRUE, FALSE,
6581 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6584 Ygrow_ew_eat, FALSE, FALSE,
6585 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6588 Xwonderwall, TRUE, FALSE,
6589 EL_MAGIC_WALL, -1, -1
6592 XwonderwallB, FALSE, FALSE,
6593 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6596 Xamoeba_1, TRUE, FALSE,
6597 EL_AMOEBA_DRY, ACTION_OTHER, -1
6600 Xamoeba_2, FALSE, FALSE,
6601 EL_AMOEBA_DRY, ACTION_OTHER, -1
6604 Xamoeba_3, FALSE, FALSE,
6605 EL_AMOEBA_DRY, ACTION_OTHER, -1
6608 Xamoeba_4, FALSE, FALSE,
6609 EL_AMOEBA_DRY, ACTION_OTHER, -1
6612 Xamoeba_5, TRUE, FALSE,
6613 EL_AMOEBA_WET, ACTION_OTHER, -1
6616 Xamoeba_6, FALSE, FALSE,
6617 EL_AMOEBA_WET, ACTION_OTHER, -1
6620 Xamoeba_7, FALSE, FALSE,
6621 EL_AMOEBA_WET, ACTION_OTHER, -1
6624 Xamoeba_8, FALSE, FALSE,
6625 EL_AMOEBA_WET, ACTION_OTHER, -1
6628 Xdoor_1, TRUE, FALSE,
6629 EL_EM_GATE_1, -1, -1
6632 Xdoor_2, TRUE, FALSE,
6633 EL_EM_GATE_2, -1, -1
6636 Xdoor_3, TRUE, FALSE,
6637 EL_EM_GATE_3, -1, -1
6640 Xdoor_4, TRUE, FALSE,
6641 EL_EM_GATE_4, -1, -1
6644 Xdoor_5, TRUE, FALSE,
6645 EL_EMC_GATE_5, -1, -1
6648 Xdoor_6, TRUE, FALSE,
6649 EL_EMC_GATE_6, -1, -1
6652 Xdoor_7, TRUE, FALSE,
6653 EL_EMC_GATE_7, -1, -1
6656 Xdoor_8, TRUE, FALSE,
6657 EL_EMC_GATE_8, -1, -1
6660 Xkey_1, TRUE, FALSE,
6664 Xkey_2, TRUE, FALSE,
6668 Xkey_3, TRUE, FALSE,
6672 Xkey_4, TRUE, FALSE,
6676 Xkey_5, TRUE, FALSE,
6677 EL_EMC_KEY_5, -1, -1
6680 Xkey_6, TRUE, FALSE,
6681 EL_EMC_KEY_6, -1, -1
6684 Xkey_7, TRUE, FALSE,
6685 EL_EMC_KEY_7, -1, -1
6688 Xkey_8, TRUE, FALSE,
6689 EL_EMC_KEY_8, -1, -1
6692 Xwind_n, TRUE, FALSE,
6693 EL_BALLOON_SWITCH_UP, -1, -1
6696 Xwind_e, TRUE, FALSE,
6697 EL_BALLOON_SWITCH_RIGHT, -1, -1
6700 Xwind_s, TRUE, FALSE,
6701 EL_BALLOON_SWITCH_DOWN, -1, -1
6704 Xwind_w, TRUE, FALSE,
6705 EL_BALLOON_SWITCH_LEFT, -1, -1
6708 Xwind_nesw, TRUE, FALSE,
6709 EL_BALLOON_SWITCH_ANY, -1, -1
6712 Xwind_stop, TRUE, FALSE,
6713 EL_BALLOON_SWITCH_NONE, -1, -1
6717 EL_EM_EXIT_CLOSED, -1, -1
6720 Xexit_1, TRUE, FALSE,
6721 EL_EM_EXIT_OPEN, -1, -1
6724 Xexit_2, FALSE, FALSE,
6725 EL_EM_EXIT_OPEN, -1, -1
6728 Xexit_3, FALSE, FALSE,
6729 EL_EM_EXIT_OPEN, -1, -1
6732 Xdynamite, TRUE, FALSE,
6733 EL_EM_DYNAMITE, -1, -1
6736 Ydynamite_eat, FALSE, FALSE,
6737 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6740 Xdynamite_1, TRUE, FALSE,
6741 EL_EM_DYNAMITE_ACTIVE, -1, -1
6744 Xdynamite_2, FALSE, FALSE,
6745 EL_EM_DYNAMITE_ACTIVE, -1, -1
6748 Xdynamite_3, FALSE, FALSE,
6749 EL_EM_DYNAMITE_ACTIVE, -1, -1
6752 Xdynamite_4, FALSE, FALSE,
6753 EL_EM_DYNAMITE_ACTIVE, -1, -1
6756 Xbumper, TRUE, FALSE,
6757 EL_EMC_SPRING_BUMPER, -1, -1
6760 XbumperB, FALSE, FALSE,
6761 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6764 Xwheel, TRUE, FALSE,
6765 EL_ROBOT_WHEEL, -1, -1
6768 XwheelB, FALSE, FALSE,
6769 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6772 Xswitch, TRUE, FALSE,
6773 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6776 XswitchB, FALSE, FALSE,
6777 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6781 EL_QUICKSAND_EMPTY, -1, -1
6784 Xsand_stone, TRUE, FALSE,
6785 EL_QUICKSAND_FULL, -1, -1
6788 Xsand_stonein_1, FALSE, TRUE,
6789 EL_ROCK, ACTION_FILLING, -1
6792 Xsand_stonein_2, FALSE, TRUE,
6793 EL_ROCK, ACTION_FILLING, -1
6796 Xsand_stonein_3, FALSE, TRUE,
6797 EL_ROCK, ACTION_FILLING, -1
6800 Xsand_stonein_4, FALSE, TRUE,
6801 EL_ROCK, ACTION_FILLING, -1
6804 Xsand_stonesand_1, FALSE, FALSE,
6805 EL_QUICKSAND_EMPTYING, -1, -1
6808 Xsand_stonesand_2, FALSE, FALSE,
6809 EL_QUICKSAND_EMPTYING, -1, -1
6812 Xsand_stonesand_3, FALSE, FALSE,
6813 EL_QUICKSAND_EMPTYING, -1, -1
6816 Xsand_stonesand_4, FALSE, FALSE,
6817 EL_QUICKSAND_EMPTYING, -1, -1
6820 Xsand_stonesand_quickout_1, FALSE, FALSE,
6821 EL_QUICKSAND_EMPTYING, -1, -1
6824 Xsand_stonesand_quickout_2, FALSE, FALSE,
6825 EL_QUICKSAND_EMPTYING, -1, -1
6828 Xsand_stoneout_1, FALSE, FALSE,
6829 EL_ROCK, ACTION_EMPTYING, -1
6832 Xsand_stoneout_2, FALSE, FALSE,
6833 EL_ROCK, ACTION_EMPTYING, -1
6836 Xsand_sandstone_1, FALSE, FALSE,
6837 EL_QUICKSAND_FILLING, -1, -1
6840 Xsand_sandstone_2, FALSE, FALSE,
6841 EL_QUICKSAND_FILLING, -1, -1
6844 Xsand_sandstone_3, FALSE, FALSE,
6845 EL_QUICKSAND_FILLING, -1, -1
6848 Xsand_sandstone_4, FALSE, FALSE,
6849 EL_QUICKSAND_FILLING, -1, -1
6852 Xplant, TRUE, FALSE,
6853 EL_EMC_PLANT, -1, -1
6856 Yplant, FALSE, FALSE,
6857 EL_EMC_PLANT, -1, -1
6860 Xlenses, TRUE, FALSE,
6861 EL_EMC_LENSES, -1, -1
6864 Xmagnify, TRUE, FALSE,
6865 EL_EMC_MAGNIFIER, -1, -1
6868 Xdripper, TRUE, FALSE,
6869 EL_EMC_DRIPPER, -1, -1
6872 XdripperB, FALSE, FALSE,
6873 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6876 Xfake_blank, TRUE, FALSE,
6877 EL_INVISIBLE_WALL, -1, -1
6880 Xfake_blankB, FALSE, FALSE,
6881 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6884 Xfake_grass, TRUE, FALSE,
6885 EL_EMC_FAKE_GRASS, -1, -1
6888 Xfake_grassB, FALSE, FALSE,
6889 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6892 Xfake_door_1, TRUE, FALSE,
6893 EL_EM_GATE_1_GRAY, -1, -1
6896 Xfake_door_2, TRUE, FALSE,
6897 EL_EM_GATE_2_GRAY, -1, -1
6900 Xfake_door_3, TRUE, FALSE,
6901 EL_EM_GATE_3_GRAY, -1, -1
6904 Xfake_door_4, TRUE, FALSE,
6905 EL_EM_GATE_4_GRAY, -1, -1
6908 Xfake_door_5, TRUE, FALSE,
6909 EL_EMC_GATE_5_GRAY, -1, -1
6912 Xfake_door_6, TRUE, FALSE,
6913 EL_EMC_GATE_6_GRAY, -1, -1
6916 Xfake_door_7, TRUE, FALSE,
6917 EL_EMC_GATE_7_GRAY, -1, -1
6920 Xfake_door_8, TRUE, FALSE,
6921 EL_EMC_GATE_8_GRAY, -1, -1
6924 Xfake_acid_1, TRUE, FALSE,
6925 EL_EMC_FAKE_ACID, -1, -1
6928 Xfake_acid_2, FALSE, FALSE,
6929 EL_EMC_FAKE_ACID, -1, -1
6932 Xfake_acid_3, FALSE, FALSE,
6933 EL_EMC_FAKE_ACID, -1, -1
6936 Xfake_acid_4, FALSE, FALSE,
6937 EL_EMC_FAKE_ACID, -1, -1
6940 Xfake_acid_5, FALSE, FALSE,
6941 EL_EMC_FAKE_ACID, -1, -1
6944 Xfake_acid_6, FALSE, FALSE,
6945 EL_EMC_FAKE_ACID, -1, -1
6948 Xfake_acid_7, FALSE, FALSE,
6949 EL_EMC_FAKE_ACID, -1, -1
6952 Xfake_acid_8, FALSE, FALSE,
6953 EL_EMC_FAKE_ACID, -1, -1
6956 Xsteel_1, TRUE, FALSE,
6957 EL_STEELWALL, -1, -1
6960 Xsteel_2, TRUE, FALSE,
6961 EL_EMC_STEELWALL_2, -1, -1
6964 Xsteel_3, TRUE, FALSE,
6965 EL_EMC_STEELWALL_3, -1, -1
6968 Xsteel_4, TRUE, FALSE,
6969 EL_EMC_STEELWALL_4, -1, -1
6972 Xwall_1, TRUE, FALSE,
6976 Xwall_2, TRUE, FALSE,
6977 EL_EMC_WALL_14, -1, -1
6980 Xwall_3, TRUE, FALSE,
6981 EL_EMC_WALL_15, -1, -1
6984 Xwall_4, TRUE, FALSE,
6985 EL_EMC_WALL_16, -1, -1
6988 Xround_wall_1, TRUE, FALSE,
6989 EL_WALL_SLIPPERY, -1, -1
6992 Xround_wall_2, TRUE, FALSE,
6993 EL_EMC_WALL_SLIPPERY_2, -1, -1
6996 Xround_wall_3, TRUE, FALSE,
6997 EL_EMC_WALL_SLIPPERY_3, -1, -1
7000 Xround_wall_4, TRUE, FALSE,
7001 EL_EMC_WALL_SLIPPERY_4, -1, -1
7004 Xdecor_1, TRUE, FALSE,
7005 EL_EMC_WALL_8, -1, -1
7008 Xdecor_2, TRUE, FALSE,
7009 EL_EMC_WALL_6, -1, -1
7012 Xdecor_3, TRUE, FALSE,
7013 EL_EMC_WALL_4, -1, -1
7016 Xdecor_4, TRUE, FALSE,
7017 EL_EMC_WALL_7, -1, -1
7020 Xdecor_5, TRUE, FALSE,
7021 EL_EMC_WALL_5, -1, -1
7024 Xdecor_6, TRUE, FALSE,
7025 EL_EMC_WALL_9, -1, -1
7028 Xdecor_7, TRUE, FALSE,
7029 EL_EMC_WALL_10, -1, -1
7032 Xdecor_8, TRUE, FALSE,
7033 EL_EMC_WALL_1, -1, -1
7036 Xdecor_9, TRUE, FALSE,
7037 EL_EMC_WALL_2, -1, -1
7040 Xdecor_10, TRUE, FALSE,
7041 EL_EMC_WALL_3, -1, -1
7044 Xdecor_11, TRUE, FALSE,
7045 EL_EMC_WALL_11, -1, -1
7048 Xdecor_12, TRUE, FALSE,
7049 EL_EMC_WALL_12, -1, -1
7052 Xalpha_0, TRUE, FALSE,
7053 EL_CHAR('0'), -1, -1
7056 Xalpha_1, TRUE, FALSE,
7057 EL_CHAR('1'), -1, -1
7060 Xalpha_2, TRUE, FALSE,
7061 EL_CHAR('2'), -1, -1
7064 Xalpha_3, TRUE, FALSE,
7065 EL_CHAR('3'), -1, -1
7068 Xalpha_4, TRUE, FALSE,
7069 EL_CHAR('4'), -1, -1
7072 Xalpha_5, TRUE, FALSE,
7073 EL_CHAR('5'), -1, -1
7076 Xalpha_6, TRUE, FALSE,
7077 EL_CHAR('6'), -1, -1
7080 Xalpha_7, TRUE, FALSE,
7081 EL_CHAR('7'), -1, -1
7084 Xalpha_8, TRUE, FALSE,
7085 EL_CHAR('8'), -1, -1
7088 Xalpha_9, TRUE, FALSE,
7089 EL_CHAR('9'), -1, -1
7092 Xalpha_excla, TRUE, FALSE,
7093 EL_CHAR('!'), -1, -1
7096 Xalpha_quote, TRUE, FALSE,
7097 EL_CHAR('"'), -1, -1
7100 Xalpha_comma, TRUE, FALSE,
7101 EL_CHAR(','), -1, -1
7104 Xalpha_minus, TRUE, FALSE,
7105 EL_CHAR('-'), -1, -1
7108 Xalpha_perio, TRUE, FALSE,
7109 EL_CHAR('.'), -1, -1
7112 Xalpha_colon, TRUE, FALSE,
7113 EL_CHAR(':'), -1, -1
7116 Xalpha_quest, TRUE, FALSE,
7117 EL_CHAR('?'), -1, -1
7120 Xalpha_a, TRUE, FALSE,
7121 EL_CHAR('A'), -1, -1
7124 Xalpha_b, TRUE, FALSE,
7125 EL_CHAR('B'), -1, -1
7128 Xalpha_c, TRUE, FALSE,
7129 EL_CHAR('C'), -1, -1
7132 Xalpha_d, TRUE, FALSE,
7133 EL_CHAR('D'), -1, -1
7136 Xalpha_e, TRUE, FALSE,
7137 EL_CHAR('E'), -1, -1
7140 Xalpha_f, TRUE, FALSE,
7141 EL_CHAR('F'), -1, -1
7144 Xalpha_g, TRUE, FALSE,
7145 EL_CHAR('G'), -1, -1
7148 Xalpha_h, TRUE, FALSE,
7149 EL_CHAR('H'), -1, -1
7152 Xalpha_i, TRUE, FALSE,
7153 EL_CHAR('I'), -1, -1
7156 Xalpha_j, TRUE, FALSE,
7157 EL_CHAR('J'), -1, -1
7160 Xalpha_k, TRUE, FALSE,
7161 EL_CHAR('K'), -1, -1
7164 Xalpha_l, TRUE, FALSE,
7165 EL_CHAR('L'), -1, -1
7168 Xalpha_m, TRUE, FALSE,
7169 EL_CHAR('M'), -1, -1
7172 Xalpha_n, TRUE, FALSE,
7173 EL_CHAR('N'), -1, -1
7176 Xalpha_o, TRUE, FALSE,
7177 EL_CHAR('O'), -1, -1
7180 Xalpha_p, TRUE, FALSE,
7181 EL_CHAR('P'), -1, -1
7184 Xalpha_q, TRUE, FALSE,
7185 EL_CHAR('Q'), -1, -1
7188 Xalpha_r, TRUE, FALSE,
7189 EL_CHAR('R'), -1, -1
7192 Xalpha_s, TRUE, FALSE,
7193 EL_CHAR('S'), -1, -1
7196 Xalpha_t, TRUE, FALSE,
7197 EL_CHAR('T'), -1, -1
7200 Xalpha_u, TRUE, FALSE,
7201 EL_CHAR('U'), -1, -1
7204 Xalpha_v, TRUE, FALSE,
7205 EL_CHAR('V'), -1, -1
7208 Xalpha_w, TRUE, FALSE,
7209 EL_CHAR('W'), -1, -1
7212 Xalpha_x, TRUE, FALSE,
7213 EL_CHAR('X'), -1, -1
7216 Xalpha_y, TRUE, FALSE,
7217 EL_CHAR('Y'), -1, -1
7220 Xalpha_z, TRUE, FALSE,
7221 EL_CHAR('Z'), -1, -1
7224 Xalpha_arrow_e, TRUE, FALSE,
7225 EL_CHAR('>'), -1, -1
7228 Xalpha_arrow_w, TRUE, FALSE,
7229 EL_CHAR('<'), -1, -1
7232 Xalpha_copyr, TRUE, FALSE,
7233 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7237 Xboom_bug, FALSE, FALSE,
7238 EL_BUG, ACTION_EXPLODING, -1
7241 Xboom_bomb, FALSE, FALSE,
7242 EL_BOMB, ACTION_EXPLODING, -1
7245 Xboom_android, FALSE, FALSE,
7246 EL_EMC_ANDROID, ACTION_OTHER, -1
7249 Xboom_1, FALSE, FALSE,
7250 EL_DEFAULT, ACTION_EXPLODING, -1
7253 Xboom_2, FALSE, FALSE,
7254 EL_DEFAULT, ACTION_EXPLODING, -1
7257 Znormal, FALSE, FALSE,
7261 Zdynamite, FALSE, FALSE,
7265 Zplayer, FALSE, FALSE,
7269 ZBORDER, FALSE, FALSE,
7279 static struct Mapping_EM_to_RND_player
7288 em_player_mapping_list[] =
7292 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7296 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7300 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7304 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7308 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7312 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7316 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7320 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7324 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7328 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7332 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7336 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7340 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7344 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7348 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7352 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7356 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7360 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7364 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7368 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7372 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7376 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7380 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7384 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7388 EL_PLAYER_1, ACTION_DEFAULT, -1,
7392 EL_PLAYER_2, ACTION_DEFAULT, -1,
7396 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7400 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7404 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7408 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7412 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7416 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7420 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7424 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7428 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7432 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7436 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7440 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7444 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7448 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7452 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7456 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7460 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7464 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7468 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7472 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7476 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7480 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7484 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7488 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7492 EL_PLAYER_3, ACTION_DEFAULT, -1,
7496 EL_PLAYER_4, ACTION_DEFAULT, -1,
7505 int map_element_RND_to_EM(int element_rnd)
7507 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7508 static boolean mapping_initialized = FALSE;
7510 if (!mapping_initialized)
7514 /* return "Xalpha_quest" for all undefined elements in mapping array */
7515 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7516 mapping_RND_to_EM[i] = Xalpha_quest;
7518 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7519 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7520 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7521 em_object_mapping_list[i].element_em;
7523 mapping_initialized = TRUE;
7526 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7527 return mapping_RND_to_EM[element_rnd];
7529 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7534 int map_element_EM_to_RND(int element_em)
7536 static unsigned short mapping_EM_to_RND[TILE_MAX];
7537 static boolean mapping_initialized = FALSE;
7539 if (!mapping_initialized)
7543 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7544 for (i = 0; i < TILE_MAX; i++)
7545 mapping_EM_to_RND[i] = EL_UNKNOWN;
7547 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7548 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7549 em_object_mapping_list[i].element_rnd;
7551 mapping_initialized = TRUE;
7554 if (element_em >= 0 && element_em < TILE_MAX)
7555 return mapping_EM_to_RND[element_em];
7557 Error(ERR_WARN, "invalid EM level element %d", element_em);
7562 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7564 struct LevelInfo_EM *level_em = level->native_em_level;
7565 struct LEVEL *lev = level_em->lev;
7568 for (i = 0; i < TILE_MAX; i++)
7569 lev->android_array[i] = Xblank;
7571 for (i = 0; i < level->num_android_clone_elements; i++)
7573 int element_rnd = level->android_clone_element[i];
7574 int element_em = map_element_RND_to_EM(element_rnd);
7576 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7577 if (em_object_mapping_list[j].element_rnd == element_rnd)
7578 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7582 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7584 struct LevelInfo_EM *level_em = level->native_em_level;
7585 struct LEVEL *lev = level_em->lev;
7588 level->num_android_clone_elements = 0;
7590 for (i = 0; i < TILE_MAX; i++)
7592 int element_em = lev->android_array[i];
7594 boolean element_found = FALSE;
7596 if (element_em == Xblank)
7599 element_rnd = map_element_EM_to_RND(element_em);
7601 for (j = 0; j < level->num_android_clone_elements; j++)
7602 if (level->android_clone_element[j] == element_rnd)
7603 element_found = TRUE;
7607 level->android_clone_element[level->num_android_clone_elements++] =
7610 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7615 if (level->num_android_clone_elements == 0)
7617 level->num_android_clone_elements = 1;
7618 level->android_clone_element[0] = EL_EMPTY;
7622 int map_direction_RND_to_EM(int direction)
7624 return (direction == MV_UP ? 0 :
7625 direction == MV_RIGHT ? 1 :
7626 direction == MV_DOWN ? 2 :
7627 direction == MV_LEFT ? 3 :
7631 int map_direction_EM_to_RND(int direction)
7633 return (direction == 0 ? MV_UP :
7634 direction == 1 ? MV_RIGHT :
7635 direction == 2 ? MV_DOWN :
7636 direction == 3 ? MV_LEFT :
7640 int map_element_RND_to_SP(int element_rnd)
7642 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7644 if (element_rnd >= EL_SP_START &&
7645 element_rnd <= EL_SP_END)
7646 element_sp = element_rnd - EL_SP_START;
7647 else if (element_rnd == EL_EMPTY_SPACE)
7649 else if (element_rnd == EL_INVISIBLE_WALL)
7655 int map_element_SP_to_RND(int element_sp)
7657 int element_rnd = EL_UNKNOWN;
7659 if (element_sp >= 0x00 &&
7661 element_rnd = EL_SP_START + element_sp;
7662 else if (element_sp == 0x28)
7663 element_rnd = EL_INVISIBLE_WALL;
7668 int map_action_SP_to_RND(int action_sp)
7672 case actActive: return ACTION_ACTIVE;
7673 case actImpact: return ACTION_IMPACT;
7674 case actExploding: return ACTION_EXPLODING;
7675 case actDigging: return ACTION_DIGGING;
7676 case actSnapping: return ACTION_SNAPPING;
7677 case actCollecting: return ACTION_COLLECTING;
7678 case actPassing: return ACTION_PASSING;
7679 case actPushing: return ACTION_PUSHING;
7680 case actDropping: return ACTION_DROPPING;
7682 default: return ACTION_DEFAULT;
7686 int map_element_RND_to_MM(int element_rnd)
7688 return (element_rnd >= EL_MM_START_1 &&
7689 element_rnd <= EL_MM_END_1 ?
7690 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7692 element_rnd >= EL_MM_START_2 &&
7693 element_rnd <= EL_MM_END_2 ?
7694 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7696 element_rnd >= EL_CHAR_START &&
7697 element_rnd <= EL_CHAR_END ?
7698 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7700 element_rnd >= EL_MM_RUNTIME_START &&
7701 element_rnd <= EL_MM_RUNTIME_END ?
7702 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7704 element_rnd >= EL_MM_DUMMY_START &&
7705 element_rnd <= EL_MM_DUMMY_END ?
7706 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7708 EL_MM_EMPTY_NATIVE);
7711 int map_element_MM_to_RND(int element_mm)
7713 return (element_mm == EL_MM_EMPTY_NATIVE ||
7714 element_mm == EL_DF_EMPTY_NATIVE ?
7717 element_mm >= EL_MM_START_1_NATIVE &&
7718 element_mm <= EL_MM_END_1_NATIVE ?
7719 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7721 element_mm >= EL_MM_START_2_NATIVE &&
7722 element_mm <= EL_MM_END_2_NATIVE ?
7723 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7725 element_mm >= EL_MM_CHAR_START_NATIVE &&
7726 element_mm <= EL_MM_CHAR_END_NATIVE ?
7727 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7729 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7730 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7731 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7733 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7734 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7735 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7740 int map_action_MM_to_RND(int action_mm)
7742 /* all MM actions are defined to exactly match their RND counterparts */
7746 int map_sound_MM_to_RND(int sound_mm)
7750 case SND_MM_GAME_LEVELTIME_CHARGING:
7751 return SND_GAME_LEVELTIME_CHARGING;
7753 case SND_MM_GAME_HEALTH_CHARGING:
7754 return SND_GAME_HEALTH_CHARGING;
7757 return SND_UNDEFINED;
7761 int map_mm_wall_element(int element)
7763 return (element >= EL_MM_STEEL_WALL_START &&
7764 element <= EL_MM_STEEL_WALL_END ?
7767 element >= EL_MM_WOODEN_WALL_START &&
7768 element <= EL_MM_WOODEN_WALL_END ?
7771 element >= EL_MM_ICE_WALL_START &&
7772 element <= EL_MM_ICE_WALL_END ?
7775 element >= EL_MM_AMOEBA_WALL_START &&
7776 element <= EL_MM_AMOEBA_WALL_END ?
7779 element >= EL_DF_STEEL_WALL_START &&
7780 element <= EL_DF_STEEL_WALL_END ?
7783 element >= EL_DF_WOODEN_WALL_START &&
7784 element <= EL_DF_WOODEN_WALL_END ?
7790 int map_mm_wall_element_editor(int element)
7794 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7795 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7796 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7797 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7798 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7799 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7801 default: return element;
7805 int get_next_element(int element)
7809 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7810 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7811 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7812 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7813 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7814 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7815 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7816 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7817 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7818 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7819 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7821 default: return element;
7825 int el2img_mm(int element_mm)
7827 return el2img(map_element_MM_to_RND(element_mm));
7830 int el_act_dir2img(int element, int action, int direction)
7832 element = GFX_ELEMENT(element);
7833 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7835 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7836 return element_info[element].direction_graphic[action][direction];
7839 static int el_act_dir2crm(int element, int action, int direction)
7841 element = GFX_ELEMENT(element);
7842 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7844 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7845 return element_info[element].direction_crumbled[action][direction];
7848 int el_act2img(int element, int action)
7850 element = GFX_ELEMENT(element);
7852 return element_info[element].graphic[action];
7855 int el_act2crm(int element, int action)
7857 element = GFX_ELEMENT(element);
7859 return element_info[element].crumbled[action];
7862 int el_dir2img(int element, int direction)
7864 element = GFX_ELEMENT(element);
7866 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7869 int el2baseimg(int element)
7871 return element_info[element].graphic[ACTION_DEFAULT];
7874 int el2img(int element)
7876 element = GFX_ELEMENT(element);
7878 return element_info[element].graphic[ACTION_DEFAULT];
7881 int el2edimg(int element)
7883 element = GFX_ELEMENT(element);
7885 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7888 int el2preimg(int element)
7890 element = GFX_ELEMENT(element);
7892 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7895 int el2panelimg(int element)
7897 element = GFX_ELEMENT(element);
7899 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7902 int font2baseimg(int font_nr)
7904 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7907 int getBeltNrFromBeltElement(int element)
7909 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7910 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7911 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7914 int getBeltNrFromBeltActiveElement(int element)
7916 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7917 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7918 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7921 int getBeltNrFromBeltSwitchElement(int element)
7923 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7924 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7925 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7928 int getBeltDirNrFromBeltElement(int element)
7930 static int belt_base_element[4] =
7932 EL_CONVEYOR_BELT_1_LEFT,
7933 EL_CONVEYOR_BELT_2_LEFT,
7934 EL_CONVEYOR_BELT_3_LEFT,
7935 EL_CONVEYOR_BELT_4_LEFT
7938 int belt_nr = getBeltNrFromBeltElement(element);
7939 int belt_dir_nr = element - belt_base_element[belt_nr];
7941 return (belt_dir_nr % 3);
7944 int getBeltDirNrFromBeltSwitchElement(int element)
7946 static int belt_base_element[4] =
7948 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7949 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7950 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7951 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7954 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7955 int belt_dir_nr = element - belt_base_element[belt_nr];
7957 return (belt_dir_nr % 3);
7960 int getBeltDirFromBeltElement(int element)
7962 static int belt_move_dir[3] =
7969 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7971 return belt_move_dir[belt_dir_nr];
7974 int getBeltDirFromBeltSwitchElement(int element)
7976 static int belt_move_dir[3] =
7983 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7985 return belt_move_dir[belt_dir_nr];
7988 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7990 static int belt_base_element[4] =
7992 EL_CONVEYOR_BELT_1_LEFT,
7993 EL_CONVEYOR_BELT_2_LEFT,
7994 EL_CONVEYOR_BELT_3_LEFT,
7995 EL_CONVEYOR_BELT_4_LEFT
7998 return belt_base_element[belt_nr] + belt_dir_nr;
8001 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8003 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8005 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8008 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8010 static int belt_base_element[4] =
8012 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8013 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8014 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8015 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8018 return belt_base_element[belt_nr] + belt_dir_nr;
8021 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8023 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8025 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8028 boolean getTeamMode_EM()
8030 return game.team_mode;
8033 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8035 int game_frame_delay_value;
8037 game_frame_delay_value =
8038 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8039 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8042 if (tape.playing && tape.warp_forward && !tape.pausing)
8043 game_frame_delay_value = 0;
8045 return game_frame_delay_value;
8048 unsigned int InitRND(int seed)
8050 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8051 return InitEngineRandom_EM(seed);
8052 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8053 return InitEngineRandom_SP(seed);
8054 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8055 return InitEngineRandom_MM(seed);
8057 return InitEngineRandom_RND(seed);
8060 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8061 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8063 inline static int get_effective_element_EM(int tile, int frame_em)
8065 int element = object_mapping[tile].element_rnd;
8066 int action = object_mapping[tile].action;
8067 boolean is_backside = object_mapping[tile].is_backside;
8068 boolean action_removing = (action == ACTION_DIGGING ||
8069 action == ACTION_SNAPPING ||
8070 action == ACTION_COLLECTING);
8076 case Yacid_splash_eB:
8077 case Yacid_splash_wB:
8078 return (frame_em > 5 ? EL_EMPTY : element);
8084 else /* frame_em == 7 */
8088 case Yacid_splash_eB:
8089 case Yacid_splash_wB:
8092 case Yemerald_stone:
8095 case Ydiamond_stone:
8099 case Xdrip_stretchB:
8118 case Xsand_stonein_1:
8119 case Xsand_stonein_2:
8120 case Xsand_stonein_3:
8121 case Xsand_stonein_4:
8125 return (is_backside || action_removing ? EL_EMPTY : element);
8130 inline static boolean check_linear_animation_EM(int tile)
8134 case Xsand_stonesand_1:
8135 case Xsand_stonesand_quickout_1:
8136 case Xsand_sandstone_1:
8137 case Xsand_stonein_1:
8138 case Xsand_stoneout_1:
8157 case Yacid_splash_eB:
8158 case Yacid_splash_wB:
8159 case Yemerald_stone:
8166 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8167 boolean has_crumbled_graphics,
8168 int crumbled, int sync_frame)
8170 /* if element can be crumbled, but certain action graphics are just empty
8171 space (like instantly snapping sand to empty space in 1 frame), do not
8172 treat these empty space graphics as crumbled graphics in EMC engine */
8173 if (crumbled == IMG_EMPTY_SPACE)
8174 has_crumbled_graphics = FALSE;
8176 if (has_crumbled_graphics)
8178 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8179 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8180 g_crumbled->anim_delay,
8181 g_crumbled->anim_mode,
8182 g_crumbled->anim_start_frame,
8185 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8186 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8188 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8189 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8191 g_em->has_crumbled_graphics = TRUE;
8195 g_em->crumbled_bitmap = NULL;
8196 g_em->crumbled_src_x = 0;
8197 g_em->crumbled_src_y = 0;
8198 g_em->crumbled_border_size = 0;
8199 g_em->crumbled_tile_size = 0;
8201 g_em->has_crumbled_graphics = FALSE;
8205 void ResetGfxAnimation_EM(int x, int y, int tile)
8210 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8211 int tile, int frame_em, int x, int y)
8213 int action = object_mapping[tile].action;
8214 int direction = object_mapping[tile].direction;
8215 int effective_element = get_effective_element_EM(tile, frame_em);
8216 int graphic = (direction == MV_NONE ?
8217 el_act2img(effective_element, action) :
8218 el_act_dir2img(effective_element, action, direction));
8219 struct GraphicInfo *g = &graphic_info[graphic];
8221 boolean action_removing = (action == ACTION_DIGGING ||
8222 action == ACTION_SNAPPING ||
8223 action == ACTION_COLLECTING);
8224 boolean action_moving = (action == ACTION_FALLING ||
8225 action == ACTION_MOVING ||
8226 action == ACTION_PUSHING ||
8227 action == ACTION_EATING ||
8228 action == ACTION_FILLING ||
8229 action == ACTION_EMPTYING);
8230 boolean action_falling = (action == ACTION_FALLING ||
8231 action == ACTION_FILLING ||
8232 action == ACTION_EMPTYING);
8234 /* special case: graphic uses "2nd movement tile" and has defined
8235 7 frames for movement animation (or less) => use default graphic
8236 for last (8th) frame which ends the movement animation */
8237 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8239 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
8240 graphic = (direction == MV_NONE ?
8241 el_act2img(effective_element, action) :
8242 el_act_dir2img(effective_element, action, direction));
8244 g = &graphic_info[graphic];
8247 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8251 else if (action_moving)
8253 boolean is_backside = object_mapping[tile].is_backside;
8257 int direction = object_mapping[tile].direction;
8258 int move_dir = (action_falling ? MV_DOWN : direction);
8263 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
8264 if (g->double_movement && frame_em == 0)
8268 if (move_dir == MV_LEFT)
8269 GfxFrame[x - 1][y] = GfxFrame[x][y];
8270 else if (move_dir == MV_RIGHT)
8271 GfxFrame[x + 1][y] = GfxFrame[x][y];
8272 else if (move_dir == MV_UP)
8273 GfxFrame[x][y - 1] = GfxFrame[x][y];
8274 else if (move_dir == MV_DOWN)
8275 GfxFrame[x][y + 1] = GfxFrame[x][y];
8282 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
8283 if (tile == Xsand_stonesand_quickout_1 ||
8284 tile == Xsand_stonesand_quickout_2)
8288 if (graphic_info[graphic].anim_global_sync)
8289 sync_frame = FrameCounter;
8290 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8291 sync_frame = GfxFrame[x][y];
8293 sync_frame = 0; /* playfield border (pseudo steel) */
8295 SetRandomAnimationValue(x, y);
8297 int frame = getAnimationFrame(g->anim_frames,
8300 g->anim_start_frame,
8303 g_em->unique_identifier =
8304 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8307 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8308 int tile, int frame_em, int x, int y)
8310 int action = object_mapping[tile].action;
8311 int direction = object_mapping[tile].direction;
8312 boolean is_backside = object_mapping[tile].is_backside;
8313 int effective_element = get_effective_element_EM(tile, frame_em);
8314 int effective_action = action;
8315 int graphic = (direction == MV_NONE ?
8316 el_act2img(effective_element, effective_action) :
8317 el_act_dir2img(effective_element, effective_action,
8319 int crumbled = (direction == MV_NONE ?
8320 el_act2crm(effective_element, effective_action) :
8321 el_act_dir2crm(effective_element, effective_action,
8323 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8324 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8325 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8326 struct GraphicInfo *g = &graphic_info[graphic];
8329 /* special case: graphic uses "2nd movement tile" and has defined
8330 7 frames for movement animation (or less) => use default graphic
8331 for last (8th) frame which ends the movement animation */
8332 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8334 effective_action = ACTION_DEFAULT;
8335 graphic = (direction == MV_NONE ?
8336 el_act2img(effective_element, effective_action) :
8337 el_act_dir2img(effective_element, effective_action,
8339 crumbled = (direction == MV_NONE ?
8340 el_act2crm(effective_element, effective_action) :
8341 el_act_dir2crm(effective_element, effective_action,
8344 g = &graphic_info[graphic];
8347 if (graphic_info[graphic].anim_global_sync)
8348 sync_frame = FrameCounter;
8349 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8350 sync_frame = GfxFrame[x][y];
8352 sync_frame = 0; /* playfield border (pseudo steel) */
8354 SetRandomAnimationValue(x, y);
8356 int frame = getAnimationFrame(g->anim_frames,
8359 g->anim_start_frame,
8362 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8363 g->double_movement && is_backside);
8365 /* (updating the "crumbled" graphic definitions is probably not really needed,
8366 as animations for crumbled graphics can't be longer than one EMC cycle) */
8367 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8371 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8372 int player_nr, int anim, int frame_em)
8374 int element = player_mapping[player_nr][anim].element_rnd;
8375 int action = player_mapping[player_nr][anim].action;
8376 int direction = player_mapping[player_nr][anim].direction;
8377 int graphic = (direction == MV_NONE ?
8378 el_act2img(element, action) :
8379 el_act_dir2img(element, action, direction));
8380 struct GraphicInfo *g = &graphic_info[graphic];
8383 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8385 stored_player[player_nr].StepFrame = frame_em;
8387 sync_frame = stored_player[player_nr].Frame;
8389 int frame = getAnimationFrame(g->anim_frames,
8392 g->anim_start_frame,
8395 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8396 &g_em->src_x, &g_em->src_y, FALSE);
8399 void InitGraphicInfo_EM(void)
8404 int num_em_gfx_errors = 0;
8406 if (graphic_info_em_object[0][0].bitmap == NULL)
8408 /* EM graphics not yet initialized in em_open_all() */
8413 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8416 /* always start with reliable default values */
8417 for (i = 0; i < TILE_MAX; i++)
8419 object_mapping[i].element_rnd = EL_UNKNOWN;
8420 object_mapping[i].is_backside = FALSE;
8421 object_mapping[i].action = ACTION_DEFAULT;
8422 object_mapping[i].direction = MV_NONE;
8425 /* always start with reliable default values */
8426 for (p = 0; p < MAX_PLAYERS; p++)
8428 for (i = 0; i < SPR_MAX; i++)
8430 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8431 player_mapping[p][i].action = ACTION_DEFAULT;
8432 player_mapping[p][i].direction = MV_NONE;
8436 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8438 int e = em_object_mapping_list[i].element_em;
8440 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8441 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8443 if (em_object_mapping_list[i].action != -1)
8444 object_mapping[e].action = em_object_mapping_list[i].action;
8446 if (em_object_mapping_list[i].direction != -1)
8447 object_mapping[e].direction =
8448 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8451 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8453 int a = em_player_mapping_list[i].action_em;
8454 int p = em_player_mapping_list[i].player_nr;
8456 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8458 if (em_player_mapping_list[i].action != -1)
8459 player_mapping[p][a].action = em_player_mapping_list[i].action;
8461 if (em_player_mapping_list[i].direction != -1)
8462 player_mapping[p][a].direction =
8463 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8466 for (i = 0; i < TILE_MAX; i++)
8468 int element = object_mapping[i].element_rnd;
8469 int action = object_mapping[i].action;
8470 int direction = object_mapping[i].direction;
8471 boolean is_backside = object_mapping[i].is_backside;
8472 boolean action_exploding = ((action == ACTION_EXPLODING ||
8473 action == ACTION_SMASHED_BY_ROCK ||
8474 action == ACTION_SMASHED_BY_SPRING) &&
8475 element != EL_DIAMOND);
8476 boolean action_active = (action == ACTION_ACTIVE);
8477 boolean action_other = (action == ACTION_OTHER);
8479 for (j = 0; j < 8; j++)
8481 int effective_element = get_effective_element_EM(i, j);
8482 int effective_action = (j < 7 ? action :
8483 i == Xdrip_stretch ? action :
8484 i == Xdrip_stretchB ? action :
8485 i == Ydrip_s1 ? action :
8486 i == Ydrip_s1B ? action :
8487 i == Xball_1B ? action :
8488 i == Xball_2 ? action :
8489 i == Xball_2B ? action :
8490 i == Yball_eat ? action :
8491 i == Ykey_1_eat ? action :
8492 i == Ykey_2_eat ? action :
8493 i == Ykey_3_eat ? action :
8494 i == Ykey_4_eat ? action :
8495 i == Ykey_5_eat ? action :
8496 i == Ykey_6_eat ? action :
8497 i == Ykey_7_eat ? action :
8498 i == Ykey_8_eat ? action :
8499 i == Ylenses_eat ? action :
8500 i == Ymagnify_eat ? action :
8501 i == Ygrass_eat ? action :
8502 i == Ydirt_eat ? action :
8503 i == Xsand_stonein_1 ? action :
8504 i == Xsand_stonein_2 ? action :
8505 i == Xsand_stonein_3 ? action :
8506 i == Xsand_stonein_4 ? action :
8507 i == Xsand_stoneout_1 ? action :
8508 i == Xsand_stoneout_2 ? action :
8509 i == Xboom_android ? ACTION_EXPLODING :
8510 action_exploding ? ACTION_EXPLODING :
8511 action_active ? action :
8512 action_other ? action :
8514 int graphic = (el_act_dir2img(effective_element, effective_action,
8516 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8518 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8519 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8520 boolean has_action_graphics = (graphic != base_graphic);
8521 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8522 struct GraphicInfo *g = &graphic_info[graphic];
8523 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8526 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8527 boolean special_animation = (action != ACTION_DEFAULT &&
8528 g->anim_frames == 3 &&
8529 g->anim_delay == 2 &&
8530 g->anim_mode & ANIM_LINEAR);
8531 int sync_frame = (i == Xdrip_stretch ? 7 :
8532 i == Xdrip_stretchB ? 7 :
8533 i == Ydrip_s2 ? j + 8 :
8534 i == Ydrip_s2B ? j + 8 :
8543 i == Xfake_acid_1 ? 0 :
8544 i == Xfake_acid_2 ? 10 :
8545 i == Xfake_acid_3 ? 20 :
8546 i == Xfake_acid_4 ? 30 :
8547 i == Xfake_acid_5 ? 40 :
8548 i == Xfake_acid_6 ? 50 :
8549 i == Xfake_acid_7 ? 60 :
8550 i == Xfake_acid_8 ? 70 :
8552 i == Xball_2B ? j + 8 :
8553 i == Yball_eat ? j + 1 :
8554 i == Ykey_1_eat ? j + 1 :
8555 i == Ykey_2_eat ? j + 1 :
8556 i == Ykey_3_eat ? j + 1 :
8557 i == Ykey_4_eat ? j + 1 :
8558 i == Ykey_5_eat ? j + 1 :
8559 i == Ykey_6_eat ? j + 1 :
8560 i == Ykey_7_eat ? j + 1 :
8561 i == Ykey_8_eat ? j + 1 :
8562 i == Ylenses_eat ? j + 1 :
8563 i == Ymagnify_eat ? j + 1 :
8564 i == Ygrass_eat ? j + 1 :
8565 i == Ydirt_eat ? j + 1 :
8566 i == Xamoeba_1 ? 0 :
8567 i == Xamoeba_2 ? 1 :
8568 i == Xamoeba_3 ? 2 :
8569 i == Xamoeba_4 ? 3 :
8570 i == Xamoeba_5 ? 0 :
8571 i == Xamoeba_6 ? 1 :
8572 i == Xamoeba_7 ? 2 :
8573 i == Xamoeba_8 ? 3 :
8574 i == Xexit_2 ? j + 8 :
8575 i == Xexit_3 ? j + 16 :
8576 i == Xdynamite_1 ? 0 :
8577 i == Xdynamite_2 ? 8 :
8578 i == Xdynamite_3 ? 16 :
8579 i == Xdynamite_4 ? 24 :
8580 i == Xsand_stonein_1 ? j + 1 :
8581 i == Xsand_stonein_2 ? j + 9 :
8582 i == Xsand_stonein_3 ? j + 17 :
8583 i == Xsand_stonein_4 ? j + 25 :
8584 i == Xsand_stoneout_1 && j == 0 ? 0 :
8585 i == Xsand_stoneout_1 && j == 1 ? 0 :
8586 i == Xsand_stoneout_1 && j == 2 ? 1 :
8587 i == Xsand_stoneout_1 && j == 3 ? 2 :
8588 i == Xsand_stoneout_1 && j == 4 ? 2 :
8589 i == Xsand_stoneout_1 && j == 5 ? 3 :
8590 i == Xsand_stoneout_1 && j == 6 ? 4 :
8591 i == Xsand_stoneout_1 && j == 7 ? 4 :
8592 i == Xsand_stoneout_2 && j == 0 ? 5 :
8593 i == Xsand_stoneout_2 && j == 1 ? 6 :
8594 i == Xsand_stoneout_2 && j == 2 ? 7 :
8595 i == Xsand_stoneout_2 && j == 3 ? 8 :
8596 i == Xsand_stoneout_2 && j == 4 ? 9 :
8597 i == Xsand_stoneout_2 && j == 5 ? 11 :
8598 i == Xsand_stoneout_2 && j == 6 ? 13 :
8599 i == Xsand_stoneout_2 && j == 7 ? 15 :
8600 i == Xboom_bug && j == 1 ? 2 :
8601 i == Xboom_bug && j == 2 ? 2 :
8602 i == Xboom_bug && j == 3 ? 4 :
8603 i == Xboom_bug && j == 4 ? 4 :
8604 i == Xboom_bug && j == 5 ? 2 :
8605 i == Xboom_bug && j == 6 ? 2 :
8606 i == Xboom_bug && j == 7 ? 0 :
8607 i == Xboom_bomb && j == 1 ? 2 :
8608 i == Xboom_bomb && j == 2 ? 2 :
8609 i == Xboom_bomb && j == 3 ? 4 :
8610 i == Xboom_bomb && j == 4 ? 4 :
8611 i == Xboom_bomb && j == 5 ? 2 :
8612 i == Xboom_bomb && j == 6 ? 2 :
8613 i == Xboom_bomb && j == 7 ? 0 :
8614 i == Xboom_android && j == 7 ? 6 :
8615 i == Xboom_1 && j == 1 ? 2 :
8616 i == Xboom_1 && j == 2 ? 2 :
8617 i == Xboom_1 && j == 3 ? 4 :
8618 i == Xboom_1 && j == 4 ? 4 :
8619 i == Xboom_1 && j == 5 ? 6 :
8620 i == Xboom_1 && j == 6 ? 6 :
8621 i == Xboom_1 && j == 7 ? 8 :
8622 i == Xboom_2 && j == 0 ? 8 :
8623 i == Xboom_2 && j == 1 ? 8 :
8624 i == Xboom_2 && j == 2 ? 10 :
8625 i == Xboom_2 && j == 3 ? 10 :
8626 i == Xboom_2 && j == 4 ? 10 :
8627 i == Xboom_2 && j == 5 ? 12 :
8628 i == Xboom_2 && j == 6 ? 12 :
8629 i == Xboom_2 && j == 7 ? 12 :
8630 special_animation && j == 4 ? 3 :
8631 effective_action != action ? 0 :
8635 Bitmap *debug_bitmap = g_em->bitmap;
8636 int debug_src_x = g_em->src_x;
8637 int debug_src_y = g_em->src_y;
8640 int frame = getAnimationFrame(g->anim_frames,
8643 g->anim_start_frame,
8646 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8647 g->double_movement && is_backside);
8649 g_em->bitmap = src_bitmap;
8650 g_em->src_x = src_x;
8651 g_em->src_y = src_y;
8652 g_em->src_offset_x = 0;
8653 g_em->src_offset_y = 0;
8654 g_em->dst_offset_x = 0;
8655 g_em->dst_offset_y = 0;
8656 g_em->width = TILEX;
8657 g_em->height = TILEY;
8659 g_em->preserve_background = FALSE;
8661 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8664 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8665 effective_action == ACTION_MOVING ||
8666 effective_action == ACTION_PUSHING ||
8667 effective_action == ACTION_EATING)) ||
8668 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8669 effective_action == ACTION_EMPTYING)))
8672 (effective_action == ACTION_FALLING ||
8673 effective_action == ACTION_FILLING ||
8674 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8675 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8676 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8677 int num_steps = (i == Ydrip_s1 ? 16 :
8678 i == Ydrip_s1B ? 16 :
8679 i == Ydrip_s2 ? 16 :
8680 i == Ydrip_s2B ? 16 :
8681 i == Xsand_stonein_1 ? 32 :
8682 i == Xsand_stonein_2 ? 32 :
8683 i == Xsand_stonein_3 ? 32 :
8684 i == Xsand_stonein_4 ? 32 :
8685 i == Xsand_stoneout_1 ? 16 :
8686 i == Xsand_stoneout_2 ? 16 : 8);
8687 int cx = ABS(dx) * (TILEX / num_steps);
8688 int cy = ABS(dy) * (TILEY / num_steps);
8689 int step_frame = (i == Ydrip_s2 ? j + 8 :
8690 i == Ydrip_s2B ? j + 8 :
8691 i == Xsand_stonein_2 ? j + 8 :
8692 i == Xsand_stonein_3 ? j + 16 :
8693 i == Xsand_stonein_4 ? j + 24 :
8694 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8695 int step = (is_backside ? step_frame : num_steps - step_frame);
8697 if (is_backside) /* tile where movement starts */
8699 if (dx < 0 || dy < 0)
8701 g_em->src_offset_x = cx * step;
8702 g_em->src_offset_y = cy * step;
8706 g_em->dst_offset_x = cx * step;
8707 g_em->dst_offset_y = cy * step;
8710 else /* tile where movement ends */
8712 if (dx < 0 || dy < 0)
8714 g_em->dst_offset_x = cx * step;
8715 g_em->dst_offset_y = cy * step;
8719 g_em->src_offset_x = cx * step;
8720 g_em->src_offset_y = cy * step;
8724 g_em->width = TILEX - cx * step;
8725 g_em->height = TILEY - cy * step;
8728 /* create unique graphic identifier to decide if tile must be redrawn */
8729 /* bit 31 - 16 (16 bit): EM style graphic
8730 bit 15 - 12 ( 4 bit): EM style frame
8731 bit 11 - 6 ( 6 bit): graphic width
8732 bit 5 - 0 ( 6 bit): graphic height */
8733 g_em->unique_identifier =
8734 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8738 /* skip check for EMC elements not contained in original EMC artwork */
8739 if (element == EL_EMC_FAKE_ACID)
8742 if (g_em->bitmap != debug_bitmap ||
8743 g_em->src_x != debug_src_x ||
8744 g_em->src_y != debug_src_y ||
8745 g_em->src_offset_x != 0 ||
8746 g_em->src_offset_y != 0 ||
8747 g_em->dst_offset_x != 0 ||
8748 g_em->dst_offset_y != 0 ||
8749 g_em->width != TILEX ||
8750 g_em->height != TILEY)
8752 static int last_i = -1;
8760 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8761 i, element, element_info[element].token_name,
8762 element_action_info[effective_action].suffix, direction);
8764 if (element != effective_element)
8765 printf(" [%d ('%s')]",
8767 element_info[effective_element].token_name);
8771 if (g_em->bitmap != debug_bitmap)
8772 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8773 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8775 if (g_em->src_x != debug_src_x ||
8776 g_em->src_y != debug_src_y)
8777 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8778 j, (is_backside ? 'B' : 'F'),
8779 g_em->src_x, g_em->src_y,
8780 g_em->src_x / 32, g_em->src_y / 32,
8781 debug_src_x, debug_src_y,
8782 debug_src_x / 32, debug_src_y / 32);
8784 if (g_em->src_offset_x != 0 ||
8785 g_em->src_offset_y != 0 ||
8786 g_em->dst_offset_x != 0 ||
8787 g_em->dst_offset_y != 0)
8788 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8790 g_em->src_offset_x, g_em->src_offset_y,
8791 g_em->dst_offset_x, g_em->dst_offset_y);
8793 if (g_em->width != TILEX ||
8794 g_em->height != TILEY)
8795 printf(" %d (%d): size %d,%d should be %d,%d\n",
8797 g_em->width, g_em->height, TILEX, TILEY);
8799 num_em_gfx_errors++;
8806 for (i = 0; i < TILE_MAX; i++)
8808 for (j = 0; j < 8; j++)
8810 int element = object_mapping[i].element_rnd;
8811 int action = object_mapping[i].action;
8812 int direction = object_mapping[i].direction;
8813 boolean is_backside = object_mapping[i].is_backside;
8814 int graphic_action = el_act_dir2img(element, action, direction);
8815 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8817 if ((action == ACTION_SMASHED_BY_ROCK ||
8818 action == ACTION_SMASHED_BY_SPRING ||
8819 action == ACTION_EATING) &&
8820 graphic_action == graphic_default)
8822 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8823 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8824 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8825 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8828 /* no separate animation for "smashed by rock" -- use rock instead */
8829 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8830 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8832 g_em->bitmap = g_xx->bitmap;
8833 g_em->src_x = g_xx->src_x;
8834 g_em->src_y = g_xx->src_y;
8835 g_em->src_offset_x = g_xx->src_offset_x;
8836 g_em->src_offset_y = g_xx->src_offset_y;
8837 g_em->dst_offset_x = g_xx->dst_offset_x;
8838 g_em->dst_offset_y = g_xx->dst_offset_y;
8839 g_em->width = g_xx->width;
8840 g_em->height = g_xx->height;
8841 g_em->unique_identifier = g_xx->unique_identifier;
8844 g_em->preserve_background = TRUE;
8849 for (p = 0; p < MAX_PLAYERS; p++)
8851 for (i = 0; i < SPR_MAX; i++)
8853 int element = player_mapping[p][i].element_rnd;
8854 int action = player_mapping[p][i].action;
8855 int direction = player_mapping[p][i].direction;
8857 for (j = 0; j < 8; j++)
8859 int effective_element = element;
8860 int effective_action = action;
8861 int graphic = (direction == MV_NONE ?
8862 el_act2img(effective_element, effective_action) :
8863 el_act_dir2img(effective_element, effective_action,
8865 struct GraphicInfo *g = &graphic_info[graphic];
8866 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8872 Bitmap *debug_bitmap = g_em->bitmap;
8873 int debug_src_x = g_em->src_x;
8874 int debug_src_y = g_em->src_y;
8877 int frame = getAnimationFrame(g->anim_frames,
8880 g->anim_start_frame,
8883 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8885 g_em->bitmap = src_bitmap;
8886 g_em->src_x = src_x;
8887 g_em->src_y = src_y;
8888 g_em->src_offset_x = 0;
8889 g_em->src_offset_y = 0;
8890 g_em->dst_offset_x = 0;
8891 g_em->dst_offset_y = 0;
8892 g_em->width = TILEX;
8893 g_em->height = TILEY;
8897 /* skip check for EMC elements not contained in original EMC artwork */
8898 if (element == EL_PLAYER_3 ||
8899 element == EL_PLAYER_4)
8902 if (g_em->bitmap != debug_bitmap ||
8903 g_em->src_x != debug_src_x ||
8904 g_em->src_y != debug_src_y)
8906 static int last_i = -1;
8914 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8915 p, i, element, element_info[element].token_name,
8916 element_action_info[effective_action].suffix, direction);
8918 if (element != effective_element)
8919 printf(" [%d ('%s')]",
8921 element_info[effective_element].token_name);
8925 if (g_em->bitmap != debug_bitmap)
8926 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8927 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8929 if (g_em->src_x != debug_src_x ||
8930 g_em->src_y != debug_src_y)
8931 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8933 g_em->src_x, g_em->src_y,
8934 g_em->src_x / 32, g_em->src_y / 32,
8935 debug_src_x, debug_src_y,
8936 debug_src_x / 32, debug_src_y / 32);
8938 num_em_gfx_errors++;
8948 printf("::: [%d errors found]\n", num_em_gfx_errors);
8954 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8955 boolean any_player_moving,
8956 boolean any_player_snapping,
8957 boolean any_player_dropping)
8959 if (frame == 0 && !any_player_dropping)
8961 if (!local_player->was_waiting)
8963 if (!CheckSaveEngineSnapshotToList())
8966 local_player->was_waiting = TRUE;
8969 else if (any_player_moving || any_player_snapping || any_player_dropping)
8971 local_player->was_waiting = FALSE;
8975 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8976 boolean murphy_is_dropping)
8978 if (murphy_is_waiting)
8980 if (!local_player->was_waiting)
8982 if (!CheckSaveEngineSnapshotToList())
8985 local_player->was_waiting = TRUE;
8990 local_player->was_waiting = FALSE;
8994 void CheckSaveEngineSnapshot_MM(boolean element_clicked,
8995 boolean button_released)
8997 if (button_released)
8999 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9000 CheckSaveEngineSnapshotToList();
9002 else if (element_clicked)
9004 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9005 CheckSaveEngineSnapshotToList();
9007 game.snapshot.changed_action = TRUE;
9011 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9012 boolean any_player_moving,
9013 boolean any_player_snapping,
9014 boolean any_player_dropping)
9016 if (tape.single_step && tape.recording && !tape.pausing)
9017 if (frame == 0 && !any_player_dropping)
9018 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9020 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9021 any_player_snapping, any_player_dropping);
9024 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9025 boolean murphy_is_dropping)
9027 boolean murphy_starts_dropping = FALSE;
9030 for (i = 0; i < MAX_PLAYERS; i++)
9031 if (stored_player[i].force_dropping)
9032 murphy_starts_dropping = TRUE;
9034 if (tape.single_step && tape.recording && !tape.pausing)
9035 if (murphy_is_waiting && !murphy_starts_dropping)
9036 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9038 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9041 void CheckSingleStepMode_MM(boolean element_clicked,
9042 boolean button_released)
9044 if (tape.single_step && tape.recording && !tape.pausing)
9045 if (button_released)
9046 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9048 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9051 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9052 int graphic, int sync_frame, int x, int y)
9054 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9056 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9059 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9061 return (IS_NEXT_FRAME(sync_frame, graphic));
9064 int getGraphicInfo_Delay(int graphic)
9066 return graphic_info[graphic].anim_delay;
9069 void PlayMenuSoundExt(int sound)
9071 if (sound == SND_UNDEFINED)
9074 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9075 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9078 if (IS_LOOP_SOUND(sound))
9079 PlaySoundLoop(sound);
9084 void PlayMenuSound()
9086 PlayMenuSoundExt(menu.sound[game_status]);
9089 void PlayMenuSoundStereo(int sound, int stereo_position)
9091 if (sound == SND_UNDEFINED)
9094 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9095 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9098 if (IS_LOOP_SOUND(sound))
9099 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9101 PlaySoundStereo(sound, stereo_position);
9104 void PlayMenuSoundIfLoopExt(int sound)
9106 if (sound == SND_UNDEFINED)
9109 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9110 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9113 if (IS_LOOP_SOUND(sound))
9114 PlaySoundLoop(sound);
9117 void PlayMenuSoundIfLoop()
9119 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9122 void PlayMenuMusicExt(int music)
9124 if (music == MUS_UNDEFINED)
9127 if (!setup.sound_music)
9133 void PlayMenuMusic()
9135 char *curr_music = getCurrentlyPlayingMusicFilename();
9136 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9138 if (!strEqual(curr_music, next_music))
9139 PlayMenuMusicExt(menu.music[game_status]);
9142 void PlayMenuSoundsAndMusic()
9148 static void FadeMenuSounds()
9153 static void FadeMenuMusic()
9155 char *curr_music = getCurrentlyPlayingMusicFilename();
9156 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9158 if (!strEqual(curr_music, next_music))
9162 void FadeMenuSoundsAndMusic()
9168 void PlaySoundActivating()
9171 PlaySound(SND_MENU_ITEM_ACTIVATING);
9175 void PlaySoundSelecting()
9178 PlaySound(SND_MENU_ITEM_SELECTING);
9182 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
9184 boolean change_fullscreen = (setup.fullscreen !=
9185 video.fullscreen_enabled);
9186 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9187 setup.window_scaling_percent !=
9188 video.window_scaling_percent);
9190 if (change_window_scaling_percent && video.fullscreen_enabled)
9193 if (!change_window_scaling_percent && !video.fullscreen_available)
9196 #if defined(TARGET_SDL2)
9197 if (change_window_scaling_percent)
9199 SDLSetWindowScaling(setup.window_scaling_percent);
9203 else if (change_fullscreen)
9205 SDLSetWindowFullscreen(setup.fullscreen);
9207 /* set setup value according to successfully changed fullscreen mode */
9208 setup.fullscreen = video.fullscreen_enabled;
9214 if (change_fullscreen ||
9215 change_window_scaling_percent)
9217 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9219 /* save backbuffer content which gets lost when toggling fullscreen mode */
9220 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9222 if (change_window_scaling_percent)
9224 /* keep window mode, but change window scaling */
9225 video.fullscreen_enabled = TRUE; /* force new window scaling */
9228 /* toggle fullscreen */
9229 ChangeVideoModeIfNeeded(setup.fullscreen);
9231 /* set setup value according to successfully changed fullscreen mode */
9232 setup.fullscreen = video.fullscreen_enabled;
9234 /* restore backbuffer content from temporary backbuffer backup bitmap */
9235 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9237 FreeBitmap(tmp_backbuffer);
9239 /* update visible window/screen */
9240 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9244 void JoinRectangles(int *x, int *y, int *width, int *height,
9245 int x2, int y2, int width2, int height2)
9247 // do not join with "off-screen" rectangle
9248 if (x2 == -1 || y2 == -1)
9253 *width = MAX(*width, width2);
9254 *height = MAX(*height, height2);
9257 void SetAnimStatus(int anim_status_new)
9259 if (anim_status_new == GAME_MODE_MAIN)
9260 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9261 else if (anim_status_new == GAME_MODE_SCORES)
9262 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9264 global.anim_status_next = anim_status_new;
9266 // directly set screen modes that are entered without fading
9267 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9268 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9269 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9270 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9271 global.anim_status = global.anim_status_next;
9274 void SetGameStatus(int game_status_new)
9276 if (game_status_new != game_status)
9277 game_status_last_screen = game_status;
9279 game_status = game_status_new;
9281 SetAnimStatus(game_status_new);
9284 void SetFontStatus(int game_status_new)
9286 static int last_game_status = -1;
9288 if (game_status_new != -1)
9290 // set game status for font use after storing last game status
9291 last_game_status = game_status;
9292 game_status = game_status_new;
9296 // reset game status after font use from last stored game status
9297 game_status = last_game_status;
9301 void ResetFontStatus()
9306 boolean CheckIfPlayfieldViewportHasChanged()
9308 // if game status has not changed, playfield viewport has not changed either
9309 if (game_status == game_status_last)
9312 // check if playfield viewport has changed with current game status
9313 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9314 int new_real_sx = vp_playfield->x;
9315 int new_real_sy = vp_playfield->y;
9316 int new_full_sxsize = vp_playfield->width;
9317 int new_full_sysize = vp_playfield->height;
9319 return (new_real_sx != REAL_SX ||
9320 new_real_sy != REAL_SY ||
9321 new_full_sxsize != FULL_SXSIZE ||
9322 new_full_sysize != FULL_SYSIZE);
9325 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
9327 return (CheckIfGlobalBorderHasChanged() ||
9328 CheckIfPlayfieldViewportHasChanged());
9331 void ChangeViewportPropertiesIfNeeded()
9333 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9334 FALSE : setup.small_game_graphics);
9335 int gfx_game_mode = game_status;
9336 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9338 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9339 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9340 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9341 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9342 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9343 int new_win_xsize = vp_window->width;
9344 int new_win_ysize = vp_window->height;
9345 int border_size = vp_playfield->border_size;
9346 int new_sx = vp_playfield->x + border_size;
9347 int new_sy = vp_playfield->y + border_size;
9348 int new_sxsize = vp_playfield->width - 2 * border_size;
9349 int new_sysize = vp_playfield->height - 2 * border_size;
9350 int new_real_sx = vp_playfield->x;
9351 int new_real_sy = vp_playfield->y;
9352 int new_full_sxsize = vp_playfield->width;
9353 int new_full_sysize = vp_playfield->height;
9354 int new_dx = vp_door_1->x;
9355 int new_dy = vp_door_1->y;
9356 int new_dxsize = vp_door_1->width;
9357 int new_dysize = vp_door_1->height;
9358 int new_vx = vp_door_2->x;
9359 int new_vy = vp_door_2->y;
9360 int new_vxsize = vp_door_2->width;
9361 int new_vysize = vp_door_2->height;
9362 int new_ex = vp_door_3->x;
9363 int new_ey = vp_door_3->y;
9364 int new_exsize = vp_door_3->width;
9365 int new_eysize = vp_door_3->height;
9366 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9367 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9368 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9369 int new_scr_fieldx = new_sxsize / tilesize;
9370 int new_scr_fieldy = new_sysize / tilesize;
9371 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9372 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9373 boolean init_gfx_buffers = FALSE;
9374 boolean init_video_buffer = FALSE;
9375 boolean init_gadgets_and_anims = FALSE;
9376 boolean init_em_graphics = FALSE;
9378 if (new_win_xsize != WIN_XSIZE ||
9379 new_win_ysize != WIN_YSIZE)
9381 WIN_XSIZE = new_win_xsize;
9382 WIN_YSIZE = new_win_ysize;
9384 init_video_buffer = TRUE;
9385 init_gfx_buffers = TRUE;
9386 init_gadgets_and_anims = TRUE;
9388 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9391 if (new_scr_fieldx != SCR_FIELDX ||
9392 new_scr_fieldy != SCR_FIELDY)
9394 /* this always toggles between MAIN and GAME when using small tile size */
9396 SCR_FIELDX = new_scr_fieldx;
9397 SCR_FIELDY = new_scr_fieldy;
9399 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9410 new_sxsize != SXSIZE ||
9411 new_sysize != SYSIZE ||
9412 new_dxsize != DXSIZE ||
9413 new_dysize != DYSIZE ||
9414 new_vxsize != VXSIZE ||
9415 new_vysize != VYSIZE ||
9416 new_exsize != EXSIZE ||
9417 new_eysize != EYSIZE ||
9418 new_real_sx != REAL_SX ||
9419 new_real_sy != REAL_SY ||
9420 new_full_sxsize != FULL_SXSIZE ||
9421 new_full_sysize != FULL_SYSIZE ||
9422 new_tilesize_var != TILESIZE_VAR
9425 // ------------------------------------------------------------------------
9426 // determine next fading area for changed viewport definitions
9427 // ------------------------------------------------------------------------
9429 // start with current playfield area (default fading area)
9432 FADE_SXSIZE = FULL_SXSIZE;
9433 FADE_SYSIZE = FULL_SYSIZE;
9435 // add new playfield area if position or size has changed
9436 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9437 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9439 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9440 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9443 // add current and new door 1 area if position or size has changed
9444 if (new_dx != DX || new_dy != DY ||
9445 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9447 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9448 DX, DY, DXSIZE, DYSIZE);
9449 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9450 new_dx, new_dy, new_dxsize, new_dysize);
9453 // add current and new door 2 area if position or size has changed
9454 if (new_dx != VX || new_dy != VY ||
9455 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9457 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9458 VX, VY, VXSIZE, VYSIZE);
9459 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9460 new_vx, new_vy, new_vxsize, new_vysize);
9463 // ------------------------------------------------------------------------
9464 // handle changed tile size
9465 // ------------------------------------------------------------------------
9467 if (new_tilesize_var != TILESIZE_VAR)
9469 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9471 // changing tile size invalidates scroll values of engine snapshots
9472 FreeEngineSnapshotSingle();
9474 // changing tile size requires update of graphic mapping for EM engine
9475 init_em_graphics = TRUE;
9486 SXSIZE = new_sxsize;
9487 SYSIZE = new_sysize;
9488 DXSIZE = new_dxsize;
9489 DYSIZE = new_dysize;
9490 VXSIZE = new_vxsize;
9491 VYSIZE = new_vysize;
9492 EXSIZE = new_exsize;
9493 EYSIZE = new_eysize;
9494 REAL_SX = new_real_sx;
9495 REAL_SY = new_real_sy;
9496 FULL_SXSIZE = new_full_sxsize;
9497 FULL_SYSIZE = new_full_sysize;
9498 TILESIZE_VAR = new_tilesize_var;
9500 init_gfx_buffers = TRUE;
9501 init_gadgets_and_anims = TRUE;
9503 // printf("::: viewports: init_gfx_buffers\n");
9504 // printf("::: viewports: init_gadgets_and_anims\n");
9507 if (init_gfx_buffers)
9509 // printf("::: init_gfx_buffers\n");
9511 SCR_FIELDX = new_scr_fieldx_buffers;
9512 SCR_FIELDY = new_scr_fieldy_buffers;
9516 SCR_FIELDX = new_scr_fieldx;
9517 SCR_FIELDY = new_scr_fieldy;
9519 SetDrawDeactivationMask(REDRAW_NONE);
9520 SetDrawBackgroundMask(REDRAW_FIELD);
9523 if (init_video_buffer)
9525 // printf("::: init_video_buffer\n");
9527 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9528 InitImageTextures();
9531 if (init_gadgets_and_anims)
9533 // printf("::: init_gadgets_and_anims\n");
9536 InitGlobalAnimations();
9539 if (init_em_graphics)
9541 InitGraphicInfo_EM();