1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 static int getFieldbufferOffsetX_RND()
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND()
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 static int getLevelFromScreenX_MM(int sx)
363 int level_xsize = level.native_mm_level->fieldx;
364 int full_xsize = level_xsize * TILESIZE_VAR;
366 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
369 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
374 static int getLevelFromScreenY_MM(int sy)
376 int level_ysize = level.native_mm_level->fieldy;
377 int full_ysize = level_ysize * TILESIZE_VAR;
379 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
382 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
387 int getLevelFromScreenX(int x)
389 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
390 return getLevelFromScreenX_EM(x);
391 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
392 return getLevelFromScreenX_SP(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
394 return getLevelFromScreenX_MM(x);
396 return getLevelFromScreenX_RND(x);
399 int getLevelFromScreenY(int y)
401 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
402 return getLevelFromScreenY_EM(y);
403 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
404 return getLevelFromScreenY_SP(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
406 return getLevelFromScreenY_MM(y);
408 return getLevelFromScreenY_RND(y);
411 void DumpTile(int x, int y)
417 printf_line("-", 79);
418 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
419 printf_line("-", 79);
421 if (!IN_LEV_FIELD(x, y))
423 printf("(not in level field)\n");
429 token_name = element_info[Feld[x][y]].token_name;
431 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
432 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
433 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
434 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
435 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
436 printf(" MovPos: %d\n", MovPos[x][y]);
437 printf(" MovDir: %d\n", MovDir[x][y]);
438 printf(" MovDelay: %d\n", MovDelay[x][y]);
439 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
440 printf(" CustomValue: %d\n", CustomValue[x][y]);
441 printf(" GfxElement: %d\n", GfxElement[x][y]);
442 printf(" GfxAction: %d\n", GfxAction[x][y]);
443 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
444 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
448 void DumpTileFromScreen(int sx, int sy)
450 int lx = getLevelFromScreenX(sx);
451 int ly = getLevelFromScreenY(sy);
456 void SetDrawtoField(int mode)
458 if (mode == DRAW_TO_FIELDBUFFER)
464 BX2 = SCR_FIELDX + 1;
465 BY2 = SCR_FIELDY + 1;
467 drawto_field = fieldbuffer;
469 else /* DRAW_TO_BACKBUFFER */
475 BX2 = SCR_FIELDX - 1;
476 BY2 = SCR_FIELDY - 1;
478 drawto_field = backbuffer;
482 static void RedrawPlayfield_RND()
484 if (game.envelope_active)
487 DrawLevel(REDRAW_ALL);
491 void RedrawPlayfield()
493 if (game_status != GAME_MODE_PLAYING)
496 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
497 RedrawPlayfield_EM(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
499 RedrawPlayfield_SP(TRUE);
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
501 RedrawPlayfield_MM();
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
503 RedrawPlayfield_RND();
505 BlitScreenToBitmap(backbuffer);
507 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
511 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
514 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
515 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
517 if (x == -1 && y == -1)
520 if (draw_target == DRAW_TO_SCREEN)
521 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
523 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
526 static void DrawMaskedBorderExt_FIELD(int draw_target)
528 if (global.border_status >= GAME_MODE_MAIN &&
529 global.border_status <= GAME_MODE_PLAYING &&
530 border.draw_masked[global.border_status])
531 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
535 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
537 // when drawing to backbuffer, never draw border over open doors
538 if (draw_target == DRAW_TO_BACKBUFFER &&
539 (GetDoorState() & DOOR_OPEN_1))
542 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
543 (global.border_status != GAME_MODE_EDITOR ||
544 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
545 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
548 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
550 // when drawing to backbuffer, never draw border over open doors
551 if (draw_target == DRAW_TO_BACKBUFFER &&
552 (GetDoorState() & DOOR_OPEN_2))
555 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
556 global.border_status != GAME_MODE_EDITOR)
557 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
560 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
562 /* currently not available */
565 static void DrawMaskedBorderExt_ALL(int draw_target)
567 DrawMaskedBorderExt_FIELD(draw_target);
568 DrawMaskedBorderExt_DOOR_1(draw_target);
569 DrawMaskedBorderExt_DOOR_2(draw_target);
570 DrawMaskedBorderExt_DOOR_3(draw_target);
573 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
575 /* never draw masked screen borders on borderless screens */
576 if (global.border_status == GAME_MODE_LOADING ||
577 global.border_status == GAME_MODE_TITLE)
580 if (redraw_mask & REDRAW_ALL)
581 DrawMaskedBorderExt_ALL(draw_target);
584 if (redraw_mask & REDRAW_FIELD)
585 DrawMaskedBorderExt_FIELD(draw_target);
586 if (redraw_mask & REDRAW_DOOR_1)
587 DrawMaskedBorderExt_DOOR_1(draw_target);
588 if (redraw_mask & REDRAW_DOOR_2)
589 DrawMaskedBorderExt_DOOR_2(draw_target);
590 if (redraw_mask & REDRAW_DOOR_3)
591 DrawMaskedBorderExt_DOOR_3(draw_target);
595 void DrawMaskedBorder_FIELD()
597 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
600 void DrawMaskedBorder(int redraw_mask)
602 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
605 void DrawMaskedBorderToTarget(int draw_target)
607 if (draw_target == DRAW_TO_BACKBUFFER ||
608 draw_target == DRAW_TO_SCREEN)
610 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
614 int last_border_status = global.border_status;
616 if (draw_target == DRAW_TO_FADE_SOURCE)
618 global.border_status = gfx.fade_border_source_status;
619 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
621 else if (draw_target == DRAW_TO_FADE_TARGET)
623 global.border_status = gfx.fade_border_target_status;
624 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
627 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
629 global.border_status = last_border_status;
630 gfx.masked_border_bitmap_ptr = backbuffer;
634 void DrawTileCursor(int draw_target)
640 int graphic = IMG_GLOBAL_TILE_CURSOR;
642 int tilesize = TILESIZE_VAR;
643 int width = tilesize;
644 int height = tilesize;
646 if (game_status != GAME_MODE_PLAYING)
649 if (!tile_cursor.enabled ||
653 if (tile_cursor.moving)
655 int step = TILESIZE_VAR / 4;
656 int dx = tile_cursor.target_x - tile_cursor.x;
657 int dy = tile_cursor.target_y - tile_cursor.y;
660 tile_cursor.x = tile_cursor.target_x;
662 tile_cursor.x += SIGN(dx) * step;
665 tile_cursor.y = tile_cursor.target_y;
667 tile_cursor.y += SIGN(dy) * step;
669 if (tile_cursor.x == tile_cursor.target_x &&
670 tile_cursor.y == tile_cursor.target_y)
671 tile_cursor.moving = FALSE;
674 dst_x = tile_cursor.x;
675 dst_y = tile_cursor.y;
677 frame = getGraphicAnimationFrame(graphic, -1);
679 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
682 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
683 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
685 if (draw_target == DRAW_TO_SCREEN)
686 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
688 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
692 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
694 int fx = getFieldbufferOffsetX_RND();
695 int fy = getFieldbufferOffsetY_RND();
697 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
700 void BlitScreenToBitmap(Bitmap *target_bitmap)
702 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
703 BlitScreenToBitmap_EM(target_bitmap);
704 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
705 BlitScreenToBitmap_SP(target_bitmap);
706 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
707 BlitScreenToBitmap_MM(target_bitmap);
708 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
709 BlitScreenToBitmap_RND(target_bitmap);
711 redraw_mask |= REDRAW_FIELD;
714 void DrawFramesPerSecond()
717 int font_nr = FONT_TEXT_2;
718 int font_width = getFontWidth(font_nr);
719 int draw_deactivation_mask = GetDrawDeactivationMask();
720 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
722 /* draw FPS with leading space (needed if field buffer deactivated) */
723 sprintf(text, " %04.1f fps", global.frames_per_second);
725 /* override draw deactivation mask (required for invisible warp mode) */
726 SetDrawDeactivationMask(REDRAW_NONE);
728 /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
729 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
730 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
732 /* set draw deactivation mask to previous value */
733 SetDrawDeactivationMask(draw_deactivation_mask);
735 /* force full-screen redraw in this frame */
736 redraw_mask = REDRAW_ALL;
740 static void PrintFrameTimeDebugging()
742 static unsigned int last_counter = 0;
743 unsigned int counter = Counter();
744 int diff_1 = counter - last_counter;
745 int diff_2 = diff_1 - GAME_FRAME_DELAY;
747 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
748 char diff_bar[2 * diff_2_max + 5];
752 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
754 for (i = 0; i < diff_2_max; i++)
755 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
756 i >= diff_2_max - diff_2_cut ? '-' : ' ');
758 diff_bar[pos++] = '|';
760 for (i = 0; i < diff_2_max; i++)
761 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
763 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
765 diff_bar[pos++] = '\0';
767 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
770 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
773 last_counter = counter;
777 static int unifiedRedrawMask(int mask)
779 if (mask & REDRAW_ALL)
782 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
788 static boolean equalRedrawMasks(int mask_1, int mask_2)
790 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
795 static int last_redraw_mask = REDRAW_NONE;
797 // force screen redraw in every frame to continue drawing global animations
798 // (but always use the last redraw mask to prevent unwanted side effects)
799 if (redraw_mask == REDRAW_NONE)
800 redraw_mask = last_redraw_mask;
802 last_redraw_mask = redraw_mask;
805 // masked border now drawn immediately when blitting backbuffer to window
807 // draw masked border to all viewports, if defined
808 DrawMaskedBorder(redraw_mask);
811 // draw frames per second (only if debug mode is enabled)
812 if (redraw_mask & REDRAW_FPS)
813 DrawFramesPerSecond();
815 // remove playfield redraw before potentially merging with doors redraw
816 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
817 redraw_mask &= ~REDRAW_FIELD;
819 // redraw complete window if both playfield and (some) doors need redraw
820 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
821 redraw_mask = REDRAW_ALL;
823 /* although redrawing the whole window would be fine for normal gameplay,
824 being able to only redraw the playfield is required for deactivating
825 certain drawing areas (mainly playfield) to work, which is needed for
826 warp-forward to be fast enough (by skipping redraw of most frames) */
828 if (redraw_mask & REDRAW_ALL)
830 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
832 else if (redraw_mask & REDRAW_FIELD)
834 BlitBitmap(backbuffer, window,
835 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
837 else if (redraw_mask & REDRAW_DOORS)
839 // merge door areas to prevent calling screen redraw more than once
845 if (redraw_mask & REDRAW_DOOR_1)
849 x2 = MAX(x2, DX + DXSIZE);
850 y2 = MAX(y2, DY + DYSIZE);
853 if (redraw_mask & REDRAW_DOOR_2)
857 x2 = MAX(x2, VX + VXSIZE);
858 y2 = MAX(y2, VY + VYSIZE);
861 if (redraw_mask & REDRAW_DOOR_3)
865 x2 = MAX(x2, EX + EXSIZE);
866 y2 = MAX(y2, EY + EYSIZE);
869 // make sure that at least one pixel is blitted, and inside the screen
870 // (else nothing is blitted, causing the animations not to be updated)
871 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
872 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
873 x2 = MIN(MAX(1, x2), WIN_XSIZE);
874 y2 = MIN(MAX(1, y2), WIN_YSIZE);
876 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
879 redraw_mask = REDRAW_NONE;
882 PrintFrameTimeDebugging();
886 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
888 unsigned int frame_delay_value_old = GetVideoFrameDelay();
890 SetVideoFrameDelay(frame_delay_value);
894 SetVideoFrameDelay(frame_delay_value_old);
897 static int fade_type_skip = FADE_TYPE_NONE;
899 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
901 void (*draw_border_function)(void) = NULL;
902 int x, y, width, height;
903 int fade_delay, post_delay;
905 if (fade_type == FADE_TYPE_FADE_OUT)
907 if (fade_type_skip != FADE_TYPE_NONE)
909 /* skip all fade operations until specified fade operation */
910 if (fade_type & fade_type_skip)
911 fade_type_skip = FADE_TYPE_NONE;
916 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
920 redraw_mask |= fade_mask;
922 if (fade_type == FADE_TYPE_SKIP)
924 fade_type_skip = fade_mode;
929 fade_delay = fading.fade_delay;
930 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
932 if (fade_type_skip != FADE_TYPE_NONE)
934 /* skip all fade operations until specified fade operation */
935 if (fade_type & fade_type_skip)
936 fade_type_skip = FADE_TYPE_NONE;
941 if (global.autoplay_leveldir)
946 if (fade_mask == REDRAW_FIELD)
951 height = FADE_SYSIZE;
953 if (border.draw_masked_when_fading)
954 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
956 DrawMaskedBorder_FIELD(); /* draw once */
958 else /* REDRAW_ALL */
966 if (!setup.fade_screens ||
968 fading.fade_mode == FADE_MODE_NONE)
970 if (fade_mode == FADE_MODE_FADE_OUT)
973 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
975 redraw_mask &= ~fade_mask;
980 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
981 draw_border_function);
983 redraw_mask &= ~fade_mask;
988 static void SetScreenStates_BeforeFadingIn()
990 // temporarily set screen mode for animations to screen after fading in
991 global.anim_status = global.anim_status_next;
993 // store backbuffer with all animations that will be started after fading in
994 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
995 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
997 // set screen mode for animations back to fading
998 global.anim_status = GAME_MODE_PSEUDO_FADING;
1001 static void SetScreenStates_AfterFadingIn()
1003 // store new source screen (to use correct masked border for fading)
1004 gfx.fade_border_source_status = global.border_status;
1006 global.anim_status = global.anim_status_next;
1009 static void SetScreenStates_BeforeFadingOut()
1011 // store new target screen (to use correct masked border for fading)
1012 gfx.fade_border_target_status = game_status;
1014 // set screen mode for animations to fading
1015 global.anim_status = GAME_MODE_PSEUDO_FADING;
1017 // store backbuffer with all animations that will be stopped for fading out
1018 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1019 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1022 static void SetScreenStates_AfterFadingOut()
1024 global.border_status = game_status;
1027 void FadeIn(int fade_mask)
1029 SetScreenStates_BeforeFadingIn();
1032 DrawMaskedBorder(REDRAW_ALL);
1035 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1036 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1038 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1042 FADE_SXSIZE = FULL_SXSIZE;
1043 FADE_SYSIZE = FULL_SYSIZE;
1045 if (game_status == GAME_MODE_PLAYING &&
1046 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1047 SetOverlayActive(TRUE);
1049 SetScreenStates_AfterFadingIn();
1051 // force update of global animation status in case of rapid screen changes
1052 redraw_mask = REDRAW_ALL;
1056 void FadeOut(int fade_mask)
1058 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1059 if (!equalRedrawMasks(fade_mask, redraw_mask))
1062 SetScreenStates_BeforeFadingOut();
1064 SetTileCursorActive(FALSE);
1065 SetOverlayActive(FALSE);
1068 DrawMaskedBorder(REDRAW_ALL);
1071 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1072 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1074 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1076 SetScreenStates_AfterFadingOut();
1079 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1081 static struct TitleFadingInfo fading_leave_stored;
1084 fading_leave_stored = fading_leave;
1086 fading = fading_leave_stored;
1089 void FadeSetEnterMenu()
1091 fading = menu.enter_menu;
1093 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1096 void FadeSetLeaveMenu()
1098 fading = menu.leave_menu;
1100 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1103 void FadeSetEnterScreen()
1105 fading = menu.enter_screen[game_status];
1107 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1110 void FadeSetNextScreen()
1112 fading = menu.next_screen[game_status];
1114 // (do not overwrite fade mode set by FadeSetEnterScreen)
1115 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1118 void FadeSetLeaveScreen()
1120 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1123 void FadeSetFromType(int type)
1125 if (type & TYPE_ENTER_SCREEN)
1126 FadeSetEnterScreen();
1127 else if (type & TYPE_ENTER)
1129 else if (type & TYPE_LEAVE)
1133 void FadeSetDisabled()
1135 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1137 fading = fading_none;
1140 void FadeSkipNextFadeIn()
1142 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1145 void FadeSkipNextFadeOut()
1147 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1150 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1152 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1154 return (graphic == IMG_UNDEFINED ? NULL :
1155 graphic_info[graphic].bitmap != NULL || redefined ?
1156 graphic_info[graphic].bitmap :
1157 graphic_info[default_graphic].bitmap);
1160 Bitmap *getBackgroundBitmap(int graphic)
1162 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1165 Bitmap *getGlobalBorderBitmap(int graphic)
1167 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1170 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1173 (status == GAME_MODE_MAIN ||
1174 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1175 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1176 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1177 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1180 return getGlobalBorderBitmap(graphic);
1183 void SetWindowBackgroundImageIfDefined(int graphic)
1185 if (graphic_info[graphic].bitmap)
1186 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1189 void SetMainBackgroundImageIfDefined(int graphic)
1191 if (graphic_info[graphic].bitmap)
1192 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1195 void SetDoorBackgroundImageIfDefined(int graphic)
1197 if (graphic_info[graphic].bitmap)
1198 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1201 void SetWindowBackgroundImage(int graphic)
1203 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1206 void SetMainBackgroundImage(int graphic)
1208 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1211 void SetDoorBackgroundImage(int graphic)
1213 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1216 void SetPanelBackground()
1218 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1220 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1221 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1223 SetDoorBackgroundBitmap(bitmap_db_panel);
1226 void DrawBackground(int x, int y, int width, int height)
1228 /* "drawto" might still point to playfield buffer here (hall of fame) */
1229 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1231 if (IN_GFX_FIELD_FULL(x, y))
1232 redraw_mask |= REDRAW_FIELD;
1233 else if (IN_GFX_DOOR_1(x, y))
1234 redraw_mask |= REDRAW_DOOR_1;
1235 else if (IN_GFX_DOOR_2(x, y))
1236 redraw_mask |= REDRAW_DOOR_2;
1237 else if (IN_GFX_DOOR_3(x, y))
1238 redraw_mask |= REDRAW_DOOR_3;
1241 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1243 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1245 if (font->bitmap == NULL)
1248 DrawBackground(x, y, width, height);
1251 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1253 struct GraphicInfo *g = &graphic_info[graphic];
1255 if (g->bitmap == NULL)
1258 DrawBackground(x, y, width, height);
1261 static int game_status_last = -1;
1262 static Bitmap *global_border_bitmap_last = NULL;
1263 static Bitmap *global_border_bitmap = NULL;
1264 static int real_sx_last = -1, real_sy_last = -1;
1265 static int full_sxsize_last = -1, full_sysize_last = -1;
1266 static int dx_last = -1, dy_last = -1;
1267 static int dxsize_last = -1, dysize_last = -1;
1268 static int vx_last = -1, vy_last = -1;
1269 static int vxsize_last = -1, vysize_last = -1;
1270 static int ex_last = -1, ey_last = -1;
1271 static int exsize_last = -1, eysize_last = -1;
1273 boolean CheckIfGlobalBorderHasChanged()
1275 // if game status has not changed, global border has not changed either
1276 if (game_status == game_status_last)
1279 // determine and store new global border bitmap for current game status
1280 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1282 return (global_border_bitmap_last != global_border_bitmap);
1285 boolean CheckIfGlobalBorderRedrawIsNeeded()
1287 // if game status has not changed, nothing has to be redrawn
1288 if (game_status == game_status_last)
1291 // redraw if last screen was title screen
1292 if (game_status_last == GAME_MODE_TITLE)
1295 // redraw if global screen border has changed
1296 if (CheckIfGlobalBorderHasChanged())
1299 // redraw if position or size of playfield area has changed
1300 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1301 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1304 // redraw if position or size of door area has changed
1305 if (dx_last != DX || dy_last != DY ||
1306 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1309 // redraw if position or size of tape area has changed
1310 if (vx_last != VX || vy_last != VY ||
1311 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1314 // redraw if position or size of editor area has changed
1315 if (ex_last != EX || ey_last != EY ||
1316 exsize_last != EXSIZE || eysize_last != EYSIZE)
1322 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1325 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1327 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1330 void RedrawGlobalBorder()
1332 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1334 RedrawGlobalBorderFromBitmap(bitmap);
1336 redraw_mask = REDRAW_ALL;
1339 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1341 static void RedrawGlobalBorderIfNeeded()
1343 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1344 if (game_status == game_status_last)
1348 // copy current draw buffer to later copy back areas that have not changed
1349 if (game_status_last != GAME_MODE_TITLE)
1350 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1352 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1353 if (CheckIfGlobalBorderRedrawIsNeeded())
1356 // redraw global screen border (or clear, if defined to be empty)
1357 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1359 if (game_status == GAME_MODE_EDITOR)
1360 DrawSpecialEditorDoor();
1362 // copy previous playfield and door areas, if they are defined on both
1363 // previous and current screen and if they still have the same size
1365 if (real_sx_last != -1 && real_sy_last != -1 &&
1366 REAL_SX != -1 && REAL_SY != -1 &&
1367 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1368 BlitBitmap(bitmap_db_store_1, backbuffer,
1369 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1372 if (dx_last != -1 && dy_last != -1 &&
1373 DX != -1 && DY != -1 &&
1374 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1375 BlitBitmap(bitmap_db_store_1, backbuffer,
1376 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1378 if (game_status != GAME_MODE_EDITOR)
1380 if (vx_last != -1 && vy_last != -1 &&
1381 VX != -1 && VY != -1 &&
1382 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1383 BlitBitmap(bitmap_db_store_1, backbuffer,
1384 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1388 if (ex_last != -1 && ey_last != -1 &&
1389 EX != -1 && EY != -1 &&
1390 exsize_last == EXSIZE && eysize_last == EYSIZE)
1391 BlitBitmap(bitmap_db_store_1, backbuffer,
1392 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1395 redraw_mask = REDRAW_ALL;
1398 game_status_last = game_status;
1400 global_border_bitmap_last = global_border_bitmap;
1402 real_sx_last = REAL_SX;
1403 real_sy_last = REAL_SY;
1404 full_sxsize_last = FULL_SXSIZE;
1405 full_sysize_last = FULL_SYSIZE;
1408 dxsize_last = DXSIZE;
1409 dysize_last = DYSIZE;
1412 vxsize_last = VXSIZE;
1413 vysize_last = VYSIZE;
1416 exsize_last = EXSIZE;
1417 eysize_last = EYSIZE;
1422 RedrawGlobalBorderIfNeeded();
1424 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1425 /* (when entering hall of fame after playing) */
1426 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1428 /* !!! maybe this should be done before clearing the background !!! */
1429 if (game_status == GAME_MODE_PLAYING)
1431 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1432 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1436 SetDrawtoField(DRAW_TO_BACKBUFFER);
1440 void MarkTileDirty(int x, int y)
1442 redraw_mask |= REDRAW_FIELD;
1445 void SetBorderElement()
1449 BorderElement = EL_EMPTY;
1451 /* the MM game engine does not use a visible border element */
1452 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1455 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1457 for (x = 0; x < lev_fieldx; x++)
1459 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1460 BorderElement = EL_STEELWALL;
1462 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1468 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1469 int max_array_fieldx, int max_array_fieldy,
1470 short field[max_array_fieldx][max_array_fieldy],
1471 int max_fieldx, int max_fieldy)
1475 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1476 static int safety = 0;
1478 /* check if starting field still has the desired content */
1479 if (field[from_x][from_y] == fill_element)
1484 if (safety > max_fieldx * max_fieldy)
1485 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1487 old_element = field[from_x][from_y];
1488 field[from_x][from_y] = fill_element;
1490 for (i = 0; i < 4; i++)
1492 x = from_x + check[i][0];
1493 y = from_y + check[i][1];
1495 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1496 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1497 field, max_fieldx, max_fieldy);
1503 void FloodFillLevel(int from_x, int from_y, int fill_element,
1504 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1505 int max_fieldx, int max_fieldy)
1507 FloodFillLevelExt(from_x, from_y, fill_element,
1508 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1509 max_fieldx, max_fieldy);
1512 void SetRandomAnimationValue(int x, int y)
1514 gfx.anim_random_frame = GfxRandom[x][y];
1517 int getGraphicAnimationFrame(int graphic, int sync_frame)
1519 /* animation synchronized with global frame counter, not move position */
1520 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1521 sync_frame = FrameCounter;
1523 return getAnimationFrame(graphic_info[graphic].anim_frames,
1524 graphic_info[graphic].anim_delay,
1525 graphic_info[graphic].anim_mode,
1526 graphic_info[graphic].anim_start_frame,
1530 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1532 struct GraphicInfo *g = &graphic_info[graphic];
1533 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1535 if (tilesize == gfx.standard_tile_size)
1536 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1537 else if (tilesize == game.tile_size)
1538 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1540 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1543 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1544 boolean get_backside)
1546 struct GraphicInfo *g = &graphic_info[graphic];
1547 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1548 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1550 if (g->offset_y == 0) /* frames are ordered horizontally */
1552 int max_width = g->anim_frames_per_line * g->width;
1553 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1555 *x = pos % max_width;
1556 *y = src_y % g->height + pos / max_width * g->height;
1558 else if (g->offset_x == 0) /* frames are ordered vertically */
1560 int max_height = g->anim_frames_per_line * g->height;
1561 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1563 *x = src_x % g->width + pos / max_height * g->width;
1564 *y = pos % max_height;
1566 else /* frames are ordered diagonally */
1568 *x = src_x + frame * g->offset_x;
1569 *y = src_y + frame * g->offset_y;
1573 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1574 Bitmap **bitmap, int *x, int *y,
1575 boolean get_backside)
1577 struct GraphicInfo *g = &graphic_info[graphic];
1579 // if no graphics defined at all, use fallback graphics
1580 if (g->bitmaps == NULL)
1581 *g = graphic_info[IMG_CHAR_EXCLAM];
1583 // if no in-game graphics defined, always use standard graphic size
1584 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1585 tilesize = TILESIZE;
1587 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1588 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1590 *x = *x * tilesize / g->tile_size;
1591 *y = *y * tilesize / g->tile_size;
1594 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1595 Bitmap **bitmap, int *x, int *y)
1597 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1600 void getFixedGraphicSource(int graphic, int frame,
1601 Bitmap **bitmap, int *x, int *y)
1603 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1606 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1608 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1611 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1612 int *x, int *y, boolean get_backside)
1614 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1618 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1620 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1623 void DrawGraphic(int x, int y, int graphic, int frame)
1626 if (!IN_SCR_FIELD(x, y))
1628 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1629 printf("DrawGraphic(): This should never happen!\n");
1634 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1637 MarkTileDirty(x, y);
1640 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1643 if (!IN_SCR_FIELD(x, y))
1645 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1646 printf("DrawGraphic(): This should never happen!\n");
1651 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1653 MarkTileDirty(x, y);
1656 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1662 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1664 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1667 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1673 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1674 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1677 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1680 if (!IN_SCR_FIELD(x, y))
1682 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1683 printf("DrawGraphicThruMask(): This should never happen!\n");
1688 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1691 MarkTileDirty(x, y);
1694 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1697 if (!IN_SCR_FIELD(x, y))
1699 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1700 printf("DrawGraphicThruMask(): This should never happen!\n");
1705 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1707 MarkTileDirty(x, y);
1710 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1716 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1718 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1722 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1723 int graphic, int frame)
1728 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1730 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1734 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1736 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1738 MarkTileDirty(x / tilesize, y / tilesize);
1741 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1744 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1745 graphic, frame, tilesize);
1746 MarkTileDirty(x / tilesize, y / tilesize);
1749 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1755 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1756 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1759 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1760 int frame, int tilesize)
1765 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1766 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1769 void DrawMiniGraphic(int x, int y, int graphic)
1771 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1772 MarkTileDirty(x / 2, y / 2);
1775 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1780 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1781 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1784 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1785 int graphic, int frame,
1786 int cut_mode, int mask_mode)
1791 int width = TILEX, height = TILEY;
1794 if (dx || dy) /* shifted graphic */
1796 if (x < BX1) /* object enters playfield from the left */
1803 else if (x > BX2) /* object enters playfield from the right */
1809 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1815 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1817 else if (dx) /* general horizontal movement */
1818 MarkTileDirty(x + SIGN(dx), y);
1820 if (y < BY1) /* object enters playfield from the top */
1822 if (cut_mode == CUT_BELOW) /* object completely above top border */
1830 else if (y > BY2) /* object enters playfield from the bottom */
1836 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1842 else if (dy > 0 && cut_mode == CUT_ABOVE)
1844 if (y == BY2) /* object completely above bottom border */
1850 MarkTileDirty(x, y + 1);
1851 } /* object leaves playfield to the bottom */
1852 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1854 else if (dy) /* general vertical movement */
1855 MarkTileDirty(x, y + SIGN(dy));
1859 if (!IN_SCR_FIELD(x, y))
1861 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1862 printf("DrawGraphicShifted(): This should never happen!\n");
1867 width = width * TILESIZE_VAR / TILESIZE;
1868 height = height * TILESIZE_VAR / TILESIZE;
1869 cx = cx * TILESIZE_VAR / TILESIZE;
1870 cy = cy * TILESIZE_VAR / TILESIZE;
1871 dx = dx * TILESIZE_VAR / TILESIZE;
1872 dy = dy * TILESIZE_VAR / TILESIZE;
1874 if (width > 0 && height > 0)
1876 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1881 dst_x = FX + x * TILEX_VAR + dx;
1882 dst_y = FY + y * TILEY_VAR + dy;
1884 if (mask_mode == USE_MASKING)
1885 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1888 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1891 MarkTileDirty(x, y);
1895 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1896 int graphic, int frame,
1897 int cut_mode, int mask_mode)
1902 int width = TILEX_VAR, height = TILEY_VAR;
1905 int x2 = x + SIGN(dx);
1906 int y2 = y + SIGN(dy);
1908 /* movement with two-tile animations must be sync'ed with movement position,
1909 not with current GfxFrame (which can be higher when using slow movement) */
1910 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1911 int anim_frames = graphic_info[graphic].anim_frames;
1913 /* (we also need anim_delay here for movement animations with less frames) */
1914 int anim_delay = graphic_info[graphic].anim_delay;
1915 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1917 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1918 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1920 /* re-calculate animation frame for two-tile movement animation */
1921 frame = getGraphicAnimationFrame(graphic, sync_frame);
1923 /* check if movement start graphic inside screen area and should be drawn */
1924 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1926 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1928 dst_x = FX + x1 * TILEX_VAR;
1929 dst_y = FY + y1 * TILEY_VAR;
1931 if (mask_mode == USE_MASKING)
1932 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1935 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1938 MarkTileDirty(x1, y1);
1941 /* check if movement end graphic inside screen area and should be drawn */
1942 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1944 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1946 dst_x = FX + x2 * TILEX_VAR;
1947 dst_y = FY + y2 * TILEY_VAR;
1949 if (mask_mode == USE_MASKING)
1950 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1953 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1956 MarkTileDirty(x2, y2);
1960 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1961 int graphic, int frame,
1962 int cut_mode, int mask_mode)
1966 DrawGraphic(x, y, graphic, frame);
1971 if (graphic_info[graphic].double_movement) /* EM style movement images */
1972 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1974 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1977 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1978 int frame, int cut_mode)
1980 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1983 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1984 int cut_mode, int mask_mode)
1986 int lx = LEVELX(x), ly = LEVELY(y);
1990 if (IN_LEV_FIELD(lx, ly))
1992 SetRandomAnimationValue(lx, ly);
1994 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1995 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1997 /* do not use double (EM style) movement graphic when not moving */
1998 if (graphic_info[graphic].double_movement && !dx && !dy)
2000 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2001 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2004 else /* border element */
2006 graphic = el2img(element);
2007 frame = getGraphicAnimationFrame(graphic, -1);
2010 if (element == EL_EXPANDABLE_WALL)
2012 boolean left_stopped = FALSE, right_stopped = FALSE;
2014 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2015 left_stopped = TRUE;
2016 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2017 right_stopped = TRUE;
2019 if (left_stopped && right_stopped)
2021 else if (left_stopped)
2023 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2024 frame = graphic_info[graphic].anim_frames - 1;
2026 else if (right_stopped)
2028 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2029 frame = graphic_info[graphic].anim_frames - 1;
2034 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2035 else if (mask_mode == USE_MASKING)
2036 DrawGraphicThruMask(x, y, graphic, frame);
2038 DrawGraphic(x, y, graphic, frame);
2041 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2042 int cut_mode, int mask_mode)
2044 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2045 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2046 cut_mode, mask_mode);
2049 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2052 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2055 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2058 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2061 void DrawLevelElementThruMask(int x, int y, int element)
2063 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2066 void DrawLevelFieldThruMask(int x, int y)
2068 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2071 /* !!! implementation of quicksand is totally broken !!! */
2072 #define IS_CRUMBLED_TILE(x, y, e) \
2073 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2074 !IS_MOVING(x, y) || \
2075 (e) == EL_QUICKSAND_EMPTYING || \
2076 (e) == EL_QUICKSAND_FAST_EMPTYING))
2078 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2083 int width, height, cx, cy;
2084 int sx = SCREENX(x), sy = SCREENY(y);
2085 int crumbled_border_size = graphic_info[graphic].border_size;
2086 int crumbled_tile_size = graphic_info[graphic].tile_size;
2087 int crumbled_border_size_var =
2088 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2091 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2093 for (i = 1; i < 4; i++)
2095 int dxx = (i & 1 ? dx : 0);
2096 int dyy = (i & 2 ? dy : 0);
2099 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2102 /* check if neighbour field is of same crumble type */
2103 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2104 graphic_info[graphic].class ==
2105 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2107 /* return if check prevents inner corner */
2108 if (same == (dxx == dx && dyy == dy))
2112 /* if we reach this point, we have an inner corner */
2114 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2116 width = crumbled_border_size_var;
2117 height = crumbled_border_size_var;
2118 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2119 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2121 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2122 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2125 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2130 int width, height, bx, by, cx, cy;
2131 int sx = SCREENX(x), sy = SCREENY(y);
2132 int crumbled_border_size = graphic_info[graphic].border_size;
2133 int crumbled_tile_size = graphic_info[graphic].tile_size;
2134 int crumbled_border_size_var =
2135 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2136 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2139 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2141 /* draw simple, sloppy, non-corner-accurate crumbled border */
2143 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2144 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2145 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2146 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2148 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2149 FX + sx * TILEX_VAR + cx,
2150 FY + sy * TILEY_VAR + cy);
2152 /* (remaining middle border part must be at least as big as corner part) */
2153 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2154 crumbled_border_size_var >= TILESIZE_VAR / 3)
2157 /* correct corners of crumbled border, if needed */
2159 for (i = -1; i <= 1; i += 2)
2161 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2162 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2163 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2166 /* check if neighbour field is of same crumble type */
2167 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2168 graphic_info[graphic].class ==
2169 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2171 /* no crumbled corner, but continued crumbled border */
2173 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2174 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2175 int b1 = (i == 1 ? crumbled_border_size_var :
2176 TILESIZE_VAR - 2 * crumbled_border_size_var);
2178 width = crumbled_border_size_var;
2179 height = crumbled_border_size_var;
2181 if (dir == 1 || dir == 2)
2196 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2198 FX + sx * TILEX_VAR + cx,
2199 FY + sy * TILEY_VAR + cy);
2204 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2206 int sx = SCREENX(x), sy = SCREENY(y);
2209 static int xy[4][2] =
2217 if (!IN_LEV_FIELD(x, y))
2220 element = TILE_GFX_ELEMENT(x, y);
2222 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2224 if (!IN_SCR_FIELD(sx, sy))
2227 /* crumble field borders towards direct neighbour fields */
2228 for (i = 0; i < 4; i++)
2230 int xx = x + xy[i][0];
2231 int yy = y + xy[i][1];
2233 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2236 /* check if neighbour field is of same crumble type */
2237 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2238 graphic_info[graphic].class ==
2239 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2242 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2245 /* crumble inner field corners towards corner neighbour fields */
2246 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2247 graphic_info[graphic].anim_frames == 2)
2249 for (i = 0; i < 4; i++)
2251 int dx = (i & 1 ? +1 : -1);
2252 int dy = (i & 2 ? +1 : -1);
2254 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2258 MarkTileDirty(sx, sy);
2260 else /* center field is not crumbled -- crumble neighbour fields */
2262 /* crumble field borders of direct neighbour fields */
2263 for (i = 0; i < 4; i++)
2265 int xx = x + xy[i][0];
2266 int yy = y + xy[i][1];
2267 int sxx = sx + xy[i][0];
2268 int syy = sy + xy[i][1];
2270 if (!IN_LEV_FIELD(xx, yy) ||
2271 !IN_SCR_FIELD(sxx, syy))
2274 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2277 element = TILE_GFX_ELEMENT(xx, yy);
2279 if (!IS_CRUMBLED_TILE(xx, yy, element))
2282 graphic = el_act2crm(element, ACTION_DEFAULT);
2284 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2286 MarkTileDirty(sxx, syy);
2289 /* crumble inner field corners of corner neighbour fields */
2290 for (i = 0; i < 4; i++)
2292 int dx = (i & 1 ? +1 : -1);
2293 int dy = (i & 2 ? +1 : -1);
2299 if (!IN_LEV_FIELD(xx, yy) ||
2300 !IN_SCR_FIELD(sxx, syy))
2303 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2306 element = TILE_GFX_ELEMENT(xx, yy);
2308 if (!IS_CRUMBLED_TILE(xx, yy, element))
2311 graphic = el_act2crm(element, ACTION_DEFAULT);
2313 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2314 graphic_info[graphic].anim_frames == 2)
2315 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2317 MarkTileDirty(sxx, syy);
2322 void DrawLevelFieldCrumbled(int x, int y)
2326 if (!IN_LEV_FIELD(x, y))
2329 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2330 GfxElement[x][y] != EL_UNDEFINED &&
2331 GFX_CRUMBLED(GfxElement[x][y]))
2333 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2338 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2340 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2343 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2346 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2347 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2348 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2349 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2350 int sx = SCREENX(x), sy = SCREENY(y);
2352 DrawGraphic(sx, sy, graphic1, frame1);
2353 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2356 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2358 int sx = SCREENX(x), sy = SCREENY(y);
2359 static int xy[4][2] =
2368 /* crumble direct neighbour fields (required for field borders) */
2369 for (i = 0; i < 4; i++)
2371 int xx = x + xy[i][0];
2372 int yy = y + xy[i][1];
2373 int sxx = sx + xy[i][0];
2374 int syy = sy + xy[i][1];
2376 if (!IN_LEV_FIELD(xx, yy) ||
2377 !IN_SCR_FIELD(sxx, syy) ||
2378 !GFX_CRUMBLED(Feld[xx][yy]) ||
2382 DrawLevelField(xx, yy);
2385 /* crumble corner neighbour fields (required for inner field corners) */
2386 for (i = 0; i < 4; i++)
2388 int dx = (i & 1 ? +1 : -1);
2389 int dy = (i & 2 ? +1 : -1);
2395 if (!IN_LEV_FIELD(xx, yy) ||
2396 !IN_SCR_FIELD(sxx, syy) ||
2397 !GFX_CRUMBLED(Feld[xx][yy]) ||
2401 int element = TILE_GFX_ELEMENT(xx, yy);
2402 int graphic = el_act2crm(element, ACTION_DEFAULT);
2404 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2405 graphic_info[graphic].anim_frames == 2)
2406 DrawLevelField(xx, yy);
2410 static int getBorderElement(int x, int y)
2414 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2415 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2416 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2417 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2418 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2419 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2420 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2422 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2423 int steel_position = (x == -1 && y == -1 ? 0 :
2424 x == lev_fieldx && y == -1 ? 1 :
2425 x == -1 && y == lev_fieldy ? 2 :
2426 x == lev_fieldx && y == lev_fieldy ? 3 :
2427 x == -1 || x == lev_fieldx ? 4 :
2428 y == -1 || y == lev_fieldy ? 5 : 6);
2430 return border[steel_position][steel_type];
2433 void DrawScreenElement(int x, int y, int element)
2435 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2436 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2439 void DrawLevelElement(int x, int y, int element)
2441 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2442 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2445 void DrawScreenField(int x, int y)
2447 int lx = LEVELX(x), ly = LEVELY(y);
2448 int element, content;
2450 if (!IN_LEV_FIELD(lx, ly))
2452 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2455 element = getBorderElement(lx, ly);
2457 DrawScreenElement(x, y, element);
2462 element = Feld[lx][ly];
2463 content = Store[lx][ly];
2465 if (IS_MOVING(lx, ly))
2467 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2468 boolean cut_mode = NO_CUTTING;
2470 if (element == EL_QUICKSAND_EMPTYING ||
2471 element == EL_QUICKSAND_FAST_EMPTYING ||
2472 element == EL_MAGIC_WALL_EMPTYING ||
2473 element == EL_BD_MAGIC_WALL_EMPTYING ||
2474 element == EL_DC_MAGIC_WALL_EMPTYING ||
2475 element == EL_AMOEBA_DROPPING)
2476 cut_mode = CUT_ABOVE;
2477 else if (element == EL_QUICKSAND_FILLING ||
2478 element == EL_QUICKSAND_FAST_FILLING ||
2479 element == EL_MAGIC_WALL_FILLING ||
2480 element == EL_BD_MAGIC_WALL_FILLING ||
2481 element == EL_DC_MAGIC_WALL_FILLING)
2482 cut_mode = CUT_BELOW;
2484 if (cut_mode == CUT_ABOVE)
2485 DrawScreenElement(x, y, element);
2487 DrawScreenElement(x, y, EL_EMPTY);
2490 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2491 else if (cut_mode == NO_CUTTING)
2492 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2495 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2497 if (cut_mode == CUT_BELOW &&
2498 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2499 DrawLevelElement(lx, ly + 1, element);
2502 if (content == EL_ACID)
2504 int dir = MovDir[lx][ly];
2505 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2506 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2508 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2510 // prevent target field from being drawn again (but without masking)
2511 // (this would happen if target field is scanned after moving element)
2512 Stop[newlx][newly] = TRUE;
2515 else if (IS_BLOCKED(lx, ly))
2520 boolean cut_mode = NO_CUTTING;
2521 int element_old, content_old;
2523 Blocked2Moving(lx, ly, &oldx, &oldy);
2526 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2527 MovDir[oldx][oldy] == MV_RIGHT);
2529 element_old = Feld[oldx][oldy];
2530 content_old = Store[oldx][oldy];
2532 if (element_old == EL_QUICKSAND_EMPTYING ||
2533 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2534 element_old == EL_MAGIC_WALL_EMPTYING ||
2535 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2536 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2537 element_old == EL_AMOEBA_DROPPING)
2538 cut_mode = CUT_ABOVE;
2540 DrawScreenElement(x, y, EL_EMPTY);
2543 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2545 else if (cut_mode == NO_CUTTING)
2546 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2549 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2552 else if (IS_DRAWABLE(element))
2553 DrawScreenElement(x, y, element);
2555 DrawScreenElement(x, y, EL_EMPTY);
2558 void DrawLevelField(int x, int y)
2560 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2561 DrawScreenField(SCREENX(x), SCREENY(y));
2562 else if (IS_MOVING(x, y))
2566 Moving2Blocked(x, y, &newx, &newy);
2567 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2568 DrawScreenField(SCREENX(newx), SCREENY(newy));
2570 else if (IS_BLOCKED(x, y))
2574 Blocked2Moving(x, y, &oldx, &oldy);
2575 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2576 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2580 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2581 int (*el2img_function)(int), boolean masked,
2582 int element_bits_draw)
2584 int element_base = map_mm_wall_element(element);
2585 int element_bits = (IS_DF_WALL(element) ?
2586 element - EL_DF_WALL_START :
2587 IS_MM_WALL(element) ?
2588 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2589 int graphic = el2img_function(element_base);
2590 int tilesize_draw = tilesize / 2;
2595 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2597 for (i = 0; i < 4; i++)
2599 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2600 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2602 if (!(element_bits_draw & (1 << i)))
2605 if (element_bits & (1 << i))
2608 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2609 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2611 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2612 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2617 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2618 tilesize_draw, tilesize_draw);
2623 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2624 boolean masked, int element_bits_draw)
2626 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2627 element, tilesize, el2edimg, masked, element_bits_draw);
2630 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2631 int (*el2img_function)(int))
2633 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2637 void DrawSizedElementExt(int x, int y, int element, int tilesize,
2640 if (IS_MM_WALL(element))
2642 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2643 element, tilesize, el2edimg, masked, 0x000f);
2647 int graphic = el2edimg(element);
2650 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2652 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2656 void DrawSizedElement(int x, int y, int element, int tilesize)
2658 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2661 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2663 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2666 void DrawMiniElement(int x, int y, int element)
2670 graphic = el2edimg(element);
2671 DrawMiniGraphic(x, y, graphic);
2674 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2677 int x = sx + scroll_x, y = sy + scroll_y;
2679 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2680 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2681 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2682 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2684 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2687 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2689 int x = sx + scroll_x, y = sy + scroll_y;
2691 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2692 DrawMiniElement(sx, sy, EL_EMPTY);
2693 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2694 DrawMiniElement(sx, sy, Feld[x][y]);
2696 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2699 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2700 int x, int y, int xsize, int ysize,
2701 int tile_width, int tile_height)
2705 int dst_x = startx + x * tile_width;
2706 int dst_y = starty + y * tile_height;
2707 int width = graphic_info[graphic].width;
2708 int height = graphic_info[graphic].height;
2709 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2710 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2711 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2712 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2713 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2714 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2715 boolean draw_masked = graphic_info[graphic].draw_masked;
2717 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2719 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2721 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2725 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2726 inner_sx + (x - 1) * tile_width % inner_width);
2727 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2728 inner_sy + (y - 1) * tile_height % inner_height);
2731 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2734 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2738 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2739 int x, int y, int xsize, int ysize, int font_nr)
2741 int font_width = getFontWidth(font_nr);
2742 int font_height = getFontHeight(font_nr);
2744 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2745 font_width, font_height);
2748 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2750 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2751 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2752 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2753 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2754 boolean no_delay = (tape.warp_forward);
2755 unsigned int anim_delay = 0;
2756 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2757 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2758 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2759 int font_width = getFontWidth(font_nr);
2760 int font_height = getFontHeight(font_nr);
2761 int max_xsize = level.envelope[envelope_nr].xsize;
2762 int max_ysize = level.envelope[envelope_nr].ysize;
2763 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2764 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2765 int xend = max_xsize;
2766 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2767 int xstep = (xstart < xend ? 1 : 0);
2768 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2770 int end = MAX(xend - xstart, yend - ystart);
2773 for (i = start; i <= end; i++)
2775 int last_frame = end; // last frame of this "for" loop
2776 int x = xstart + i * xstep;
2777 int y = ystart + i * ystep;
2778 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2779 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2780 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2781 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2784 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2786 BlitScreenToBitmap(backbuffer);
2788 SetDrawtoField(DRAW_TO_BACKBUFFER);
2790 for (yy = 0; yy < ysize; yy++)
2791 for (xx = 0; xx < xsize; xx++)
2792 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2794 DrawTextBuffer(sx + font_width, sy + font_height,
2795 level.envelope[envelope_nr].text, font_nr, max_xsize,
2796 xsize - 2, ysize - 2, 0, mask_mode,
2797 level.envelope[envelope_nr].autowrap,
2798 level.envelope[envelope_nr].centered, FALSE);
2800 redraw_mask |= REDRAW_FIELD;
2803 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2809 void ShowEnvelope(int envelope_nr)
2811 int element = EL_ENVELOPE_1 + envelope_nr;
2812 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2813 int sound_opening = element_info[element].sound[ACTION_OPENING];
2814 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2815 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2816 boolean no_delay = (tape.warp_forward);
2817 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2818 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2819 int anim_mode = graphic_info[graphic].anim_mode;
2820 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2821 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2823 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2825 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2827 if (anim_mode == ANIM_DEFAULT)
2828 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2830 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2833 Delay(wait_delay_value);
2835 WaitForEventToContinue();
2837 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2839 if (anim_mode != ANIM_NONE)
2840 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2842 if (anim_mode == ANIM_DEFAULT)
2843 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2845 game.envelope_active = FALSE;
2847 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2849 redraw_mask |= REDRAW_FIELD;
2853 static void setRequestBasePosition(int *x, int *y)
2855 int sx_base, sy_base;
2857 if (request.x != -1)
2858 sx_base = request.x;
2859 else if (request.align == ALIGN_LEFT)
2861 else if (request.align == ALIGN_RIGHT)
2862 sx_base = SX + SXSIZE;
2864 sx_base = SX + SXSIZE / 2;
2866 if (request.y != -1)
2867 sy_base = request.y;
2868 else if (request.valign == VALIGN_TOP)
2870 else if (request.valign == VALIGN_BOTTOM)
2871 sy_base = SY + SYSIZE;
2873 sy_base = SY + SYSIZE / 2;
2879 static void setRequestPositionExt(int *x, int *y, int width, int height,
2880 boolean add_border_size)
2882 int border_size = request.border_size;
2883 int sx_base, sy_base;
2886 setRequestBasePosition(&sx_base, &sy_base);
2888 if (request.align == ALIGN_LEFT)
2890 else if (request.align == ALIGN_RIGHT)
2891 sx = sx_base - width;
2893 sx = sx_base - width / 2;
2895 if (request.valign == VALIGN_TOP)
2897 else if (request.valign == VALIGN_BOTTOM)
2898 sy = sy_base - height;
2900 sy = sy_base - height / 2;
2902 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2903 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2905 if (add_border_size)
2915 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2917 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2920 void DrawEnvelopeRequest(char *text)
2922 char *text_final = text;
2923 char *text_door_style = NULL;
2924 int graphic = IMG_BACKGROUND_REQUEST;
2925 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2926 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2927 int font_nr = FONT_REQUEST;
2928 int font_width = getFontWidth(font_nr);
2929 int font_height = getFontHeight(font_nr);
2930 int border_size = request.border_size;
2931 int line_spacing = request.line_spacing;
2932 int line_height = font_height + line_spacing;
2933 int max_text_width = request.width - 2 * border_size;
2934 int max_text_height = request.height - 2 * border_size;
2935 int line_length = max_text_width / font_width;
2936 int max_lines = max_text_height / line_height;
2937 int text_width = line_length * font_width;
2938 int width = request.width;
2939 int height = request.height;
2940 int tile_size = MAX(request.step_offset, 1);
2941 int x_steps = width / tile_size;
2942 int y_steps = height / tile_size;
2943 int sx_offset = border_size;
2944 int sy_offset = border_size;
2948 if (request.centered)
2949 sx_offset = (request.width - text_width) / 2;
2951 if (request.wrap_single_words && !request.autowrap)
2953 char *src_text_ptr, *dst_text_ptr;
2955 text_door_style = checked_malloc(2 * strlen(text) + 1);
2957 src_text_ptr = text;
2958 dst_text_ptr = text_door_style;
2960 while (*src_text_ptr)
2962 if (*src_text_ptr == ' ' ||
2963 *src_text_ptr == '?' ||
2964 *src_text_ptr == '!')
2965 *dst_text_ptr++ = '\n';
2967 if (*src_text_ptr != ' ')
2968 *dst_text_ptr++ = *src_text_ptr;
2973 *dst_text_ptr = '\0';
2975 text_final = text_door_style;
2978 setRequestPosition(&sx, &sy, FALSE);
2980 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2982 for (y = 0; y < y_steps; y++)
2983 for (x = 0; x < x_steps; x++)
2984 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2985 x, y, x_steps, y_steps,
2986 tile_size, tile_size);
2988 /* force DOOR font inside door area */
2989 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2991 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2992 line_length, -1, max_lines, line_spacing, mask_mode,
2993 request.autowrap, request.centered, FALSE);
2997 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2998 RedrawGadget(tool_gadget[i]);
3000 // store readily prepared envelope request for later use when animating
3001 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3003 if (text_door_style)
3004 free(text_door_style);
3007 void AnimateEnvelopeRequest(int anim_mode, int action)
3009 int graphic = IMG_BACKGROUND_REQUEST;
3010 boolean draw_masked = graphic_info[graphic].draw_masked;
3011 int delay_value_normal = request.step_delay;
3012 int delay_value_fast = delay_value_normal / 2;
3013 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3014 boolean no_delay = (tape.warp_forward);
3015 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3016 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3017 unsigned int anim_delay = 0;
3019 int tile_size = MAX(request.step_offset, 1);
3020 int max_xsize = request.width / tile_size;
3021 int max_ysize = request.height / tile_size;
3022 int max_xsize_inner = max_xsize - 2;
3023 int max_ysize_inner = max_ysize - 2;
3025 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3026 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3027 int xend = max_xsize_inner;
3028 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3029 int xstep = (xstart < xend ? 1 : 0);
3030 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3032 int end = MAX(xend - xstart, yend - ystart);
3035 if (setup.quick_doors)
3042 for (i = start; i <= end; i++)
3044 int last_frame = end; // last frame of this "for" loop
3045 int x = xstart + i * xstep;
3046 int y = ystart + i * ystep;
3047 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3048 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3049 int xsize_size_left = (xsize - 1) * tile_size;
3050 int ysize_size_top = (ysize - 1) * tile_size;
3051 int max_xsize_pos = (max_xsize - 1) * tile_size;
3052 int max_ysize_pos = (max_ysize - 1) * tile_size;
3053 int width = xsize * tile_size;
3054 int height = ysize * tile_size;
3059 setRequestPosition(&src_x, &src_y, FALSE);
3060 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3062 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3064 for (yy = 0; yy < 2; yy++)
3066 for (xx = 0; xx < 2; xx++)
3068 int src_xx = src_x + xx * max_xsize_pos;
3069 int src_yy = src_y + yy * max_ysize_pos;
3070 int dst_xx = dst_x + xx * xsize_size_left;
3071 int dst_yy = dst_y + yy * ysize_size_top;
3072 int xx_size = (xx ? tile_size : xsize_size_left);
3073 int yy_size = (yy ? tile_size : ysize_size_top);
3076 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3077 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3079 BlitBitmap(bitmap_db_store_2, backbuffer,
3080 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3084 redraw_mask |= REDRAW_FIELD;
3088 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3094 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3096 int graphic = IMG_BACKGROUND_REQUEST;
3097 int sound_opening = SND_REQUEST_OPENING;
3098 int sound_closing = SND_REQUEST_CLOSING;
3099 int anim_mode_1 = request.anim_mode; /* (higher priority) */
3100 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
3101 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3102 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3103 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3105 if (game_status == GAME_MODE_PLAYING)
3106 BlitScreenToBitmap(backbuffer);
3108 SetDrawtoField(DRAW_TO_BACKBUFFER);
3110 // SetDrawBackgroundMask(REDRAW_NONE);
3112 if (action == ACTION_OPENING)
3114 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3116 if (req_state & REQ_ASK)
3118 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3119 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3121 else if (req_state & REQ_CONFIRM)
3123 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3125 else if (req_state & REQ_PLAYER)
3127 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3128 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3129 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3130 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3133 DrawEnvelopeRequest(text);
3136 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
3138 if (action == ACTION_OPENING)
3140 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3142 if (anim_mode == ANIM_DEFAULT)
3143 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3145 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3149 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3151 if (anim_mode != ANIM_NONE)
3152 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3154 if (anim_mode == ANIM_DEFAULT)
3155 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3158 game.envelope_active = FALSE;
3160 if (action == ACTION_CLOSING)
3161 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3163 // SetDrawBackgroundMask(last_draw_background_mask);
3165 redraw_mask |= REDRAW_FIELD;
3169 if (action == ACTION_CLOSING &&
3170 game_status == GAME_MODE_PLAYING &&
3171 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3172 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3175 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3177 if (IS_MM_WALL(element))
3179 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3185 int graphic = el2preimg(element);
3187 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3188 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3193 void DrawLevel(int draw_background_mask)
3197 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3198 SetDrawBackgroundMask(draw_background_mask);
3202 for (x = BX1; x <= BX2; x++)
3203 for (y = BY1; y <= BY2; y++)
3204 DrawScreenField(x, y);
3206 redraw_mask |= REDRAW_FIELD;
3209 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3214 for (x = 0; x < size_x; x++)
3215 for (y = 0; y < size_y; y++)
3216 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3218 redraw_mask |= REDRAW_FIELD;
3221 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3225 for (x = 0; x < size_x; x++)
3226 for (y = 0; y < size_y; y++)
3227 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3229 redraw_mask |= REDRAW_FIELD;
3232 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3234 boolean show_level_border = (BorderElement != EL_EMPTY);
3235 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3236 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3237 int tile_size = preview.tile_size;
3238 int preview_width = preview.xsize * tile_size;
3239 int preview_height = preview.ysize * tile_size;
3240 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3241 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3242 int real_preview_width = real_preview_xsize * tile_size;
3243 int real_preview_height = real_preview_ysize * tile_size;
3244 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3245 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3248 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3251 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3253 dst_x += (preview_width - real_preview_width) / 2;
3254 dst_y += (preview_height - real_preview_height) / 2;
3256 for (x = 0; x < real_preview_xsize; x++)
3258 for (y = 0; y < real_preview_ysize; y++)
3260 int lx = from_x + x + (show_level_border ? -1 : 0);
3261 int ly = from_y + y + (show_level_border ? -1 : 0);
3262 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3263 getBorderElement(lx, ly));
3265 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3266 element, tile_size);
3270 redraw_mask |= REDRAW_FIELD;
3273 #define MICROLABEL_EMPTY 0
3274 #define MICROLABEL_LEVEL_NAME 1
3275 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3276 #define MICROLABEL_LEVEL_AUTHOR 3
3277 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3278 #define MICROLABEL_IMPORTED_FROM 5
3279 #define MICROLABEL_IMPORTED_BY_HEAD 6
3280 #define MICROLABEL_IMPORTED_BY 7
3282 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3284 int max_text_width = SXSIZE;
3285 int font_width = getFontWidth(font_nr);
3287 if (pos->align == ALIGN_CENTER)
3288 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3289 else if (pos->align == ALIGN_RIGHT)
3290 max_text_width = pos->x;
3292 max_text_width = SXSIZE - pos->x;
3294 return max_text_width / font_width;
3297 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3299 char label_text[MAX_OUTPUT_LINESIZE + 1];
3300 int max_len_label_text;
3301 int font_nr = pos->font;
3304 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3307 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3308 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3309 mode == MICROLABEL_IMPORTED_BY_HEAD)
3310 font_nr = pos->font_alt;
3312 max_len_label_text = getMaxTextLength(pos, font_nr);
3314 if (pos->size != -1)
3315 max_len_label_text = pos->size;
3317 for (i = 0; i < max_len_label_text; i++)
3318 label_text[i] = ' ';
3319 label_text[max_len_label_text] = '\0';
3321 if (strlen(label_text) > 0)
3322 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3325 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3326 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3327 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3328 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3329 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3330 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3331 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3332 max_len_label_text);
3333 label_text[max_len_label_text] = '\0';
3335 if (strlen(label_text) > 0)
3336 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3338 redraw_mask |= REDRAW_FIELD;
3341 static void DrawPreviewLevelLabel(int mode)
3343 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3346 static void DrawPreviewLevelInfo(int mode)
3348 if (mode == MICROLABEL_LEVEL_NAME)
3349 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3350 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3351 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3354 static void DrawPreviewLevelExt(boolean restart)
3356 static unsigned int scroll_delay = 0;
3357 static unsigned int label_delay = 0;
3358 static int from_x, from_y, scroll_direction;
3359 static int label_state, label_counter;
3360 unsigned int scroll_delay_value = preview.step_delay;
3361 boolean show_level_border = (BorderElement != EL_EMPTY);
3362 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3363 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3370 if (preview.anim_mode == ANIM_CENTERED)
3372 if (level_xsize > preview.xsize)
3373 from_x = (level_xsize - preview.xsize) / 2;
3374 if (level_ysize > preview.ysize)
3375 from_y = (level_ysize - preview.ysize) / 2;
3378 from_x += preview.xoffset;
3379 from_y += preview.yoffset;
3381 scroll_direction = MV_RIGHT;
3385 DrawPreviewLevelPlayfield(from_x, from_y);
3386 DrawPreviewLevelLabel(label_state);
3388 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3389 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3391 /* initialize delay counters */
3392 DelayReached(&scroll_delay, 0);
3393 DelayReached(&label_delay, 0);
3395 if (leveldir_current->name)
3397 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3398 char label_text[MAX_OUTPUT_LINESIZE + 1];
3399 int font_nr = pos->font;
3400 int max_len_label_text = getMaxTextLength(pos, font_nr);
3402 if (pos->size != -1)
3403 max_len_label_text = pos->size;
3405 strncpy(label_text, leveldir_current->name, max_len_label_text);
3406 label_text[max_len_label_text] = '\0';
3408 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3409 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3415 /* scroll preview level, if needed */
3416 if (preview.anim_mode != ANIM_NONE &&
3417 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3418 DelayReached(&scroll_delay, scroll_delay_value))
3420 switch (scroll_direction)
3425 from_x -= preview.step_offset;
3426 from_x = (from_x < 0 ? 0 : from_x);
3429 scroll_direction = MV_UP;
3433 if (from_x < level_xsize - preview.xsize)
3435 from_x += preview.step_offset;
3436 from_x = (from_x > level_xsize - preview.xsize ?
3437 level_xsize - preview.xsize : from_x);
3440 scroll_direction = MV_DOWN;
3446 from_y -= preview.step_offset;
3447 from_y = (from_y < 0 ? 0 : from_y);
3450 scroll_direction = MV_RIGHT;
3454 if (from_y < level_ysize - preview.ysize)
3456 from_y += preview.step_offset;
3457 from_y = (from_y > level_ysize - preview.ysize ?
3458 level_ysize - preview.ysize : from_y);
3461 scroll_direction = MV_LEFT;
3468 DrawPreviewLevelPlayfield(from_x, from_y);
3471 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3472 /* redraw micro level label, if needed */
3473 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3474 !strEqual(level.author, ANONYMOUS_NAME) &&
3475 !strEqual(level.author, leveldir_current->name) &&
3476 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3478 int max_label_counter = 23;
3480 if (leveldir_current->imported_from != NULL &&
3481 strlen(leveldir_current->imported_from) > 0)
3482 max_label_counter += 14;
3483 if (leveldir_current->imported_by != NULL &&
3484 strlen(leveldir_current->imported_by) > 0)
3485 max_label_counter += 14;
3487 label_counter = (label_counter + 1) % max_label_counter;
3488 label_state = (label_counter >= 0 && label_counter <= 7 ?
3489 MICROLABEL_LEVEL_NAME :
3490 label_counter >= 9 && label_counter <= 12 ?
3491 MICROLABEL_LEVEL_AUTHOR_HEAD :
3492 label_counter >= 14 && label_counter <= 21 ?
3493 MICROLABEL_LEVEL_AUTHOR :
3494 label_counter >= 23 && label_counter <= 26 ?
3495 MICROLABEL_IMPORTED_FROM_HEAD :
3496 label_counter >= 28 && label_counter <= 35 ?
3497 MICROLABEL_IMPORTED_FROM :
3498 label_counter >= 37 && label_counter <= 40 ?
3499 MICROLABEL_IMPORTED_BY_HEAD :
3500 label_counter >= 42 && label_counter <= 49 ?
3501 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3503 if (leveldir_current->imported_from == NULL &&
3504 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3505 label_state == MICROLABEL_IMPORTED_FROM))
3506 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3507 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3509 DrawPreviewLevelLabel(label_state);
3513 void DrawPreviewPlayers()
3515 if (game_status != GAME_MODE_MAIN)
3518 if (!network.enabled && !setup.team_mode)
3521 boolean player_found[MAX_PLAYERS];
3522 int num_players = 0;
3525 for (i = 0; i < MAX_PLAYERS; i++)
3526 player_found[i] = FALSE;
3528 /* check which players can be found in the level (simple approach) */
3529 for (x = 0; x < lev_fieldx; x++)
3531 for (y = 0; y < lev_fieldy; y++)
3533 int element = level.field[x][y];
3535 if (ELEM_IS_PLAYER(element))
3537 int player_nr = GET_PLAYER_NR(element);
3539 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3541 if (!player_found[player_nr])
3544 player_found[player_nr] = TRUE;
3549 struct TextPosInfo *pos = &menu.main.preview_players;
3550 int tile_size = pos->tile_size;
3551 int border_size = pos->border_size;
3552 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3553 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3554 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3555 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3556 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3557 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3558 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3559 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3560 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3561 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3562 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3563 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3565 /* clear area in which the players will be drawn */
3566 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3567 max_players_width, max_players_height);
3569 /* only draw players if level is suited for team mode */
3570 if (num_players < 2)
3573 /* draw all players that were found in the level */
3574 for (i = 0; i < MAX_PLAYERS; i++)
3576 if (player_found[i])
3578 int graphic = el2img(EL_PLAYER_1 + i);
3580 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3582 xpos += player_xoffset;
3583 ypos += player_yoffset;
3588 void DrawPreviewLevelInitial()
3590 DrawPreviewLevelExt(TRUE);
3591 DrawPreviewPlayers();
3594 void DrawPreviewLevelAnimation()
3596 DrawPreviewLevelExt(FALSE);
3599 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3600 int border_size, int font_nr)
3602 int graphic = el2img(EL_PLAYER_1 + player_nr);
3603 int font_height = getFontHeight(font_nr);
3604 int player_height = MAX(tile_size, font_height);
3605 int xoffset_text = tile_size + border_size;
3606 int yoffset_text = (player_height - font_height) / 2;
3607 int yoffset_graphic = (player_height - tile_size) / 2;
3608 char *player_name = getNetworkPlayerName(player_nr + 1);
3610 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3612 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3615 void DrawNetworkPlayersExt(boolean force)
3617 if (game_status != GAME_MODE_MAIN)
3620 if (!network.connected && !force)
3623 int num_players = 0;
3626 for (i = 0; i < MAX_PLAYERS; i++)
3627 if (stored_player[i].connected_network)
3630 struct TextPosInfo *pos = &menu.main.network_players;
3631 int tile_size = pos->tile_size;
3632 int border_size = pos->border_size;
3633 int xoffset_text = tile_size + border_size;
3634 int font_nr = pos->font;
3635 int font_width = getFontWidth(font_nr);
3636 int font_height = getFontHeight(font_nr);
3637 int player_height = MAX(tile_size, font_height);
3638 int player_yoffset = player_height + border_size;
3639 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3640 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3641 int all_players_height = num_players * player_yoffset - border_size;
3642 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3643 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3644 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3646 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3647 max_players_width, max_players_height);
3649 /* first draw local network player ... */
3650 for (i = 0; i < MAX_PLAYERS; i++)
3652 if (stored_player[i].connected_network &&
3653 stored_player[i].connected_locally)
3655 char *player_name = getNetworkPlayerName(i + 1);
3656 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3657 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3659 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3661 ypos += player_yoffset;
3665 /* ... then draw all other network players */
3666 for (i = 0; i < MAX_PLAYERS; i++)
3668 if (stored_player[i].connected_network &&
3669 !stored_player[i].connected_locally)
3671 char *player_name = getNetworkPlayerName(i + 1);
3672 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3673 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3675 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3677 ypos += player_yoffset;
3682 void DrawNetworkPlayers()
3684 DrawNetworkPlayersExt(FALSE);
3687 void ClearNetworkPlayers()
3689 DrawNetworkPlayersExt(TRUE);
3692 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3693 int graphic, int sync_frame,
3696 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3698 if (mask_mode == USE_MASKING)
3699 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3701 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3704 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3705 int graphic, int sync_frame, int mask_mode)
3707 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3709 if (mask_mode == USE_MASKING)
3710 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3712 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3715 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3717 int lx = LEVELX(x), ly = LEVELY(y);
3719 if (!IN_SCR_FIELD(x, y))
3722 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3723 graphic, GfxFrame[lx][ly], NO_MASKING);
3725 MarkTileDirty(x, y);
3728 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3730 int lx = LEVELX(x), ly = LEVELY(y);
3732 if (!IN_SCR_FIELD(x, y))
3735 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3736 graphic, GfxFrame[lx][ly], NO_MASKING);
3737 MarkTileDirty(x, y);
3740 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3742 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3745 void DrawLevelElementAnimation(int x, int y, int element)
3747 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3749 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3752 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3754 int sx = SCREENX(x), sy = SCREENY(y);
3756 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3759 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3762 DrawGraphicAnimation(sx, sy, graphic);
3765 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3766 DrawLevelFieldCrumbled(x, y);
3768 if (GFX_CRUMBLED(Feld[x][y]))
3769 DrawLevelFieldCrumbled(x, y);
3773 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3775 int sx = SCREENX(x), sy = SCREENY(y);
3778 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3781 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3783 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3786 DrawGraphicAnimation(sx, sy, graphic);
3788 if (GFX_CRUMBLED(element))
3789 DrawLevelFieldCrumbled(x, y);
3792 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3794 if (player->use_murphy)
3796 /* this works only because currently only one player can be "murphy" ... */
3797 static int last_horizontal_dir = MV_LEFT;
3798 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3800 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3801 last_horizontal_dir = move_dir;
3803 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3805 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3807 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3813 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3816 static boolean equalGraphics(int graphic1, int graphic2)
3818 struct GraphicInfo *g1 = &graphic_info[graphic1];
3819 struct GraphicInfo *g2 = &graphic_info[graphic2];
3821 return (g1->bitmap == g2->bitmap &&
3822 g1->src_x == g2->src_x &&
3823 g1->src_y == g2->src_y &&
3824 g1->anim_frames == g2->anim_frames &&
3825 g1->anim_delay == g2->anim_delay &&
3826 g1->anim_mode == g2->anim_mode);
3829 void DrawAllPlayers()
3833 for (i = 0; i < MAX_PLAYERS; i++)
3834 if (stored_player[i].active)
3835 DrawPlayer(&stored_player[i]);
3838 void DrawPlayerField(int x, int y)
3840 if (!IS_PLAYER(x, y))
3843 DrawPlayer(PLAYERINFO(x, y));
3846 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3848 void DrawPlayer(struct PlayerInfo *player)
3850 int jx = player->jx;
3851 int jy = player->jy;
3852 int move_dir = player->MovDir;
3853 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3854 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3855 int last_jx = (player->is_moving ? jx - dx : jx);
3856 int last_jy = (player->is_moving ? jy - dy : jy);
3857 int next_jx = jx + dx;
3858 int next_jy = jy + dy;
3859 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3860 boolean player_is_opaque = FALSE;
3861 int sx = SCREENX(jx), sy = SCREENY(jy);
3862 int sxx = 0, syy = 0;
3863 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3865 int action = ACTION_DEFAULT;
3866 int last_player_graphic = getPlayerGraphic(player, move_dir);
3867 int last_player_frame = player->Frame;
3870 /* GfxElement[][] is set to the element the player is digging or collecting;
3871 remove also for off-screen player if the player is not moving anymore */
3872 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3873 GfxElement[jx][jy] = EL_UNDEFINED;
3875 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3879 if (!IN_LEV_FIELD(jx, jy))
3881 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3882 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3883 printf("DrawPlayerField(): This should never happen!\n");
3888 if (element == EL_EXPLOSION)
3891 action = (player->is_pushing ? ACTION_PUSHING :
3892 player->is_digging ? ACTION_DIGGING :
3893 player->is_collecting ? ACTION_COLLECTING :
3894 player->is_moving ? ACTION_MOVING :
3895 player->is_snapping ? ACTION_SNAPPING :
3896 player->is_dropping ? ACTION_DROPPING :
3897 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3899 if (player->is_waiting)
3900 move_dir = player->dir_waiting;
3902 InitPlayerGfxAnimation(player, action, move_dir);
3904 /* ----------------------------------------------------------------------- */
3905 /* draw things in the field the player is leaving, if needed */
3906 /* ----------------------------------------------------------------------- */
3908 if (player->is_moving)
3910 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3912 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3914 if (last_element == EL_DYNAMITE_ACTIVE ||
3915 last_element == EL_EM_DYNAMITE_ACTIVE ||
3916 last_element == EL_SP_DISK_RED_ACTIVE)
3917 DrawDynamite(last_jx, last_jy);
3919 DrawLevelFieldThruMask(last_jx, last_jy);
3921 else if (last_element == EL_DYNAMITE_ACTIVE ||
3922 last_element == EL_EM_DYNAMITE_ACTIVE ||
3923 last_element == EL_SP_DISK_RED_ACTIVE)
3924 DrawDynamite(last_jx, last_jy);
3926 /* !!! this is not enough to prevent flickering of players which are
3927 moving next to each others without a free tile between them -- this
3928 can only be solved by drawing all players layer by layer (first the
3929 background, then the foreground etc.) !!! => TODO */
3930 else if (!IS_PLAYER(last_jx, last_jy))
3931 DrawLevelField(last_jx, last_jy);
3934 DrawLevelField(last_jx, last_jy);
3937 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3938 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3941 if (!IN_SCR_FIELD(sx, sy))
3944 /* ----------------------------------------------------------------------- */
3945 /* draw things behind the player, if needed */
3946 /* ----------------------------------------------------------------------- */
3949 DrawLevelElement(jx, jy, Back[jx][jy]);
3950 else if (IS_ACTIVE_BOMB(element))
3951 DrawLevelElement(jx, jy, EL_EMPTY);
3954 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3956 int old_element = GfxElement[jx][jy];
3957 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3958 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3960 if (GFX_CRUMBLED(old_element))
3961 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3963 DrawGraphic(sx, sy, old_graphic, frame);
3965 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3966 player_is_opaque = TRUE;
3970 GfxElement[jx][jy] = EL_UNDEFINED;
3972 /* make sure that pushed elements are drawn with correct frame rate */
3973 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3975 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3976 GfxFrame[jx][jy] = player->StepFrame;
3978 DrawLevelField(jx, jy);
3982 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3983 /* ----------------------------------------------------------------------- */
3984 /* draw player himself */
3985 /* ----------------------------------------------------------------------- */
3987 graphic = getPlayerGraphic(player, move_dir);
3989 /* in the case of changed player action or direction, prevent the current
3990 animation frame from being restarted for identical animations */
3991 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3992 player->Frame = last_player_frame;
3994 frame = getGraphicAnimationFrame(graphic, player->Frame);
3998 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3999 sxx = player->GfxPos;
4001 syy = player->GfxPos;
4004 if (player_is_opaque)
4005 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4007 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4009 if (SHIELD_ON(player))
4011 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4012 IMG_SHIELD_NORMAL_ACTIVE);
4013 int frame = getGraphicAnimationFrame(graphic, -1);
4015 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4019 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4022 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4023 sxx = player->GfxPos;
4025 syy = player->GfxPos;
4029 /* ----------------------------------------------------------------------- */
4030 /* draw things the player is pushing, if needed */
4031 /* ----------------------------------------------------------------------- */
4033 if (player->is_pushing && player->is_moving)
4035 int px = SCREENX(jx), py = SCREENY(jy);
4036 int pxx = (TILEX - ABS(sxx)) * dx;
4037 int pyy = (TILEY - ABS(syy)) * dy;
4038 int gfx_frame = GfxFrame[jx][jy];
4044 if (!IS_MOVING(jx, jy)) /* push movement already finished */
4046 element = Feld[next_jx][next_jy];
4047 gfx_frame = GfxFrame[next_jx][next_jy];
4050 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4052 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4053 frame = getGraphicAnimationFrame(graphic, sync_frame);
4055 /* draw background element under pushed element (like the Sokoban field) */
4056 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4058 /* this allows transparent pushing animation over non-black background */
4061 DrawLevelElement(jx, jy, Back[jx][jy]);
4063 DrawLevelElement(jx, jy, EL_EMPTY);
4065 if (Back[next_jx][next_jy])
4066 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4068 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4070 else if (Back[next_jx][next_jy])
4071 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4074 /* do not draw (EM style) pushing animation when pushing is finished */
4075 /* (two-tile animations usually do not contain start and end frame) */
4076 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4077 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4079 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4081 /* masked drawing is needed for EMC style (double) movement graphics */
4082 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
4083 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4087 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4088 /* ----------------------------------------------------------------------- */
4089 /* draw player himself */
4090 /* ----------------------------------------------------------------------- */
4092 graphic = getPlayerGraphic(player, move_dir);
4094 /* in the case of changed player action or direction, prevent the current
4095 animation frame from being restarted for identical animations */
4096 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4097 player->Frame = last_player_frame;
4099 frame = getGraphicAnimationFrame(graphic, player->Frame);
4103 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4104 sxx = player->GfxPos;
4106 syy = player->GfxPos;
4109 if (player_is_opaque)
4110 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4112 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4114 if (SHIELD_ON(player))
4116 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4117 IMG_SHIELD_NORMAL_ACTIVE);
4118 int frame = getGraphicAnimationFrame(graphic, -1);
4120 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4124 /* ----------------------------------------------------------------------- */
4125 /* draw things in front of player (active dynamite or dynabombs) */
4126 /* ----------------------------------------------------------------------- */
4128 if (IS_ACTIVE_BOMB(element))
4130 graphic = el2img(element);
4131 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4133 if (game.emulation == EMU_SUPAPLEX)
4134 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4136 DrawGraphicThruMask(sx, sy, graphic, frame);
4139 if (player_is_moving && last_element == EL_EXPLOSION)
4141 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4142 GfxElement[last_jx][last_jy] : EL_EMPTY);
4143 int graphic = el_act2img(element, ACTION_EXPLODING);
4144 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4145 int phase = ExplodePhase[last_jx][last_jy] - 1;
4146 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4149 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4152 /* ----------------------------------------------------------------------- */
4153 /* draw elements the player is just walking/passing through/under */
4154 /* ----------------------------------------------------------------------- */
4156 if (player_is_moving)
4158 /* handle the field the player is leaving ... */
4159 if (IS_ACCESSIBLE_INSIDE(last_element))
4160 DrawLevelField(last_jx, last_jy);
4161 else if (IS_ACCESSIBLE_UNDER(last_element))
4162 DrawLevelFieldThruMask(last_jx, last_jy);
4165 /* do not redraw accessible elements if the player is just pushing them */
4166 if (!player_is_moving || !player->is_pushing)
4168 /* ... and the field the player is entering */
4169 if (IS_ACCESSIBLE_INSIDE(element))
4170 DrawLevelField(jx, jy);
4171 else if (IS_ACCESSIBLE_UNDER(element))
4172 DrawLevelFieldThruMask(jx, jy);
4175 MarkTileDirty(sx, sy);
4178 /* ------------------------------------------------------------------------- */
4180 void WaitForEventToContinue()
4182 boolean still_wait = TRUE;
4184 if (program.headless)
4187 /* simulate releasing mouse button over last gadget, if still pressed */
4189 HandleGadgets(-1, -1, 0);
4191 button_status = MB_RELEASED;
4199 if (NextValidEvent(&event))
4203 case EVENT_BUTTONPRESS:
4204 case EVENT_KEYPRESS:
4205 #if defined(TARGET_SDL2)
4206 case SDL_CONTROLLERBUTTONDOWN:
4208 case SDL_JOYBUTTONDOWN:
4212 case EVENT_KEYRELEASE:
4213 ClearPlayerAction();
4217 HandleOtherEvents(&event);
4221 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4230 #define MAX_REQUEST_LINES 13
4231 #define MAX_REQUEST_LINE_FONT1_LEN 7
4232 #define MAX_REQUEST_LINE_FONT2_LEN 10
4234 static int RequestHandleEvents(unsigned int req_state)
4236 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
4237 local_player->LevelSolved_GameEnd);
4238 int width = request.width;
4239 int height = request.height;
4243 setRequestPosition(&sx, &sy, FALSE);
4245 button_status = MB_RELEASED;
4247 request_gadget_id = -1;
4254 /* the MM game engine does not use a special (scrollable) field buffer */
4255 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4256 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4258 HandleGameActions();
4260 SetDrawtoField(DRAW_TO_BACKBUFFER);
4262 if (global.use_envelope_request)
4264 /* copy current state of request area to middle of playfield area */
4265 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4273 while (NextValidEvent(&event))
4277 case EVENT_BUTTONPRESS:
4278 case EVENT_BUTTONRELEASE:
4279 case EVENT_MOTIONNOTIFY:
4283 if (event.type == EVENT_MOTIONNOTIFY)
4288 motion_status = TRUE;
4289 mx = ((MotionEvent *) &event)->x;
4290 my = ((MotionEvent *) &event)->y;
4294 motion_status = FALSE;
4295 mx = ((ButtonEvent *) &event)->x;
4296 my = ((ButtonEvent *) &event)->y;
4297 if (event.type == EVENT_BUTTONPRESS)
4298 button_status = ((ButtonEvent *) &event)->button;
4300 button_status = MB_RELEASED;
4303 /* this sets 'request_gadget_id' */
4304 HandleGadgets(mx, my, button_status);
4306 switch (request_gadget_id)
4308 case TOOL_CTRL_ID_YES:
4311 case TOOL_CTRL_ID_NO:
4314 case TOOL_CTRL_ID_CONFIRM:
4315 result = TRUE | FALSE;
4318 case TOOL_CTRL_ID_PLAYER_1:
4321 case TOOL_CTRL_ID_PLAYER_2:
4324 case TOOL_CTRL_ID_PLAYER_3:
4327 case TOOL_CTRL_ID_PLAYER_4:
4338 #if defined(TARGET_SDL2)
4339 case SDL_WINDOWEVENT:
4340 HandleWindowEvent((WindowEvent *) &event);
4343 case SDL_APP_WILLENTERBACKGROUND:
4344 case SDL_APP_DIDENTERBACKGROUND:
4345 case SDL_APP_WILLENTERFOREGROUND:
4346 case SDL_APP_DIDENTERFOREGROUND:
4347 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4351 case EVENT_KEYPRESS:
4353 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4358 if (req_state & REQ_CONFIRM)
4364 #if defined(TARGET_SDL2)
4368 #if defined(KSYM_Rewind)
4369 case KSYM_Rewind: /* for Amazon Fire TV remote */
4377 #if defined(TARGET_SDL2)
4380 #if defined(KSYM_FastForward)
4381 case KSYM_FastForward: /* for Amazon Fire TV remote */
4388 HandleKeysDebug(key);
4392 if (req_state & REQ_PLAYER)
4394 int old_player_nr = setup.network_player_nr;
4397 result = old_player_nr + 1;
4402 result = old_player_nr + 1;
4433 case EVENT_KEYRELEASE:
4434 ClearPlayerAction();
4437 #if defined(TARGET_SDL2)
4438 case SDL_CONTROLLERBUTTONDOWN:
4439 switch (event.cbutton.button)
4441 case SDL_CONTROLLER_BUTTON_A:
4442 case SDL_CONTROLLER_BUTTON_X:
4443 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4444 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4448 case SDL_CONTROLLER_BUTTON_B:
4449 case SDL_CONTROLLER_BUTTON_Y:
4450 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4451 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4452 case SDL_CONTROLLER_BUTTON_BACK:
4457 if (req_state & REQ_PLAYER)
4459 int old_player_nr = setup.network_player_nr;
4462 result = old_player_nr + 1;
4464 switch (event.cbutton.button)
4466 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4467 case SDL_CONTROLLER_BUTTON_Y:
4471 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4472 case SDL_CONTROLLER_BUTTON_B:
4476 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4477 case SDL_CONTROLLER_BUTTON_A:
4481 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4482 case SDL_CONTROLLER_BUTTON_X:
4493 case SDL_CONTROLLERBUTTONUP:
4494 HandleJoystickEvent(&event);
4495 ClearPlayerAction();
4500 HandleOtherEvents(&event);
4505 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4507 int joy = AnyJoystick();
4509 if (joy & JOY_BUTTON_1)
4511 else if (joy & JOY_BUTTON_2)
4514 else if (AnyJoystick())
4516 int joy = AnyJoystick();
4518 if (req_state & REQ_PLAYER)
4522 else if (joy & JOY_RIGHT)
4524 else if (joy & JOY_DOWN)
4526 else if (joy & JOY_LEFT)
4533 if (global.use_envelope_request)
4535 /* copy back current state of pressed buttons inside request area */
4536 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4546 static boolean RequestDoor(char *text, unsigned int req_state)
4548 unsigned int old_door_state;
4549 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4550 int font_nr = FONT_TEXT_2;
4555 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4557 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4558 font_nr = FONT_TEXT_1;
4561 if (game_status == GAME_MODE_PLAYING)
4562 BlitScreenToBitmap(backbuffer);
4564 /* disable deactivated drawing when quick-loading level tape recording */
4565 if (tape.playing && tape.deactivate_display)
4566 TapeDeactivateDisplayOff(TRUE);
4568 SetMouseCursor(CURSOR_DEFAULT);
4570 /* pause network game while waiting for request to answer */
4571 if (network.enabled &&
4572 game_status == GAME_MODE_PLAYING &&
4573 req_state & REQUEST_WAIT_FOR_INPUT)
4574 SendToServer_PausePlaying();
4576 old_door_state = GetDoorState();
4578 /* simulate releasing mouse button over last gadget, if still pressed */
4580 HandleGadgets(-1, -1, 0);
4584 /* draw released gadget before proceeding */
4587 if (old_door_state & DOOR_OPEN_1)
4589 CloseDoor(DOOR_CLOSE_1);
4591 /* save old door content */
4592 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4593 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4596 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4597 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4599 /* clear door drawing field */
4600 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4602 /* force DOOR font inside door area */
4603 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4605 /* write text for request */
4606 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4608 char text_line[max_request_line_len + 1];
4614 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4616 tc = *(text_ptr + tx);
4617 // if (!tc || tc == ' ')
4618 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4622 if ((tc == '?' || tc == '!') && tl == 0)
4632 strncpy(text_line, text_ptr, tl);
4635 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4636 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4637 text_line, font_nr);
4639 text_ptr += tl + (tc == ' ' ? 1 : 0);
4640 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4645 if (req_state & REQ_ASK)
4647 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4648 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4650 else if (req_state & REQ_CONFIRM)
4652 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4654 else if (req_state & REQ_PLAYER)
4656 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4657 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4658 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4659 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4662 /* copy request gadgets to door backbuffer */
4663 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4665 OpenDoor(DOOR_OPEN_1);
4667 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4669 if (game_status == GAME_MODE_PLAYING)
4671 SetPanelBackground();
4672 SetDrawBackgroundMask(REDRAW_DOOR_1);
4676 SetDrawBackgroundMask(REDRAW_FIELD);
4682 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4684 // ---------- handle request buttons ----------
4685 result = RequestHandleEvents(req_state);
4689 if (!(req_state & REQ_STAY_OPEN))
4691 CloseDoor(DOOR_CLOSE_1);
4693 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4694 (req_state & REQ_REOPEN))
4695 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4700 if (game_status == GAME_MODE_PLAYING)
4702 SetPanelBackground();
4703 SetDrawBackgroundMask(REDRAW_DOOR_1);
4707 SetDrawBackgroundMask(REDRAW_FIELD);
4710 /* continue network game after request */
4711 if (network.enabled &&
4712 game_status == GAME_MODE_PLAYING &&
4713 req_state & REQUEST_WAIT_FOR_INPUT)
4714 SendToServer_ContinuePlaying();
4716 /* restore deactivated drawing when quick-loading level tape recording */
4717 if (tape.playing && tape.deactivate_display)
4718 TapeDeactivateDisplayOn();
4723 static boolean RequestEnvelope(char *text, unsigned int req_state)
4727 if (game_status == GAME_MODE_PLAYING)
4728 BlitScreenToBitmap(backbuffer);
4730 /* disable deactivated drawing when quick-loading level tape recording */
4731 if (tape.playing && tape.deactivate_display)
4732 TapeDeactivateDisplayOff(TRUE);
4734 SetMouseCursor(CURSOR_DEFAULT);
4736 /* pause network game while waiting for request to answer */
4737 if (network.enabled &&
4738 game_status == GAME_MODE_PLAYING &&
4739 req_state & REQUEST_WAIT_FOR_INPUT)
4740 SendToServer_PausePlaying();
4742 /* simulate releasing mouse button over last gadget, if still pressed */
4744 HandleGadgets(-1, -1, 0);
4748 // (replace with setting corresponding request background)
4749 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4750 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4752 /* clear door drawing field */
4753 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4755 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4757 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4759 if (game_status == GAME_MODE_PLAYING)
4761 SetPanelBackground();
4762 SetDrawBackgroundMask(REDRAW_DOOR_1);
4766 SetDrawBackgroundMask(REDRAW_FIELD);
4772 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4774 // ---------- handle request buttons ----------
4775 result = RequestHandleEvents(req_state);
4779 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4783 if (game_status == GAME_MODE_PLAYING)
4785 SetPanelBackground();
4786 SetDrawBackgroundMask(REDRAW_DOOR_1);
4790 SetDrawBackgroundMask(REDRAW_FIELD);
4793 /* continue network game after request */
4794 if (network.enabled &&
4795 game_status == GAME_MODE_PLAYING &&
4796 req_state & REQUEST_WAIT_FOR_INPUT)
4797 SendToServer_ContinuePlaying();
4799 /* restore deactivated drawing when quick-loading level tape recording */
4800 if (tape.playing && tape.deactivate_display)
4801 TapeDeactivateDisplayOn();
4806 boolean Request(char *text, unsigned int req_state)
4808 boolean overlay_active = GetOverlayActive();
4811 SetOverlayActive(FALSE);
4813 if (global.use_envelope_request)
4814 result = RequestEnvelope(text, req_state);
4816 result = RequestDoor(text, req_state);
4818 SetOverlayActive(overlay_active);
4823 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4825 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4826 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4829 if (dpo1->sort_priority != dpo2->sort_priority)
4830 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4832 compare_result = dpo1->nr - dpo2->nr;
4834 return compare_result;
4837 void InitGraphicCompatibilityInfo_Doors()
4843 struct DoorInfo *door;
4847 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4848 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4850 { -1, -1, -1, NULL }
4852 struct Rect door_rect_list[] =
4854 { DX, DY, DXSIZE, DYSIZE },
4855 { VX, VY, VXSIZE, VYSIZE }
4859 for (i = 0; doors[i].door_token != -1; i++)
4861 int door_token = doors[i].door_token;
4862 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4863 int part_1 = doors[i].part_1;
4864 int part_8 = doors[i].part_8;
4865 int part_2 = part_1 + 1;
4866 int part_3 = part_1 + 2;
4867 struct DoorInfo *door = doors[i].door;
4868 struct Rect *door_rect = &door_rect_list[door_index];
4869 boolean door_gfx_redefined = FALSE;
4871 /* check if any door part graphic definitions have been redefined */
4873 for (j = 0; door_part_controls[j].door_token != -1; j++)
4875 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4876 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4878 if (dpc->door_token == door_token && fi->redefined)
4879 door_gfx_redefined = TRUE;
4882 /* check for old-style door graphic/animation modifications */
4884 if (!door_gfx_redefined)
4886 if (door->anim_mode & ANIM_STATIC_PANEL)
4888 door->panel.step_xoffset = 0;
4889 door->panel.step_yoffset = 0;
4892 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4894 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4895 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4896 int num_door_steps, num_panel_steps;
4898 /* remove door part graphics other than the two default wings */
4900 for (j = 0; door_part_controls[j].door_token != -1; j++)
4902 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4903 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4905 if (dpc->graphic >= part_3 &&
4906 dpc->graphic <= part_8)
4910 /* set graphics and screen positions of the default wings */
4912 g_part_1->width = door_rect->width;
4913 g_part_1->height = door_rect->height;
4914 g_part_2->width = door_rect->width;
4915 g_part_2->height = door_rect->height;
4916 g_part_2->src_x = door_rect->width;
4917 g_part_2->src_y = g_part_1->src_y;
4919 door->part_2.x = door->part_1.x;
4920 door->part_2.y = door->part_1.y;
4922 if (door->width != -1)
4924 g_part_1->width = door->width;
4925 g_part_2->width = door->width;
4927 // special treatment for graphics and screen position of right wing
4928 g_part_2->src_x += door_rect->width - door->width;
4929 door->part_2.x += door_rect->width - door->width;
4932 if (door->height != -1)
4934 g_part_1->height = door->height;
4935 g_part_2->height = door->height;
4937 // special treatment for graphics and screen position of bottom wing
4938 g_part_2->src_y += door_rect->height - door->height;
4939 door->part_2.y += door_rect->height - door->height;
4942 /* set animation delays for the default wings and panels */
4944 door->part_1.step_delay = door->step_delay;
4945 door->part_2.step_delay = door->step_delay;
4946 door->panel.step_delay = door->step_delay;
4948 /* set animation draw order for the default wings */
4950 door->part_1.sort_priority = 2; /* draw left wing over ... */
4951 door->part_2.sort_priority = 1; /* ... right wing */
4953 /* set animation draw offset for the default wings */
4955 if (door->anim_mode & ANIM_HORIZONTAL)
4957 door->part_1.step_xoffset = door->step_offset;
4958 door->part_1.step_yoffset = 0;
4959 door->part_2.step_xoffset = door->step_offset * -1;
4960 door->part_2.step_yoffset = 0;
4962 num_door_steps = g_part_1->width / door->step_offset;
4964 else // ANIM_VERTICAL
4966 door->part_1.step_xoffset = 0;
4967 door->part_1.step_yoffset = door->step_offset;
4968 door->part_2.step_xoffset = 0;
4969 door->part_2.step_yoffset = door->step_offset * -1;
4971 num_door_steps = g_part_1->height / door->step_offset;
4974 /* set animation draw offset for the default panels */
4976 if (door->step_offset > 1)
4978 num_panel_steps = 2 * door_rect->height / door->step_offset;
4979 door->panel.start_step = num_panel_steps - num_door_steps;
4980 door->panel.start_step_closing = door->panel.start_step;
4984 num_panel_steps = door_rect->height / door->step_offset;
4985 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4986 door->panel.start_step_closing = door->panel.start_step;
4987 door->panel.step_delay *= 2;
4998 for (i = 0; door_part_controls[i].door_token != -1; i++)
5000 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5001 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5003 /* initialize "start_step_opening" and "start_step_closing", if needed */
5004 if (dpc->pos->start_step_opening == 0 &&
5005 dpc->pos->start_step_closing == 0)
5007 // dpc->pos->start_step_opening = dpc->pos->start_step;
5008 dpc->pos->start_step_closing = dpc->pos->start_step;
5011 /* fill structure for door part draw order (sorted below) */
5013 dpo->sort_priority = dpc->pos->sort_priority;
5016 /* sort door part controls according to sort_priority and graphic number */
5017 qsort(door_part_order, MAX_DOOR_PARTS,
5018 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5021 unsigned int OpenDoor(unsigned int door_state)
5023 if (door_state & DOOR_COPY_BACK)
5025 if (door_state & DOOR_OPEN_1)
5026 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5027 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5029 if (door_state & DOOR_OPEN_2)
5030 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5031 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5033 door_state &= ~DOOR_COPY_BACK;
5036 return MoveDoor(door_state);
5039 unsigned int CloseDoor(unsigned int door_state)
5041 unsigned int old_door_state = GetDoorState();
5043 if (!(door_state & DOOR_NO_COPY_BACK))
5045 if (old_door_state & DOOR_OPEN_1)
5046 BlitBitmap(backbuffer, bitmap_db_door_1,
5047 DX, DY, DXSIZE, DYSIZE, 0, 0);
5049 if (old_door_state & DOOR_OPEN_2)
5050 BlitBitmap(backbuffer, bitmap_db_door_2,
5051 VX, VY, VXSIZE, VYSIZE, 0, 0);
5053 door_state &= ~DOOR_NO_COPY_BACK;
5056 return MoveDoor(door_state);
5059 unsigned int GetDoorState()
5061 return MoveDoor(DOOR_GET_STATE);
5064 unsigned int SetDoorState(unsigned int door_state)
5066 return MoveDoor(door_state | DOOR_SET_STATE);
5069 int euclid(int a, int b)
5071 return (b ? euclid(b, a % b) : a);
5074 unsigned int MoveDoor(unsigned int door_state)
5076 struct Rect door_rect_list[] =
5078 { DX, DY, DXSIZE, DYSIZE },
5079 { VX, VY, VXSIZE, VYSIZE }
5081 static int door1 = DOOR_CLOSE_1;
5082 static int door2 = DOOR_CLOSE_2;
5083 unsigned int door_delay = 0;
5084 unsigned int door_delay_value;
5087 if (door_state == DOOR_GET_STATE)
5088 return (door1 | door2);
5090 if (door_state & DOOR_SET_STATE)
5092 if (door_state & DOOR_ACTION_1)
5093 door1 = door_state & DOOR_ACTION_1;
5094 if (door_state & DOOR_ACTION_2)
5095 door2 = door_state & DOOR_ACTION_2;
5097 return (door1 | door2);
5100 if (!(door_state & DOOR_FORCE_REDRAW))
5102 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5103 door_state &= ~DOOR_OPEN_1;
5104 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5105 door_state &= ~DOOR_CLOSE_1;
5106 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5107 door_state &= ~DOOR_OPEN_2;
5108 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5109 door_state &= ~DOOR_CLOSE_2;
5112 if (global.autoplay_leveldir)
5114 door_state |= DOOR_NO_DELAY;
5115 door_state &= ~DOOR_CLOSE_ALL;
5118 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5119 door_state |= DOOR_NO_DELAY;
5121 if (door_state & DOOR_ACTION)
5123 boolean door_panel_drawn[NUM_DOORS];
5124 boolean panel_has_doors[NUM_DOORS];
5125 boolean door_part_skip[MAX_DOOR_PARTS];
5126 boolean door_part_done[MAX_DOOR_PARTS];
5127 boolean door_part_done_all;
5128 int num_steps[MAX_DOOR_PARTS];
5129 int max_move_delay = 0; // delay for complete animations of all doors
5130 int max_step_delay = 0; // delay (ms) between two animation frames
5131 int num_move_steps = 0; // number of animation steps for all doors
5132 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5133 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5134 int current_move_delay = 0;
5138 for (i = 0; i < NUM_DOORS; i++)
5139 panel_has_doors[i] = FALSE;
5141 for (i = 0; i < MAX_DOOR_PARTS; i++)
5143 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5144 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5145 int door_token = dpc->door_token;
5147 door_part_done[i] = FALSE;
5148 door_part_skip[i] = (!(door_state & door_token) ||
5152 for (i = 0; i < MAX_DOOR_PARTS; i++)
5154 int nr = door_part_order[i].nr;
5155 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5156 struct DoorPartPosInfo *pos = dpc->pos;
5157 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5158 int door_token = dpc->door_token;
5159 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5160 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5161 int step_xoffset = ABS(pos->step_xoffset);
5162 int step_yoffset = ABS(pos->step_yoffset);
5163 int step_delay = pos->step_delay;
5164 int current_door_state = door_state & door_token;
5165 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5166 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5167 boolean part_opening = (is_panel ? door_closing : door_opening);
5168 int start_step = (part_opening ? pos->start_step_opening :
5169 pos->start_step_closing);
5170 float move_xsize = (step_xoffset ? g->width : 0);
5171 float move_ysize = (step_yoffset ? g->height : 0);
5172 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5173 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5174 int move_steps = (move_xsteps && move_ysteps ?
5175 MIN(move_xsteps, move_ysteps) :
5176 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5177 int move_delay = move_steps * step_delay;
5179 if (door_part_skip[nr])
5182 max_move_delay = MAX(max_move_delay, move_delay);
5183 max_step_delay = (max_step_delay == 0 ? step_delay :
5184 euclid(max_step_delay, step_delay));
5185 num_steps[nr] = move_steps;
5189 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5191 panel_has_doors[door_index] = TRUE;
5195 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5197 num_move_steps = max_move_delay / max_step_delay;
5198 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5200 door_delay_value = max_step_delay;
5202 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5204 start = num_move_steps - 1;
5208 /* opening door sound has priority over simultaneously closing door */
5209 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5211 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5213 if (door_state & DOOR_OPEN_1)
5214 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5215 if (door_state & DOOR_OPEN_2)
5216 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5218 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5220 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5222 if (door_state & DOOR_CLOSE_1)
5223 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5224 if (door_state & DOOR_CLOSE_2)
5225 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5229 for (k = start; k < num_move_steps; k++)
5231 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5233 door_part_done_all = TRUE;
5235 for (i = 0; i < NUM_DOORS; i++)
5236 door_panel_drawn[i] = FALSE;
5238 for (i = 0; i < MAX_DOOR_PARTS; i++)
5240 int nr = door_part_order[i].nr;
5241 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5242 struct DoorPartPosInfo *pos = dpc->pos;
5243 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5244 int door_token = dpc->door_token;
5245 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5246 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5247 boolean is_panel_and_door_has_closed = FALSE;
5248 struct Rect *door_rect = &door_rect_list[door_index];
5249 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5251 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5252 int current_door_state = door_state & door_token;
5253 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5254 boolean door_closing = !door_opening;
5255 boolean part_opening = (is_panel ? door_closing : door_opening);
5256 boolean part_closing = !part_opening;
5257 int start_step = (part_opening ? pos->start_step_opening :
5258 pos->start_step_closing);
5259 int step_delay = pos->step_delay;
5260 int step_factor = step_delay / max_step_delay;
5261 int k1 = (step_factor ? k / step_factor + 1 : k);
5262 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5263 int kk = MAX(0, k2);
5266 int src_x, src_y, src_xx, src_yy;
5267 int dst_x, dst_y, dst_xx, dst_yy;
5270 if (door_part_skip[nr])
5273 if (!(door_state & door_token))
5281 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5282 int kk_door = MAX(0, k2_door);
5283 int sync_frame = kk_door * door_delay_value;
5284 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5286 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5287 &g_src_x, &g_src_y);
5292 if (!door_panel_drawn[door_index])
5294 ClearRectangle(drawto, door_rect->x, door_rect->y,
5295 door_rect->width, door_rect->height);
5297 door_panel_drawn[door_index] = TRUE;
5300 // draw opening or closing door parts
5302 if (pos->step_xoffset < 0) // door part on right side
5305 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5308 if (dst_xx + width > door_rect->width)
5309 width = door_rect->width - dst_xx;
5311 else // door part on left side
5314 dst_xx = pos->x - kk * pos->step_xoffset;
5318 src_xx = ABS(dst_xx);
5322 width = g->width - src_xx;
5324 if (width > door_rect->width)
5325 width = door_rect->width;
5327 // printf("::: k == %d [%d] \n", k, start_step);
5330 if (pos->step_yoffset < 0) // door part on bottom side
5333 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5336 if (dst_yy + height > door_rect->height)
5337 height = door_rect->height - dst_yy;
5339 else // door part on top side
5342 dst_yy = pos->y - kk * pos->step_yoffset;
5346 src_yy = ABS(dst_yy);
5350 height = g->height - src_yy;
5353 src_x = g_src_x + src_xx;
5354 src_y = g_src_y + src_yy;
5356 dst_x = door_rect->x + dst_xx;
5357 dst_y = door_rect->y + dst_yy;
5359 is_panel_and_door_has_closed =
5362 panel_has_doors[door_index] &&
5363 k >= num_move_steps_doors_only - 1);
5365 if (width >= 0 && width <= g->width &&
5366 height >= 0 && height <= g->height &&
5367 !is_panel_and_door_has_closed)
5369 if (is_panel || !pos->draw_masked)
5370 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5373 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5377 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5379 if ((part_opening && (width < 0 || height < 0)) ||
5380 (part_closing && (width >= g->width && height >= g->height)))
5381 door_part_done[nr] = TRUE;
5383 // continue door part animations, but not panel after door has closed
5384 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5385 door_part_done_all = FALSE;
5388 if (!(door_state & DOOR_NO_DELAY))
5392 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5394 current_move_delay += max_step_delay;
5396 /* prevent OS (Windows) from complaining about program not responding */
5400 if (door_part_done_all)
5404 if (!(door_state & DOOR_NO_DELAY))
5406 /* wait for specified door action post delay */
5407 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5408 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5409 else if (door_state & DOOR_ACTION_1)
5410 door_delay_value = door_1.post_delay;
5411 else if (door_state & DOOR_ACTION_2)
5412 door_delay_value = door_2.post_delay;
5414 while (!DelayReached(&door_delay, door_delay_value))
5419 if (door_state & DOOR_ACTION_1)
5420 door1 = door_state & DOOR_ACTION_1;
5421 if (door_state & DOOR_ACTION_2)
5422 door2 = door_state & DOOR_ACTION_2;
5424 // draw masked border over door area
5425 DrawMaskedBorder(REDRAW_DOOR_1);
5426 DrawMaskedBorder(REDRAW_DOOR_2);
5430 return (door1 | door2);
5433 static boolean useSpecialEditorDoor()
5435 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5436 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5438 // do not draw special editor door if editor border defined or redefined
5439 if (graphic_info[graphic].bitmap != NULL || redefined)
5442 // do not draw special editor door if global border defined to be empty
5443 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5446 // do not draw special editor door if viewport definitions do not match
5450 EY + EYSIZE != VY + VYSIZE)
5456 void DrawSpecialEditorDoor()
5458 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5459 int top_border_width = gfx1->width;
5460 int top_border_height = gfx1->height;
5461 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5462 int ex = EX - outer_border;
5463 int ey = EY - outer_border;
5464 int vy = VY - outer_border;
5465 int exsize = EXSIZE + 2 * outer_border;
5467 if (!useSpecialEditorDoor())
5470 /* draw bigger level editor toolbox window */
5471 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5472 top_border_width, top_border_height, ex, ey - top_border_height);
5473 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5474 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5476 redraw_mask |= REDRAW_ALL;
5479 void UndrawSpecialEditorDoor()
5481 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5482 int top_border_width = gfx1->width;
5483 int top_border_height = gfx1->height;
5484 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5485 int ex = EX - outer_border;
5486 int ey = EY - outer_border;
5487 int ey_top = ey - top_border_height;
5488 int exsize = EXSIZE + 2 * outer_border;
5489 int eysize = EYSIZE + 2 * outer_border;
5491 if (!useSpecialEditorDoor())
5494 /* draw normal tape recorder window */
5495 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5497 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5498 ex, ey_top, top_border_width, top_border_height,
5500 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5501 ex, ey, exsize, eysize, ex, ey);
5505 // if screen background is set to "[NONE]", clear editor toolbox window
5506 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5507 ClearRectangle(drawto, ex, ey, exsize, eysize);
5510 redraw_mask |= REDRAW_ALL;
5514 /* ---------- new tool button stuff ---------------------------------------- */
5519 struct TextPosInfo *pos;
5522 } toolbutton_info[NUM_TOOL_BUTTONS] =
5525 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5526 TOOL_CTRL_ID_YES, "yes"
5529 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5530 TOOL_CTRL_ID_NO, "no"
5533 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5534 TOOL_CTRL_ID_CONFIRM, "confirm"
5537 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5538 TOOL_CTRL_ID_PLAYER_1, "player 1"
5541 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5542 TOOL_CTRL_ID_PLAYER_2, "player 2"
5545 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5546 TOOL_CTRL_ID_PLAYER_3, "player 3"
5549 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5550 TOOL_CTRL_ID_PLAYER_4, "player 4"
5554 void CreateToolButtons()
5558 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5560 int graphic = toolbutton_info[i].graphic;
5561 struct GraphicInfo *gfx = &graphic_info[graphic];
5562 struct TextPosInfo *pos = toolbutton_info[i].pos;
5563 struct GadgetInfo *gi;
5564 Bitmap *deco_bitmap = None;
5565 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5566 unsigned int event_mask = GD_EVENT_RELEASED;
5569 int gd_x = gfx->src_x;
5570 int gd_y = gfx->src_y;
5571 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5572 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5577 if (global.use_envelope_request)
5579 setRequestPosition(&dx, &dy, TRUE);
5581 // check if request buttons are outside of envelope and fix, if needed
5582 if (x < 0 || x + gfx->width > request.width ||
5583 y < 0 || y + gfx->height > request.height)
5585 if (id == TOOL_CTRL_ID_YES)
5588 y = request.height - 2 * request.border_size - gfx->height;
5590 else if (id == TOOL_CTRL_ID_NO)
5592 x = request.width - 2 * request.border_size - gfx->width;
5593 y = request.height - 2 * request.border_size - gfx->height;
5595 else if (id == TOOL_CTRL_ID_CONFIRM)
5597 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5598 y = request.height - 2 * request.border_size - gfx->height;
5600 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5602 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5604 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5605 y = request.height - 2 * request.border_size - gfx->height * 2;
5607 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5608 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5613 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5615 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5617 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5618 pos->size, &deco_bitmap, &deco_x, &deco_y);
5619 deco_xpos = (gfx->width - pos->size) / 2;
5620 deco_ypos = (gfx->height - pos->size) / 2;
5623 gi = CreateGadget(GDI_CUSTOM_ID, id,
5624 GDI_IMAGE_ID, graphic,
5625 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5628 GDI_WIDTH, gfx->width,
5629 GDI_HEIGHT, gfx->height,
5630 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5631 GDI_STATE, GD_BUTTON_UNPRESSED,
5632 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5633 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5634 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5635 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5636 GDI_DECORATION_SIZE, pos->size, pos->size,
5637 GDI_DECORATION_SHIFTING, 1, 1,
5638 GDI_DIRECT_DRAW, FALSE,
5639 GDI_EVENT_MASK, event_mask,
5640 GDI_CALLBACK_ACTION, HandleToolButtons,
5644 Error(ERR_EXIT, "cannot create gadget");
5646 tool_gadget[id] = gi;
5650 void FreeToolButtons()
5654 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5655 FreeGadget(tool_gadget[i]);
5658 static void UnmapToolButtons()
5662 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5663 UnmapGadget(tool_gadget[i]);
5666 static void HandleToolButtons(struct GadgetInfo *gi)
5668 request_gadget_id = gi->custom_id;
5671 static struct Mapping_EM_to_RND_object
5674 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5675 boolean is_backside; /* backside of moving element */
5681 em_object_mapping_list[] =
5684 Xblank, TRUE, FALSE,
5688 Yacid_splash_eB, FALSE, FALSE,
5689 EL_ACID_SPLASH_RIGHT, -1, -1
5692 Yacid_splash_wB, FALSE, FALSE,
5693 EL_ACID_SPLASH_LEFT, -1, -1
5696 #ifdef EM_ENGINE_BAD_ROLL
5698 Xstone_force_e, FALSE, FALSE,
5699 EL_ROCK, -1, MV_BIT_RIGHT
5702 Xstone_force_w, FALSE, FALSE,
5703 EL_ROCK, -1, MV_BIT_LEFT
5706 Xnut_force_e, FALSE, FALSE,
5707 EL_NUT, -1, MV_BIT_RIGHT
5710 Xnut_force_w, FALSE, FALSE,
5711 EL_NUT, -1, MV_BIT_LEFT
5714 Xspring_force_e, FALSE, FALSE,
5715 EL_SPRING, -1, MV_BIT_RIGHT
5718 Xspring_force_w, FALSE, FALSE,
5719 EL_SPRING, -1, MV_BIT_LEFT
5722 Xemerald_force_e, FALSE, FALSE,
5723 EL_EMERALD, -1, MV_BIT_RIGHT
5726 Xemerald_force_w, FALSE, FALSE,
5727 EL_EMERALD, -1, MV_BIT_LEFT
5730 Xdiamond_force_e, FALSE, FALSE,
5731 EL_DIAMOND, -1, MV_BIT_RIGHT
5734 Xdiamond_force_w, FALSE, FALSE,
5735 EL_DIAMOND, -1, MV_BIT_LEFT
5738 Xbomb_force_e, FALSE, FALSE,
5739 EL_BOMB, -1, MV_BIT_RIGHT
5742 Xbomb_force_w, FALSE, FALSE,
5743 EL_BOMB, -1, MV_BIT_LEFT
5745 #endif /* EM_ENGINE_BAD_ROLL */
5748 Xstone, TRUE, FALSE,
5752 Xstone_pause, FALSE, FALSE,
5756 Xstone_fall, FALSE, FALSE,
5760 Ystone_s, FALSE, FALSE,
5761 EL_ROCK, ACTION_FALLING, -1
5764 Ystone_sB, FALSE, TRUE,
5765 EL_ROCK, ACTION_FALLING, -1
5768 Ystone_e, FALSE, FALSE,
5769 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5772 Ystone_eB, FALSE, TRUE,
5773 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5776 Ystone_w, FALSE, FALSE,
5777 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5780 Ystone_wB, FALSE, TRUE,
5781 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5788 Xnut_pause, FALSE, FALSE,
5792 Xnut_fall, FALSE, FALSE,
5796 Ynut_s, FALSE, FALSE,
5797 EL_NUT, ACTION_FALLING, -1
5800 Ynut_sB, FALSE, TRUE,
5801 EL_NUT, ACTION_FALLING, -1
5804 Ynut_e, FALSE, FALSE,
5805 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5808 Ynut_eB, FALSE, TRUE,
5809 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5812 Ynut_w, FALSE, FALSE,
5813 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5816 Ynut_wB, FALSE, TRUE,
5817 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5820 Xbug_n, TRUE, FALSE,
5824 Xbug_e, TRUE, FALSE,
5825 EL_BUG_RIGHT, -1, -1
5828 Xbug_s, TRUE, FALSE,
5832 Xbug_w, TRUE, FALSE,
5836 Xbug_gon, FALSE, FALSE,
5840 Xbug_goe, FALSE, FALSE,
5841 EL_BUG_RIGHT, -1, -1
5844 Xbug_gos, FALSE, FALSE,
5848 Xbug_gow, FALSE, FALSE,
5852 Ybug_n, FALSE, FALSE,
5853 EL_BUG, ACTION_MOVING, MV_BIT_UP
5856 Ybug_nB, FALSE, TRUE,
5857 EL_BUG, ACTION_MOVING, MV_BIT_UP
5860 Ybug_e, FALSE, FALSE,
5861 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5864 Ybug_eB, FALSE, TRUE,
5865 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5868 Ybug_s, FALSE, FALSE,
5869 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5872 Ybug_sB, FALSE, TRUE,
5873 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5876 Ybug_w, FALSE, FALSE,
5877 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5880 Ybug_wB, FALSE, TRUE,
5881 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5884 Ybug_w_n, FALSE, FALSE,
5885 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5888 Ybug_n_e, FALSE, FALSE,
5889 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5892 Ybug_e_s, FALSE, FALSE,
5893 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5896 Ybug_s_w, FALSE, FALSE,
5897 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5900 Ybug_e_n, FALSE, FALSE,
5901 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5904 Ybug_s_e, FALSE, FALSE,
5905 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5908 Ybug_w_s, FALSE, FALSE,
5909 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5912 Ybug_n_w, FALSE, FALSE,
5913 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5916 Ybug_stone, FALSE, FALSE,
5917 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5920 Ybug_spring, FALSE, FALSE,
5921 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5924 Xtank_n, TRUE, FALSE,
5925 EL_SPACESHIP_UP, -1, -1
5928 Xtank_e, TRUE, FALSE,
5929 EL_SPACESHIP_RIGHT, -1, -1
5932 Xtank_s, TRUE, FALSE,
5933 EL_SPACESHIP_DOWN, -1, -1
5936 Xtank_w, TRUE, FALSE,
5937 EL_SPACESHIP_LEFT, -1, -1
5940 Xtank_gon, FALSE, FALSE,
5941 EL_SPACESHIP_UP, -1, -1
5944 Xtank_goe, FALSE, FALSE,
5945 EL_SPACESHIP_RIGHT, -1, -1
5948 Xtank_gos, FALSE, FALSE,
5949 EL_SPACESHIP_DOWN, -1, -1
5952 Xtank_gow, FALSE, FALSE,
5953 EL_SPACESHIP_LEFT, -1, -1
5956 Ytank_n, FALSE, FALSE,
5957 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5960 Ytank_nB, FALSE, TRUE,
5961 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5964 Ytank_e, FALSE, FALSE,
5965 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5968 Ytank_eB, FALSE, TRUE,
5969 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5972 Ytank_s, FALSE, FALSE,
5973 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5976 Ytank_sB, FALSE, TRUE,
5977 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5980 Ytank_w, FALSE, FALSE,
5981 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5984 Ytank_wB, FALSE, TRUE,
5985 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5988 Ytank_w_n, FALSE, FALSE,
5989 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5992 Ytank_n_e, FALSE, FALSE,
5993 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5996 Ytank_e_s, FALSE, FALSE,
5997 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6000 Ytank_s_w, FALSE, FALSE,
6001 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6004 Ytank_e_n, FALSE, FALSE,
6005 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6008 Ytank_s_e, FALSE, FALSE,
6009 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6012 Ytank_w_s, FALSE, FALSE,
6013 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6016 Ytank_n_w, FALSE, FALSE,
6017 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6020 Ytank_stone, FALSE, FALSE,
6021 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6024 Ytank_spring, FALSE, FALSE,
6025 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6028 Xandroid, TRUE, FALSE,
6029 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6032 Xandroid_1_n, FALSE, FALSE,
6033 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6036 Xandroid_2_n, FALSE, FALSE,
6037 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6040 Xandroid_1_e, FALSE, FALSE,
6041 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6044 Xandroid_2_e, FALSE, FALSE,
6045 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6048 Xandroid_1_w, FALSE, FALSE,
6049 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6052 Xandroid_2_w, FALSE, FALSE,
6053 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6056 Xandroid_1_s, FALSE, FALSE,
6057 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6060 Xandroid_2_s, FALSE, FALSE,
6061 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6064 Yandroid_n, FALSE, FALSE,
6065 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6068 Yandroid_nB, FALSE, TRUE,
6069 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6072 Yandroid_ne, FALSE, FALSE,
6073 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6076 Yandroid_neB, FALSE, TRUE,
6077 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6080 Yandroid_e, FALSE, FALSE,
6081 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6084 Yandroid_eB, FALSE, TRUE,
6085 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6088 Yandroid_se, FALSE, FALSE,
6089 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6092 Yandroid_seB, FALSE, TRUE,
6093 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6096 Yandroid_s, FALSE, FALSE,
6097 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6100 Yandroid_sB, FALSE, TRUE,
6101 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6104 Yandroid_sw, FALSE, FALSE,
6105 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6108 Yandroid_swB, FALSE, TRUE,
6109 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6112 Yandroid_w, FALSE, FALSE,
6113 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6116 Yandroid_wB, FALSE, TRUE,
6117 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6120 Yandroid_nw, FALSE, FALSE,
6121 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6124 Yandroid_nwB, FALSE, TRUE,
6125 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6128 Xspring, TRUE, FALSE,
6132 Xspring_pause, FALSE, FALSE,
6136 Xspring_e, FALSE, FALSE,
6140 Xspring_w, FALSE, FALSE,
6144 Xspring_fall, FALSE, FALSE,
6148 Yspring_s, FALSE, FALSE,
6149 EL_SPRING, ACTION_FALLING, -1
6152 Yspring_sB, FALSE, TRUE,
6153 EL_SPRING, ACTION_FALLING, -1
6156 Yspring_e, FALSE, FALSE,
6157 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6160 Yspring_eB, FALSE, TRUE,
6161 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6164 Yspring_w, FALSE, FALSE,
6165 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6168 Yspring_wB, FALSE, TRUE,
6169 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6172 Yspring_kill_e, FALSE, FALSE,
6173 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6176 Yspring_kill_eB, FALSE, TRUE,
6177 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6180 Yspring_kill_w, FALSE, FALSE,
6181 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6184 Yspring_kill_wB, FALSE, TRUE,
6185 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6188 Xeater_n, TRUE, FALSE,
6189 EL_YAMYAM_UP, -1, -1
6192 Xeater_e, TRUE, FALSE,
6193 EL_YAMYAM_RIGHT, -1, -1
6196 Xeater_w, TRUE, FALSE,
6197 EL_YAMYAM_LEFT, -1, -1
6200 Xeater_s, TRUE, FALSE,
6201 EL_YAMYAM_DOWN, -1, -1
6204 Yeater_n, FALSE, FALSE,
6205 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6208 Yeater_nB, FALSE, TRUE,
6209 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6212 Yeater_e, FALSE, FALSE,
6213 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6216 Yeater_eB, FALSE, TRUE,
6217 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6220 Yeater_s, FALSE, FALSE,
6221 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6224 Yeater_sB, FALSE, TRUE,
6225 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6228 Yeater_w, FALSE, FALSE,
6229 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6232 Yeater_wB, FALSE, TRUE,
6233 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6236 Yeater_stone, FALSE, FALSE,
6237 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6240 Yeater_spring, FALSE, FALSE,
6241 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6244 Xalien, TRUE, FALSE,
6248 Xalien_pause, FALSE, FALSE,
6252 Yalien_n, FALSE, FALSE,
6253 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6256 Yalien_nB, FALSE, TRUE,
6257 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6260 Yalien_e, FALSE, FALSE,
6261 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6264 Yalien_eB, FALSE, TRUE,
6265 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6268 Yalien_s, FALSE, FALSE,
6269 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6272 Yalien_sB, FALSE, TRUE,
6273 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6276 Yalien_w, FALSE, FALSE,
6277 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6280 Yalien_wB, FALSE, TRUE,
6281 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6284 Yalien_stone, FALSE, FALSE,
6285 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6288 Yalien_spring, FALSE, FALSE,
6289 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6292 Xemerald, TRUE, FALSE,
6296 Xemerald_pause, FALSE, FALSE,
6300 Xemerald_fall, FALSE, FALSE,
6304 Xemerald_shine, FALSE, FALSE,
6305 EL_EMERALD, ACTION_TWINKLING, -1
6308 Yemerald_s, FALSE, FALSE,
6309 EL_EMERALD, ACTION_FALLING, -1
6312 Yemerald_sB, FALSE, TRUE,
6313 EL_EMERALD, ACTION_FALLING, -1
6316 Yemerald_e, FALSE, FALSE,
6317 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6320 Yemerald_eB, FALSE, TRUE,
6321 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6324 Yemerald_w, FALSE, FALSE,
6325 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6328 Yemerald_wB, FALSE, TRUE,
6329 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6332 Yemerald_eat, FALSE, FALSE,
6333 EL_EMERALD, ACTION_COLLECTING, -1
6336 Yemerald_stone, FALSE, FALSE,
6337 EL_NUT, ACTION_BREAKING, -1
6340 Xdiamond, TRUE, FALSE,
6344 Xdiamond_pause, FALSE, FALSE,
6348 Xdiamond_fall, FALSE, FALSE,
6352 Xdiamond_shine, FALSE, FALSE,
6353 EL_DIAMOND, ACTION_TWINKLING, -1
6356 Ydiamond_s, FALSE, FALSE,
6357 EL_DIAMOND, ACTION_FALLING, -1
6360 Ydiamond_sB, FALSE, TRUE,
6361 EL_DIAMOND, ACTION_FALLING, -1
6364 Ydiamond_e, FALSE, FALSE,
6365 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6368 Ydiamond_eB, FALSE, TRUE,
6369 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6372 Ydiamond_w, FALSE, FALSE,
6373 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6376 Ydiamond_wB, FALSE, TRUE,
6377 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6380 Ydiamond_eat, FALSE, FALSE,
6381 EL_DIAMOND, ACTION_COLLECTING, -1
6384 Ydiamond_stone, FALSE, FALSE,
6385 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6388 Xdrip_fall, TRUE, FALSE,
6389 EL_AMOEBA_DROP, -1, -1
6392 Xdrip_stretch, FALSE, FALSE,
6393 EL_AMOEBA_DROP, ACTION_FALLING, -1
6396 Xdrip_stretchB, FALSE, TRUE,
6397 EL_AMOEBA_DROP, ACTION_FALLING, -1
6400 Xdrip_eat, FALSE, FALSE,
6401 EL_AMOEBA_DROP, ACTION_GROWING, -1
6404 Ydrip_s1, FALSE, FALSE,
6405 EL_AMOEBA_DROP, ACTION_FALLING, -1
6408 Ydrip_s1B, FALSE, TRUE,
6409 EL_AMOEBA_DROP, ACTION_FALLING, -1
6412 Ydrip_s2, FALSE, FALSE,
6413 EL_AMOEBA_DROP, ACTION_FALLING, -1
6416 Ydrip_s2B, FALSE, TRUE,
6417 EL_AMOEBA_DROP, ACTION_FALLING, -1
6424 Xbomb_pause, FALSE, FALSE,
6428 Xbomb_fall, FALSE, FALSE,
6432 Ybomb_s, FALSE, FALSE,
6433 EL_BOMB, ACTION_FALLING, -1
6436 Ybomb_sB, FALSE, TRUE,
6437 EL_BOMB, ACTION_FALLING, -1
6440 Ybomb_e, FALSE, FALSE,
6441 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6444 Ybomb_eB, FALSE, TRUE,
6445 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6448 Ybomb_w, FALSE, FALSE,
6449 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6452 Ybomb_wB, FALSE, TRUE,
6453 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6456 Ybomb_eat, FALSE, FALSE,
6457 EL_BOMB, ACTION_ACTIVATING, -1
6460 Xballoon, TRUE, FALSE,
6464 Yballoon_n, FALSE, FALSE,
6465 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6468 Yballoon_nB, FALSE, TRUE,
6469 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6472 Yballoon_e, FALSE, FALSE,
6473 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6476 Yballoon_eB, FALSE, TRUE,
6477 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6480 Yballoon_s, FALSE, FALSE,
6481 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6484 Yballoon_sB, FALSE, TRUE,
6485 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6488 Yballoon_w, FALSE, FALSE,
6489 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6492 Yballoon_wB, FALSE, TRUE,
6493 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6496 Xgrass, TRUE, FALSE,
6497 EL_EMC_GRASS, -1, -1
6500 Ygrass_nB, FALSE, FALSE,
6501 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6504 Ygrass_eB, FALSE, FALSE,
6505 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6508 Ygrass_sB, FALSE, FALSE,
6509 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6512 Ygrass_wB, FALSE, FALSE,
6513 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6520 Ydirt_nB, FALSE, FALSE,
6521 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6524 Ydirt_eB, FALSE, FALSE,
6525 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6528 Ydirt_sB, FALSE, FALSE,
6529 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6532 Ydirt_wB, FALSE, FALSE,
6533 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6536 Xacid_ne, TRUE, FALSE,
6537 EL_ACID_POOL_TOPRIGHT, -1, -1
6540 Xacid_se, TRUE, FALSE,
6541 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6544 Xacid_s, TRUE, FALSE,
6545 EL_ACID_POOL_BOTTOM, -1, -1
6548 Xacid_sw, TRUE, FALSE,
6549 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6552 Xacid_nw, TRUE, FALSE,
6553 EL_ACID_POOL_TOPLEFT, -1, -1
6556 Xacid_1, TRUE, FALSE,
6560 Xacid_2, FALSE, FALSE,
6564 Xacid_3, FALSE, FALSE,
6568 Xacid_4, FALSE, FALSE,
6572 Xacid_5, FALSE, FALSE,
6576 Xacid_6, FALSE, FALSE,
6580 Xacid_7, FALSE, FALSE,
6584 Xacid_8, FALSE, FALSE,
6588 Xball_1, TRUE, FALSE,
6589 EL_EMC_MAGIC_BALL, -1, -1
6592 Xball_1B, FALSE, FALSE,
6593 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6596 Xball_2, FALSE, FALSE,
6597 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6600 Xball_2B, FALSE, FALSE,
6601 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6604 Yball_eat, FALSE, FALSE,
6605 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6608 Ykey_1_eat, FALSE, FALSE,
6609 EL_EM_KEY_1, ACTION_COLLECTING, -1
6612 Ykey_2_eat, FALSE, FALSE,
6613 EL_EM_KEY_2, ACTION_COLLECTING, -1
6616 Ykey_3_eat, FALSE, FALSE,
6617 EL_EM_KEY_3, ACTION_COLLECTING, -1
6620 Ykey_4_eat, FALSE, FALSE,
6621 EL_EM_KEY_4, ACTION_COLLECTING, -1
6624 Ykey_5_eat, FALSE, FALSE,
6625 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6628 Ykey_6_eat, FALSE, FALSE,
6629 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6632 Ykey_7_eat, FALSE, FALSE,
6633 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6636 Ykey_8_eat, FALSE, FALSE,
6637 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6640 Ylenses_eat, FALSE, FALSE,
6641 EL_EMC_LENSES, ACTION_COLLECTING, -1
6644 Ymagnify_eat, FALSE, FALSE,
6645 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6648 Ygrass_eat, FALSE, FALSE,
6649 EL_EMC_GRASS, ACTION_SNAPPING, -1
6652 Ydirt_eat, FALSE, FALSE,
6653 EL_SAND, ACTION_SNAPPING, -1
6656 Xgrow_ns, TRUE, FALSE,
6657 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6660 Ygrow_ns_eat, FALSE, FALSE,
6661 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6664 Xgrow_ew, TRUE, FALSE,
6665 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6668 Ygrow_ew_eat, FALSE, FALSE,
6669 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6672 Xwonderwall, TRUE, FALSE,
6673 EL_MAGIC_WALL, -1, -1
6676 XwonderwallB, FALSE, FALSE,
6677 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6680 Xamoeba_1, TRUE, FALSE,
6681 EL_AMOEBA_DRY, ACTION_OTHER, -1
6684 Xamoeba_2, FALSE, FALSE,
6685 EL_AMOEBA_DRY, ACTION_OTHER, -1
6688 Xamoeba_3, FALSE, FALSE,
6689 EL_AMOEBA_DRY, ACTION_OTHER, -1
6692 Xamoeba_4, FALSE, FALSE,
6693 EL_AMOEBA_DRY, ACTION_OTHER, -1
6696 Xamoeba_5, TRUE, FALSE,
6697 EL_AMOEBA_WET, ACTION_OTHER, -1
6700 Xamoeba_6, FALSE, FALSE,
6701 EL_AMOEBA_WET, ACTION_OTHER, -1
6704 Xamoeba_7, FALSE, FALSE,
6705 EL_AMOEBA_WET, ACTION_OTHER, -1
6708 Xamoeba_8, FALSE, FALSE,
6709 EL_AMOEBA_WET, ACTION_OTHER, -1
6712 Xdoor_1, TRUE, FALSE,
6713 EL_EM_GATE_1, -1, -1
6716 Xdoor_2, TRUE, FALSE,
6717 EL_EM_GATE_2, -1, -1
6720 Xdoor_3, TRUE, FALSE,
6721 EL_EM_GATE_3, -1, -1
6724 Xdoor_4, TRUE, FALSE,
6725 EL_EM_GATE_4, -1, -1
6728 Xdoor_5, TRUE, FALSE,
6729 EL_EMC_GATE_5, -1, -1
6732 Xdoor_6, TRUE, FALSE,
6733 EL_EMC_GATE_6, -1, -1
6736 Xdoor_7, TRUE, FALSE,
6737 EL_EMC_GATE_7, -1, -1
6740 Xdoor_8, TRUE, FALSE,
6741 EL_EMC_GATE_8, -1, -1
6744 Xkey_1, TRUE, FALSE,
6748 Xkey_2, TRUE, FALSE,
6752 Xkey_3, TRUE, FALSE,
6756 Xkey_4, TRUE, FALSE,
6760 Xkey_5, TRUE, FALSE,
6761 EL_EMC_KEY_5, -1, -1
6764 Xkey_6, TRUE, FALSE,
6765 EL_EMC_KEY_6, -1, -1
6768 Xkey_7, TRUE, FALSE,
6769 EL_EMC_KEY_7, -1, -1
6772 Xkey_8, TRUE, FALSE,
6773 EL_EMC_KEY_8, -1, -1
6776 Xwind_n, TRUE, FALSE,
6777 EL_BALLOON_SWITCH_UP, -1, -1
6780 Xwind_e, TRUE, FALSE,
6781 EL_BALLOON_SWITCH_RIGHT, -1, -1
6784 Xwind_s, TRUE, FALSE,
6785 EL_BALLOON_SWITCH_DOWN, -1, -1
6788 Xwind_w, TRUE, FALSE,
6789 EL_BALLOON_SWITCH_LEFT, -1, -1
6792 Xwind_nesw, TRUE, FALSE,
6793 EL_BALLOON_SWITCH_ANY, -1, -1
6796 Xwind_stop, TRUE, FALSE,
6797 EL_BALLOON_SWITCH_NONE, -1, -1
6801 EL_EM_EXIT_CLOSED, -1, -1
6804 Xexit_1, TRUE, FALSE,
6805 EL_EM_EXIT_OPEN, -1, -1
6808 Xexit_2, FALSE, FALSE,
6809 EL_EM_EXIT_OPEN, -1, -1
6812 Xexit_3, FALSE, FALSE,
6813 EL_EM_EXIT_OPEN, -1, -1
6816 Xdynamite, TRUE, FALSE,
6817 EL_EM_DYNAMITE, -1, -1
6820 Ydynamite_eat, FALSE, FALSE,
6821 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6824 Xdynamite_1, TRUE, FALSE,
6825 EL_EM_DYNAMITE_ACTIVE, -1, -1
6828 Xdynamite_2, FALSE, FALSE,
6829 EL_EM_DYNAMITE_ACTIVE, -1, -1
6832 Xdynamite_3, FALSE, FALSE,
6833 EL_EM_DYNAMITE_ACTIVE, -1, -1
6836 Xdynamite_4, FALSE, FALSE,
6837 EL_EM_DYNAMITE_ACTIVE, -1, -1
6840 Xbumper, TRUE, FALSE,
6841 EL_EMC_SPRING_BUMPER, -1, -1
6844 XbumperB, FALSE, FALSE,
6845 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6848 Xwheel, TRUE, FALSE,
6849 EL_ROBOT_WHEEL, -1, -1
6852 XwheelB, FALSE, FALSE,
6853 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6856 Xswitch, TRUE, FALSE,
6857 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6860 XswitchB, FALSE, FALSE,
6861 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6865 EL_QUICKSAND_EMPTY, -1, -1
6868 Xsand_stone, TRUE, FALSE,
6869 EL_QUICKSAND_FULL, -1, -1
6872 Xsand_stonein_1, FALSE, TRUE,
6873 EL_ROCK, ACTION_FILLING, -1
6876 Xsand_stonein_2, FALSE, TRUE,
6877 EL_ROCK, ACTION_FILLING, -1
6880 Xsand_stonein_3, FALSE, TRUE,
6881 EL_ROCK, ACTION_FILLING, -1
6884 Xsand_stonein_4, FALSE, TRUE,
6885 EL_ROCK, ACTION_FILLING, -1
6888 Xsand_stonesand_1, FALSE, FALSE,
6889 EL_QUICKSAND_EMPTYING, -1, -1
6892 Xsand_stonesand_2, FALSE, FALSE,
6893 EL_QUICKSAND_EMPTYING, -1, -1
6896 Xsand_stonesand_3, FALSE, FALSE,
6897 EL_QUICKSAND_EMPTYING, -1, -1
6900 Xsand_stonesand_4, FALSE, FALSE,
6901 EL_QUICKSAND_EMPTYING, -1, -1
6904 Xsand_stonesand_quickout_1, FALSE, FALSE,
6905 EL_QUICKSAND_EMPTYING, -1, -1
6908 Xsand_stonesand_quickout_2, FALSE, FALSE,
6909 EL_QUICKSAND_EMPTYING, -1, -1
6912 Xsand_stoneout_1, FALSE, FALSE,
6913 EL_ROCK, ACTION_EMPTYING, -1
6916 Xsand_stoneout_2, FALSE, FALSE,
6917 EL_ROCK, ACTION_EMPTYING, -1
6920 Xsand_sandstone_1, FALSE, FALSE,
6921 EL_QUICKSAND_FILLING, -1, -1
6924 Xsand_sandstone_2, FALSE, FALSE,
6925 EL_QUICKSAND_FILLING, -1, -1
6928 Xsand_sandstone_3, FALSE, FALSE,
6929 EL_QUICKSAND_FILLING, -1, -1
6932 Xsand_sandstone_4, FALSE, FALSE,
6933 EL_QUICKSAND_FILLING, -1, -1
6936 Xplant, TRUE, FALSE,
6937 EL_EMC_PLANT, -1, -1
6940 Yplant, FALSE, FALSE,
6941 EL_EMC_PLANT, -1, -1
6944 Xlenses, TRUE, FALSE,
6945 EL_EMC_LENSES, -1, -1
6948 Xmagnify, TRUE, FALSE,
6949 EL_EMC_MAGNIFIER, -1, -1
6952 Xdripper, TRUE, FALSE,
6953 EL_EMC_DRIPPER, -1, -1
6956 XdripperB, FALSE, FALSE,
6957 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6960 Xfake_blank, TRUE, FALSE,
6961 EL_INVISIBLE_WALL, -1, -1
6964 Xfake_blankB, FALSE, FALSE,
6965 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6968 Xfake_grass, TRUE, FALSE,
6969 EL_EMC_FAKE_GRASS, -1, -1
6972 Xfake_grassB, FALSE, FALSE,
6973 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6976 Xfake_door_1, TRUE, FALSE,
6977 EL_EM_GATE_1_GRAY, -1, -1
6980 Xfake_door_2, TRUE, FALSE,
6981 EL_EM_GATE_2_GRAY, -1, -1
6984 Xfake_door_3, TRUE, FALSE,
6985 EL_EM_GATE_3_GRAY, -1, -1
6988 Xfake_door_4, TRUE, FALSE,
6989 EL_EM_GATE_4_GRAY, -1, -1
6992 Xfake_door_5, TRUE, FALSE,
6993 EL_EMC_GATE_5_GRAY, -1, -1
6996 Xfake_door_6, TRUE, FALSE,
6997 EL_EMC_GATE_6_GRAY, -1, -1
7000 Xfake_door_7, TRUE, FALSE,
7001 EL_EMC_GATE_7_GRAY, -1, -1
7004 Xfake_door_8, TRUE, FALSE,
7005 EL_EMC_GATE_8_GRAY, -1, -1
7008 Xfake_acid_1, TRUE, FALSE,
7009 EL_EMC_FAKE_ACID, -1, -1
7012 Xfake_acid_2, FALSE, FALSE,
7013 EL_EMC_FAKE_ACID, -1, -1
7016 Xfake_acid_3, FALSE, FALSE,
7017 EL_EMC_FAKE_ACID, -1, -1
7020 Xfake_acid_4, FALSE, FALSE,
7021 EL_EMC_FAKE_ACID, -1, -1
7024 Xfake_acid_5, FALSE, FALSE,
7025 EL_EMC_FAKE_ACID, -1, -1
7028 Xfake_acid_6, FALSE, FALSE,
7029 EL_EMC_FAKE_ACID, -1, -1
7032 Xfake_acid_7, FALSE, FALSE,
7033 EL_EMC_FAKE_ACID, -1, -1
7036 Xfake_acid_8, FALSE, FALSE,
7037 EL_EMC_FAKE_ACID, -1, -1
7040 Xsteel_1, TRUE, FALSE,
7041 EL_STEELWALL, -1, -1
7044 Xsteel_2, TRUE, FALSE,
7045 EL_EMC_STEELWALL_2, -1, -1
7048 Xsteel_3, TRUE, FALSE,
7049 EL_EMC_STEELWALL_3, -1, -1
7052 Xsteel_4, TRUE, FALSE,
7053 EL_EMC_STEELWALL_4, -1, -1
7056 Xwall_1, TRUE, FALSE,
7060 Xwall_2, TRUE, FALSE,
7061 EL_EMC_WALL_14, -1, -1
7064 Xwall_3, TRUE, FALSE,
7065 EL_EMC_WALL_15, -1, -1
7068 Xwall_4, TRUE, FALSE,
7069 EL_EMC_WALL_16, -1, -1
7072 Xround_wall_1, TRUE, FALSE,
7073 EL_WALL_SLIPPERY, -1, -1
7076 Xround_wall_2, TRUE, FALSE,
7077 EL_EMC_WALL_SLIPPERY_2, -1, -1
7080 Xround_wall_3, TRUE, FALSE,
7081 EL_EMC_WALL_SLIPPERY_3, -1, -1
7084 Xround_wall_4, TRUE, FALSE,
7085 EL_EMC_WALL_SLIPPERY_4, -1, -1
7088 Xdecor_1, TRUE, FALSE,
7089 EL_EMC_WALL_8, -1, -1
7092 Xdecor_2, TRUE, FALSE,
7093 EL_EMC_WALL_6, -1, -1
7096 Xdecor_3, TRUE, FALSE,
7097 EL_EMC_WALL_4, -1, -1
7100 Xdecor_4, TRUE, FALSE,
7101 EL_EMC_WALL_7, -1, -1
7104 Xdecor_5, TRUE, FALSE,
7105 EL_EMC_WALL_5, -1, -1
7108 Xdecor_6, TRUE, FALSE,
7109 EL_EMC_WALL_9, -1, -1
7112 Xdecor_7, TRUE, FALSE,
7113 EL_EMC_WALL_10, -1, -1
7116 Xdecor_8, TRUE, FALSE,
7117 EL_EMC_WALL_1, -1, -1
7120 Xdecor_9, TRUE, FALSE,
7121 EL_EMC_WALL_2, -1, -1
7124 Xdecor_10, TRUE, FALSE,
7125 EL_EMC_WALL_3, -1, -1
7128 Xdecor_11, TRUE, FALSE,
7129 EL_EMC_WALL_11, -1, -1
7132 Xdecor_12, TRUE, FALSE,
7133 EL_EMC_WALL_12, -1, -1
7136 Xalpha_0, TRUE, FALSE,
7137 EL_CHAR('0'), -1, -1
7140 Xalpha_1, TRUE, FALSE,
7141 EL_CHAR('1'), -1, -1
7144 Xalpha_2, TRUE, FALSE,
7145 EL_CHAR('2'), -1, -1
7148 Xalpha_3, TRUE, FALSE,
7149 EL_CHAR('3'), -1, -1
7152 Xalpha_4, TRUE, FALSE,
7153 EL_CHAR('4'), -1, -1
7156 Xalpha_5, TRUE, FALSE,
7157 EL_CHAR('5'), -1, -1
7160 Xalpha_6, TRUE, FALSE,
7161 EL_CHAR('6'), -1, -1
7164 Xalpha_7, TRUE, FALSE,
7165 EL_CHAR('7'), -1, -1
7168 Xalpha_8, TRUE, FALSE,
7169 EL_CHAR('8'), -1, -1
7172 Xalpha_9, TRUE, FALSE,
7173 EL_CHAR('9'), -1, -1
7176 Xalpha_excla, TRUE, FALSE,
7177 EL_CHAR('!'), -1, -1
7180 Xalpha_quote, TRUE, FALSE,
7181 EL_CHAR('"'), -1, -1
7184 Xalpha_comma, TRUE, FALSE,
7185 EL_CHAR(','), -1, -1
7188 Xalpha_minus, TRUE, FALSE,
7189 EL_CHAR('-'), -1, -1
7192 Xalpha_perio, TRUE, FALSE,
7193 EL_CHAR('.'), -1, -1
7196 Xalpha_colon, TRUE, FALSE,
7197 EL_CHAR(':'), -1, -1
7200 Xalpha_quest, TRUE, FALSE,
7201 EL_CHAR('?'), -1, -1
7204 Xalpha_a, TRUE, FALSE,
7205 EL_CHAR('A'), -1, -1
7208 Xalpha_b, TRUE, FALSE,
7209 EL_CHAR('B'), -1, -1
7212 Xalpha_c, TRUE, FALSE,
7213 EL_CHAR('C'), -1, -1
7216 Xalpha_d, TRUE, FALSE,
7217 EL_CHAR('D'), -1, -1
7220 Xalpha_e, TRUE, FALSE,
7221 EL_CHAR('E'), -1, -1
7224 Xalpha_f, TRUE, FALSE,
7225 EL_CHAR('F'), -1, -1
7228 Xalpha_g, TRUE, FALSE,
7229 EL_CHAR('G'), -1, -1
7232 Xalpha_h, TRUE, FALSE,
7233 EL_CHAR('H'), -1, -1
7236 Xalpha_i, TRUE, FALSE,
7237 EL_CHAR('I'), -1, -1
7240 Xalpha_j, TRUE, FALSE,
7241 EL_CHAR('J'), -1, -1
7244 Xalpha_k, TRUE, FALSE,
7245 EL_CHAR('K'), -1, -1
7248 Xalpha_l, TRUE, FALSE,
7249 EL_CHAR('L'), -1, -1
7252 Xalpha_m, TRUE, FALSE,
7253 EL_CHAR('M'), -1, -1
7256 Xalpha_n, TRUE, FALSE,
7257 EL_CHAR('N'), -1, -1
7260 Xalpha_o, TRUE, FALSE,
7261 EL_CHAR('O'), -1, -1
7264 Xalpha_p, TRUE, FALSE,
7265 EL_CHAR('P'), -1, -1
7268 Xalpha_q, TRUE, FALSE,
7269 EL_CHAR('Q'), -1, -1
7272 Xalpha_r, TRUE, FALSE,
7273 EL_CHAR('R'), -1, -1
7276 Xalpha_s, TRUE, FALSE,
7277 EL_CHAR('S'), -1, -1
7280 Xalpha_t, TRUE, FALSE,
7281 EL_CHAR('T'), -1, -1
7284 Xalpha_u, TRUE, FALSE,
7285 EL_CHAR('U'), -1, -1
7288 Xalpha_v, TRUE, FALSE,
7289 EL_CHAR('V'), -1, -1
7292 Xalpha_w, TRUE, FALSE,
7293 EL_CHAR('W'), -1, -1
7296 Xalpha_x, TRUE, FALSE,
7297 EL_CHAR('X'), -1, -1
7300 Xalpha_y, TRUE, FALSE,
7301 EL_CHAR('Y'), -1, -1
7304 Xalpha_z, TRUE, FALSE,
7305 EL_CHAR('Z'), -1, -1
7308 Xalpha_arrow_e, TRUE, FALSE,
7309 EL_CHAR('>'), -1, -1
7312 Xalpha_arrow_w, TRUE, FALSE,
7313 EL_CHAR('<'), -1, -1
7316 Xalpha_copyr, TRUE, FALSE,
7317 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7321 Xboom_bug, FALSE, FALSE,
7322 EL_BUG, ACTION_EXPLODING, -1
7325 Xboom_bomb, FALSE, FALSE,
7326 EL_BOMB, ACTION_EXPLODING, -1
7329 Xboom_android, FALSE, FALSE,
7330 EL_EMC_ANDROID, ACTION_OTHER, -1
7333 Xboom_1, FALSE, FALSE,
7334 EL_DEFAULT, ACTION_EXPLODING, -1
7337 Xboom_2, FALSE, FALSE,
7338 EL_DEFAULT, ACTION_EXPLODING, -1
7341 Znormal, FALSE, FALSE,
7345 Zdynamite, FALSE, FALSE,
7349 Zplayer, FALSE, FALSE,
7353 ZBORDER, FALSE, FALSE,
7363 static struct Mapping_EM_to_RND_player
7372 em_player_mapping_list[] =
7376 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7380 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7384 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7388 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7392 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7396 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7400 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7404 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7408 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7412 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7416 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7420 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7424 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7428 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7432 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7436 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7440 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7444 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7448 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7452 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7456 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7460 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7464 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7468 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7472 EL_PLAYER_1, ACTION_DEFAULT, -1,
7476 EL_PLAYER_2, ACTION_DEFAULT, -1,
7480 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7484 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7488 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7492 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7496 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7500 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7504 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7508 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7512 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7516 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7520 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7524 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7528 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7532 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7536 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7540 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7544 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7548 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7552 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7556 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7560 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7564 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7568 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7572 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7576 EL_PLAYER_3, ACTION_DEFAULT, -1,
7580 EL_PLAYER_4, ACTION_DEFAULT, -1,
7589 int map_element_RND_to_EM(int element_rnd)
7591 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7592 static boolean mapping_initialized = FALSE;
7594 if (!mapping_initialized)
7598 /* return "Xalpha_quest" for all undefined elements in mapping array */
7599 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7600 mapping_RND_to_EM[i] = Xalpha_quest;
7602 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7603 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7604 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7605 em_object_mapping_list[i].element_em;
7607 mapping_initialized = TRUE;
7610 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7611 return mapping_RND_to_EM[element_rnd];
7613 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7618 int map_element_EM_to_RND(int element_em)
7620 static unsigned short mapping_EM_to_RND[TILE_MAX];
7621 static boolean mapping_initialized = FALSE;
7623 if (!mapping_initialized)
7627 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7628 for (i = 0; i < TILE_MAX; i++)
7629 mapping_EM_to_RND[i] = EL_UNKNOWN;
7631 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7632 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7633 em_object_mapping_list[i].element_rnd;
7635 mapping_initialized = TRUE;
7638 if (element_em >= 0 && element_em < TILE_MAX)
7639 return mapping_EM_to_RND[element_em];
7641 Error(ERR_WARN, "invalid EM level element %d", element_em);
7646 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7648 struct LevelInfo_EM *level_em = level->native_em_level;
7649 struct LEVEL *lev = level_em->lev;
7652 for (i = 0; i < TILE_MAX; i++)
7653 lev->android_array[i] = Xblank;
7655 for (i = 0; i < level->num_android_clone_elements; i++)
7657 int element_rnd = level->android_clone_element[i];
7658 int element_em = map_element_RND_to_EM(element_rnd);
7660 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7661 if (em_object_mapping_list[j].element_rnd == element_rnd)
7662 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7666 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7668 struct LevelInfo_EM *level_em = level->native_em_level;
7669 struct LEVEL *lev = level_em->lev;
7672 level->num_android_clone_elements = 0;
7674 for (i = 0; i < TILE_MAX; i++)
7676 int element_em = lev->android_array[i];
7678 boolean element_found = FALSE;
7680 if (element_em == Xblank)
7683 element_rnd = map_element_EM_to_RND(element_em);
7685 for (j = 0; j < level->num_android_clone_elements; j++)
7686 if (level->android_clone_element[j] == element_rnd)
7687 element_found = TRUE;
7691 level->android_clone_element[level->num_android_clone_elements++] =
7694 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7699 if (level->num_android_clone_elements == 0)
7701 level->num_android_clone_elements = 1;
7702 level->android_clone_element[0] = EL_EMPTY;
7706 int map_direction_RND_to_EM(int direction)
7708 return (direction == MV_UP ? 0 :
7709 direction == MV_RIGHT ? 1 :
7710 direction == MV_DOWN ? 2 :
7711 direction == MV_LEFT ? 3 :
7715 int map_direction_EM_to_RND(int direction)
7717 return (direction == 0 ? MV_UP :
7718 direction == 1 ? MV_RIGHT :
7719 direction == 2 ? MV_DOWN :
7720 direction == 3 ? MV_LEFT :
7724 int map_element_RND_to_SP(int element_rnd)
7726 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7728 if (element_rnd >= EL_SP_START &&
7729 element_rnd <= EL_SP_END)
7730 element_sp = element_rnd - EL_SP_START;
7731 else if (element_rnd == EL_EMPTY_SPACE)
7733 else if (element_rnd == EL_INVISIBLE_WALL)
7739 int map_element_SP_to_RND(int element_sp)
7741 int element_rnd = EL_UNKNOWN;
7743 if (element_sp >= 0x00 &&
7745 element_rnd = EL_SP_START + element_sp;
7746 else if (element_sp == 0x28)
7747 element_rnd = EL_INVISIBLE_WALL;
7752 int map_action_SP_to_RND(int action_sp)
7756 case actActive: return ACTION_ACTIVE;
7757 case actImpact: return ACTION_IMPACT;
7758 case actExploding: return ACTION_EXPLODING;
7759 case actDigging: return ACTION_DIGGING;
7760 case actSnapping: return ACTION_SNAPPING;
7761 case actCollecting: return ACTION_COLLECTING;
7762 case actPassing: return ACTION_PASSING;
7763 case actPushing: return ACTION_PUSHING;
7764 case actDropping: return ACTION_DROPPING;
7766 default: return ACTION_DEFAULT;
7770 int map_element_RND_to_MM(int element_rnd)
7772 return (element_rnd >= EL_MM_START_1 &&
7773 element_rnd <= EL_MM_END_1 ?
7774 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7776 element_rnd >= EL_MM_START_2 &&
7777 element_rnd <= EL_MM_END_2 ?
7778 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7780 element_rnd >= EL_CHAR_START &&
7781 element_rnd <= EL_CHAR_END ?
7782 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7784 element_rnd >= EL_MM_RUNTIME_START &&
7785 element_rnd <= EL_MM_RUNTIME_END ?
7786 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7788 element_rnd >= EL_MM_DUMMY_START &&
7789 element_rnd <= EL_MM_DUMMY_END ?
7790 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7792 EL_MM_EMPTY_NATIVE);
7795 int map_element_MM_to_RND(int element_mm)
7797 return (element_mm == EL_MM_EMPTY_NATIVE ||
7798 element_mm == EL_DF_EMPTY_NATIVE ?
7801 element_mm >= EL_MM_START_1_NATIVE &&
7802 element_mm <= EL_MM_END_1_NATIVE ?
7803 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7805 element_mm >= EL_MM_START_2_NATIVE &&
7806 element_mm <= EL_MM_END_2_NATIVE ?
7807 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7809 element_mm >= EL_MM_CHAR_START_NATIVE &&
7810 element_mm <= EL_MM_CHAR_END_NATIVE ?
7811 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7813 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7814 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7815 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7817 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7818 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7819 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7824 int map_action_MM_to_RND(int action_mm)
7826 /* all MM actions are defined to exactly match their RND counterparts */
7830 int map_sound_MM_to_RND(int sound_mm)
7834 case SND_MM_GAME_LEVELTIME_CHARGING:
7835 return SND_GAME_LEVELTIME_CHARGING;
7837 case SND_MM_GAME_HEALTH_CHARGING:
7838 return SND_GAME_HEALTH_CHARGING;
7841 return SND_UNDEFINED;
7845 int map_mm_wall_element(int element)
7847 return (element >= EL_MM_STEEL_WALL_START &&
7848 element <= EL_MM_STEEL_WALL_END ?
7851 element >= EL_MM_WOODEN_WALL_START &&
7852 element <= EL_MM_WOODEN_WALL_END ?
7855 element >= EL_MM_ICE_WALL_START &&
7856 element <= EL_MM_ICE_WALL_END ?
7859 element >= EL_MM_AMOEBA_WALL_START &&
7860 element <= EL_MM_AMOEBA_WALL_END ?
7863 element >= EL_DF_STEEL_WALL_START &&
7864 element <= EL_DF_STEEL_WALL_END ?
7867 element >= EL_DF_WOODEN_WALL_START &&
7868 element <= EL_DF_WOODEN_WALL_END ?
7874 int map_mm_wall_element_editor(int element)
7878 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7879 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7880 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7881 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7882 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7883 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7885 default: return element;
7889 int get_next_element(int element)
7893 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7894 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7895 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7896 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7897 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7898 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7899 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7900 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7901 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7902 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7903 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7905 default: return element;
7909 int el2img_mm(int element_mm)
7911 return el2img(map_element_MM_to_RND(element_mm));
7914 int el_act_dir2img(int element, int action, int direction)
7916 element = GFX_ELEMENT(element);
7917 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7919 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7920 return element_info[element].direction_graphic[action][direction];
7923 static int el_act_dir2crm(int element, int action, int direction)
7925 element = GFX_ELEMENT(element);
7926 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7928 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7929 return element_info[element].direction_crumbled[action][direction];
7932 int el_act2img(int element, int action)
7934 element = GFX_ELEMENT(element);
7936 return element_info[element].graphic[action];
7939 int el_act2crm(int element, int action)
7941 element = GFX_ELEMENT(element);
7943 return element_info[element].crumbled[action];
7946 int el_dir2img(int element, int direction)
7948 element = GFX_ELEMENT(element);
7950 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7953 int el2baseimg(int element)
7955 return element_info[element].graphic[ACTION_DEFAULT];
7958 int el2img(int element)
7960 element = GFX_ELEMENT(element);
7962 return element_info[element].graphic[ACTION_DEFAULT];
7965 int el2edimg(int element)
7967 element = GFX_ELEMENT(element);
7969 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7972 int el2preimg(int element)
7974 element = GFX_ELEMENT(element);
7976 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7979 int el2panelimg(int element)
7981 element = GFX_ELEMENT(element);
7983 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7986 int font2baseimg(int font_nr)
7988 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7991 int getBeltNrFromBeltElement(int element)
7993 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7994 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7995 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7998 int getBeltNrFromBeltActiveElement(int element)
8000 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8001 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8002 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8005 int getBeltNrFromBeltSwitchElement(int element)
8007 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8008 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8009 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8012 int getBeltDirNrFromBeltElement(int element)
8014 static int belt_base_element[4] =
8016 EL_CONVEYOR_BELT_1_LEFT,
8017 EL_CONVEYOR_BELT_2_LEFT,
8018 EL_CONVEYOR_BELT_3_LEFT,
8019 EL_CONVEYOR_BELT_4_LEFT
8022 int belt_nr = getBeltNrFromBeltElement(element);
8023 int belt_dir_nr = element - belt_base_element[belt_nr];
8025 return (belt_dir_nr % 3);
8028 int getBeltDirNrFromBeltSwitchElement(int element)
8030 static int belt_base_element[4] =
8032 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8033 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8034 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8035 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8038 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8039 int belt_dir_nr = element - belt_base_element[belt_nr];
8041 return (belt_dir_nr % 3);
8044 int getBeltDirFromBeltElement(int element)
8046 static int belt_move_dir[3] =
8053 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8055 return belt_move_dir[belt_dir_nr];
8058 int getBeltDirFromBeltSwitchElement(int element)
8060 static int belt_move_dir[3] =
8067 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8069 return belt_move_dir[belt_dir_nr];
8072 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8074 static int belt_base_element[4] =
8076 EL_CONVEYOR_BELT_1_LEFT,
8077 EL_CONVEYOR_BELT_2_LEFT,
8078 EL_CONVEYOR_BELT_3_LEFT,
8079 EL_CONVEYOR_BELT_4_LEFT
8082 return belt_base_element[belt_nr] + belt_dir_nr;
8085 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8087 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8089 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8092 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8094 static int belt_base_element[4] =
8096 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8097 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8098 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8099 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8102 return belt_base_element[belt_nr] + belt_dir_nr;
8105 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8107 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8109 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8112 boolean getTeamMode_EM()
8114 return game.team_mode;
8117 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8119 int game_frame_delay_value;
8121 game_frame_delay_value =
8122 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8123 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8126 if (tape.playing && tape.warp_forward && !tape.pausing)
8127 game_frame_delay_value = 0;
8129 return game_frame_delay_value;
8132 unsigned int InitRND(int seed)
8134 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8135 return InitEngineRandom_EM(seed);
8136 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8137 return InitEngineRandom_SP(seed);
8138 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8139 return InitEngineRandom_MM(seed);
8141 return InitEngineRandom_RND(seed);
8144 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8145 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8147 inline static int get_effective_element_EM(int tile, int frame_em)
8149 int element = object_mapping[tile].element_rnd;
8150 int action = object_mapping[tile].action;
8151 boolean is_backside = object_mapping[tile].is_backside;
8152 boolean action_removing = (action == ACTION_DIGGING ||
8153 action == ACTION_SNAPPING ||
8154 action == ACTION_COLLECTING);
8160 case Yacid_splash_eB:
8161 case Yacid_splash_wB:
8162 return (frame_em > 5 ? EL_EMPTY : element);
8168 else /* frame_em == 7 */
8172 case Yacid_splash_eB:
8173 case Yacid_splash_wB:
8176 case Yemerald_stone:
8179 case Ydiamond_stone:
8183 case Xdrip_stretchB:
8202 case Xsand_stonein_1:
8203 case Xsand_stonein_2:
8204 case Xsand_stonein_3:
8205 case Xsand_stonein_4:
8209 return (is_backside || action_removing ? EL_EMPTY : element);
8214 inline static boolean check_linear_animation_EM(int tile)
8218 case Xsand_stonesand_1:
8219 case Xsand_stonesand_quickout_1:
8220 case Xsand_sandstone_1:
8221 case Xsand_stonein_1:
8222 case Xsand_stoneout_1:
8241 case Yacid_splash_eB:
8242 case Yacid_splash_wB:
8243 case Yemerald_stone:
8250 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8251 boolean has_crumbled_graphics,
8252 int crumbled, int sync_frame)
8254 /* if element can be crumbled, but certain action graphics are just empty
8255 space (like instantly snapping sand to empty space in 1 frame), do not
8256 treat these empty space graphics as crumbled graphics in EMC engine */
8257 if (crumbled == IMG_EMPTY_SPACE)
8258 has_crumbled_graphics = FALSE;
8260 if (has_crumbled_graphics)
8262 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8263 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8264 g_crumbled->anim_delay,
8265 g_crumbled->anim_mode,
8266 g_crumbled->anim_start_frame,
8269 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8270 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8272 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8273 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8275 g_em->has_crumbled_graphics = TRUE;
8279 g_em->crumbled_bitmap = NULL;
8280 g_em->crumbled_src_x = 0;
8281 g_em->crumbled_src_y = 0;
8282 g_em->crumbled_border_size = 0;
8283 g_em->crumbled_tile_size = 0;
8285 g_em->has_crumbled_graphics = FALSE;
8289 void ResetGfxAnimation_EM(int x, int y, int tile)
8294 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8295 int tile, int frame_em, int x, int y)
8297 int action = object_mapping[tile].action;
8298 int direction = object_mapping[tile].direction;
8299 int effective_element = get_effective_element_EM(tile, frame_em);
8300 int graphic = (direction == MV_NONE ?
8301 el_act2img(effective_element, action) :
8302 el_act_dir2img(effective_element, action, direction));
8303 struct GraphicInfo *g = &graphic_info[graphic];
8305 boolean action_removing = (action == ACTION_DIGGING ||
8306 action == ACTION_SNAPPING ||
8307 action == ACTION_COLLECTING);
8308 boolean action_moving = (action == ACTION_FALLING ||
8309 action == ACTION_MOVING ||
8310 action == ACTION_PUSHING ||
8311 action == ACTION_EATING ||
8312 action == ACTION_FILLING ||
8313 action == ACTION_EMPTYING);
8314 boolean action_falling = (action == ACTION_FALLING ||
8315 action == ACTION_FILLING ||
8316 action == ACTION_EMPTYING);
8318 /* special case: graphic uses "2nd movement tile" and has defined
8319 7 frames for movement animation (or less) => use default graphic
8320 for last (8th) frame which ends the movement animation */
8321 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8323 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
8324 graphic = (direction == MV_NONE ?
8325 el_act2img(effective_element, action) :
8326 el_act_dir2img(effective_element, action, direction));
8328 g = &graphic_info[graphic];
8331 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8335 else if (action_moving)
8337 boolean is_backside = object_mapping[tile].is_backside;
8341 int direction = object_mapping[tile].direction;
8342 int move_dir = (action_falling ? MV_DOWN : direction);
8347 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
8348 if (g->double_movement && frame_em == 0)
8352 if (move_dir == MV_LEFT)
8353 GfxFrame[x - 1][y] = GfxFrame[x][y];
8354 else if (move_dir == MV_RIGHT)
8355 GfxFrame[x + 1][y] = GfxFrame[x][y];
8356 else if (move_dir == MV_UP)
8357 GfxFrame[x][y - 1] = GfxFrame[x][y];
8358 else if (move_dir == MV_DOWN)
8359 GfxFrame[x][y + 1] = GfxFrame[x][y];
8366 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
8367 if (tile == Xsand_stonesand_quickout_1 ||
8368 tile == Xsand_stonesand_quickout_2)
8372 if (graphic_info[graphic].anim_global_sync)
8373 sync_frame = FrameCounter;
8374 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8375 sync_frame = GfxFrame[x][y];
8377 sync_frame = 0; /* playfield border (pseudo steel) */
8379 SetRandomAnimationValue(x, y);
8381 int frame = getAnimationFrame(g->anim_frames,
8384 g->anim_start_frame,
8387 g_em->unique_identifier =
8388 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8391 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8392 int tile, int frame_em, int x, int y)
8394 int action = object_mapping[tile].action;
8395 int direction = object_mapping[tile].direction;
8396 boolean is_backside = object_mapping[tile].is_backside;
8397 int effective_element = get_effective_element_EM(tile, frame_em);
8398 int effective_action = action;
8399 int graphic = (direction == MV_NONE ?
8400 el_act2img(effective_element, effective_action) :
8401 el_act_dir2img(effective_element, effective_action,
8403 int crumbled = (direction == MV_NONE ?
8404 el_act2crm(effective_element, effective_action) :
8405 el_act_dir2crm(effective_element, effective_action,
8407 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8408 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8409 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8410 struct GraphicInfo *g = &graphic_info[graphic];
8413 /* special case: graphic uses "2nd movement tile" and has defined
8414 7 frames for movement animation (or less) => use default graphic
8415 for last (8th) frame which ends the movement animation */
8416 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8418 effective_action = ACTION_DEFAULT;
8419 graphic = (direction == MV_NONE ?
8420 el_act2img(effective_element, effective_action) :
8421 el_act_dir2img(effective_element, effective_action,
8423 crumbled = (direction == MV_NONE ?
8424 el_act2crm(effective_element, effective_action) :
8425 el_act_dir2crm(effective_element, effective_action,
8428 g = &graphic_info[graphic];
8431 if (graphic_info[graphic].anim_global_sync)
8432 sync_frame = FrameCounter;
8433 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8434 sync_frame = GfxFrame[x][y];
8436 sync_frame = 0; /* playfield border (pseudo steel) */
8438 SetRandomAnimationValue(x, y);
8440 int frame = getAnimationFrame(g->anim_frames,
8443 g->anim_start_frame,
8446 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8447 g->double_movement && is_backside);
8449 /* (updating the "crumbled" graphic definitions is probably not really needed,
8450 as animations for crumbled graphics can't be longer than one EMC cycle) */
8451 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8455 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8456 int player_nr, int anim, int frame_em)
8458 int element = player_mapping[player_nr][anim].element_rnd;
8459 int action = player_mapping[player_nr][anim].action;
8460 int direction = player_mapping[player_nr][anim].direction;
8461 int graphic = (direction == MV_NONE ?
8462 el_act2img(element, action) :
8463 el_act_dir2img(element, action, direction));
8464 struct GraphicInfo *g = &graphic_info[graphic];
8467 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8469 stored_player[player_nr].StepFrame = frame_em;
8471 sync_frame = stored_player[player_nr].Frame;
8473 int frame = getAnimationFrame(g->anim_frames,
8476 g->anim_start_frame,
8479 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8480 &g_em->src_x, &g_em->src_y, FALSE);
8483 void InitGraphicInfo_EM(void)
8488 int num_em_gfx_errors = 0;
8490 if (graphic_info_em_object[0][0].bitmap == NULL)
8492 /* EM graphics not yet initialized in em_open_all() */
8497 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8500 /* always start with reliable default values */
8501 for (i = 0; i < TILE_MAX; i++)
8503 object_mapping[i].element_rnd = EL_UNKNOWN;
8504 object_mapping[i].is_backside = FALSE;
8505 object_mapping[i].action = ACTION_DEFAULT;
8506 object_mapping[i].direction = MV_NONE;
8509 /* always start with reliable default values */
8510 for (p = 0; p < MAX_PLAYERS; p++)
8512 for (i = 0; i < SPR_MAX; i++)
8514 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8515 player_mapping[p][i].action = ACTION_DEFAULT;
8516 player_mapping[p][i].direction = MV_NONE;
8520 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8522 int e = em_object_mapping_list[i].element_em;
8524 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8525 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8527 if (em_object_mapping_list[i].action != -1)
8528 object_mapping[e].action = em_object_mapping_list[i].action;
8530 if (em_object_mapping_list[i].direction != -1)
8531 object_mapping[e].direction =
8532 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8535 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8537 int a = em_player_mapping_list[i].action_em;
8538 int p = em_player_mapping_list[i].player_nr;
8540 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8542 if (em_player_mapping_list[i].action != -1)
8543 player_mapping[p][a].action = em_player_mapping_list[i].action;
8545 if (em_player_mapping_list[i].direction != -1)
8546 player_mapping[p][a].direction =
8547 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8550 for (i = 0; i < TILE_MAX; i++)
8552 int element = object_mapping[i].element_rnd;
8553 int action = object_mapping[i].action;
8554 int direction = object_mapping[i].direction;
8555 boolean is_backside = object_mapping[i].is_backside;
8556 boolean action_exploding = ((action == ACTION_EXPLODING ||
8557 action == ACTION_SMASHED_BY_ROCK ||
8558 action == ACTION_SMASHED_BY_SPRING) &&
8559 element != EL_DIAMOND);
8560 boolean action_active = (action == ACTION_ACTIVE);
8561 boolean action_other = (action == ACTION_OTHER);
8563 for (j = 0; j < 8; j++)
8565 int effective_element = get_effective_element_EM(i, j);
8566 int effective_action = (j < 7 ? action :
8567 i == Xdrip_stretch ? action :
8568 i == Xdrip_stretchB ? action :
8569 i == Ydrip_s1 ? action :
8570 i == Ydrip_s1B ? action :
8571 i == Xball_1B ? action :
8572 i == Xball_2 ? action :
8573 i == Xball_2B ? action :
8574 i == Yball_eat ? action :
8575 i == Ykey_1_eat ? action :
8576 i == Ykey_2_eat ? action :
8577 i == Ykey_3_eat ? action :
8578 i == Ykey_4_eat ? action :
8579 i == Ykey_5_eat ? action :
8580 i == Ykey_6_eat ? action :
8581 i == Ykey_7_eat ? action :
8582 i == Ykey_8_eat ? action :
8583 i == Ylenses_eat ? action :
8584 i == Ymagnify_eat ? action :
8585 i == Ygrass_eat ? action :
8586 i == Ydirt_eat ? action :
8587 i == Xsand_stonein_1 ? action :
8588 i == Xsand_stonein_2 ? action :
8589 i == Xsand_stonein_3 ? action :
8590 i == Xsand_stonein_4 ? action :
8591 i == Xsand_stoneout_1 ? action :
8592 i == Xsand_stoneout_2 ? action :
8593 i == Xboom_android ? ACTION_EXPLODING :
8594 action_exploding ? ACTION_EXPLODING :
8595 action_active ? action :
8596 action_other ? action :
8598 int graphic = (el_act_dir2img(effective_element, effective_action,
8600 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8602 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8603 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8604 boolean has_action_graphics = (graphic != base_graphic);
8605 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8606 struct GraphicInfo *g = &graphic_info[graphic];
8607 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8610 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8611 boolean special_animation = (action != ACTION_DEFAULT &&
8612 g->anim_frames == 3 &&
8613 g->anim_delay == 2 &&
8614 g->anim_mode & ANIM_LINEAR);
8615 int sync_frame = (i == Xdrip_stretch ? 7 :
8616 i == Xdrip_stretchB ? 7 :
8617 i == Ydrip_s2 ? j + 8 :
8618 i == Ydrip_s2B ? j + 8 :
8627 i == Xfake_acid_1 ? 0 :
8628 i == Xfake_acid_2 ? 10 :
8629 i == Xfake_acid_3 ? 20 :
8630 i == Xfake_acid_4 ? 30 :
8631 i == Xfake_acid_5 ? 40 :
8632 i == Xfake_acid_6 ? 50 :
8633 i == Xfake_acid_7 ? 60 :
8634 i == Xfake_acid_8 ? 70 :
8636 i == Xball_2B ? j + 8 :
8637 i == Yball_eat ? j + 1 :
8638 i == Ykey_1_eat ? j + 1 :
8639 i == Ykey_2_eat ? j + 1 :
8640 i == Ykey_3_eat ? j + 1 :
8641 i == Ykey_4_eat ? j + 1 :
8642 i == Ykey_5_eat ? j + 1 :
8643 i == Ykey_6_eat ? j + 1 :
8644 i == Ykey_7_eat ? j + 1 :
8645 i == Ykey_8_eat ? j + 1 :
8646 i == Ylenses_eat ? j + 1 :
8647 i == Ymagnify_eat ? j + 1 :
8648 i == Ygrass_eat ? j + 1 :
8649 i == Ydirt_eat ? j + 1 :
8650 i == Xamoeba_1 ? 0 :
8651 i == Xamoeba_2 ? 1 :
8652 i == Xamoeba_3 ? 2 :
8653 i == Xamoeba_4 ? 3 :
8654 i == Xamoeba_5 ? 0 :
8655 i == Xamoeba_6 ? 1 :
8656 i == Xamoeba_7 ? 2 :
8657 i == Xamoeba_8 ? 3 :
8658 i == Xexit_2 ? j + 8 :
8659 i == Xexit_3 ? j + 16 :
8660 i == Xdynamite_1 ? 0 :
8661 i == Xdynamite_2 ? 8 :
8662 i == Xdynamite_3 ? 16 :
8663 i == Xdynamite_4 ? 24 :
8664 i == Xsand_stonein_1 ? j + 1 :
8665 i == Xsand_stonein_2 ? j + 9 :
8666 i == Xsand_stonein_3 ? j + 17 :
8667 i == Xsand_stonein_4 ? j + 25 :
8668 i == Xsand_stoneout_1 && j == 0 ? 0 :
8669 i == Xsand_stoneout_1 && j == 1 ? 0 :
8670 i == Xsand_stoneout_1 && j == 2 ? 1 :
8671 i == Xsand_stoneout_1 && j == 3 ? 2 :
8672 i == Xsand_stoneout_1 && j == 4 ? 2 :
8673 i == Xsand_stoneout_1 && j == 5 ? 3 :
8674 i == Xsand_stoneout_1 && j == 6 ? 4 :
8675 i == Xsand_stoneout_1 && j == 7 ? 4 :
8676 i == Xsand_stoneout_2 && j == 0 ? 5 :
8677 i == Xsand_stoneout_2 && j == 1 ? 6 :
8678 i == Xsand_stoneout_2 && j == 2 ? 7 :
8679 i == Xsand_stoneout_2 && j == 3 ? 8 :
8680 i == Xsand_stoneout_2 && j == 4 ? 9 :
8681 i == Xsand_stoneout_2 && j == 5 ? 11 :
8682 i == Xsand_stoneout_2 && j == 6 ? 13 :
8683 i == Xsand_stoneout_2 && j == 7 ? 15 :
8684 i == Xboom_bug && j == 1 ? 2 :
8685 i == Xboom_bug && j == 2 ? 2 :
8686 i == Xboom_bug && j == 3 ? 4 :
8687 i == Xboom_bug && j == 4 ? 4 :
8688 i == Xboom_bug && j == 5 ? 2 :
8689 i == Xboom_bug && j == 6 ? 2 :
8690 i == Xboom_bug && j == 7 ? 0 :
8691 i == Xboom_bomb && j == 1 ? 2 :
8692 i == Xboom_bomb && j == 2 ? 2 :
8693 i == Xboom_bomb && j == 3 ? 4 :
8694 i == Xboom_bomb && j == 4 ? 4 :
8695 i == Xboom_bomb && j == 5 ? 2 :
8696 i == Xboom_bomb && j == 6 ? 2 :
8697 i == Xboom_bomb && j == 7 ? 0 :
8698 i == Xboom_android && j == 7 ? 6 :
8699 i == Xboom_1 && j == 1 ? 2 :
8700 i == Xboom_1 && j == 2 ? 2 :
8701 i == Xboom_1 && j == 3 ? 4 :
8702 i == Xboom_1 && j == 4 ? 4 :
8703 i == Xboom_1 && j == 5 ? 6 :
8704 i == Xboom_1 && j == 6 ? 6 :
8705 i == Xboom_1 && j == 7 ? 8 :
8706 i == Xboom_2 && j == 0 ? 8 :
8707 i == Xboom_2 && j == 1 ? 8 :
8708 i == Xboom_2 && j == 2 ? 10 :
8709 i == Xboom_2 && j == 3 ? 10 :
8710 i == Xboom_2 && j == 4 ? 10 :
8711 i == Xboom_2 && j == 5 ? 12 :
8712 i == Xboom_2 && j == 6 ? 12 :
8713 i == Xboom_2 && j == 7 ? 12 :
8714 special_animation && j == 4 ? 3 :
8715 effective_action != action ? 0 :
8719 Bitmap *debug_bitmap = g_em->bitmap;
8720 int debug_src_x = g_em->src_x;
8721 int debug_src_y = g_em->src_y;
8724 int frame = getAnimationFrame(g->anim_frames,
8727 g->anim_start_frame,
8730 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8731 g->double_movement && is_backside);
8733 g_em->bitmap = src_bitmap;
8734 g_em->src_x = src_x;
8735 g_em->src_y = src_y;
8736 g_em->src_offset_x = 0;
8737 g_em->src_offset_y = 0;
8738 g_em->dst_offset_x = 0;
8739 g_em->dst_offset_y = 0;
8740 g_em->width = TILEX;
8741 g_em->height = TILEY;
8743 g_em->preserve_background = FALSE;
8745 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8748 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8749 effective_action == ACTION_MOVING ||
8750 effective_action == ACTION_PUSHING ||
8751 effective_action == ACTION_EATING)) ||
8752 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8753 effective_action == ACTION_EMPTYING)))
8756 (effective_action == ACTION_FALLING ||
8757 effective_action == ACTION_FILLING ||
8758 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8759 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8760 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8761 int num_steps = (i == Ydrip_s1 ? 16 :
8762 i == Ydrip_s1B ? 16 :
8763 i == Ydrip_s2 ? 16 :
8764 i == Ydrip_s2B ? 16 :
8765 i == Xsand_stonein_1 ? 32 :
8766 i == Xsand_stonein_2 ? 32 :
8767 i == Xsand_stonein_3 ? 32 :
8768 i == Xsand_stonein_4 ? 32 :
8769 i == Xsand_stoneout_1 ? 16 :
8770 i == Xsand_stoneout_2 ? 16 : 8);
8771 int cx = ABS(dx) * (TILEX / num_steps);
8772 int cy = ABS(dy) * (TILEY / num_steps);
8773 int step_frame = (i == Ydrip_s2 ? j + 8 :
8774 i == Ydrip_s2B ? j + 8 :
8775 i == Xsand_stonein_2 ? j + 8 :
8776 i == Xsand_stonein_3 ? j + 16 :
8777 i == Xsand_stonein_4 ? j + 24 :
8778 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8779 int step = (is_backside ? step_frame : num_steps - step_frame);
8781 if (is_backside) /* tile where movement starts */
8783 if (dx < 0 || dy < 0)
8785 g_em->src_offset_x = cx * step;
8786 g_em->src_offset_y = cy * step;
8790 g_em->dst_offset_x = cx * step;
8791 g_em->dst_offset_y = cy * step;
8794 else /* tile where movement ends */
8796 if (dx < 0 || dy < 0)
8798 g_em->dst_offset_x = cx * step;
8799 g_em->dst_offset_y = cy * step;
8803 g_em->src_offset_x = cx * step;
8804 g_em->src_offset_y = cy * step;
8808 g_em->width = TILEX - cx * step;
8809 g_em->height = TILEY - cy * step;
8812 /* create unique graphic identifier to decide if tile must be redrawn */
8813 /* bit 31 - 16 (16 bit): EM style graphic
8814 bit 15 - 12 ( 4 bit): EM style frame
8815 bit 11 - 6 ( 6 bit): graphic width
8816 bit 5 - 0 ( 6 bit): graphic height */
8817 g_em->unique_identifier =
8818 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8822 /* skip check for EMC elements not contained in original EMC artwork */
8823 if (element == EL_EMC_FAKE_ACID)
8826 if (g_em->bitmap != debug_bitmap ||
8827 g_em->src_x != debug_src_x ||
8828 g_em->src_y != debug_src_y ||
8829 g_em->src_offset_x != 0 ||
8830 g_em->src_offset_y != 0 ||
8831 g_em->dst_offset_x != 0 ||
8832 g_em->dst_offset_y != 0 ||
8833 g_em->width != TILEX ||
8834 g_em->height != TILEY)
8836 static int last_i = -1;
8844 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8845 i, element, element_info[element].token_name,
8846 element_action_info[effective_action].suffix, direction);
8848 if (element != effective_element)
8849 printf(" [%d ('%s')]",
8851 element_info[effective_element].token_name);
8855 if (g_em->bitmap != debug_bitmap)
8856 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8857 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8859 if (g_em->src_x != debug_src_x ||
8860 g_em->src_y != debug_src_y)
8861 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8862 j, (is_backside ? 'B' : 'F'),
8863 g_em->src_x, g_em->src_y,
8864 g_em->src_x / 32, g_em->src_y / 32,
8865 debug_src_x, debug_src_y,
8866 debug_src_x / 32, debug_src_y / 32);
8868 if (g_em->src_offset_x != 0 ||
8869 g_em->src_offset_y != 0 ||
8870 g_em->dst_offset_x != 0 ||
8871 g_em->dst_offset_y != 0)
8872 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8874 g_em->src_offset_x, g_em->src_offset_y,
8875 g_em->dst_offset_x, g_em->dst_offset_y);
8877 if (g_em->width != TILEX ||
8878 g_em->height != TILEY)
8879 printf(" %d (%d): size %d,%d should be %d,%d\n",
8881 g_em->width, g_em->height, TILEX, TILEY);
8883 num_em_gfx_errors++;
8890 for (i = 0; i < TILE_MAX; i++)
8892 for (j = 0; j < 8; j++)
8894 int element = object_mapping[i].element_rnd;
8895 int action = object_mapping[i].action;
8896 int direction = object_mapping[i].direction;
8897 boolean is_backside = object_mapping[i].is_backside;
8898 int graphic_action = el_act_dir2img(element, action, direction);
8899 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8901 if ((action == ACTION_SMASHED_BY_ROCK ||
8902 action == ACTION_SMASHED_BY_SPRING ||
8903 action == ACTION_EATING) &&
8904 graphic_action == graphic_default)
8906 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8907 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8908 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8909 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8912 /* no separate animation for "smashed by rock" -- use rock instead */
8913 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8914 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8916 g_em->bitmap = g_xx->bitmap;
8917 g_em->src_x = g_xx->src_x;
8918 g_em->src_y = g_xx->src_y;
8919 g_em->src_offset_x = g_xx->src_offset_x;
8920 g_em->src_offset_y = g_xx->src_offset_y;
8921 g_em->dst_offset_x = g_xx->dst_offset_x;
8922 g_em->dst_offset_y = g_xx->dst_offset_y;
8923 g_em->width = g_xx->width;
8924 g_em->height = g_xx->height;
8925 g_em->unique_identifier = g_xx->unique_identifier;
8928 g_em->preserve_background = TRUE;
8933 for (p = 0; p < MAX_PLAYERS; p++)
8935 for (i = 0; i < SPR_MAX; i++)
8937 int element = player_mapping[p][i].element_rnd;
8938 int action = player_mapping[p][i].action;
8939 int direction = player_mapping[p][i].direction;
8941 for (j = 0; j < 8; j++)
8943 int effective_element = element;
8944 int effective_action = action;
8945 int graphic = (direction == MV_NONE ?
8946 el_act2img(effective_element, effective_action) :
8947 el_act_dir2img(effective_element, effective_action,
8949 struct GraphicInfo *g = &graphic_info[graphic];
8950 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8956 Bitmap *debug_bitmap = g_em->bitmap;
8957 int debug_src_x = g_em->src_x;
8958 int debug_src_y = g_em->src_y;
8961 int frame = getAnimationFrame(g->anim_frames,
8964 g->anim_start_frame,
8967 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8969 g_em->bitmap = src_bitmap;
8970 g_em->src_x = src_x;
8971 g_em->src_y = src_y;
8972 g_em->src_offset_x = 0;
8973 g_em->src_offset_y = 0;
8974 g_em->dst_offset_x = 0;
8975 g_em->dst_offset_y = 0;
8976 g_em->width = TILEX;
8977 g_em->height = TILEY;
8981 /* skip check for EMC elements not contained in original EMC artwork */
8982 if (element == EL_PLAYER_3 ||
8983 element == EL_PLAYER_4)
8986 if (g_em->bitmap != debug_bitmap ||
8987 g_em->src_x != debug_src_x ||
8988 g_em->src_y != debug_src_y)
8990 static int last_i = -1;
8998 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8999 p, i, element, element_info[element].token_name,
9000 element_action_info[effective_action].suffix, direction);
9002 if (element != effective_element)
9003 printf(" [%d ('%s')]",
9005 element_info[effective_element].token_name);
9009 if (g_em->bitmap != debug_bitmap)
9010 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9011 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9013 if (g_em->src_x != debug_src_x ||
9014 g_em->src_y != debug_src_y)
9015 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9017 g_em->src_x, g_em->src_y,
9018 g_em->src_x / 32, g_em->src_y / 32,
9019 debug_src_x, debug_src_y,
9020 debug_src_x / 32, debug_src_y / 32);
9022 num_em_gfx_errors++;
9032 printf("::: [%d errors found]\n", num_em_gfx_errors);
9038 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9039 boolean any_player_moving,
9040 boolean any_player_snapping,
9041 boolean any_player_dropping)
9043 if (frame == 0 && !any_player_dropping)
9045 if (!local_player->was_waiting)
9047 if (!CheckSaveEngineSnapshotToList())
9050 local_player->was_waiting = TRUE;
9053 else if (any_player_moving || any_player_snapping || any_player_dropping)
9055 local_player->was_waiting = FALSE;
9059 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9060 boolean murphy_is_dropping)
9062 if (murphy_is_waiting)
9064 if (!local_player->was_waiting)
9066 if (!CheckSaveEngineSnapshotToList())
9069 local_player->was_waiting = TRUE;
9074 local_player->was_waiting = FALSE;
9078 void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9079 boolean button_released)
9081 if (button_released)
9083 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9084 CheckSaveEngineSnapshotToList();
9086 else if (element_clicked)
9088 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9089 CheckSaveEngineSnapshotToList();
9091 game.snapshot.changed_action = TRUE;
9095 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9096 boolean any_player_moving,
9097 boolean any_player_snapping,
9098 boolean any_player_dropping)
9100 if (tape.single_step && tape.recording && !tape.pausing)
9101 if (frame == 0 && !any_player_dropping)
9102 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9104 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9105 any_player_snapping, any_player_dropping);
9108 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9109 boolean murphy_is_dropping)
9111 boolean murphy_starts_dropping = FALSE;
9114 for (i = 0; i < MAX_PLAYERS; i++)
9115 if (stored_player[i].force_dropping)
9116 murphy_starts_dropping = TRUE;
9118 if (tape.single_step && tape.recording && !tape.pausing)
9119 if (murphy_is_waiting && !murphy_starts_dropping)
9120 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9122 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9125 void CheckSingleStepMode_MM(boolean element_clicked,
9126 boolean button_released)
9128 if (tape.single_step && tape.recording && !tape.pausing)
9129 if (button_released)
9130 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9132 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9135 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9136 int graphic, int sync_frame, int x, int y)
9138 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9140 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9143 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9145 return (IS_NEXT_FRAME(sync_frame, graphic));
9148 int getGraphicInfo_Delay(int graphic)
9150 return graphic_info[graphic].anim_delay;
9153 void PlayMenuSoundExt(int sound)
9155 if (sound == SND_UNDEFINED)
9158 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9159 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9162 if (IS_LOOP_SOUND(sound))
9163 PlaySoundLoop(sound);
9168 void PlayMenuSound()
9170 PlayMenuSoundExt(menu.sound[game_status]);
9173 void PlayMenuSoundStereo(int sound, int stereo_position)
9175 if (sound == SND_UNDEFINED)
9178 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9179 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9182 if (IS_LOOP_SOUND(sound))
9183 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9185 PlaySoundStereo(sound, stereo_position);
9188 void PlayMenuSoundIfLoopExt(int sound)
9190 if (sound == SND_UNDEFINED)
9193 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9194 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9197 if (IS_LOOP_SOUND(sound))
9198 PlaySoundLoop(sound);
9201 void PlayMenuSoundIfLoop()
9203 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9206 void PlayMenuMusicExt(int music)
9208 if (music == MUS_UNDEFINED)
9211 if (!setup.sound_music)
9217 void PlayMenuMusic()
9219 char *curr_music = getCurrentlyPlayingMusicFilename();
9220 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9222 if (!strEqual(curr_music, next_music))
9223 PlayMenuMusicExt(menu.music[game_status]);
9226 void PlayMenuSoundsAndMusic()
9232 static void FadeMenuSounds()
9237 static void FadeMenuMusic()
9239 char *curr_music = getCurrentlyPlayingMusicFilename();
9240 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9242 if (!strEqual(curr_music, next_music))
9246 void FadeMenuSoundsAndMusic()
9252 void PlaySoundActivating()
9255 PlaySound(SND_MENU_ITEM_ACTIVATING);
9259 void PlaySoundSelecting()
9262 PlaySound(SND_MENU_ITEM_SELECTING);
9266 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
9268 boolean change_fullscreen = (setup.fullscreen !=
9269 video.fullscreen_enabled);
9270 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9271 setup.window_scaling_percent !=
9272 video.window_scaling_percent);
9274 if (change_window_scaling_percent && video.fullscreen_enabled)
9277 if (!change_window_scaling_percent && !video.fullscreen_available)
9280 #if defined(TARGET_SDL2)
9281 if (change_window_scaling_percent)
9283 SDLSetWindowScaling(setup.window_scaling_percent);
9287 else if (change_fullscreen)
9289 SDLSetWindowFullscreen(setup.fullscreen);
9291 /* set setup value according to successfully changed fullscreen mode */
9292 setup.fullscreen = video.fullscreen_enabled;
9298 if (change_fullscreen ||
9299 change_window_scaling_percent)
9301 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9303 /* save backbuffer content which gets lost when toggling fullscreen mode */
9304 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9306 if (change_window_scaling_percent)
9308 /* keep window mode, but change window scaling */
9309 video.fullscreen_enabled = TRUE; /* force new window scaling */
9312 /* toggle fullscreen */
9313 ChangeVideoModeIfNeeded(setup.fullscreen);
9315 /* set setup value according to successfully changed fullscreen mode */
9316 setup.fullscreen = video.fullscreen_enabled;
9318 /* restore backbuffer content from temporary backbuffer backup bitmap */
9319 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9321 FreeBitmap(tmp_backbuffer);
9323 /* update visible window/screen */
9324 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9328 void JoinRectangles(int *x, int *y, int *width, int *height,
9329 int x2, int y2, int width2, int height2)
9331 // do not join with "off-screen" rectangle
9332 if (x2 == -1 || y2 == -1)
9337 *width = MAX(*width, width2);
9338 *height = MAX(*height, height2);
9341 void SetAnimStatus(int anim_status_new)
9343 if (anim_status_new == GAME_MODE_MAIN)
9344 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9345 else if (anim_status_new == GAME_MODE_SCORES)
9346 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9348 global.anim_status_next = anim_status_new;
9350 // directly set screen modes that are entered without fading
9351 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9352 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9353 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9354 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9355 global.anim_status = global.anim_status_next;
9358 void SetGameStatus(int game_status_new)
9360 if (game_status_new != game_status)
9361 game_status_last_screen = game_status;
9363 game_status = game_status_new;
9365 SetAnimStatus(game_status_new);
9368 void SetFontStatus(int game_status_new)
9370 static int last_game_status = -1;
9372 if (game_status_new != -1)
9374 // set game status for font use after storing last game status
9375 last_game_status = game_status;
9376 game_status = game_status_new;
9380 // reset game status after font use from last stored game status
9381 game_status = last_game_status;
9385 void ResetFontStatus()
9390 boolean CheckIfPlayfieldViewportHasChanged()
9392 // if game status has not changed, playfield viewport has not changed either
9393 if (game_status == game_status_last)
9396 // check if playfield viewport has changed with current game status
9397 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9398 int new_real_sx = vp_playfield->x;
9399 int new_real_sy = vp_playfield->y;
9400 int new_full_sxsize = vp_playfield->width;
9401 int new_full_sysize = vp_playfield->height;
9403 return (new_real_sx != REAL_SX ||
9404 new_real_sy != REAL_SY ||
9405 new_full_sxsize != FULL_SXSIZE ||
9406 new_full_sysize != FULL_SYSIZE);
9409 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
9411 return (CheckIfGlobalBorderHasChanged() ||
9412 CheckIfPlayfieldViewportHasChanged());
9415 void ChangeViewportPropertiesIfNeeded()
9417 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9418 FALSE : setup.small_game_graphics);
9419 int gfx_game_mode = game_status;
9420 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9422 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9423 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9424 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9425 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9426 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9427 int new_win_xsize = vp_window->width;
9428 int new_win_ysize = vp_window->height;
9429 int border_size = vp_playfield->border_size;
9430 int new_sx = vp_playfield->x + border_size;
9431 int new_sy = vp_playfield->y + border_size;
9432 int new_sxsize = vp_playfield->width - 2 * border_size;
9433 int new_sysize = vp_playfield->height - 2 * border_size;
9434 int new_real_sx = vp_playfield->x;
9435 int new_real_sy = vp_playfield->y;
9436 int new_full_sxsize = vp_playfield->width;
9437 int new_full_sysize = vp_playfield->height;
9438 int new_dx = vp_door_1->x;
9439 int new_dy = vp_door_1->y;
9440 int new_dxsize = vp_door_1->width;
9441 int new_dysize = vp_door_1->height;
9442 int new_vx = vp_door_2->x;
9443 int new_vy = vp_door_2->y;
9444 int new_vxsize = vp_door_2->width;
9445 int new_vysize = vp_door_2->height;
9446 int new_ex = vp_door_3->x;
9447 int new_ey = vp_door_3->y;
9448 int new_exsize = vp_door_3->width;
9449 int new_eysize = vp_door_3->height;
9450 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9451 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9452 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9453 int new_scr_fieldx = new_sxsize / tilesize;
9454 int new_scr_fieldy = new_sysize / tilesize;
9455 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9456 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9457 boolean init_gfx_buffers = FALSE;
9458 boolean init_video_buffer = FALSE;
9459 boolean init_gadgets_and_anims = FALSE;
9460 boolean init_em_graphics = FALSE;
9462 if (new_win_xsize != WIN_XSIZE ||
9463 new_win_ysize != WIN_YSIZE)
9465 WIN_XSIZE = new_win_xsize;
9466 WIN_YSIZE = new_win_ysize;
9468 init_video_buffer = TRUE;
9469 init_gfx_buffers = TRUE;
9470 init_gadgets_and_anims = TRUE;
9472 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9475 if (new_scr_fieldx != SCR_FIELDX ||
9476 new_scr_fieldy != SCR_FIELDY)
9478 /* this always toggles between MAIN and GAME when using small tile size */
9480 SCR_FIELDX = new_scr_fieldx;
9481 SCR_FIELDY = new_scr_fieldy;
9483 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9494 new_sxsize != SXSIZE ||
9495 new_sysize != SYSIZE ||
9496 new_dxsize != DXSIZE ||
9497 new_dysize != DYSIZE ||
9498 new_vxsize != VXSIZE ||
9499 new_vysize != VYSIZE ||
9500 new_exsize != EXSIZE ||
9501 new_eysize != EYSIZE ||
9502 new_real_sx != REAL_SX ||
9503 new_real_sy != REAL_SY ||
9504 new_full_sxsize != FULL_SXSIZE ||
9505 new_full_sysize != FULL_SYSIZE ||
9506 new_tilesize_var != TILESIZE_VAR
9509 // ------------------------------------------------------------------------
9510 // determine next fading area for changed viewport definitions
9511 // ------------------------------------------------------------------------
9513 // start with current playfield area (default fading area)
9516 FADE_SXSIZE = FULL_SXSIZE;
9517 FADE_SYSIZE = FULL_SYSIZE;
9519 // add new playfield area if position or size has changed
9520 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9521 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9523 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9524 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9527 // add current and new door 1 area if position or size has changed
9528 if (new_dx != DX || new_dy != DY ||
9529 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9531 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9532 DX, DY, DXSIZE, DYSIZE);
9533 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9534 new_dx, new_dy, new_dxsize, new_dysize);
9537 // add current and new door 2 area if position or size has changed
9538 if (new_dx != VX || new_dy != VY ||
9539 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9541 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9542 VX, VY, VXSIZE, VYSIZE);
9543 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9544 new_vx, new_vy, new_vxsize, new_vysize);
9547 // ------------------------------------------------------------------------
9548 // handle changed tile size
9549 // ------------------------------------------------------------------------
9551 if (new_tilesize_var != TILESIZE_VAR)
9553 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9555 // changing tile size invalidates scroll values of engine snapshots
9556 FreeEngineSnapshotSingle();
9558 // changing tile size requires update of graphic mapping for EM engine
9559 init_em_graphics = TRUE;
9570 SXSIZE = new_sxsize;
9571 SYSIZE = new_sysize;
9572 DXSIZE = new_dxsize;
9573 DYSIZE = new_dysize;
9574 VXSIZE = new_vxsize;
9575 VYSIZE = new_vysize;
9576 EXSIZE = new_exsize;
9577 EYSIZE = new_eysize;
9578 REAL_SX = new_real_sx;
9579 REAL_SY = new_real_sy;
9580 FULL_SXSIZE = new_full_sxsize;
9581 FULL_SYSIZE = new_full_sysize;
9582 TILESIZE_VAR = new_tilesize_var;
9584 init_gfx_buffers = TRUE;
9585 init_gadgets_and_anims = TRUE;
9587 // printf("::: viewports: init_gfx_buffers\n");
9588 // printf("::: viewports: init_gadgets_and_anims\n");
9591 if (init_gfx_buffers)
9593 // printf("::: init_gfx_buffers\n");
9595 SCR_FIELDX = new_scr_fieldx_buffers;
9596 SCR_FIELDY = new_scr_fieldy_buffers;
9600 SCR_FIELDX = new_scr_fieldx;
9601 SCR_FIELDY = new_scr_fieldy;
9603 SetDrawDeactivationMask(REDRAW_NONE);
9604 SetDrawBackgroundMask(REDRAW_FIELD);
9607 if (init_video_buffer)
9609 // printf("::: init_video_buffer\n");
9611 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9612 InitImageTextures();
9615 if (init_gadgets_and_anims)
9617 // printf("::: init_gadgets_and_anims\n");
9620 InitGlobalAnimations();
9623 if (init_em_graphics)
9625 InitGraphicInfo_EM();