1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 // select level set with EMC X11 graphics before activating EM GFX debugging
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 // tool button identifiers
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 // constants for number of doors and door parts
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 // forward declaration for internal use
169 static void UnmapToolButtons(void);
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 static int getFieldbufferOffsetX_RND(void)
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND(void)
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 static int getLevelFromScreenX_MM(int sx)
363 int level_xsize = level.native_mm_level->fieldx;
364 int full_xsize = level_xsize * TILESIZE_VAR;
366 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
369 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
374 static int getLevelFromScreenY_MM(int sy)
376 int level_ysize = level.native_mm_level->fieldy;
377 int full_ysize = level_ysize * TILESIZE_VAR;
379 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
382 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
387 int getLevelFromScreenX(int x)
389 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
390 return getLevelFromScreenX_EM(x);
391 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
392 return getLevelFromScreenX_SP(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
394 return getLevelFromScreenX_MM(x);
396 return getLevelFromScreenX_RND(x);
399 int getLevelFromScreenY(int y)
401 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
402 return getLevelFromScreenY_EM(y);
403 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
404 return getLevelFromScreenY_SP(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
406 return getLevelFromScreenY_MM(y);
408 return getLevelFromScreenY_RND(y);
411 void DumpTile(int x, int y)
417 printf_line("-", 79);
418 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
419 printf_line("-", 79);
421 if (!IN_LEV_FIELD(x, y))
423 printf("(not in level field)\n");
429 token_name = element_info[Feld[x][y]].token_name;
431 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
432 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
433 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
434 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
435 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
436 printf(" MovPos: %d\n", MovPos[x][y]);
437 printf(" MovDir: %d\n", MovDir[x][y]);
438 printf(" MovDelay: %d\n", MovDelay[x][y]);
439 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
440 printf(" CustomValue: %d\n", CustomValue[x][y]);
441 printf(" GfxElement: %d\n", GfxElement[x][y]);
442 printf(" GfxAction: %d\n", GfxAction[x][y]);
443 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
444 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
448 void DumpTileFromScreen(int sx, int sy)
450 int lx = getLevelFromScreenX(sx);
451 int ly = getLevelFromScreenY(sy);
456 void SetDrawtoField(int mode)
458 if (mode == DRAW_TO_FIELDBUFFER)
464 BX2 = SCR_FIELDX + 1;
465 BY2 = SCR_FIELDY + 1;
467 drawto_field = fieldbuffer;
469 else // DRAW_TO_BACKBUFFER
475 BX2 = SCR_FIELDX - 1;
476 BY2 = SCR_FIELDY - 1;
478 drawto_field = backbuffer;
482 static void RedrawPlayfield_RND(void)
484 if (game.envelope_active)
487 DrawLevel(REDRAW_ALL);
491 void RedrawPlayfield(void)
493 if (game_status != GAME_MODE_PLAYING)
496 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
497 RedrawPlayfield_EM(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
499 RedrawPlayfield_SP(TRUE);
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
501 RedrawPlayfield_MM();
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
503 RedrawPlayfield_RND();
505 BlitScreenToBitmap(backbuffer);
507 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
511 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
514 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
515 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
517 if (x == -1 && y == -1)
520 if (draw_target == DRAW_TO_SCREEN)
521 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
523 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
526 static void DrawMaskedBorderExt_FIELD(int draw_target)
528 if (global.border_status >= GAME_MODE_MAIN &&
529 global.border_status <= GAME_MODE_PLAYING &&
530 border.draw_masked[global.border_status])
531 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
535 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
537 // when drawing to backbuffer, never draw border over open doors
538 if (draw_target == DRAW_TO_BACKBUFFER &&
539 (GetDoorState() & DOOR_OPEN_1))
542 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
543 (global.border_status != GAME_MODE_EDITOR ||
544 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
545 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
548 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
550 // when drawing to backbuffer, never draw border over open doors
551 if (draw_target == DRAW_TO_BACKBUFFER &&
552 (GetDoorState() & DOOR_OPEN_2))
555 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
556 global.border_status != GAME_MODE_EDITOR)
557 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
560 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
562 // currently not available
565 static void DrawMaskedBorderExt_ALL(int draw_target)
567 DrawMaskedBorderExt_FIELD(draw_target);
568 DrawMaskedBorderExt_DOOR_1(draw_target);
569 DrawMaskedBorderExt_DOOR_2(draw_target);
570 DrawMaskedBorderExt_DOOR_3(draw_target);
573 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
575 // never draw masked screen borders on borderless screens
576 if (global.border_status == GAME_MODE_LOADING ||
577 global.border_status == GAME_MODE_TITLE)
580 if (redraw_mask & REDRAW_ALL)
581 DrawMaskedBorderExt_ALL(draw_target);
584 if (redraw_mask & REDRAW_FIELD)
585 DrawMaskedBorderExt_FIELD(draw_target);
586 if (redraw_mask & REDRAW_DOOR_1)
587 DrawMaskedBorderExt_DOOR_1(draw_target);
588 if (redraw_mask & REDRAW_DOOR_2)
589 DrawMaskedBorderExt_DOOR_2(draw_target);
590 if (redraw_mask & REDRAW_DOOR_3)
591 DrawMaskedBorderExt_DOOR_3(draw_target);
595 void DrawMaskedBorder_FIELD(void)
597 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
600 void DrawMaskedBorder(int redraw_mask)
602 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
605 void DrawMaskedBorderToTarget(int draw_target)
607 if (draw_target == DRAW_TO_BACKBUFFER ||
608 draw_target == DRAW_TO_SCREEN)
610 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
614 int last_border_status = global.border_status;
616 if (draw_target == DRAW_TO_FADE_SOURCE)
618 global.border_status = gfx.fade_border_source_status;
619 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
621 else if (draw_target == DRAW_TO_FADE_TARGET)
623 global.border_status = gfx.fade_border_target_status;
624 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
627 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
629 global.border_status = last_border_status;
630 gfx.masked_border_bitmap_ptr = backbuffer;
634 void DrawTileCursor(int draw_target)
640 int graphic = IMG_GLOBAL_TILE_CURSOR;
642 int tilesize = TILESIZE_VAR;
643 int width = tilesize;
644 int height = tilesize;
646 if (game_status != GAME_MODE_PLAYING)
649 if (!tile_cursor.enabled ||
653 if (tile_cursor.moving)
655 int step = TILESIZE_VAR / 4;
656 int dx = tile_cursor.target_x - tile_cursor.x;
657 int dy = tile_cursor.target_y - tile_cursor.y;
660 tile_cursor.x = tile_cursor.target_x;
662 tile_cursor.x += SIGN(dx) * step;
665 tile_cursor.y = tile_cursor.target_y;
667 tile_cursor.y += SIGN(dy) * step;
669 if (tile_cursor.x == tile_cursor.target_x &&
670 tile_cursor.y == tile_cursor.target_y)
671 tile_cursor.moving = FALSE;
674 dst_x = tile_cursor.x;
675 dst_y = tile_cursor.y;
677 frame = getGraphicAnimationFrame(graphic, -1);
679 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
682 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
683 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
685 if (draw_target == DRAW_TO_SCREEN)
686 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
688 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
692 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
694 int fx = getFieldbufferOffsetX_RND();
695 int fy = getFieldbufferOffsetY_RND();
697 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
700 void BlitScreenToBitmap(Bitmap *target_bitmap)
702 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
703 BlitScreenToBitmap_EM(target_bitmap);
704 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
705 BlitScreenToBitmap_SP(target_bitmap);
706 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
707 BlitScreenToBitmap_MM(target_bitmap);
708 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
709 BlitScreenToBitmap_RND(target_bitmap);
711 redraw_mask |= REDRAW_FIELD;
714 static void DrawFramesPerSecond(void)
717 int font_nr = FONT_TEXT_2;
718 int font_width = getFontWidth(font_nr);
719 int draw_deactivation_mask = GetDrawDeactivationMask();
720 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
722 // draw FPS with leading space (needed if field buffer deactivated)
723 sprintf(text, " %04.1f fps", global.frames_per_second);
725 // override draw deactivation mask (required for invisible warp mode)
726 SetDrawDeactivationMask(REDRAW_NONE);
728 // draw opaque FPS if field buffer deactivated, else draw masked FPS
729 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
730 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
732 // set draw deactivation mask to previous value
733 SetDrawDeactivationMask(draw_deactivation_mask);
735 // force full-screen redraw in this frame
736 redraw_mask = REDRAW_ALL;
740 static void PrintFrameTimeDebugging(void)
742 static unsigned int last_counter = 0;
743 unsigned int counter = Counter();
744 int diff_1 = counter - last_counter;
745 int diff_2 = diff_1 - GAME_FRAME_DELAY;
747 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
748 char diff_bar[2 * diff_2_max + 5];
752 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
754 for (i = 0; i < diff_2_max; i++)
755 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
756 i >= diff_2_max - diff_2_cut ? '-' : ' ');
758 diff_bar[pos++] = '|';
760 for (i = 0; i < diff_2_max; i++)
761 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
763 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
765 diff_bar[pos++] = '\0';
767 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
770 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
773 last_counter = counter;
777 static int unifiedRedrawMask(int mask)
779 if (mask & REDRAW_ALL)
782 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
788 static boolean equalRedrawMasks(int mask_1, int mask_2)
790 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
793 void BackToFront(void)
795 static int last_redraw_mask = REDRAW_NONE;
797 // force screen redraw in every frame to continue drawing global animations
798 // (but always use the last redraw mask to prevent unwanted side effects)
799 if (redraw_mask == REDRAW_NONE)
800 redraw_mask = last_redraw_mask;
802 last_redraw_mask = redraw_mask;
805 // masked border now drawn immediately when blitting backbuffer to window
807 // draw masked border to all viewports, if defined
808 DrawMaskedBorder(redraw_mask);
811 // draw frames per second (only if debug mode is enabled)
812 if (redraw_mask & REDRAW_FPS)
813 DrawFramesPerSecond();
815 // remove playfield redraw before potentially merging with doors redraw
816 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
817 redraw_mask &= ~REDRAW_FIELD;
819 // redraw complete window if both playfield and (some) doors need redraw
820 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
821 redraw_mask = REDRAW_ALL;
823 /* although redrawing the whole window would be fine for normal gameplay,
824 being able to only redraw the playfield is required for deactivating
825 certain drawing areas (mainly playfield) to work, which is needed for
826 warp-forward to be fast enough (by skipping redraw of most frames) */
828 if (redraw_mask & REDRAW_ALL)
830 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
832 else if (redraw_mask & REDRAW_FIELD)
834 BlitBitmap(backbuffer, window,
835 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
837 else if (redraw_mask & REDRAW_DOORS)
839 // merge door areas to prevent calling screen redraw more than once
845 if (redraw_mask & REDRAW_DOOR_1)
849 x2 = MAX(x2, DX + DXSIZE);
850 y2 = MAX(y2, DY + DYSIZE);
853 if (redraw_mask & REDRAW_DOOR_2)
857 x2 = MAX(x2, VX + VXSIZE);
858 y2 = MAX(y2, VY + VYSIZE);
861 if (redraw_mask & REDRAW_DOOR_3)
865 x2 = MAX(x2, EX + EXSIZE);
866 y2 = MAX(y2, EY + EYSIZE);
869 // make sure that at least one pixel is blitted, and inside the screen
870 // (else nothing is blitted, causing the animations not to be updated)
871 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
872 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
873 x2 = MIN(MAX(1, x2), WIN_XSIZE);
874 y2 = MIN(MAX(1, y2), WIN_YSIZE);
876 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
879 redraw_mask = REDRAW_NONE;
882 PrintFrameTimeDebugging();
886 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
888 unsigned int frame_delay_value_old = GetVideoFrameDelay();
890 SetVideoFrameDelay(frame_delay_value);
894 SetVideoFrameDelay(frame_delay_value_old);
897 static int fade_type_skip = FADE_TYPE_NONE;
899 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
901 void (*draw_border_function)(void) = NULL;
902 int x, y, width, height;
903 int fade_delay, post_delay;
905 if (fade_type == FADE_TYPE_FADE_OUT)
907 if (fade_type_skip != FADE_TYPE_NONE)
909 // skip all fade operations until specified fade operation
910 if (fade_type & fade_type_skip)
911 fade_type_skip = FADE_TYPE_NONE;
916 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
920 redraw_mask |= fade_mask;
922 if (fade_type == FADE_TYPE_SKIP)
924 fade_type_skip = fade_mode;
929 fade_delay = fading.fade_delay;
930 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
932 if (fade_type_skip != FADE_TYPE_NONE)
934 // skip all fade operations until specified fade operation
935 if (fade_type & fade_type_skip)
936 fade_type_skip = FADE_TYPE_NONE;
941 if (global.autoplay_leveldir)
946 if (fade_mask == REDRAW_FIELD)
951 height = FADE_SYSIZE;
953 if (border.draw_masked_when_fading)
954 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
956 DrawMaskedBorder_FIELD(); // draw once
966 if (!setup.fade_screens ||
968 fading.fade_mode == FADE_MODE_NONE)
970 if (fade_mode == FADE_MODE_FADE_OUT)
973 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
975 redraw_mask &= ~fade_mask;
980 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
981 draw_border_function);
983 redraw_mask &= ~fade_mask;
985 ClearAutoRepeatKeyEvents();
988 static void SetScreenStates_BeforeFadingIn(void)
990 // temporarily set screen mode for animations to screen after fading in
991 global.anim_status = global.anim_status_next;
993 // store backbuffer with all animations that will be started after fading in
994 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
995 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
997 // set screen mode for animations back to fading
998 global.anim_status = GAME_MODE_PSEUDO_FADING;
1001 static void SetScreenStates_AfterFadingIn(void)
1003 // store new source screen (to use correct masked border for fading)
1004 gfx.fade_border_source_status = global.border_status;
1006 global.anim_status = global.anim_status_next;
1009 static void SetScreenStates_BeforeFadingOut(void)
1011 // store new target screen (to use correct masked border for fading)
1012 gfx.fade_border_target_status = game_status;
1014 // set screen mode for animations to fading
1015 global.anim_status = GAME_MODE_PSEUDO_FADING;
1017 // store backbuffer with all animations that will be stopped for fading out
1018 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1019 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1022 static void SetScreenStates_AfterFadingOut(void)
1024 global.border_status = game_status;
1027 void FadeIn(int fade_mask)
1029 SetScreenStates_BeforeFadingIn();
1032 DrawMaskedBorder(REDRAW_ALL);
1035 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1036 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1038 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1042 FADE_SXSIZE = FULL_SXSIZE;
1043 FADE_SYSIZE = FULL_SYSIZE;
1045 // activate virtual buttons depending on upcoming game status
1046 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1047 game_status == GAME_MODE_PLAYING && !tape.playing)
1048 SetOverlayActive(TRUE);
1050 SetScreenStates_AfterFadingIn();
1052 // force update of global animation status in case of rapid screen changes
1053 redraw_mask = REDRAW_ALL;
1057 void FadeOut(int fade_mask)
1059 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1060 if (!equalRedrawMasks(fade_mask, redraw_mask))
1063 SetScreenStates_BeforeFadingOut();
1065 SetTileCursorActive(FALSE);
1066 SetOverlayActive(FALSE);
1069 DrawMaskedBorder(REDRAW_ALL);
1072 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1073 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1075 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1077 SetScreenStates_AfterFadingOut();
1080 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1082 static struct TitleFadingInfo fading_leave_stored;
1085 fading_leave_stored = fading_leave;
1087 fading = fading_leave_stored;
1090 void FadeSetEnterMenu(void)
1092 fading = menu.enter_menu;
1094 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1097 void FadeSetLeaveMenu(void)
1099 fading = menu.leave_menu;
1101 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1104 void FadeSetEnterScreen(void)
1106 fading = menu.enter_screen[game_status];
1108 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1111 void FadeSetNextScreen(void)
1113 fading = menu.next_screen[game_status];
1115 // (do not overwrite fade mode set by FadeSetEnterScreen)
1116 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1119 void FadeSetLeaveScreen(void)
1121 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1124 void FadeSetFromType(int type)
1126 if (type & TYPE_ENTER_SCREEN)
1127 FadeSetEnterScreen();
1128 else if (type & TYPE_ENTER)
1130 else if (type & TYPE_LEAVE)
1134 void FadeSetDisabled(void)
1136 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1138 fading = fading_none;
1141 void FadeSkipNextFadeIn(void)
1143 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1146 void FadeSkipNextFadeOut(void)
1148 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1151 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1153 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1155 return (graphic == IMG_UNDEFINED ? NULL :
1156 graphic_info[graphic].bitmap != NULL || redefined ?
1157 graphic_info[graphic].bitmap :
1158 graphic_info[default_graphic].bitmap);
1161 static Bitmap *getBackgroundBitmap(int graphic)
1163 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1166 static Bitmap *getGlobalBorderBitmap(int graphic)
1168 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1171 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1174 (status == GAME_MODE_MAIN ||
1175 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1176 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1177 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1178 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1181 return getGlobalBorderBitmap(graphic);
1184 void SetWindowBackgroundImageIfDefined(int graphic)
1186 if (graphic_info[graphic].bitmap)
1187 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1190 void SetMainBackgroundImageIfDefined(int graphic)
1192 if (graphic_info[graphic].bitmap)
1193 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1196 void SetDoorBackgroundImageIfDefined(int graphic)
1198 if (graphic_info[graphic].bitmap)
1199 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1202 void SetWindowBackgroundImage(int graphic)
1204 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1207 void SetMainBackgroundImage(int graphic)
1209 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1212 void SetDoorBackgroundImage(int graphic)
1214 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1217 void SetPanelBackground(void)
1219 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1221 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1222 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1224 SetDoorBackgroundBitmap(bitmap_db_panel);
1227 void DrawBackground(int x, int y, int width, int height)
1229 // "drawto" might still point to playfield buffer here (hall of fame)
1230 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1232 if (IN_GFX_FIELD_FULL(x, y))
1233 redraw_mask |= REDRAW_FIELD;
1234 else if (IN_GFX_DOOR_1(x, y))
1235 redraw_mask |= REDRAW_DOOR_1;
1236 else if (IN_GFX_DOOR_2(x, y))
1237 redraw_mask |= REDRAW_DOOR_2;
1238 else if (IN_GFX_DOOR_3(x, y))
1239 redraw_mask |= REDRAW_DOOR_3;
1242 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1244 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1246 if (font->bitmap == NULL)
1249 DrawBackground(x, y, width, height);
1252 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1254 struct GraphicInfo *g = &graphic_info[graphic];
1256 if (g->bitmap == NULL)
1259 DrawBackground(x, y, width, height);
1262 static int game_status_last = -1;
1263 static Bitmap *global_border_bitmap_last = NULL;
1264 static Bitmap *global_border_bitmap = NULL;
1265 static int real_sx_last = -1, real_sy_last = -1;
1266 static int full_sxsize_last = -1, full_sysize_last = -1;
1267 static int dx_last = -1, dy_last = -1;
1268 static int dxsize_last = -1, dysize_last = -1;
1269 static int vx_last = -1, vy_last = -1;
1270 static int vxsize_last = -1, vysize_last = -1;
1271 static int ex_last = -1, ey_last = -1;
1272 static int exsize_last = -1, eysize_last = -1;
1274 boolean CheckIfGlobalBorderHasChanged(void)
1276 // if game status has not changed, global border has not changed either
1277 if (game_status == game_status_last)
1280 // determine and store new global border bitmap for current game status
1281 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1283 return (global_border_bitmap_last != global_border_bitmap);
1286 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1288 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1289 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1291 // if game status has not changed, nothing has to be redrawn
1292 if (game_status == game_status_last)
1295 // redraw if last screen was title screen
1296 if (game_status_last == GAME_MODE_TITLE)
1299 // redraw if global screen border has changed
1300 if (CheckIfGlobalBorderHasChanged())
1303 // redraw if position or size of playfield area has changed
1304 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1305 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1308 // redraw if position or size of door area has changed
1309 if (dx_last != DX || dy_last != DY ||
1310 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1313 // redraw if position or size of tape area has changed
1314 if (vx_last != VX || vy_last != VY ||
1315 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1318 // redraw if position or size of editor area has changed
1319 if (ex_last != EX || ey_last != EY ||
1320 exsize_last != EXSIZE || eysize_last != EYSIZE)
1327 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1330 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1332 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1335 void RedrawGlobalBorder(void)
1337 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1339 RedrawGlobalBorderFromBitmap(bitmap);
1341 redraw_mask = REDRAW_ALL;
1344 static void RedrawGlobalBorderIfNeeded(void)
1346 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1347 if (game_status == game_status_last)
1351 // copy current draw buffer to later copy back areas that have not changed
1352 if (game_status_last != GAME_MODE_TITLE)
1353 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1355 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1356 if (CheckIfGlobalBorderRedrawIsNeeded())
1359 // redraw global screen border (or clear, if defined to be empty)
1360 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1362 if (game_status == GAME_MODE_EDITOR)
1363 DrawSpecialEditorDoor();
1365 // copy previous playfield and door areas, if they are defined on both
1366 // previous and current screen and if they still have the same size
1368 if (real_sx_last != -1 && real_sy_last != -1 &&
1369 REAL_SX != -1 && REAL_SY != -1 &&
1370 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1371 BlitBitmap(bitmap_db_store_1, backbuffer,
1372 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1375 if (dx_last != -1 && dy_last != -1 &&
1376 DX != -1 && DY != -1 &&
1377 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1378 BlitBitmap(bitmap_db_store_1, backbuffer,
1379 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1381 if (game_status != GAME_MODE_EDITOR)
1383 if (vx_last != -1 && vy_last != -1 &&
1384 VX != -1 && VY != -1 &&
1385 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1386 BlitBitmap(bitmap_db_store_1, backbuffer,
1387 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1391 if (ex_last != -1 && ey_last != -1 &&
1392 EX != -1 && EY != -1 &&
1393 exsize_last == EXSIZE && eysize_last == EYSIZE)
1394 BlitBitmap(bitmap_db_store_1, backbuffer,
1395 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1398 redraw_mask = REDRAW_ALL;
1401 game_status_last = game_status;
1403 global_border_bitmap_last = global_border_bitmap;
1405 real_sx_last = REAL_SX;
1406 real_sy_last = REAL_SY;
1407 full_sxsize_last = FULL_SXSIZE;
1408 full_sysize_last = FULL_SYSIZE;
1411 dxsize_last = DXSIZE;
1412 dysize_last = DYSIZE;
1415 vxsize_last = VXSIZE;
1416 vysize_last = VYSIZE;
1419 exsize_last = EXSIZE;
1420 eysize_last = EYSIZE;
1423 void ClearField(void)
1425 RedrawGlobalBorderIfNeeded();
1427 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1428 // (when entering hall of fame after playing)
1429 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1431 // !!! maybe this should be done before clearing the background !!!
1432 if (game_status == GAME_MODE_PLAYING)
1434 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1435 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1439 SetDrawtoField(DRAW_TO_BACKBUFFER);
1443 void MarkTileDirty(int x, int y)
1445 redraw_mask |= REDRAW_FIELD;
1448 void SetBorderElement(void)
1452 BorderElement = EL_EMPTY;
1454 // the MM game engine does not use a visible border element
1455 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1458 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1460 for (x = 0; x < lev_fieldx; x++)
1462 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1463 BorderElement = EL_STEELWALL;
1465 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1471 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1472 int max_array_fieldx, int max_array_fieldy,
1473 short field[max_array_fieldx][max_array_fieldy],
1474 int max_fieldx, int max_fieldy)
1478 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1479 static int safety = 0;
1481 // check if starting field still has the desired content
1482 if (field[from_x][from_y] == fill_element)
1487 if (safety > max_fieldx * max_fieldy)
1488 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1490 old_element = field[from_x][from_y];
1491 field[from_x][from_y] = fill_element;
1493 for (i = 0; i < 4; i++)
1495 x = from_x + check[i][0];
1496 y = from_y + check[i][1];
1498 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1499 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1500 field, max_fieldx, max_fieldy);
1506 void FloodFillLevel(int from_x, int from_y, int fill_element,
1507 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1508 int max_fieldx, int max_fieldy)
1510 FloodFillLevelExt(from_x, from_y, fill_element,
1511 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1512 max_fieldx, max_fieldy);
1515 void SetRandomAnimationValue(int x, int y)
1517 gfx.anim_random_frame = GfxRandom[x][y];
1520 int getGraphicAnimationFrame(int graphic, int sync_frame)
1522 // animation synchronized with global frame counter, not move position
1523 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1524 sync_frame = FrameCounter;
1526 return getAnimationFrame(graphic_info[graphic].anim_frames,
1527 graphic_info[graphic].anim_delay,
1528 graphic_info[graphic].anim_mode,
1529 graphic_info[graphic].anim_start_frame,
1533 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1535 struct GraphicInfo *g = &graphic_info[graphic];
1536 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1538 if (tilesize == gfx.standard_tile_size)
1539 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1540 else if (tilesize == game.tile_size)
1541 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1543 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1546 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1547 boolean get_backside)
1549 struct GraphicInfo *g = &graphic_info[graphic];
1550 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1551 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1553 if (g->offset_y == 0) // frames are ordered horizontally
1555 int max_width = g->anim_frames_per_line * g->width;
1556 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1558 *x = pos % max_width;
1559 *y = src_y % g->height + pos / max_width * g->height;
1561 else if (g->offset_x == 0) // frames are ordered vertically
1563 int max_height = g->anim_frames_per_line * g->height;
1564 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1566 *x = src_x % g->width + pos / max_height * g->width;
1567 *y = pos % max_height;
1569 else // frames are ordered diagonally
1571 *x = src_x + frame * g->offset_x;
1572 *y = src_y + frame * g->offset_y;
1576 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1577 Bitmap **bitmap, int *x, int *y,
1578 boolean get_backside)
1580 struct GraphicInfo *g = &graphic_info[graphic];
1582 // if no graphics defined at all, use fallback graphics
1583 if (g->bitmaps == NULL)
1584 *g = graphic_info[IMG_CHAR_EXCLAM];
1586 // if no in-game graphics defined, always use standard graphic size
1587 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1588 tilesize = TILESIZE;
1590 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1591 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1593 *x = *x * tilesize / g->tile_size;
1594 *y = *y * tilesize / g->tile_size;
1597 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1598 Bitmap **bitmap, int *x, int *y)
1600 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1603 void getFixedGraphicSource(int graphic, int frame,
1604 Bitmap **bitmap, int *x, int *y)
1606 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1609 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1611 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1614 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1615 int *x, int *y, boolean get_backside)
1617 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1621 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1623 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1626 void DrawGraphic(int x, int y, int graphic, int frame)
1629 if (!IN_SCR_FIELD(x, y))
1631 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1632 printf("DrawGraphic(): This should never happen!\n");
1637 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1640 MarkTileDirty(x, y);
1643 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1646 if (!IN_SCR_FIELD(x, y))
1648 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1649 printf("DrawGraphic(): This should never happen!\n");
1654 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1656 MarkTileDirty(x, y);
1659 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1665 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1667 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1670 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1676 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1677 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1680 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1683 if (!IN_SCR_FIELD(x, y))
1685 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1686 printf("DrawGraphicThruMask(): This should never happen!\n");
1691 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1694 MarkTileDirty(x, y);
1697 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1700 if (!IN_SCR_FIELD(x, y))
1702 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1703 printf("DrawGraphicThruMask(): This should never happen!\n");
1708 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1710 MarkTileDirty(x, y);
1713 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1719 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1721 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1725 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1726 int graphic, int frame)
1731 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1733 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1737 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1739 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1741 MarkTileDirty(x / tilesize, y / tilesize);
1744 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1747 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1748 graphic, frame, tilesize);
1749 MarkTileDirty(x / tilesize, y / tilesize);
1752 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1758 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1759 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1762 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1763 int frame, int tilesize)
1768 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1769 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1772 void DrawMiniGraphic(int x, int y, int graphic)
1774 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1775 MarkTileDirty(x / 2, y / 2);
1778 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1783 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1784 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1787 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1788 int graphic, int frame,
1789 int cut_mode, int mask_mode)
1794 int width = TILEX, height = TILEY;
1797 if (dx || dy) // shifted graphic
1799 if (x < BX1) // object enters playfield from the left
1806 else if (x > BX2) // object enters playfield from the right
1812 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1818 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1820 else if (dx) // general horizontal movement
1821 MarkTileDirty(x + SIGN(dx), y);
1823 if (y < BY1) // object enters playfield from the top
1825 if (cut_mode == CUT_BELOW) // object completely above top border
1833 else if (y > BY2) // object enters playfield from the bottom
1839 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1845 else if (dy > 0 && cut_mode == CUT_ABOVE)
1847 if (y == BY2) // object completely above bottom border
1853 MarkTileDirty(x, y + 1);
1854 } // object leaves playfield to the bottom
1855 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1857 else if (dy) // general vertical movement
1858 MarkTileDirty(x, y + SIGN(dy));
1862 if (!IN_SCR_FIELD(x, y))
1864 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1865 printf("DrawGraphicShifted(): This should never happen!\n");
1870 width = width * TILESIZE_VAR / TILESIZE;
1871 height = height * TILESIZE_VAR / TILESIZE;
1872 cx = cx * TILESIZE_VAR / TILESIZE;
1873 cy = cy * TILESIZE_VAR / TILESIZE;
1874 dx = dx * TILESIZE_VAR / TILESIZE;
1875 dy = dy * TILESIZE_VAR / TILESIZE;
1877 if (width > 0 && height > 0)
1879 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1884 dst_x = FX + x * TILEX_VAR + dx;
1885 dst_y = FY + y * TILEY_VAR + dy;
1887 if (mask_mode == USE_MASKING)
1888 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1891 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1894 MarkTileDirty(x, y);
1898 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1899 int graphic, int frame,
1900 int cut_mode, int mask_mode)
1905 int width = TILEX_VAR, height = TILEY_VAR;
1908 int x2 = x + SIGN(dx);
1909 int y2 = y + SIGN(dy);
1911 // movement with two-tile animations must be sync'ed with movement position,
1912 // not with current GfxFrame (which can be higher when using slow movement)
1913 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1914 int anim_frames = graphic_info[graphic].anim_frames;
1916 // (we also need anim_delay here for movement animations with less frames)
1917 int anim_delay = graphic_info[graphic].anim_delay;
1918 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1920 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1921 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1923 // re-calculate animation frame for two-tile movement animation
1924 frame = getGraphicAnimationFrame(graphic, sync_frame);
1926 // check if movement start graphic inside screen area and should be drawn
1927 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1929 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1931 dst_x = FX + x1 * TILEX_VAR;
1932 dst_y = FY + y1 * TILEY_VAR;
1934 if (mask_mode == USE_MASKING)
1935 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1938 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1941 MarkTileDirty(x1, y1);
1944 // check if movement end graphic inside screen area and should be drawn
1945 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1947 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1949 dst_x = FX + x2 * TILEX_VAR;
1950 dst_y = FY + y2 * TILEY_VAR;
1952 if (mask_mode == USE_MASKING)
1953 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1956 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1959 MarkTileDirty(x2, y2);
1963 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1964 int graphic, int frame,
1965 int cut_mode, int mask_mode)
1969 DrawGraphic(x, y, graphic, frame);
1974 if (graphic_info[graphic].double_movement) // EM style movement images
1975 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1977 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1980 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1981 int graphic, int frame, int cut_mode)
1983 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1986 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1987 int cut_mode, int mask_mode)
1989 int lx = LEVELX(x), ly = LEVELY(y);
1993 if (IN_LEV_FIELD(lx, ly))
1995 SetRandomAnimationValue(lx, ly);
1997 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1998 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2000 // do not use double (EM style) movement graphic when not moving
2001 if (graphic_info[graphic].double_movement && !dx && !dy)
2003 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2004 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2007 else // border element
2009 graphic = el2img(element);
2010 frame = getGraphicAnimationFrame(graphic, -1);
2013 if (element == EL_EXPANDABLE_WALL)
2015 boolean left_stopped = FALSE, right_stopped = FALSE;
2017 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2018 left_stopped = TRUE;
2019 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2020 right_stopped = TRUE;
2022 if (left_stopped && right_stopped)
2024 else if (left_stopped)
2026 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2027 frame = graphic_info[graphic].anim_frames - 1;
2029 else if (right_stopped)
2031 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2032 frame = graphic_info[graphic].anim_frames - 1;
2037 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2038 else if (mask_mode == USE_MASKING)
2039 DrawGraphicThruMask(x, y, graphic, frame);
2041 DrawGraphic(x, y, graphic, frame);
2044 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2045 int cut_mode, int mask_mode)
2047 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2048 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2049 cut_mode, mask_mode);
2052 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2055 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2058 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2061 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2064 void DrawLevelElementThruMask(int x, int y, int element)
2066 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2069 void DrawLevelFieldThruMask(int x, int y)
2071 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2074 // !!! implementation of quicksand is totally broken !!!
2075 #define IS_CRUMBLED_TILE(x, y, e) \
2076 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2077 !IS_MOVING(x, y) || \
2078 (e) == EL_QUICKSAND_EMPTYING || \
2079 (e) == EL_QUICKSAND_FAST_EMPTYING))
2081 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2086 int width, height, cx, cy;
2087 int sx = SCREENX(x), sy = SCREENY(y);
2088 int crumbled_border_size = graphic_info[graphic].border_size;
2089 int crumbled_tile_size = graphic_info[graphic].tile_size;
2090 int crumbled_border_size_var =
2091 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2094 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2096 for (i = 1; i < 4; i++)
2098 int dxx = (i & 1 ? dx : 0);
2099 int dyy = (i & 2 ? dy : 0);
2102 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2105 // check if neighbour field is of same crumble type
2106 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2107 graphic_info[graphic].class ==
2108 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2110 // return if check prevents inner corner
2111 if (same == (dxx == dx && dyy == dy))
2115 // if we reach this point, we have an inner corner
2117 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2119 width = crumbled_border_size_var;
2120 height = crumbled_border_size_var;
2121 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2122 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2124 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2125 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2128 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2133 int width, height, bx, by, cx, cy;
2134 int sx = SCREENX(x), sy = SCREENY(y);
2135 int crumbled_border_size = graphic_info[graphic].border_size;
2136 int crumbled_tile_size = graphic_info[graphic].tile_size;
2137 int crumbled_border_size_var =
2138 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2139 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2142 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2144 // draw simple, sloppy, non-corner-accurate crumbled border
2146 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2147 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2148 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2149 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2151 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2152 FX + sx * TILEX_VAR + cx,
2153 FY + sy * TILEY_VAR + cy);
2155 // (remaining middle border part must be at least as big as corner part)
2156 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2157 crumbled_border_size_var >= TILESIZE_VAR / 3)
2160 // correct corners of crumbled border, if needed
2162 for (i = -1; i <= 1; i += 2)
2164 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2165 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2166 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2169 // check if neighbour field is of same crumble type
2170 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2171 graphic_info[graphic].class ==
2172 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2174 // no crumbled corner, but continued crumbled border
2176 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2177 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2178 int b1 = (i == 1 ? crumbled_border_size_var :
2179 TILESIZE_VAR - 2 * crumbled_border_size_var);
2181 width = crumbled_border_size_var;
2182 height = crumbled_border_size_var;
2184 if (dir == 1 || dir == 2)
2199 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2201 FX + sx * TILEX_VAR + cx,
2202 FY + sy * TILEY_VAR + cy);
2207 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2209 int sx = SCREENX(x), sy = SCREENY(y);
2212 static int xy[4][2] =
2220 if (!IN_LEV_FIELD(x, y))
2223 element = TILE_GFX_ELEMENT(x, y);
2225 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2227 if (!IN_SCR_FIELD(sx, sy))
2230 // crumble field borders towards direct neighbour fields
2231 for (i = 0; i < 4; i++)
2233 int xx = x + xy[i][0];
2234 int yy = y + xy[i][1];
2236 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2239 // check if neighbour field is of same crumble type
2240 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2241 graphic_info[graphic].class ==
2242 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2245 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2248 // crumble inner field corners towards corner neighbour fields
2249 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2250 graphic_info[graphic].anim_frames == 2)
2252 for (i = 0; i < 4; i++)
2254 int dx = (i & 1 ? +1 : -1);
2255 int dy = (i & 2 ? +1 : -1);
2257 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2261 MarkTileDirty(sx, sy);
2263 else // center field is not crumbled -- crumble neighbour fields
2265 // crumble field borders of direct neighbour fields
2266 for (i = 0; i < 4; i++)
2268 int xx = x + xy[i][0];
2269 int yy = y + xy[i][1];
2270 int sxx = sx + xy[i][0];
2271 int syy = sy + xy[i][1];
2273 if (!IN_LEV_FIELD(xx, yy) ||
2274 !IN_SCR_FIELD(sxx, syy))
2277 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2280 element = TILE_GFX_ELEMENT(xx, yy);
2282 if (!IS_CRUMBLED_TILE(xx, yy, element))
2285 graphic = el_act2crm(element, ACTION_DEFAULT);
2287 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2289 MarkTileDirty(sxx, syy);
2292 // crumble inner field corners of corner neighbour fields
2293 for (i = 0; i < 4; i++)
2295 int dx = (i & 1 ? +1 : -1);
2296 int dy = (i & 2 ? +1 : -1);
2302 if (!IN_LEV_FIELD(xx, yy) ||
2303 !IN_SCR_FIELD(sxx, syy))
2306 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2309 element = TILE_GFX_ELEMENT(xx, yy);
2311 if (!IS_CRUMBLED_TILE(xx, yy, element))
2314 graphic = el_act2crm(element, ACTION_DEFAULT);
2316 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2317 graphic_info[graphic].anim_frames == 2)
2318 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2320 MarkTileDirty(sxx, syy);
2325 void DrawLevelFieldCrumbled(int x, int y)
2329 if (!IN_LEV_FIELD(x, y))
2332 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2333 GfxElement[x][y] != EL_UNDEFINED &&
2334 GFX_CRUMBLED(GfxElement[x][y]))
2336 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2341 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2343 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2346 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2349 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2350 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2351 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2352 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2353 int sx = SCREENX(x), sy = SCREENY(y);
2355 DrawGraphic(sx, sy, graphic1, frame1);
2356 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2359 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2361 int sx = SCREENX(x), sy = SCREENY(y);
2362 static int xy[4][2] =
2371 // crumble direct neighbour fields (required for field borders)
2372 for (i = 0; i < 4; i++)
2374 int xx = x + xy[i][0];
2375 int yy = y + xy[i][1];
2376 int sxx = sx + xy[i][0];
2377 int syy = sy + xy[i][1];
2379 if (!IN_LEV_FIELD(xx, yy) ||
2380 !IN_SCR_FIELD(sxx, syy) ||
2381 !GFX_CRUMBLED(Feld[xx][yy]) ||
2385 DrawLevelField(xx, yy);
2388 // crumble corner neighbour fields (required for inner field corners)
2389 for (i = 0; i < 4; i++)
2391 int dx = (i & 1 ? +1 : -1);
2392 int dy = (i & 2 ? +1 : -1);
2398 if (!IN_LEV_FIELD(xx, yy) ||
2399 !IN_SCR_FIELD(sxx, syy) ||
2400 !GFX_CRUMBLED(Feld[xx][yy]) ||
2404 int element = TILE_GFX_ELEMENT(xx, yy);
2405 int graphic = el_act2crm(element, ACTION_DEFAULT);
2407 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2408 graphic_info[graphic].anim_frames == 2)
2409 DrawLevelField(xx, yy);
2413 static int getBorderElement(int x, int y)
2417 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2418 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2419 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2420 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2421 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2422 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2423 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2425 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2426 int steel_position = (x == -1 && y == -1 ? 0 :
2427 x == lev_fieldx && y == -1 ? 1 :
2428 x == -1 && y == lev_fieldy ? 2 :
2429 x == lev_fieldx && y == lev_fieldy ? 3 :
2430 x == -1 || x == lev_fieldx ? 4 :
2431 y == -1 || y == lev_fieldy ? 5 : 6);
2433 return border[steel_position][steel_type];
2436 void DrawScreenElement(int x, int y, int element)
2438 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2439 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2442 void DrawLevelElement(int x, int y, int element)
2444 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2445 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2448 void DrawScreenField(int x, int y)
2450 int lx = LEVELX(x), ly = LEVELY(y);
2451 int element, content;
2453 if (!IN_LEV_FIELD(lx, ly))
2455 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2458 element = getBorderElement(lx, ly);
2460 DrawScreenElement(x, y, element);
2465 element = Feld[lx][ly];
2466 content = Store[lx][ly];
2468 if (IS_MOVING(lx, ly))
2470 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2471 boolean cut_mode = NO_CUTTING;
2473 if (element == EL_QUICKSAND_EMPTYING ||
2474 element == EL_QUICKSAND_FAST_EMPTYING ||
2475 element == EL_MAGIC_WALL_EMPTYING ||
2476 element == EL_BD_MAGIC_WALL_EMPTYING ||
2477 element == EL_DC_MAGIC_WALL_EMPTYING ||
2478 element == EL_AMOEBA_DROPPING)
2479 cut_mode = CUT_ABOVE;
2480 else if (element == EL_QUICKSAND_FILLING ||
2481 element == EL_QUICKSAND_FAST_FILLING ||
2482 element == EL_MAGIC_WALL_FILLING ||
2483 element == EL_BD_MAGIC_WALL_FILLING ||
2484 element == EL_DC_MAGIC_WALL_FILLING)
2485 cut_mode = CUT_BELOW;
2487 if (cut_mode == CUT_ABOVE)
2488 DrawScreenElement(x, y, element);
2490 DrawScreenElement(x, y, EL_EMPTY);
2493 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2494 else if (cut_mode == NO_CUTTING)
2495 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2498 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2500 if (cut_mode == CUT_BELOW &&
2501 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2502 DrawLevelElement(lx, ly + 1, element);
2505 if (content == EL_ACID)
2507 int dir = MovDir[lx][ly];
2508 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2509 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2511 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2513 // prevent target field from being drawn again (but without masking)
2514 // (this would happen if target field is scanned after moving element)
2515 Stop[newlx][newly] = TRUE;
2518 else if (IS_BLOCKED(lx, ly))
2523 boolean cut_mode = NO_CUTTING;
2524 int element_old, content_old;
2526 Blocked2Moving(lx, ly, &oldx, &oldy);
2529 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2530 MovDir[oldx][oldy] == MV_RIGHT);
2532 element_old = Feld[oldx][oldy];
2533 content_old = Store[oldx][oldy];
2535 if (element_old == EL_QUICKSAND_EMPTYING ||
2536 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2537 element_old == EL_MAGIC_WALL_EMPTYING ||
2538 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2539 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2540 element_old == EL_AMOEBA_DROPPING)
2541 cut_mode = CUT_ABOVE;
2543 DrawScreenElement(x, y, EL_EMPTY);
2546 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2548 else if (cut_mode == NO_CUTTING)
2549 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2552 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2555 else if (IS_DRAWABLE(element))
2556 DrawScreenElement(x, y, element);
2558 DrawScreenElement(x, y, EL_EMPTY);
2561 void DrawLevelField(int x, int y)
2563 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2564 DrawScreenField(SCREENX(x), SCREENY(y));
2565 else if (IS_MOVING(x, y))
2569 Moving2Blocked(x, y, &newx, &newy);
2570 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2571 DrawScreenField(SCREENX(newx), SCREENY(newy));
2573 else if (IS_BLOCKED(x, y))
2577 Blocked2Moving(x, y, &oldx, &oldy);
2578 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2579 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2583 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2584 int (*el2img_function)(int), boolean masked,
2585 int element_bits_draw)
2587 int element_base = map_mm_wall_element(element);
2588 int element_bits = (IS_DF_WALL(element) ?
2589 element - EL_DF_WALL_START :
2590 IS_MM_WALL(element) ?
2591 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2592 int graphic = el2img_function(element_base);
2593 int tilesize_draw = tilesize / 2;
2598 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2600 for (i = 0; i < 4; i++)
2602 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2603 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2605 if (!(element_bits_draw & (1 << i)))
2608 if (element_bits & (1 << i))
2611 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2612 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2614 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2615 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2620 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2621 tilesize_draw, tilesize_draw);
2626 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2627 boolean masked, int element_bits_draw)
2629 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2630 element, tilesize, el2edimg, masked, element_bits_draw);
2633 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2634 int (*el2img_function)(int))
2636 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2640 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2643 if (IS_MM_WALL(element))
2645 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2646 element, tilesize, el2edimg, masked, 0x000f);
2650 int graphic = el2edimg(element);
2653 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2655 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2659 void DrawSizedElement(int x, int y, int element, int tilesize)
2661 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2664 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2666 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2669 void DrawMiniElement(int x, int y, int element)
2673 graphic = el2edimg(element);
2674 DrawMiniGraphic(x, y, graphic);
2677 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2680 int x = sx + scroll_x, y = sy + scroll_y;
2682 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2683 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2684 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2685 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2687 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2690 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2692 int x = sx + scroll_x, y = sy + scroll_y;
2694 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2695 DrawMiniElement(sx, sy, EL_EMPTY);
2696 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2697 DrawMiniElement(sx, sy, Feld[x][y]);
2699 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2702 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2703 int x, int y, int xsize, int ysize,
2704 int tile_width, int tile_height)
2708 int dst_x = startx + x * tile_width;
2709 int dst_y = starty + y * tile_height;
2710 int width = graphic_info[graphic].width;
2711 int height = graphic_info[graphic].height;
2712 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2713 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2714 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2715 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2716 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2717 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2718 boolean draw_masked = graphic_info[graphic].draw_masked;
2720 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2722 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2724 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2728 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2729 inner_sx + (x - 1) * tile_width % inner_width);
2730 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2731 inner_sy + (y - 1) * tile_height % inner_height);
2734 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2737 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2741 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2742 int x, int y, int xsize, int ysize,
2745 int font_width = getFontWidth(font_nr);
2746 int font_height = getFontHeight(font_nr);
2748 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2749 font_width, font_height);
2752 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2754 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2755 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2756 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2757 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2758 boolean no_delay = (tape.warp_forward);
2759 unsigned int anim_delay = 0;
2760 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2761 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2762 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2763 int font_width = getFontWidth(font_nr);
2764 int font_height = getFontHeight(font_nr);
2765 int max_xsize = level.envelope[envelope_nr].xsize;
2766 int max_ysize = level.envelope[envelope_nr].ysize;
2767 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2768 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2769 int xend = max_xsize;
2770 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2771 int xstep = (xstart < xend ? 1 : 0);
2772 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2774 int end = MAX(xend - xstart, yend - ystart);
2777 for (i = start; i <= end; i++)
2779 int last_frame = end; // last frame of this "for" loop
2780 int x = xstart + i * xstep;
2781 int y = ystart + i * ystep;
2782 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2783 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2784 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2785 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2788 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2790 BlitScreenToBitmap(backbuffer);
2792 SetDrawtoField(DRAW_TO_BACKBUFFER);
2794 for (yy = 0; yy < ysize; yy++)
2795 for (xx = 0; xx < xsize; xx++)
2796 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2798 DrawTextBuffer(sx + font_width, sy + font_height,
2799 level.envelope[envelope_nr].text, font_nr, max_xsize,
2800 xsize - 2, ysize - 2, 0, mask_mode,
2801 level.envelope[envelope_nr].autowrap,
2802 level.envelope[envelope_nr].centered, FALSE);
2804 redraw_mask |= REDRAW_FIELD;
2807 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2810 ClearAutoRepeatKeyEvents();
2813 void ShowEnvelope(int envelope_nr)
2815 int element = EL_ENVELOPE_1 + envelope_nr;
2816 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2817 int sound_opening = element_info[element].sound[ACTION_OPENING];
2818 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2819 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2820 boolean no_delay = (tape.warp_forward);
2821 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2822 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2823 int anim_mode = graphic_info[graphic].anim_mode;
2824 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2825 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2827 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2829 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2831 if (anim_mode == ANIM_DEFAULT)
2832 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2834 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2837 Delay(wait_delay_value);
2839 WaitForEventToContinue();
2841 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2843 if (anim_mode != ANIM_NONE)
2844 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2846 if (anim_mode == ANIM_DEFAULT)
2847 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2849 game.envelope_active = FALSE;
2851 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2853 redraw_mask |= REDRAW_FIELD;
2857 static void setRequestBasePosition(int *x, int *y)
2859 int sx_base, sy_base;
2861 if (request.x != -1)
2862 sx_base = request.x;
2863 else if (request.align == ALIGN_LEFT)
2865 else if (request.align == ALIGN_RIGHT)
2866 sx_base = SX + SXSIZE;
2868 sx_base = SX + SXSIZE / 2;
2870 if (request.y != -1)
2871 sy_base = request.y;
2872 else if (request.valign == VALIGN_TOP)
2874 else if (request.valign == VALIGN_BOTTOM)
2875 sy_base = SY + SYSIZE;
2877 sy_base = SY + SYSIZE / 2;
2883 static void setRequestPositionExt(int *x, int *y, int width, int height,
2884 boolean add_border_size)
2886 int border_size = request.border_size;
2887 int sx_base, sy_base;
2890 setRequestBasePosition(&sx_base, &sy_base);
2892 if (request.align == ALIGN_LEFT)
2894 else if (request.align == ALIGN_RIGHT)
2895 sx = sx_base - width;
2897 sx = sx_base - width / 2;
2899 if (request.valign == VALIGN_TOP)
2901 else if (request.valign == VALIGN_BOTTOM)
2902 sy = sy_base - height;
2904 sy = sy_base - height / 2;
2906 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2907 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2909 if (add_border_size)
2919 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2921 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2924 static void DrawEnvelopeRequest(char *text)
2926 char *text_final = text;
2927 char *text_door_style = NULL;
2928 int graphic = IMG_BACKGROUND_REQUEST;
2929 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2930 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2931 int font_nr = FONT_REQUEST;
2932 int font_width = getFontWidth(font_nr);
2933 int font_height = getFontHeight(font_nr);
2934 int border_size = request.border_size;
2935 int line_spacing = request.line_spacing;
2936 int line_height = font_height + line_spacing;
2937 int max_text_width = request.width - 2 * border_size;
2938 int max_text_height = request.height - 2 * border_size;
2939 int line_length = max_text_width / font_width;
2940 int max_lines = max_text_height / line_height;
2941 int text_width = line_length * font_width;
2942 int width = request.width;
2943 int height = request.height;
2944 int tile_size = MAX(request.step_offset, 1);
2945 int x_steps = width / tile_size;
2946 int y_steps = height / tile_size;
2947 int sx_offset = border_size;
2948 int sy_offset = border_size;
2952 if (request.centered)
2953 sx_offset = (request.width - text_width) / 2;
2955 if (request.wrap_single_words && !request.autowrap)
2957 char *src_text_ptr, *dst_text_ptr;
2959 text_door_style = checked_malloc(2 * strlen(text) + 1);
2961 src_text_ptr = text;
2962 dst_text_ptr = text_door_style;
2964 while (*src_text_ptr)
2966 if (*src_text_ptr == ' ' ||
2967 *src_text_ptr == '?' ||
2968 *src_text_ptr == '!')
2969 *dst_text_ptr++ = '\n';
2971 if (*src_text_ptr != ' ')
2972 *dst_text_ptr++ = *src_text_ptr;
2977 *dst_text_ptr = '\0';
2979 text_final = text_door_style;
2982 setRequestPosition(&sx, &sy, FALSE);
2984 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2986 for (y = 0; y < y_steps; y++)
2987 for (x = 0; x < x_steps; x++)
2988 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2989 x, y, x_steps, y_steps,
2990 tile_size, tile_size);
2992 // force DOOR font inside door area
2993 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2995 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2996 line_length, -1, max_lines, line_spacing, mask_mode,
2997 request.autowrap, request.centered, FALSE);
3001 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3002 RedrawGadget(tool_gadget[i]);
3004 // store readily prepared envelope request for later use when animating
3005 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3007 if (text_door_style)
3008 free(text_door_style);
3011 static void AnimateEnvelopeRequest(int anim_mode, int action)
3013 int graphic = IMG_BACKGROUND_REQUEST;
3014 boolean draw_masked = graphic_info[graphic].draw_masked;
3015 int delay_value_normal = request.step_delay;
3016 int delay_value_fast = delay_value_normal / 2;
3017 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3018 boolean no_delay = (tape.warp_forward);
3019 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3020 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3021 unsigned int anim_delay = 0;
3023 int tile_size = MAX(request.step_offset, 1);
3024 int max_xsize = request.width / tile_size;
3025 int max_ysize = request.height / tile_size;
3026 int max_xsize_inner = max_xsize - 2;
3027 int max_ysize_inner = max_ysize - 2;
3029 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3030 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3031 int xend = max_xsize_inner;
3032 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3033 int xstep = (xstart < xend ? 1 : 0);
3034 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3036 int end = MAX(xend - xstart, yend - ystart);
3039 if (setup.quick_doors)
3046 for (i = start; i <= end; i++)
3048 int last_frame = end; // last frame of this "for" loop
3049 int x = xstart + i * xstep;
3050 int y = ystart + i * ystep;
3051 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3052 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3053 int xsize_size_left = (xsize - 1) * tile_size;
3054 int ysize_size_top = (ysize - 1) * tile_size;
3055 int max_xsize_pos = (max_xsize - 1) * tile_size;
3056 int max_ysize_pos = (max_ysize - 1) * tile_size;
3057 int width = xsize * tile_size;
3058 int height = ysize * tile_size;
3063 setRequestPosition(&src_x, &src_y, FALSE);
3064 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3066 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3068 for (yy = 0; yy < 2; yy++)
3070 for (xx = 0; xx < 2; xx++)
3072 int src_xx = src_x + xx * max_xsize_pos;
3073 int src_yy = src_y + yy * max_ysize_pos;
3074 int dst_xx = dst_x + xx * xsize_size_left;
3075 int dst_yy = dst_y + yy * ysize_size_top;
3076 int xx_size = (xx ? tile_size : xsize_size_left);
3077 int yy_size = (yy ? tile_size : ysize_size_top);
3080 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3081 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3083 BlitBitmap(bitmap_db_store_2, backbuffer,
3084 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3088 redraw_mask |= REDRAW_FIELD;
3092 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3095 ClearAutoRepeatKeyEvents();
3098 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3100 int graphic = IMG_BACKGROUND_REQUEST;
3101 int sound_opening = SND_REQUEST_OPENING;
3102 int sound_closing = SND_REQUEST_CLOSING;
3103 int anim_mode_1 = request.anim_mode; // (higher priority)
3104 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3105 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3106 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3107 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3109 if (game_status == GAME_MODE_PLAYING)
3110 BlitScreenToBitmap(backbuffer);
3112 SetDrawtoField(DRAW_TO_BACKBUFFER);
3114 // SetDrawBackgroundMask(REDRAW_NONE);
3116 if (action == ACTION_OPENING)
3118 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3120 if (req_state & REQ_ASK)
3122 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3123 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3125 else if (req_state & REQ_CONFIRM)
3127 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3129 else if (req_state & REQ_PLAYER)
3131 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3132 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3133 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3134 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3137 DrawEnvelopeRequest(text);
3140 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3142 if (action == ACTION_OPENING)
3144 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3146 if (anim_mode == ANIM_DEFAULT)
3147 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3149 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3153 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3155 if (anim_mode != ANIM_NONE)
3156 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3158 if (anim_mode == ANIM_DEFAULT)
3159 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3162 game.envelope_active = FALSE;
3164 if (action == ACTION_CLOSING)
3165 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3167 // SetDrawBackgroundMask(last_draw_background_mask);
3169 redraw_mask |= REDRAW_FIELD;
3173 if (action == ACTION_CLOSING &&
3174 game_status == GAME_MODE_PLAYING &&
3175 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3176 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3179 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3181 if (IS_MM_WALL(element))
3183 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3189 int graphic = el2preimg(element);
3191 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3192 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3197 void DrawLevel(int draw_background_mask)
3201 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3202 SetDrawBackgroundMask(draw_background_mask);
3206 for (x = BX1; x <= BX2; x++)
3207 for (y = BY1; y <= BY2; y++)
3208 DrawScreenField(x, y);
3210 redraw_mask |= REDRAW_FIELD;
3213 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3218 for (x = 0; x < size_x; x++)
3219 for (y = 0; y < size_y; y++)
3220 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3222 redraw_mask |= REDRAW_FIELD;
3225 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3229 for (x = 0; x < size_x; x++)
3230 for (y = 0; y < size_y; y++)
3231 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3233 redraw_mask |= REDRAW_FIELD;
3236 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3238 boolean show_level_border = (BorderElement != EL_EMPTY);
3239 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3240 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3241 int tile_size = preview.tile_size;
3242 int preview_width = preview.xsize * tile_size;
3243 int preview_height = preview.ysize * tile_size;
3244 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3245 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3246 int real_preview_width = real_preview_xsize * tile_size;
3247 int real_preview_height = real_preview_ysize * tile_size;
3248 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3249 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3252 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3255 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3257 dst_x += (preview_width - real_preview_width) / 2;
3258 dst_y += (preview_height - real_preview_height) / 2;
3260 for (x = 0; x < real_preview_xsize; x++)
3262 for (y = 0; y < real_preview_ysize; y++)
3264 int lx = from_x + x + (show_level_border ? -1 : 0);
3265 int ly = from_y + y + (show_level_border ? -1 : 0);
3266 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3267 getBorderElement(lx, ly));
3269 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3270 element, tile_size);
3274 redraw_mask |= REDRAW_FIELD;
3277 #define MICROLABEL_EMPTY 0
3278 #define MICROLABEL_LEVEL_NAME 1
3279 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3280 #define MICROLABEL_LEVEL_AUTHOR 3
3281 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3282 #define MICROLABEL_IMPORTED_FROM 5
3283 #define MICROLABEL_IMPORTED_BY_HEAD 6
3284 #define MICROLABEL_IMPORTED_BY 7
3286 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3288 int max_text_width = SXSIZE;
3289 int font_width = getFontWidth(font_nr);
3291 if (pos->align == ALIGN_CENTER)
3292 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3293 else if (pos->align == ALIGN_RIGHT)
3294 max_text_width = pos->x;
3296 max_text_width = SXSIZE - pos->x;
3298 return max_text_width / font_width;
3301 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3303 char label_text[MAX_OUTPUT_LINESIZE + 1];
3304 int max_len_label_text;
3305 int font_nr = pos->font;
3308 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3311 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3312 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3313 mode == MICROLABEL_IMPORTED_BY_HEAD)
3314 font_nr = pos->font_alt;
3316 max_len_label_text = getMaxTextLength(pos, font_nr);
3318 if (pos->size != -1)
3319 max_len_label_text = pos->size;
3321 for (i = 0; i < max_len_label_text; i++)
3322 label_text[i] = ' ';
3323 label_text[max_len_label_text] = '\0';
3325 if (strlen(label_text) > 0)
3326 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3329 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3330 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3331 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3332 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3333 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3334 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3335 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3336 max_len_label_text);
3337 label_text[max_len_label_text] = '\0';
3339 if (strlen(label_text) > 0)
3340 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3342 redraw_mask |= REDRAW_FIELD;
3345 static void DrawPreviewLevelLabel(int mode)
3347 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3350 static void DrawPreviewLevelInfo(int mode)
3352 if (mode == MICROLABEL_LEVEL_NAME)
3353 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3354 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3355 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3358 static void DrawPreviewLevelExt(boolean restart)
3360 static unsigned int scroll_delay = 0;
3361 static unsigned int label_delay = 0;
3362 static int from_x, from_y, scroll_direction;
3363 static int label_state, label_counter;
3364 unsigned int scroll_delay_value = preview.step_delay;
3365 boolean show_level_border = (BorderElement != EL_EMPTY);
3366 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3367 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3374 if (preview.anim_mode == ANIM_CENTERED)
3376 if (level_xsize > preview.xsize)
3377 from_x = (level_xsize - preview.xsize) / 2;
3378 if (level_ysize > preview.ysize)
3379 from_y = (level_ysize - preview.ysize) / 2;
3382 from_x += preview.xoffset;
3383 from_y += preview.yoffset;
3385 scroll_direction = MV_RIGHT;
3389 DrawPreviewLevelPlayfield(from_x, from_y);
3390 DrawPreviewLevelLabel(label_state);
3392 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3393 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3395 // initialize delay counters
3396 DelayReached(&scroll_delay, 0);
3397 DelayReached(&label_delay, 0);
3399 if (leveldir_current->name)
3401 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3402 char label_text[MAX_OUTPUT_LINESIZE + 1];
3403 int font_nr = pos->font;
3404 int max_len_label_text = getMaxTextLength(pos, font_nr);
3406 if (pos->size != -1)
3407 max_len_label_text = pos->size;
3409 strncpy(label_text, leveldir_current->name, max_len_label_text);
3410 label_text[max_len_label_text] = '\0';
3412 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3413 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3419 // scroll preview level, if needed
3420 if (preview.anim_mode != ANIM_NONE &&
3421 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3422 DelayReached(&scroll_delay, scroll_delay_value))
3424 switch (scroll_direction)
3429 from_x -= preview.step_offset;
3430 from_x = (from_x < 0 ? 0 : from_x);
3433 scroll_direction = MV_UP;
3437 if (from_x < level_xsize - preview.xsize)
3439 from_x += preview.step_offset;
3440 from_x = (from_x > level_xsize - preview.xsize ?
3441 level_xsize - preview.xsize : from_x);
3444 scroll_direction = MV_DOWN;
3450 from_y -= preview.step_offset;
3451 from_y = (from_y < 0 ? 0 : from_y);
3454 scroll_direction = MV_RIGHT;
3458 if (from_y < level_ysize - preview.ysize)
3460 from_y += preview.step_offset;
3461 from_y = (from_y > level_ysize - preview.ysize ?
3462 level_ysize - preview.ysize : from_y);
3465 scroll_direction = MV_LEFT;
3472 DrawPreviewLevelPlayfield(from_x, from_y);
3475 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3476 // redraw micro level label, if needed
3477 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3478 !strEqual(level.author, ANONYMOUS_NAME) &&
3479 !strEqual(level.author, leveldir_current->name) &&
3480 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3482 int max_label_counter = 23;
3484 if (leveldir_current->imported_from != NULL &&
3485 strlen(leveldir_current->imported_from) > 0)
3486 max_label_counter += 14;
3487 if (leveldir_current->imported_by != NULL &&
3488 strlen(leveldir_current->imported_by) > 0)
3489 max_label_counter += 14;
3491 label_counter = (label_counter + 1) % max_label_counter;
3492 label_state = (label_counter >= 0 && label_counter <= 7 ?
3493 MICROLABEL_LEVEL_NAME :
3494 label_counter >= 9 && label_counter <= 12 ?
3495 MICROLABEL_LEVEL_AUTHOR_HEAD :
3496 label_counter >= 14 && label_counter <= 21 ?
3497 MICROLABEL_LEVEL_AUTHOR :
3498 label_counter >= 23 && label_counter <= 26 ?
3499 MICROLABEL_IMPORTED_FROM_HEAD :
3500 label_counter >= 28 && label_counter <= 35 ?
3501 MICROLABEL_IMPORTED_FROM :
3502 label_counter >= 37 && label_counter <= 40 ?
3503 MICROLABEL_IMPORTED_BY_HEAD :
3504 label_counter >= 42 && label_counter <= 49 ?
3505 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3507 if (leveldir_current->imported_from == NULL &&
3508 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3509 label_state == MICROLABEL_IMPORTED_FROM))
3510 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3511 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3513 DrawPreviewLevelLabel(label_state);
3517 static void DrawPreviewPlayers(void)
3519 if (game_status != GAME_MODE_MAIN)
3522 if (!network.enabled && !setup.team_mode)
3525 boolean player_found[MAX_PLAYERS];
3526 int num_players = 0;
3529 for (i = 0; i < MAX_PLAYERS; i++)
3530 player_found[i] = FALSE;
3532 // check which players can be found in the level (simple approach)
3533 for (x = 0; x < lev_fieldx; x++)
3535 for (y = 0; y < lev_fieldy; y++)
3537 int element = level.field[x][y];
3539 if (ELEM_IS_PLAYER(element))
3541 int player_nr = GET_PLAYER_NR(element);
3543 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3545 if (!player_found[player_nr])
3548 player_found[player_nr] = TRUE;
3553 struct TextPosInfo *pos = &menu.main.preview_players;
3554 int tile_size = pos->tile_size;
3555 int border_size = pos->border_size;
3556 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3557 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3558 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3559 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3560 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3561 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3562 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3563 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3564 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3565 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3566 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3567 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3569 // clear area in which the players will be drawn
3570 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3571 max_players_width, max_players_height);
3573 // only draw players if level is suited for team mode
3574 if (num_players < 2)
3577 // draw all players that were found in the level
3578 for (i = 0; i < MAX_PLAYERS; i++)
3580 if (player_found[i])
3582 int graphic = el2img(EL_PLAYER_1 + i);
3584 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3586 xpos += player_xoffset;
3587 ypos += player_yoffset;
3592 void DrawPreviewLevelInitial(void)
3594 DrawPreviewLevelExt(TRUE);
3595 DrawPreviewPlayers();
3598 void DrawPreviewLevelAnimation(void)
3600 DrawPreviewLevelExt(FALSE);
3603 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3604 int border_size, int font_nr)
3606 int graphic = el2img(EL_PLAYER_1 + player_nr);
3607 int font_height = getFontHeight(font_nr);
3608 int player_height = MAX(tile_size, font_height);
3609 int xoffset_text = tile_size + border_size;
3610 int yoffset_text = (player_height - font_height) / 2;
3611 int yoffset_graphic = (player_height - tile_size) / 2;
3612 char *player_name = getNetworkPlayerName(player_nr + 1);
3614 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3616 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3619 static void DrawNetworkPlayersExt(boolean force)
3621 if (game_status != GAME_MODE_MAIN)
3624 if (!network.connected && !force)
3627 int num_players = 0;
3630 for (i = 0; i < MAX_PLAYERS; i++)
3631 if (stored_player[i].connected_network)
3634 struct TextPosInfo *pos = &menu.main.network_players;
3635 int tile_size = pos->tile_size;
3636 int border_size = pos->border_size;
3637 int xoffset_text = tile_size + border_size;
3638 int font_nr = pos->font;
3639 int font_width = getFontWidth(font_nr);
3640 int font_height = getFontHeight(font_nr);
3641 int player_height = MAX(tile_size, font_height);
3642 int player_yoffset = player_height + border_size;
3643 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3644 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3645 int all_players_height = num_players * player_yoffset - border_size;
3646 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3647 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3648 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3650 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3651 max_players_width, max_players_height);
3653 // first draw local network player ...
3654 for (i = 0; i < MAX_PLAYERS; i++)
3656 if (stored_player[i].connected_network &&
3657 stored_player[i].connected_locally)
3659 char *player_name = getNetworkPlayerName(i + 1);
3660 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3661 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3663 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3665 ypos += player_yoffset;
3669 // ... then draw all other network players
3670 for (i = 0; i < MAX_PLAYERS; i++)
3672 if (stored_player[i].connected_network &&
3673 !stored_player[i].connected_locally)
3675 char *player_name = getNetworkPlayerName(i + 1);
3676 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3677 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3679 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3681 ypos += player_yoffset;
3686 void DrawNetworkPlayers(void)
3688 DrawNetworkPlayersExt(FALSE);
3691 void ClearNetworkPlayers(void)
3693 DrawNetworkPlayersExt(TRUE);
3696 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3697 int graphic, int sync_frame,
3700 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3702 if (mask_mode == USE_MASKING)
3703 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3705 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3708 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3709 int graphic, int sync_frame, int mask_mode)
3711 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3713 if (mask_mode == USE_MASKING)
3714 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3716 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3719 static void DrawGraphicAnimation(int x, int y, int graphic)
3721 int lx = LEVELX(x), ly = LEVELY(y);
3723 if (!IN_SCR_FIELD(x, y))
3726 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3727 graphic, GfxFrame[lx][ly], NO_MASKING);
3729 MarkTileDirty(x, y);
3732 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3734 int lx = LEVELX(x), ly = LEVELY(y);
3736 if (!IN_SCR_FIELD(x, y))
3739 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3740 graphic, GfxFrame[lx][ly], NO_MASKING);
3741 MarkTileDirty(x, y);
3744 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3746 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3749 void DrawLevelElementAnimation(int x, int y, int element)
3751 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3753 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3756 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3758 int sx = SCREENX(x), sy = SCREENY(y);
3760 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3763 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3766 DrawGraphicAnimation(sx, sy, graphic);
3769 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3770 DrawLevelFieldCrumbled(x, y);
3772 if (GFX_CRUMBLED(Feld[x][y]))
3773 DrawLevelFieldCrumbled(x, y);
3777 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3779 int sx = SCREENX(x), sy = SCREENY(y);
3782 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3785 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3787 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3790 DrawGraphicAnimation(sx, sy, graphic);
3792 if (GFX_CRUMBLED(element))
3793 DrawLevelFieldCrumbled(x, y);
3796 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3798 if (player->use_murphy)
3800 // this works only because currently only one player can be "murphy" ...
3801 static int last_horizontal_dir = MV_LEFT;
3802 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3804 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3805 last_horizontal_dir = move_dir;
3807 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3809 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3811 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3817 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3820 static boolean equalGraphics(int graphic1, int graphic2)
3822 struct GraphicInfo *g1 = &graphic_info[graphic1];
3823 struct GraphicInfo *g2 = &graphic_info[graphic2];
3825 return (g1->bitmap == g2->bitmap &&
3826 g1->src_x == g2->src_x &&
3827 g1->src_y == g2->src_y &&
3828 g1->anim_frames == g2->anim_frames &&
3829 g1->anim_delay == g2->anim_delay &&
3830 g1->anim_mode == g2->anim_mode);
3833 void DrawAllPlayers(void)
3837 for (i = 0; i < MAX_PLAYERS; i++)
3838 if (stored_player[i].active)
3839 DrawPlayer(&stored_player[i]);
3842 void DrawPlayerField(int x, int y)
3844 if (!IS_PLAYER(x, y))
3847 DrawPlayer(PLAYERINFO(x, y));
3850 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3852 void DrawPlayer(struct PlayerInfo *player)
3854 int jx = player->jx;
3855 int jy = player->jy;
3856 int move_dir = player->MovDir;
3857 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3858 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3859 int last_jx = (player->is_moving ? jx - dx : jx);
3860 int last_jy = (player->is_moving ? jy - dy : jy);
3861 int next_jx = jx + dx;
3862 int next_jy = jy + dy;
3863 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3864 boolean player_is_opaque = FALSE;
3865 int sx = SCREENX(jx), sy = SCREENY(jy);
3866 int sxx = 0, syy = 0;
3867 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3869 int action = ACTION_DEFAULT;
3870 int last_player_graphic = getPlayerGraphic(player, move_dir);
3871 int last_player_frame = player->Frame;
3874 // GfxElement[][] is set to the element the player is digging or collecting;
3875 // remove also for off-screen player if the player is not moving anymore
3876 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3877 GfxElement[jx][jy] = EL_UNDEFINED;
3879 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3883 if (!IN_LEV_FIELD(jx, jy))
3885 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3886 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3887 printf("DrawPlayerField(): This should never happen!\n");
3892 if (element == EL_EXPLOSION)
3895 action = (player->is_pushing ? ACTION_PUSHING :
3896 player->is_digging ? ACTION_DIGGING :
3897 player->is_collecting ? ACTION_COLLECTING :
3898 player->is_moving ? ACTION_MOVING :
3899 player->is_snapping ? ACTION_SNAPPING :
3900 player->is_dropping ? ACTION_DROPPING :
3901 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3903 if (player->is_waiting)
3904 move_dir = player->dir_waiting;
3906 InitPlayerGfxAnimation(player, action, move_dir);
3908 // --------------------------------------------------------------------------
3909 // draw things in the field the player is leaving, if needed
3910 // --------------------------------------------------------------------------
3912 if (player->is_moving)
3914 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3916 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3918 if (last_element == EL_DYNAMITE_ACTIVE ||
3919 last_element == EL_EM_DYNAMITE_ACTIVE ||
3920 last_element == EL_SP_DISK_RED_ACTIVE)
3921 DrawDynamite(last_jx, last_jy);
3923 DrawLevelFieldThruMask(last_jx, last_jy);
3925 else if (last_element == EL_DYNAMITE_ACTIVE ||
3926 last_element == EL_EM_DYNAMITE_ACTIVE ||
3927 last_element == EL_SP_DISK_RED_ACTIVE)
3928 DrawDynamite(last_jx, last_jy);
3930 /* !!! this is not enough to prevent flickering of players which are
3931 moving next to each others without a free tile between them -- this
3932 can only be solved by drawing all players layer by layer (first the
3933 background, then the foreground etc.) !!! => TODO */
3934 else if (!IS_PLAYER(last_jx, last_jy))
3935 DrawLevelField(last_jx, last_jy);
3938 DrawLevelField(last_jx, last_jy);
3941 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3942 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3945 if (!IN_SCR_FIELD(sx, sy))
3948 // --------------------------------------------------------------------------
3949 // draw things behind the player, if needed
3950 // --------------------------------------------------------------------------
3953 DrawLevelElement(jx, jy, Back[jx][jy]);
3954 else if (IS_ACTIVE_BOMB(element))
3955 DrawLevelElement(jx, jy, EL_EMPTY);
3958 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3960 int old_element = GfxElement[jx][jy];
3961 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3962 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3964 if (GFX_CRUMBLED(old_element))
3965 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3967 DrawGraphic(sx, sy, old_graphic, frame);
3969 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3970 player_is_opaque = TRUE;
3974 GfxElement[jx][jy] = EL_UNDEFINED;
3976 // make sure that pushed elements are drawn with correct frame rate
3977 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3979 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3980 GfxFrame[jx][jy] = player->StepFrame;
3982 DrawLevelField(jx, jy);
3986 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3987 // -----------------------------------------------------------------------
3988 // draw player himself
3989 // -----------------------------------------------------------------------
3991 graphic = getPlayerGraphic(player, move_dir);
3993 // in the case of changed player action or direction, prevent the current
3994 // animation frame from being restarted for identical animations
3995 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3996 player->Frame = last_player_frame;
3998 frame = getGraphicAnimationFrame(graphic, player->Frame);
4002 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4003 sxx = player->GfxPos;
4005 syy = player->GfxPos;
4008 if (player_is_opaque)
4009 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4011 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4013 if (SHIELD_ON(player))
4015 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4016 IMG_SHIELD_NORMAL_ACTIVE);
4017 int frame = getGraphicAnimationFrame(graphic, -1);
4019 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4023 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4026 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4027 sxx = player->GfxPos;
4029 syy = player->GfxPos;
4033 // --------------------------------------------------------------------------
4034 // draw things the player is pushing, if needed
4035 // --------------------------------------------------------------------------
4037 if (player->is_pushing && player->is_moving)
4039 int px = SCREENX(jx), py = SCREENY(jy);
4040 int pxx = (TILEX - ABS(sxx)) * dx;
4041 int pyy = (TILEY - ABS(syy)) * dy;
4042 int gfx_frame = GfxFrame[jx][jy];
4048 if (!IS_MOVING(jx, jy)) // push movement already finished
4050 element = Feld[next_jx][next_jy];
4051 gfx_frame = GfxFrame[next_jx][next_jy];
4054 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4056 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4057 frame = getGraphicAnimationFrame(graphic, sync_frame);
4059 // draw background element under pushed element (like the Sokoban field)
4060 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4062 // this allows transparent pushing animation over non-black background
4065 DrawLevelElement(jx, jy, Back[jx][jy]);
4067 DrawLevelElement(jx, jy, EL_EMPTY);
4069 if (Back[next_jx][next_jy])
4070 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4072 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4074 else if (Back[next_jx][next_jy])
4075 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4078 // do not draw (EM style) pushing animation when pushing is finished
4079 // (two-tile animations usually do not contain start and end frame)
4080 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4081 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4083 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4085 // masked drawing is needed for EMC style (double) movement graphics
4086 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4087 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4091 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4092 // -----------------------------------------------------------------------
4093 // draw player himself
4094 // -----------------------------------------------------------------------
4096 graphic = getPlayerGraphic(player, move_dir);
4098 // in the case of changed player action or direction, prevent the current
4099 // animation frame from being restarted for identical animations
4100 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4101 player->Frame = last_player_frame;
4103 frame = getGraphicAnimationFrame(graphic, player->Frame);
4107 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4108 sxx = player->GfxPos;
4110 syy = player->GfxPos;
4113 if (player_is_opaque)
4114 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4116 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4118 if (SHIELD_ON(player))
4120 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4121 IMG_SHIELD_NORMAL_ACTIVE);
4122 int frame = getGraphicAnimationFrame(graphic, -1);
4124 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4128 // --------------------------------------------------------------------------
4129 // draw things in front of player (active dynamite or dynabombs)
4130 // --------------------------------------------------------------------------
4132 if (IS_ACTIVE_BOMB(element))
4134 graphic = el2img(element);
4135 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4137 if (game.emulation == EMU_SUPAPLEX)
4138 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4140 DrawGraphicThruMask(sx, sy, graphic, frame);
4143 if (player_is_moving && last_element == EL_EXPLOSION)
4145 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4146 GfxElement[last_jx][last_jy] : EL_EMPTY);
4147 int graphic = el_act2img(element, ACTION_EXPLODING);
4148 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4149 int phase = ExplodePhase[last_jx][last_jy] - 1;
4150 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4153 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4156 // --------------------------------------------------------------------------
4157 // draw elements the player is just walking/passing through/under
4158 // --------------------------------------------------------------------------
4160 if (player_is_moving)
4162 // handle the field the player is leaving ...
4163 if (IS_ACCESSIBLE_INSIDE(last_element))
4164 DrawLevelField(last_jx, last_jy);
4165 else if (IS_ACCESSIBLE_UNDER(last_element))
4166 DrawLevelFieldThruMask(last_jx, last_jy);
4169 // do not redraw accessible elements if the player is just pushing them
4170 if (!player_is_moving || !player->is_pushing)
4172 // ... and the field the player is entering
4173 if (IS_ACCESSIBLE_INSIDE(element))
4174 DrawLevelField(jx, jy);
4175 else if (IS_ACCESSIBLE_UNDER(element))
4176 DrawLevelFieldThruMask(jx, jy);
4179 MarkTileDirty(sx, sy);
4182 // ----------------------------------------------------------------------------
4184 void WaitForEventToContinue(void)
4186 boolean still_wait = TRUE;
4188 if (program.headless)
4191 // simulate releasing mouse button over last gadget, if still pressed
4193 HandleGadgets(-1, -1, 0);
4195 button_status = MB_RELEASED;
4203 if (NextValidEvent(&event))
4207 case EVENT_BUTTONRELEASE:
4208 case EVENT_KEYPRESS:
4209 case SDL_CONTROLLERBUTTONDOWN:
4210 case SDL_JOYBUTTONDOWN:
4214 case EVENT_KEYRELEASE:
4215 ClearPlayerAction();
4219 HandleOtherEvents(&event);
4223 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4232 #define MAX_REQUEST_LINES 13
4233 #define MAX_REQUEST_LINE_FONT1_LEN 7
4234 #define MAX_REQUEST_LINE_FONT2_LEN 10
4236 static int RequestHandleEvents(unsigned int req_state)
4238 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4240 int width = request.width;
4241 int height = request.height;
4245 // when showing request dialog after game ended, deactivate game panel
4246 if (game_just_ended)
4247 game.panel.active = FALSE;
4249 game.request_active = TRUE;
4251 setRequestPosition(&sx, &sy, FALSE);
4253 button_status = MB_RELEASED;
4255 request_gadget_id = -1;
4260 if (game_just_ended)
4262 // the MM game engine does not use a special (scrollable) field buffer
4263 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4264 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4266 HandleGameActions();
4268 SetDrawtoField(DRAW_TO_BACKBUFFER);
4270 if (global.use_envelope_request)
4272 // copy current state of request area to middle of playfield area
4273 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4281 while (NextValidEvent(&event))
4285 case EVENT_BUTTONPRESS:
4286 case EVENT_BUTTONRELEASE:
4287 case EVENT_MOTIONNOTIFY:
4291 if (event.type == EVENT_MOTIONNOTIFY)
4296 motion_status = TRUE;
4297 mx = ((MotionEvent *) &event)->x;
4298 my = ((MotionEvent *) &event)->y;
4302 motion_status = FALSE;
4303 mx = ((ButtonEvent *) &event)->x;
4304 my = ((ButtonEvent *) &event)->y;
4305 if (event.type == EVENT_BUTTONPRESS)
4306 button_status = ((ButtonEvent *) &event)->button;
4308 button_status = MB_RELEASED;
4311 // this sets 'request_gadget_id'
4312 HandleGadgets(mx, my, button_status);
4314 switch (request_gadget_id)
4316 case TOOL_CTRL_ID_YES:
4319 case TOOL_CTRL_ID_NO:
4322 case TOOL_CTRL_ID_CONFIRM:
4323 result = TRUE | FALSE;
4326 case TOOL_CTRL_ID_PLAYER_1:
4329 case TOOL_CTRL_ID_PLAYER_2:
4332 case TOOL_CTRL_ID_PLAYER_3:
4335 case TOOL_CTRL_ID_PLAYER_4:
4346 case SDL_WINDOWEVENT:
4347 HandleWindowEvent((WindowEvent *) &event);
4350 case SDL_APP_WILLENTERBACKGROUND:
4351 case SDL_APP_DIDENTERBACKGROUND:
4352 case SDL_APP_WILLENTERFOREGROUND:
4353 case SDL_APP_DIDENTERFOREGROUND:
4354 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4357 case EVENT_KEYPRESS:
4359 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4364 if (req_state & REQ_CONFIRM)
4373 #if defined(KSYM_Rewind)
4374 case KSYM_Rewind: // for Amazon Fire TV remote
4383 #if defined(KSYM_FastForward)
4384 case KSYM_FastForward: // for Amazon Fire TV remote
4390 HandleKeysDebug(key, KEY_PRESSED);
4394 if (req_state & REQ_PLAYER)
4396 int old_player_nr = setup.network_player_nr;
4399 result = old_player_nr + 1;
4404 result = old_player_nr + 1;
4435 case EVENT_KEYRELEASE:
4436 ClearPlayerAction();
4439 case SDL_CONTROLLERBUTTONDOWN:
4440 switch (event.cbutton.button)
4442 case SDL_CONTROLLER_BUTTON_A:
4443 case SDL_CONTROLLER_BUTTON_X:
4444 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4445 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4449 case SDL_CONTROLLER_BUTTON_B:
4450 case SDL_CONTROLLER_BUTTON_Y:
4451 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4452 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4453 case SDL_CONTROLLER_BUTTON_BACK:
4458 if (req_state & REQ_PLAYER)
4460 int old_player_nr = setup.network_player_nr;
4463 result = old_player_nr + 1;
4465 switch (event.cbutton.button)
4467 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4468 case SDL_CONTROLLER_BUTTON_Y:
4472 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4473 case SDL_CONTROLLER_BUTTON_B:
4477 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4478 case SDL_CONTROLLER_BUTTON_A:
4482 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4483 case SDL_CONTROLLER_BUTTON_X:
4494 case SDL_CONTROLLERBUTTONUP:
4495 HandleJoystickEvent(&event);
4496 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)
4531 if (game_just_ended)
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);
4543 game.request_active = FALSE;
4548 static boolean RequestDoor(char *text, unsigned int req_state)
4550 unsigned int old_door_state;
4551 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4552 int font_nr = FONT_TEXT_2;
4557 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4559 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4560 font_nr = FONT_TEXT_1;
4563 if (game_status == GAME_MODE_PLAYING)
4564 BlitScreenToBitmap(backbuffer);
4566 // disable deactivated drawing when quick-loading level tape recording
4567 if (tape.playing && tape.deactivate_display)
4568 TapeDeactivateDisplayOff(TRUE);
4570 SetMouseCursor(CURSOR_DEFAULT);
4572 // pause network game while waiting for request to answer
4573 if (network.enabled &&
4574 game_status == GAME_MODE_PLAYING &&
4575 !game.all_players_gone &&
4576 req_state & REQUEST_WAIT_FOR_INPUT)
4577 SendToServer_PausePlaying();
4579 old_door_state = GetDoorState();
4581 // simulate releasing mouse button over last gadget, if still pressed
4583 HandleGadgets(-1, -1, 0);
4587 // draw released gadget before proceeding
4590 if (old_door_state & DOOR_OPEN_1)
4592 CloseDoor(DOOR_CLOSE_1);
4594 // save old door content
4595 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4596 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4599 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4600 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4602 // clear door drawing field
4603 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4605 // force DOOR font inside door area
4606 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4608 // write text for request
4609 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4611 char text_line[max_request_line_len + 1];
4617 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4619 tc = *(text_ptr + tx);
4620 // if (!tc || tc == ' ')
4621 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4625 if ((tc == '?' || tc == '!') && tl == 0)
4635 strncpy(text_line, text_ptr, tl);
4638 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4639 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4640 text_line, font_nr);
4642 text_ptr += tl + (tc == ' ' ? 1 : 0);
4643 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4648 if (req_state & REQ_ASK)
4650 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4651 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4653 else if (req_state & REQ_CONFIRM)
4655 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4657 else if (req_state & REQ_PLAYER)
4659 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4660 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4661 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4662 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4665 // copy request gadgets to door backbuffer
4666 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4668 OpenDoor(DOOR_OPEN_1);
4670 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4672 if (game_status == GAME_MODE_PLAYING)
4674 SetPanelBackground();
4675 SetDrawBackgroundMask(REDRAW_DOOR_1);
4679 SetDrawBackgroundMask(REDRAW_FIELD);
4685 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4687 // ---------- handle request buttons ----------
4688 result = RequestHandleEvents(req_state);
4692 if (!(req_state & REQ_STAY_OPEN))
4694 CloseDoor(DOOR_CLOSE_1);
4696 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4697 (req_state & REQ_REOPEN))
4698 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4703 if (game_status == GAME_MODE_PLAYING)
4705 SetPanelBackground();
4706 SetDrawBackgroundMask(REDRAW_DOOR_1);
4710 SetDrawBackgroundMask(REDRAW_FIELD);
4713 // continue network game after request
4714 if (network.enabled &&
4715 game_status == GAME_MODE_PLAYING &&
4716 !game.all_players_gone &&
4717 req_state & REQUEST_WAIT_FOR_INPUT)
4718 SendToServer_ContinuePlaying();
4720 // restore deactivated drawing when quick-loading level tape recording
4721 if (tape.playing && tape.deactivate_display)
4722 TapeDeactivateDisplayOn();
4727 static boolean RequestEnvelope(char *text, unsigned int req_state)
4731 if (game_status == GAME_MODE_PLAYING)
4732 BlitScreenToBitmap(backbuffer);
4734 // disable deactivated drawing when quick-loading level tape recording
4735 if (tape.playing && tape.deactivate_display)
4736 TapeDeactivateDisplayOff(TRUE);
4738 SetMouseCursor(CURSOR_DEFAULT);
4740 // pause network game while waiting for request to answer
4741 if (network.enabled &&
4742 game_status == GAME_MODE_PLAYING &&
4743 !game.all_players_gone &&
4744 req_state & REQUEST_WAIT_FOR_INPUT)
4745 SendToServer_PausePlaying();
4747 // simulate releasing mouse button over last gadget, if still pressed
4749 HandleGadgets(-1, -1, 0);
4753 // (replace with setting corresponding request background)
4754 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4755 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4757 // clear door drawing field
4758 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4760 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4762 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4764 if (game_status == GAME_MODE_PLAYING)
4766 SetPanelBackground();
4767 SetDrawBackgroundMask(REDRAW_DOOR_1);
4771 SetDrawBackgroundMask(REDRAW_FIELD);
4777 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4779 // ---------- handle request buttons ----------
4780 result = RequestHandleEvents(req_state);
4784 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4788 if (game_status == GAME_MODE_PLAYING)
4790 SetPanelBackground();
4791 SetDrawBackgroundMask(REDRAW_DOOR_1);
4795 SetDrawBackgroundMask(REDRAW_FIELD);
4798 // continue network game after request
4799 if (network.enabled &&
4800 game_status == GAME_MODE_PLAYING &&
4801 !game.all_players_gone &&
4802 req_state & REQUEST_WAIT_FOR_INPUT)
4803 SendToServer_ContinuePlaying();
4805 // restore deactivated drawing when quick-loading level tape recording
4806 if (tape.playing && tape.deactivate_display)
4807 TapeDeactivateDisplayOn();
4812 boolean Request(char *text, unsigned int req_state)
4814 boolean overlay_active = GetOverlayActive();
4817 SetOverlayActive(FALSE);
4819 if (global.use_envelope_request)
4820 result = RequestEnvelope(text, req_state);
4822 result = RequestDoor(text, req_state);
4824 SetOverlayActive(overlay_active);
4829 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4831 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4832 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4835 if (dpo1->sort_priority != dpo2->sort_priority)
4836 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4838 compare_result = dpo1->nr - dpo2->nr;
4840 return compare_result;
4843 void InitGraphicCompatibilityInfo_Doors(void)
4849 struct DoorInfo *door;
4853 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4854 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4856 { -1, -1, -1, NULL }
4858 struct Rect door_rect_list[] =
4860 { DX, DY, DXSIZE, DYSIZE },
4861 { VX, VY, VXSIZE, VYSIZE }
4865 for (i = 0; doors[i].door_token != -1; i++)
4867 int door_token = doors[i].door_token;
4868 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4869 int part_1 = doors[i].part_1;
4870 int part_8 = doors[i].part_8;
4871 int part_2 = part_1 + 1;
4872 int part_3 = part_1 + 2;
4873 struct DoorInfo *door = doors[i].door;
4874 struct Rect *door_rect = &door_rect_list[door_index];
4875 boolean door_gfx_redefined = FALSE;
4877 // check if any door part graphic definitions have been redefined
4879 for (j = 0; door_part_controls[j].door_token != -1; j++)
4881 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4882 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4884 if (dpc->door_token == door_token && fi->redefined)
4885 door_gfx_redefined = TRUE;
4888 // check for old-style door graphic/animation modifications
4890 if (!door_gfx_redefined)
4892 if (door->anim_mode & ANIM_STATIC_PANEL)
4894 door->panel.step_xoffset = 0;
4895 door->panel.step_yoffset = 0;
4898 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4900 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4901 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4902 int num_door_steps, num_panel_steps;
4904 // remove door part graphics other than the two default wings
4906 for (j = 0; door_part_controls[j].door_token != -1; j++)
4908 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4909 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4911 if (dpc->graphic >= part_3 &&
4912 dpc->graphic <= part_8)
4916 // set graphics and screen positions of the default wings
4918 g_part_1->width = door_rect->width;
4919 g_part_1->height = door_rect->height;
4920 g_part_2->width = door_rect->width;
4921 g_part_2->height = door_rect->height;
4922 g_part_2->src_x = door_rect->width;
4923 g_part_2->src_y = g_part_1->src_y;
4925 door->part_2.x = door->part_1.x;
4926 door->part_2.y = door->part_1.y;
4928 if (door->width != -1)
4930 g_part_1->width = door->width;
4931 g_part_2->width = door->width;
4933 // special treatment for graphics and screen position of right wing
4934 g_part_2->src_x += door_rect->width - door->width;
4935 door->part_2.x += door_rect->width - door->width;
4938 if (door->height != -1)
4940 g_part_1->height = door->height;
4941 g_part_2->height = door->height;
4943 // special treatment for graphics and screen position of bottom wing
4944 g_part_2->src_y += door_rect->height - door->height;
4945 door->part_2.y += door_rect->height - door->height;
4948 // set animation delays for the default wings and panels
4950 door->part_1.step_delay = door->step_delay;
4951 door->part_2.step_delay = door->step_delay;
4952 door->panel.step_delay = door->step_delay;
4954 // set animation draw order for the default wings
4956 door->part_1.sort_priority = 2; // draw left wing over ...
4957 door->part_2.sort_priority = 1; // ... right wing
4959 // set animation draw offset for the default wings
4961 if (door->anim_mode & ANIM_HORIZONTAL)
4963 door->part_1.step_xoffset = door->step_offset;
4964 door->part_1.step_yoffset = 0;
4965 door->part_2.step_xoffset = door->step_offset * -1;
4966 door->part_2.step_yoffset = 0;
4968 num_door_steps = g_part_1->width / door->step_offset;
4970 else // ANIM_VERTICAL
4972 door->part_1.step_xoffset = 0;
4973 door->part_1.step_yoffset = door->step_offset;
4974 door->part_2.step_xoffset = 0;
4975 door->part_2.step_yoffset = door->step_offset * -1;
4977 num_door_steps = g_part_1->height / door->step_offset;
4980 // set animation draw offset for the default panels
4982 if (door->step_offset > 1)
4984 num_panel_steps = 2 * door_rect->height / door->step_offset;
4985 door->panel.start_step = num_panel_steps - num_door_steps;
4986 door->panel.start_step_closing = door->panel.start_step;
4990 num_panel_steps = door_rect->height / door->step_offset;
4991 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4992 door->panel.start_step_closing = door->panel.start_step;
4993 door->panel.step_delay *= 2;
5000 void InitDoors(void)
5004 for (i = 0; door_part_controls[i].door_token != -1; i++)
5006 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5007 struct DoorPartOrderInfo *dpo = &door_part_order[i];
5009 // initialize "start_step_opening" and "start_step_closing", if needed
5010 if (dpc->pos->start_step_opening == 0 &&
5011 dpc->pos->start_step_closing == 0)
5013 // dpc->pos->start_step_opening = dpc->pos->start_step;
5014 dpc->pos->start_step_closing = dpc->pos->start_step;
5017 // fill structure for door part draw order (sorted below)
5019 dpo->sort_priority = dpc->pos->sort_priority;
5022 // sort door part controls according to sort_priority and graphic number
5023 qsort(door_part_order, MAX_DOOR_PARTS,
5024 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5027 unsigned int OpenDoor(unsigned int door_state)
5029 if (door_state & DOOR_COPY_BACK)
5031 if (door_state & DOOR_OPEN_1)
5032 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5033 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5035 if (door_state & DOOR_OPEN_2)
5036 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5037 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5039 door_state &= ~DOOR_COPY_BACK;
5042 return MoveDoor(door_state);
5045 unsigned int CloseDoor(unsigned int door_state)
5047 unsigned int old_door_state = GetDoorState();
5049 if (!(door_state & DOOR_NO_COPY_BACK))
5051 if (old_door_state & DOOR_OPEN_1)
5052 BlitBitmap(backbuffer, bitmap_db_door_1,
5053 DX, DY, DXSIZE, DYSIZE, 0, 0);
5055 if (old_door_state & DOOR_OPEN_2)
5056 BlitBitmap(backbuffer, bitmap_db_door_2,
5057 VX, VY, VXSIZE, VYSIZE, 0, 0);
5059 door_state &= ~DOOR_NO_COPY_BACK;
5062 return MoveDoor(door_state);
5065 unsigned int GetDoorState(void)
5067 return MoveDoor(DOOR_GET_STATE);
5070 unsigned int SetDoorState(unsigned int door_state)
5072 return MoveDoor(door_state | DOOR_SET_STATE);
5075 static int euclid(int a, int b)
5077 return (b ? euclid(b, a % b) : a);
5080 unsigned int MoveDoor(unsigned int door_state)
5082 struct Rect door_rect_list[] =
5084 { DX, DY, DXSIZE, DYSIZE },
5085 { VX, VY, VXSIZE, VYSIZE }
5087 static int door1 = DOOR_CLOSE_1;
5088 static int door2 = DOOR_CLOSE_2;
5089 unsigned int door_delay = 0;
5090 unsigned int door_delay_value;
5093 if (door_state == DOOR_GET_STATE)
5094 return (door1 | door2);
5096 if (door_state & DOOR_SET_STATE)
5098 if (door_state & DOOR_ACTION_1)
5099 door1 = door_state & DOOR_ACTION_1;
5100 if (door_state & DOOR_ACTION_2)
5101 door2 = door_state & DOOR_ACTION_2;
5103 return (door1 | door2);
5106 if (!(door_state & DOOR_FORCE_REDRAW))
5108 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5109 door_state &= ~DOOR_OPEN_1;
5110 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5111 door_state &= ~DOOR_CLOSE_1;
5112 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5113 door_state &= ~DOOR_OPEN_2;
5114 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5115 door_state &= ~DOOR_CLOSE_2;
5118 if (global.autoplay_leveldir)
5120 door_state |= DOOR_NO_DELAY;
5121 door_state &= ~DOOR_CLOSE_ALL;
5124 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5125 door_state |= DOOR_NO_DELAY;
5127 if (door_state & DOOR_ACTION)
5129 boolean door_panel_drawn[NUM_DOORS];
5130 boolean panel_has_doors[NUM_DOORS];
5131 boolean door_part_skip[MAX_DOOR_PARTS];
5132 boolean door_part_done[MAX_DOOR_PARTS];
5133 boolean door_part_done_all;
5134 int num_steps[MAX_DOOR_PARTS];
5135 int max_move_delay = 0; // delay for complete animations of all doors
5136 int max_step_delay = 0; // delay (ms) between two animation frames
5137 int num_move_steps = 0; // number of animation steps for all doors
5138 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5139 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5140 int current_move_delay = 0;
5144 for (i = 0; i < NUM_DOORS; i++)
5145 panel_has_doors[i] = FALSE;
5147 for (i = 0; i < MAX_DOOR_PARTS; i++)
5149 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5150 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5151 int door_token = dpc->door_token;
5153 door_part_done[i] = FALSE;
5154 door_part_skip[i] = (!(door_state & door_token) ||
5158 for (i = 0; i < MAX_DOOR_PARTS; i++)
5160 int nr = door_part_order[i].nr;
5161 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5162 struct DoorPartPosInfo *pos = dpc->pos;
5163 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5164 int door_token = dpc->door_token;
5165 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5166 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5167 int step_xoffset = ABS(pos->step_xoffset);
5168 int step_yoffset = ABS(pos->step_yoffset);
5169 int step_delay = pos->step_delay;
5170 int current_door_state = door_state & door_token;
5171 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5172 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5173 boolean part_opening = (is_panel ? door_closing : door_opening);
5174 int start_step = (part_opening ? pos->start_step_opening :
5175 pos->start_step_closing);
5176 float move_xsize = (step_xoffset ? g->width : 0);
5177 float move_ysize = (step_yoffset ? g->height : 0);
5178 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5179 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5180 int move_steps = (move_xsteps && move_ysteps ?
5181 MIN(move_xsteps, move_ysteps) :
5182 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5183 int move_delay = move_steps * step_delay;
5185 if (door_part_skip[nr])
5188 max_move_delay = MAX(max_move_delay, move_delay);
5189 max_step_delay = (max_step_delay == 0 ? step_delay :
5190 euclid(max_step_delay, step_delay));
5191 num_steps[nr] = move_steps;
5195 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5197 panel_has_doors[door_index] = TRUE;
5201 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5203 num_move_steps = max_move_delay / max_step_delay;
5204 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5206 door_delay_value = max_step_delay;
5208 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5210 start = num_move_steps - 1;
5214 // opening door sound has priority over simultaneously closing door
5215 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5217 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5219 if (door_state & DOOR_OPEN_1)
5220 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5221 if (door_state & DOOR_OPEN_2)
5222 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5224 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5226 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5228 if (door_state & DOOR_CLOSE_1)
5229 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5230 if (door_state & DOOR_CLOSE_2)
5231 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5235 for (k = start; k < num_move_steps; k++)
5237 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5239 door_part_done_all = TRUE;
5241 for (i = 0; i < NUM_DOORS; i++)
5242 door_panel_drawn[i] = FALSE;
5244 for (i = 0; i < MAX_DOOR_PARTS; i++)
5246 int nr = door_part_order[i].nr;
5247 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5248 struct DoorPartPosInfo *pos = dpc->pos;
5249 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5250 int door_token = dpc->door_token;
5251 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5252 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5253 boolean is_panel_and_door_has_closed = FALSE;
5254 struct Rect *door_rect = &door_rect_list[door_index];
5255 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5257 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5258 int current_door_state = door_state & door_token;
5259 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5260 boolean door_closing = !door_opening;
5261 boolean part_opening = (is_panel ? door_closing : door_opening);
5262 boolean part_closing = !part_opening;
5263 int start_step = (part_opening ? pos->start_step_opening :
5264 pos->start_step_closing);
5265 int step_delay = pos->step_delay;
5266 int step_factor = step_delay / max_step_delay;
5267 int k1 = (step_factor ? k / step_factor + 1 : k);
5268 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5269 int kk = MAX(0, k2);
5272 int src_x, src_y, src_xx, src_yy;
5273 int dst_x, dst_y, dst_xx, dst_yy;
5276 if (door_part_skip[nr])
5279 if (!(door_state & door_token))
5287 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5288 int kk_door = MAX(0, k2_door);
5289 int sync_frame = kk_door * door_delay_value;
5290 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5292 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5293 &g_src_x, &g_src_y);
5298 if (!door_panel_drawn[door_index])
5300 ClearRectangle(drawto, door_rect->x, door_rect->y,
5301 door_rect->width, door_rect->height);
5303 door_panel_drawn[door_index] = TRUE;
5306 // draw opening or closing door parts
5308 if (pos->step_xoffset < 0) // door part on right side
5311 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5314 if (dst_xx + width > door_rect->width)
5315 width = door_rect->width - dst_xx;
5317 else // door part on left side
5320 dst_xx = pos->x - kk * pos->step_xoffset;
5324 src_xx = ABS(dst_xx);
5328 width = g->width - src_xx;
5330 if (width > door_rect->width)
5331 width = door_rect->width;
5333 // printf("::: k == %d [%d] \n", k, start_step);
5336 if (pos->step_yoffset < 0) // door part on bottom side
5339 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5342 if (dst_yy + height > door_rect->height)
5343 height = door_rect->height - dst_yy;
5345 else // door part on top side
5348 dst_yy = pos->y - kk * pos->step_yoffset;
5352 src_yy = ABS(dst_yy);
5356 height = g->height - src_yy;
5359 src_x = g_src_x + src_xx;
5360 src_y = g_src_y + src_yy;
5362 dst_x = door_rect->x + dst_xx;
5363 dst_y = door_rect->y + dst_yy;
5365 is_panel_and_door_has_closed =
5368 panel_has_doors[door_index] &&
5369 k >= num_move_steps_doors_only - 1);
5371 if (width >= 0 && width <= g->width &&
5372 height >= 0 && height <= g->height &&
5373 !is_panel_and_door_has_closed)
5375 if (is_panel || !pos->draw_masked)
5376 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5379 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5383 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5385 if ((part_opening && (width < 0 || height < 0)) ||
5386 (part_closing && (width >= g->width && height >= g->height)))
5387 door_part_done[nr] = TRUE;
5389 // continue door part animations, but not panel after door has closed
5390 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5391 door_part_done_all = FALSE;
5394 if (!(door_state & DOOR_NO_DELAY))
5398 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5400 current_move_delay += max_step_delay;
5402 // prevent OS (Windows) from complaining about program not responding
5406 if (door_part_done_all)
5410 if (!(door_state & DOOR_NO_DELAY))
5412 // wait for specified door action post delay
5413 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5414 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5415 else if (door_state & DOOR_ACTION_1)
5416 door_delay_value = door_1.post_delay;
5417 else if (door_state & DOOR_ACTION_2)
5418 door_delay_value = door_2.post_delay;
5420 while (!DelayReached(&door_delay, door_delay_value))
5425 if (door_state & DOOR_ACTION_1)
5426 door1 = door_state & DOOR_ACTION_1;
5427 if (door_state & DOOR_ACTION_2)
5428 door2 = door_state & DOOR_ACTION_2;
5430 // draw masked border over door area
5431 DrawMaskedBorder(REDRAW_DOOR_1);
5432 DrawMaskedBorder(REDRAW_DOOR_2);
5434 ClearAutoRepeatKeyEvents();
5436 return (door1 | door2);
5439 static boolean useSpecialEditorDoor(void)
5441 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5442 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5444 // do not draw special editor door if editor border defined or redefined
5445 if (graphic_info[graphic].bitmap != NULL || redefined)
5448 // do not draw special editor door if global border defined to be empty
5449 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5452 // do not draw special editor door if viewport definitions do not match
5456 EY + EYSIZE != VY + VYSIZE)
5462 void DrawSpecialEditorDoor(void)
5464 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5465 int top_border_width = gfx1->width;
5466 int top_border_height = gfx1->height;
5467 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5468 int ex = EX - outer_border;
5469 int ey = EY - outer_border;
5470 int vy = VY - outer_border;
5471 int exsize = EXSIZE + 2 * outer_border;
5473 if (!useSpecialEditorDoor())
5476 // draw bigger level editor toolbox window
5477 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5478 top_border_width, top_border_height, ex, ey - top_border_height);
5479 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5480 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5482 redraw_mask |= REDRAW_ALL;
5485 void UndrawSpecialEditorDoor(void)
5487 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5488 int top_border_width = gfx1->width;
5489 int top_border_height = gfx1->height;
5490 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5491 int ex = EX - outer_border;
5492 int ey = EY - outer_border;
5493 int ey_top = ey - top_border_height;
5494 int exsize = EXSIZE + 2 * outer_border;
5495 int eysize = EYSIZE + 2 * outer_border;
5497 if (!useSpecialEditorDoor())
5500 // draw normal tape recorder window
5501 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5503 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5504 ex, ey_top, top_border_width, top_border_height,
5506 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5507 ex, ey, exsize, eysize, ex, ey);
5511 // if screen background is set to "[NONE]", clear editor toolbox window
5512 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5513 ClearRectangle(drawto, ex, ey, exsize, eysize);
5516 redraw_mask |= REDRAW_ALL;
5520 // ---------- new tool button stuff -------------------------------------------
5525 struct TextPosInfo *pos;
5528 } toolbutton_info[NUM_TOOL_BUTTONS] =
5531 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5532 TOOL_CTRL_ID_YES, "yes"
5535 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5536 TOOL_CTRL_ID_NO, "no"
5539 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5540 TOOL_CTRL_ID_CONFIRM, "confirm"
5543 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5544 TOOL_CTRL_ID_PLAYER_1, "player 1"
5547 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5548 TOOL_CTRL_ID_PLAYER_2, "player 2"
5551 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5552 TOOL_CTRL_ID_PLAYER_3, "player 3"
5555 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5556 TOOL_CTRL_ID_PLAYER_4, "player 4"
5560 void CreateToolButtons(void)
5564 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5566 int graphic = toolbutton_info[i].graphic;
5567 struct GraphicInfo *gfx = &graphic_info[graphic];
5568 struct TextPosInfo *pos = toolbutton_info[i].pos;
5569 struct GadgetInfo *gi;
5570 Bitmap *deco_bitmap = None;
5571 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5572 unsigned int event_mask = GD_EVENT_RELEASED;
5575 int gd_x = gfx->src_x;
5576 int gd_y = gfx->src_y;
5577 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5578 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5583 if (global.use_envelope_request)
5585 setRequestPosition(&dx, &dy, TRUE);
5587 // check if request buttons are outside of envelope and fix, if needed
5588 if (x < 0 || x + gfx->width > request.width ||
5589 y < 0 || y + gfx->height > request.height)
5591 if (id == TOOL_CTRL_ID_YES)
5594 y = request.height - 2 * request.border_size - gfx->height;
5596 else if (id == TOOL_CTRL_ID_NO)
5598 x = request.width - 2 * request.border_size - gfx->width;
5599 y = request.height - 2 * request.border_size - gfx->height;
5601 else if (id == TOOL_CTRL_ID_CONFIRM)
5603 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5604 y = request.height - 2 * request.border_size - gfx->height;
5606 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5608 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5610 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5611 y = request.height - 2 * request.border_size - gfx->height * 2;
5613 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5614 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5619 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5621 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5623 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5624 pos->size, &deco_bitmap, &deco_x, &deco_y);
5625 deco_xpos = (gfx->width - pos->size) / 2;
5626 deco_ypos = (gfx->height - pos->size) / 2;
5629 gi = CreateGadget(GDI_CUSTOM_ID, id,
5630 GDI_IMAGE_ID, graphic,
5631 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5634 GDI_WIDTH, gfx->width,
5635 GDI_HEIGHT, gfx->height,
5636 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5637 GDI_STATE, GD_BUTTON_UNPRESSED,
5638 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5639 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5640 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5641 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5642 GDI_DECORATION_SIZE, pos->size, pos->size,
5643 GDI_DECORATION_SHIFTING, 1, 1,
5644 GDI_DIRECT_DRAW, FALSE,
5645 GDI_EVENT_MASK, event_mask,
5646 GDI_CALLBACK_ACTION, HandleToolButtons,
5650 Error(ERR_EXIT, "cannot create gadget");
5652 tool_gadget[id] = gi;
5656 void FreeToolButtons(void)
5660 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5661 FreeGadget(tool_gadget[i]);
5664 static void UnmapToolButtons(void)
5668 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5669 UnmapGadget(tool_gadget[i]);
5672 static void HandleToolButtons(struct GadgetInfo *gi)
5674 request_gadget_id = gi->custom_id;
5677 static struct Mapping_EM_to_RND_object
5680 boolean is_rnd_to_em_mapping; // unique mapping EM <-> RND
5681 boolean is_backside; // backside of moving element
5687 em_object_mapping_list[] =
5690 Xblank, TRUE, FALSE,
5694 Yacid_splash_eB, FALSE, FALSE,
5695 EL_ACID_SPLASH_RIGHT, -1, -1
5698 Yacid_splash_wB, FALSE, FALSE,
5699 EL_ACID_SPLASH_LEFT, -1, -1
5702 #ifdef EM_ENGINE_BAD_ROLL
5704 Xstone_force_e, FALSE, FALSE,
5705 EL_ROCK, -1, MV_BIT_RIGHT
5708 Xstone_force_w, FALSE, FALSE,
5709 EL_ROCK, -1, MV_BIT_LEFT
5712 Xnut_force_e, FALSE, FALSE,
5713 EL_NUT, -1, MV_BIT_RIGHT
5716 Xnut_force_w, FALSE, FALSE,
5717 EL_NUT, -1, MV_BIT_LEFT
5720 Xspring_force_e, FALSE, FALSE,
5721 EL_SPRING, -1, MV_BIT_RIGHT
5724 Xspring_force_w, FALSE, FALSE,
5725 EL_SPRING, -1, MV_BIT_LEFT
5728 Xemerald_force_e, FALSE, FALSE,
5729 EL_EMERALD, -1, MV_BIT_RIGHT
5732 Xemerald_force_w, FALSE, FALSE,
5733 EL_EMERALD, -1, MV_BIT_LEFT
5736 Xdiamond_force_e, FALSE, FALSE,
5737 EL_DIAMOND, -1, MV_BIT_RIGHT
5740 Xdiamond_force_w, FALSE, FALSE,
5741 EL_DIAMOND, -1, MV_BIT_LEFT
5744 Xbomb_force_e, FALSE, FALSE,
5745 EL_BOMB, -1, MV_BIT_RIGHT
5748 Xbomb_force_w, FALSE, FALSE,
5749 EL_BOMB, -1, MV_BIT_LEFT
5751 #endif // EM_ENGINE_BAD_ROLL
5754 Xstone, TRUE, FALSE,
5758 Xstone_pause, FALSE, FALSE,
5762 Xstone_fall, FALSE, FALSE,
5766 Ystone_s, FALSE, FALSE,
5767 EL_ROCK, ACTION_FALLING, -1
5770 Ystone_sB, FALSE, TRUE,
5771 EL_ROCK, ACTION_FALLING, -1
5774 Ystone_e, FALSE, FALSE,
5775 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5778 Ystone_eB, FALSE, TRUE,
5779 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5782 Ystone_w, FALSE, FALSE,
5783 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5786 Ystone_wB, FALSE, TRUE,
5787 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5794 Xnut_pause, FALSE, FALSE,
5798 Xnut_fall, FALSE, FALSE,
5802 Ynut_s, FALSE, FALSE,
5803 EL_NUT, ACTION_FALLING, -1
5806 Ynut_sB, FALSE, TRUE,
5807 EL_NUT, ACTION_FALLING, -1
5810 Ynut_e, FALSE, FALSE,
5811 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5814 Ynut_eB, FALSE, TRUE,
5815 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5818 Ynut_w, FALSE, FALSE,
5819 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5822 Ynut_wB, FALSE, TRUE,
5823 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5826 Xbug_n, TRUE, FALSE,
5830 Xbug_e, TRUE, FALSE,
5831 EL_BUG_RIGHT, -1, -1
5834 Xbug_s, TRUE, FALSE,
5838 Xbug_w, TRUE, FALSE,
5842 Xbug_gon, FALSE, FALSE,
5846 Xbug_goe, FALSE, FALSE,
5847 EL_BUG_RIGHT, -1, -1
5850 Xbug_gos, FALSE, FALSE,
5854 Xbug_gow, FALSE, FALSE,
5858 Ybug_n, FALSE, FALSE,
5859 EL_BUG, ACTION_MOVING, MV_BIT_UP
5862 Ybug_nB, FALSE, TRUE,
5863 EL_BUG, ACTION_MOVING, MV_BIT_UP
5866 Ybug_e, FALSE, FALSE,
5867 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5870 Ybug_eB, FALSE, TRUE,
5871 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5874 Ybug_s, FALSE, FALSE,
5875 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5878 Ybug_sB, FALSE, TRUE,
5879 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5882 Ybug_w, FALSE, FALSE,
5883 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5886 Ybug_wB, FALSE, TRUE,
5887 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5890 Ybug_w_n, FALSE, FALSE,
5891 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5894 Ybug_n_e, FALSE, FALSE,
5895 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5898 Ybug_e_s, FALSE, FALSE,
5899 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5902 Ybug_s_w, FALSE, FALSE,
5903 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5906 Ybug_e_n, FALSE, FALSE,
5907 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5910 Ybug_s_e, FALSE, FALSE,
5911 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5914 Ybug_w_s, FALSE, FALSE,
5915 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5918 Ybug_n_w, FALSE, FALSE,
5919 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5922 Ybug_stone, FALSE, FALSE,
5923 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5926 Ybug_spring, FALSE, FALSE,
5927 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5930 Xtank_n, TRUE, FALSE,
5931 EL_SPACESHIP_UP, -1, -1
5934 Xtank_e, TRUE, FALSE,
5935 EL_SPACESHIP_RIGHT, -1, -1
5938 Xtank_s, TRUE, FALSE,
5939 EL_SPACESHIP_DOWN, -1, -1
5942 Xtank_w, TRUE, FALSE,
5943 EL_SPACESHIP_LEFT, -1, -1
5946 Xtank_gon, FALSE, FALSE,
5947 EL_SPACESHIP_UP, -1, -1
5950 Xtank_goe, FALSE, FALSE,
5951 EL_SPACESHIP_RIGHT, -1, -1
5954 Xtank_gos, FALSE, FALSE,
5955 EL_SPACESHIP_DOWN, -1, -1
5958 Xtank_gow, FALSE, FALSE,
5959 EL_SPACESHIP_LEFT, -1, -1
5962 Ytank_n, FALSE, FALSE,
5963 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5966 Ytank_nB, FALSE, TRUE,
5967 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5970 Ytank_e, FALSE, FALSE,
5971 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5974 Ytank_eB, FALSE, TRUE,
5975 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5978 Ytank_s, FALSE, FALSE,
5979 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5982 Ytank_sB, FALSE, TRUE,
5983 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5986 Ytank_w, FALSE, FALSE,
5987 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5990 Ytank_wB, FALSE, TRUE,
5991 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5994 Ytank_w_n, FALSE, FALSE,
5995 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5998 Ytank_n_e, FALSE, FALSE,
5999 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
6002 Ytank_e_s, FALSE, FALSE,
6003 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
6006 Ytank_s_w, FALSE, FALSE,
6007 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
6010 Ytank_e_n, FALSE, FALSE,
6011 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6014 Ytank_s_e, FALSE, FALSE,
6015 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6018 Ytank_w_s, FALSE, FALSE,
6019 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6022 Ytank_n_w, FALSE, FALSE,
6023 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6026 Ytank_stone, FALSE, FALSE,
6027 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6030 Ytank_spring, FALSE, FALSE,
6031 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6034 Xandroid, TRUE, FALSE,
6035 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6038 Xandroid_1_n, FALSE, FALSE,
6039 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6042 Xandroid_2_n, FALSE, FALSE,
6043 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6046 Xandroid_1_e, FALSE, FALSE,
6047 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6050 Xandroid_2_e, FALSE, FALSE,
6051 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6054 Xandroid_1_w, FALSE, FALSE,
6055 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6058 Xandroid_2_w, FALSE, FALSE,
6059 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6062 Xandroid_1_s, FALSE, FALSE,
6063 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6066 Xandroid_2_s, FALSE, FALSE,
6067 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6070 Yandroid_n, FALSE, FALSE,
6071 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6074 Yandroid_nB, FALSE, TRUE,
6075 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6078 Yandroid_ne, FALSE, FALSE,
6079 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6082 Yandroid_neB, FALSE, TRUE,
6083 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6086 Yandroid_e, FALSE, FALSE,
6087 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6090 Yandroid_eB, FALSE, TRUE,
6091 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6094 Yandroid_se, FALSE, FALSE,
6095 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6098 Yandroid_seB, FALSE, TRUE,
6099 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6102 Yandroid_s, FALSE, FALSE,
6103 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6106 Yandroid_sB, FALSE, TRUE,
6107 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6110 Yandroid_sw, FALSE, FALSE,
6111 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6114 Yandroid_swB, FALSE, TRUE,
6115 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6118 Yandroid_w, FALSE, FALSE,
6119 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6122 Yandroid_wB, FALSE, TRUE,
6123 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6126 Yandroid_nw, FALSE, FALSE,
6127 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6130 Yandroid_nwB, FALSE, TRUE,
6131 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6134 Xspring, TRUE, FALSE,
6138 Xspring_pause, FALSE, FALSE,
6142 Xspring_e, FALSE, FALSE,
6146 Xspring_w, FALSE, FALSE,
6150 Xspring_fall, FALSE, FALSE,
6154 Yspring_s, FALSE, FALSE,
6155 EL_SPRING, ACTION_FALLING, -1
6158 Yspring_sB, FALSE, TRUE,
6159 EL_SPRING, ACTION_FALLING, -1
6162 Yspring_e, FALSE, FALSE,
6163 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6166 Yspring_eB, FALSE, TRUE,
6167 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6170 Yspring_w, FALSE, FALSE,
6171 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6174 Yspring_wB, FALSE, TRUE,
6175 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6178 Yspring_kill_e, FALSE, FALSE,
6179 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6182 Yspring_kill_eB, FALSE, TRUE,
6183 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6186 Yspring_kill_w, FALSE, FALSE,
6187 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6190 Yspring_kill_wB, FALSE, TRUE,
6191 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6194 Xeater_n, TRUE, FALSE,
6195 EL_YAMYAM_UP, -1, -1
6198 Xeater_e, TRUE, FALSE,
6199 EL_YAMYAM_RIGHT, -1, -1
6202 Xeater_w, TRUE, FALSE,
6203 EL_YAMYAM_LEFT, -1, -1
6206 Xeater_s, TRUE, FALSE,
6207 EL_YAMYAM_DOWN, -1, -1
6210 Yeater_n, FALSE, FALSE,
6211 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6214 Yeater_nB, FALSE, TRUE,
6215 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6218 Yeater_e, FALSE, FALSE,
6219 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6222 Yeater_eB, FALSE, TRUE,
6223 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6226 Yeater_s, FALSE, FALSE,
6227 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6230 Yeater_sB, FALSE, TRUE,
6231 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6234 Yeater_w, FALSE, FALSE,
6235 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6238 Yeater_wB, FALSE, TRUE,
6239 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6242 Yeater_stone, FALSE, FALSE,
6243 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6246 Yeater_spring, FALSE, FALSE,
6247 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6250 Xalien, TRUE, FALSE,
6254 Xalien_pause, FALSE, FALSE,
6258 Yalien_n, FALSE, FALSE,
6259 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6262 Yalien_nB, FALSE, TRUE,
6263 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6266 Yalien_e, FALSE, FALSE,
6267 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6270 Yalien_eB, FALSE, TRUE,
6271 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6274 Yalien_s, FALSE, FALSE,
6275 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6278 Yalien_sB, FALSE, TRUE,
6279 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6282 Yalien_w, FALSE, FALSE,
6283 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6286 Yalien_wB, FALSE, TRUE,
6287 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6290 Yalien_stone, FALSE, FALSE,
6291 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6294 Yalien_spring, FALSE, FALSE,
6295 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6298 Xemerald, TRUE, FALSE,
6302 Xemerald_pause, FALSE, FALSE,
6306 Xemerald_fall, FALSE, FALSE,
6310 Xemerald_shine, FALSE, FALSE,
6311 EL_EMERALD, ACTION_TWINKLING, -1
6314 Yemerald_s, FALSE, FALSE,
6315 EL_EMERALD, ACTION_FALLING, -1
6318 Yemerald_sB, FALSE, TRUE,
6319 EL_EMERALD, ACTION_FALLING, -1
6322 Yemerald_e, FALSE, FALSE,
6323 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6326 Yemerald_eB, FALSE, TRUE,
6327 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6330 Yemerald_w, FALSE, FALSE,
6331 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6334 Yemerald_wB, FALSE, TRUE,
6335 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6338 Yemerald_eat, FALSE, FALSE,
6339 EL_EMERALD, ACTION_COLLECTING, -1
6342 Yemerald_stone, FALSE, FALSE,
6343 EL_NUT, ACTION_BREAKING, -1
6346 Xdiamond, TRUE, FALSE,
6350 Xdiamond_pause, FALSE, FALSE,
6354 Xdiamond_fall, FALSE, FALSE,
6358 Xdiamond_shine, FALSE, FALSE,
6359 EL_DIAMOND, ACTION_TWINKLING, -1
6362 Ydiamond_s, FALSE, FALSE,
6363 EL_DIAMOND, ACTION_FALLING, -1
6366 Ydiamond_sB, FALSE, TRUE,
6367 EL_DIAMOND, ACTION_FALLING, -1
6370 Ydiamond_e, FALSE, FALSE,
6371 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6374 Ydiamond_eB, FALSE, TRUE,
6375 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6378 Ydiamond_w, FALSE, FALSE,
6379 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6382 Ydiamond_wB, FALSE, TRUE,
6383 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6386 Ydiamond_eat, FALSE, FALSE,
6387 EL_DIAMOND, ACTION_COLLECTING, -1
6390 Ydiamond_stone, FALSE, FALSE,
6391 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6394 Xdrip_fall, TRUE, FALSE,
6395 EL_AMOEBA_DROP, -1, -1
6398 Xdrip_stretch, FALSE, FALSE,
6399 EL_AMOEBA_DROP, ACTION_FALLING, -1
6402 Xdrip_stretchB, FALSE, TRUE,
6403 EL_AMOEBA_DROP, ACTION_FALLING, -1
6406 Xdrip_eat, FALSE, FALSE,
6407 EL_AMOEBA_DROP, ACTION_GROWING, -1
6410 Ydrip_s1, FALSE, FALSE,
6411 EL_AMOEBA_DROP, ACTION_FALLING, -1
6414 Ydrip_s1B, FALSE, TRUE,
6415 EL_AMOEBA_DROP, ACTION_FALLING, -1
6418 Ydrip_s2, FALSE, FALSE,
6419 EL_AMOEBA_DROP, ACTION_FALLING, -1
6422 Ydrip_s2B, FALSE, TRUE,
6423 EL_AMOEBA_DROP, ACTION_FALLING, -1
6430 Xbomb_pause, FALSE, FALSE,
6434 Xbomb_fall, FALSE, FALSE,
6438 Ybomb_s, FALSE, FALSE,
6439 EL_BOMB, ACTION_FALLING, -1
6442 Ybomb_sB, FALSE, TRUE,
6443 EL_BOMB, ACTION_FALLING, -1
6446 Ybomb_e, FALSE, FALSE,
6447 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6450 Ybomb_eB, FALSE, TRUE,
6451 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6454 Ybomb_w, FALSE, FALSE,
6455 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6458 Ybomb_wB, FALSE, TRUE,
6459 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6462 Ybomb_eat, FALSE, FALSE,
6463 EL_BOMB, ACTION_ACTIVATING, -1
6466 Xballoon, TRUE, FALSE,
6470 Yballoon_n, FALSE, FALSE,
6471 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6474 Yballoon_nB, FALSE, TRUE,
6475 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6478 Yballoon_e, FALSE, FALSE,
6479 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6482 Yballoon_eB, FALSE, TRUE,
6483 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6486 Yballoon_s, FALSE, FALSE,
6487 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6490 Yballoon_sB, FALSE, TRUE,
6491 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6494 Yballoon_w, FALSE, FALSE,
6495 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6498 Yballoon_wB, FALSE, TRUE,
6499 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6502 Xgrass, TRUE, FALSE,
6503 EL_EMC_GRASS, -1, -1
6506 Ygrass_nB, FALSE, FALSE,
6507 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6510 Ygrass_eB, FALSE, FALSE,
6511 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6514 Ygrass_sB, FALSE, FALSE,
6515 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6518 Ygrass_wB, FALSE, FALSE,
6519 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6526 Ydirt_nB, FALSE, FALSE,
6527 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6530 Ydirt_eB, FALSE, FALSE,
6531 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6534 Ydirt_sB, FALSE, FALSE,
6535 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6538 Ydirt_wB, FALSE, FALSE,
6539 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6542 Xacid_ne, TRUE, FALSE,
6543 EL_ACID_POOL_TOPRIGHT, -1, -1
6546 Xacid_se, TRUE, FALSE,
6547 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6550 Xacid_s, TRUE, FALSE,
6551 EL_ACID_POOL_BOTTOM, -1, -1
6554 Xacid_sw, TRUE, FALSE,
6555 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6558 Xacid_nw, TRUE, FALSE,
6559 EL_ACID_POOL_TOPLEFT, -1, -1
6562 Xacid_1, TRUE, FALSE,
6566 Xacid_2, FALSE, FALSE,
6570 Xacid_3, FALSE, FALSE,
6574 Xacid_4, FALSE, FALSE,
6578 Xacid_5, FALSE, FALSE,
6582 Xacid_6, FALSE, FALSE,
6586 Xacid_7, FALSE, FALSE,
6590 Xacid_8, FALSE, FALSE,
6594 Xball_1, TRUE, FALSE,
6595 EL_EMC_MAGIC_BALL, -1, -1
6598 Xball_1B, FALSE, FALSE,
6599 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6602 Xball_2, FALSE, FALSE,
6603 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6606 Xball_2B, FALSE, FALSE,
6607 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6610 Yball_eat, FALSE, FALSE,
6611 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6614 Ykey_1_eat, FALSE, FALSE,
6615 EL_EM_KEY_1, ACTION_COLLECTING, -1
6618 Ykey_2_eat, FALSE, FALSE,
6619 EL_EM_KEY_2, ACTION_COLLECTING, -1
6622 Ykey_3_eat, FALSE, FALSE,
6623 EL_EM_KEY_3, ACTION_COLLECTING, -1
6626 Ykey_4_eat, FALSE, FALSE,
6627 EL_EM_KEY_4, ACTION_COLLECTING, -1
6630 Ykey_5_eat, FALSE, FALSE,
6631 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6634 Ykey_6_eat, FALSE, FALSE,
6635 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6638 Ykey_7_eat, FALSE, FALSE,
6639 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6642 Ykey_8_eat, FALSE, FALSE,
6643 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6646 Ylenses_eat, FALSE, FALSE,
6647 EL_EMC_LENSES, ACTION_COLLECTING, -1
6650 Ymagnify_eat, FALSE, FALSE,
6651 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6654 Ygrass_eat, FALSE, FALSE,
6655 EL_EMC_GRASS, ACTION_SNAPPING, -1
6658 Ydirt_eat, FALSE, FALSE,
6659 EL_SAND, ACTION_SNAPPING, -1
6662 Xgrow_ns, TRUE, FALSE,
6663 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6666 Ygrow_ns_eat, FALSE, FALSE,
6667 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6670 Xgrow_ew, TRUE, FALSE,
6671 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6674 Ygrow_ew_eat, FALSE, FALSE,
6675 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6678 Xwonderwall, TRUE, FALSE,
6679 EL_MAGIC_WALL, -1, -1
6682 XwonderwallB, FALSE, FALSE,
6683 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6686 Xamoeba_1, TRUE, FALSE,
6687 EL_AMOEBA_DRY, ACTION_OTHER, -1
6690 Xamoeba_2, FALSE, FALSE,
6691 EL_AMOEBA_DRY, ACTION_OTHER, -1
6694 Xamoeba_3, FALSE, FALSE,
6695 EL_AMOEBA_DRY, ACTION_OTHER, -1
6698 Xamoeba_4, FALSE, FALSE,
6699 EL_AMOEBA_DRY, ACTION_OTHER, -1
6702 Xamoeba_5, TRUE, FALSE,
6703 EL_AMOEBA_WET, ACTION_OTHER, -1
6706 Xamoeba_6, FALSE, FALSE,
6707 EL_AMOEBA_WET, ACTION_OTHER, -1
6710 Xamoeba_7, FALSE, FALSE,
6711 EL_AMOEBA_WET, ACTION_OTHER, -1
6714 Xamoeba_8, FALSE, FALSE,
6715 EL_AMOEBA_WET, ACTION_OTHER, -1
6718 Xdoor_1, TRUE, FALSE,
6719 EL_EM_GATE_1, -1, -1
6722 Xdoor_2, TRUE, FALSE,
6723 EL_EM_GATE_2, -1, -1
6726 Xdoor_3, TRUE, FALSE,
6727 EL_EM_GATE_3, -1, -1
6730 Xdoor_4, TRUE, FALSE,
6731 EL_EM_GATE_4, -1, -1
6734 Xdoor_5, TRUE, FALSE,
6735 EL_EMC_GATE_5, -1, -1
6738 Xdoor_6, TRUE, FALSE,
6739 EL_EMC_GATE_6, -1, -1
6742 Xdoor_7, TRUE, FALSE,
6743 EL_EMC_GATE_7, -1, -1
6746 Xdoor_8, TRUE, FALSE,
6747 EL_EMC_GATE_8, -1, -1
6750 Xkey_1, TRUE, FALSE,
6754 Xkey_2, TRUE, FALSE,
6758 Xkey_3, TRUE, FALSE,
6762 Xkey_4, TRUE, FALSE,
6766 Xkey_5, TRUE, FALSE,
6767 EL_EMC_KEY_5, -1, -1
6770 Xkey_6, TRUE, FALSE,
6771 EL_EMC_KEY_6, -1, -1
6774 Xkey_7, TRUE, FALSE,
6775 EL_EMC_KEY_7, -1, -1
6778 Xkey_8, TRUE, FALSE,
6779 EL_EMC_KEY_8, -1, -1
6782 Xwind_n, TRUE, FALSE,
6783 EL_BALLOON_SWITCH_UP, -1, -1
6786 Xwind_e, TRUE, FALSE,
6787 EL_BALLOON_SWITCH_RIGHT, -1, -1
6790 Xwind_s, TRUE, FALSE,
6791 EL_BALLOON_SWITCH_DOWN, -1, -1
6794 Xwind_w, TRUE, FALSE,
6795 EL_BALLOON_SWITCH_LEFT, -1, -1
6798 Xwind_nesw, TRUE, FALSE,
6799 EL_BALLOON_SWITCH_ANY, -1, -1
6802 Xwind_stop, TRUE, FALSE,
6803 EL_BALLOON_SWITCH_NONE, -1, -1
6807 EL_EM_EXIT_CLOSED, -1, -1
6810 Xexit_1, TRUE, FALSE,
6811 EL_EM_EXIT_OPEN, -1, -1
6814 Xexit_2, FALSE, FALSE,
6815 EL_EM_EXIT_OPEN, -1, -1
6818 Xexit_3, FALSE, FALSE,
6819 EL_EM_EXIT_OPEN, -1, -1
6822 Xdynamite, TRUE, FALSE,
6823 EL_EM_DYNAMITE, -1, -1
6826 Ydynamite_eat, FALSE, FALSE,
6827 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6830 Xdynamite_1, TRUE, FALSE,
6831 EL_EM_DYNAMITE_ACTIVE, -1, -1
6834 Xdynamite_2, FALSE, FALSE,
6835 EL_EM_DYNAMITE_ACTIVE, -1, -1
6838 Xdynamite_3, FALSE, FALSE,
6839 EL_EM_DYNAMITE_ACTIVE, -1, -1
6842 Xdynamite_4, FALSE, FALSE,
6843 EL_EM_DYNAMITE_ACTIVE, -1, -1
6846 Xbumper, TRUE, FALSE,
6847 EL_EMC_SPRING_BUMPER, -1, -1
6850 XbumperB, FALSE, FALSE,
6851 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6854 Xwheel, TRUE, FALSE,
6855 EL_ROBOT_WHEEL, -1, -1
6858 XwheelB, FALSE, FALSE,
6859 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6862 Xswitch, TRUE, FALSE,
6863 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6866 XswitchB, FALSE, FALSE,
6867 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6871 EL_QUICKSAND_EMPTY, -1, -1
6874 Xsand_stone, TRUE, FALSE,
6875 EL_QUICKSAND_FULL, -1, -1
6878 Xsand_stonein_1, FALSE, TRUE,
6879 EL_ROCK, ACTION_FILLING, -1
6882 Xsand_stonein_2, FALSE, TRUE,
6883 EL_ROCK, ACTION_FILLING, -1
6886 Xsand_stonein_3, FALSE, TRUE,
6887 EL_ROCK, ACTION_FILLING, -1
6890 Xsand_stonein_4, FALSE, TRUE,
6891 EL_ROCK, ACTION_FILLING, -1
6894 Xsand_stonesand_1, FALSE, FALSE,
6895 EL_QUICKSAND_EMPTYING, -1, -1
6898 Xsand_stonesand_2, FALSE, FALSE,
6899 EL_QUICKSAND_EMPTYING, -1, -1
6902 Xsand_stonesand_3, FALSE, FALSE,
6903 EL_QUICKSAND_EMPTYING, -1, -1
6906 Xsand_stonesand_4, FALSE, FALSE,
6907 EL_QUICKSAND_EMPTYING, -1, -1
6910 Xsand_stonesand_quickout_1, FALSE, FALSE,
6911 EL_QUICKSAND_EMPTYING, -1, -1
6914 Xsand_stonesand_quickout_2, FALSE, FALSE,
6915 EL_QUICKSAND_EMPTYING, -1, -1
6918 Xsand_stoneout_1, FALSE, FALSE,
6919 EL_ROCK, ACTION_EMPTYING, -1
6922 Xsand_stoneout_2, FALSE, FALSE,
6923 EL_ROCK, ACTION_EMPTYING, -1
6926 Xsand_sandstone_1, FALSE, FALSE,
6927 EL_QUICKSAND_FILLING, -1, -1
6930 Xsand_sandstone_2, FALSE, FALSE,
6931 EL_QUICKSAND_FILLING, -1, -1
6934 Xsand_sandstone_3, FALSE, FALSE,
6935 EL_QUICKSAND_FILLING, -1, -1
6938 Xsand_sandstone_4, FALSE, FALSE,
6939 EL_QUICKSAND_FILLING, -1, -1
6942 Xplant, TRUE, FALSE,
6943 EL_EMC_PLANT, -1, -1
6946 Yplant, FALSE, FALSE,
6947 EL_EMC_PLANT, -1, -1
6950 Xlenses, TRUE, FALSE,
6951 EL_EMC_LENSES, -1, -1
6954 Xmagnify, TRUE, FALSE,
6955 EL_EMC_MAGNIFIER, -1, -1
6958 Xdripper, TRUE, FALSE,
6959 EL_EMC_DRIPPER, -1, -1
6962 XdripperB, FALSE, FALSE,
6963 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6966 Xfake_blank, TRUE, FALSE,
6967 EL_INVISIBLE_WALL, -1, -1
6970 Xfake_blankB, FALSE, FALSE,
6971 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6974 Xfake_grass, TRUE, FALSE,
6975 EL_EMC_FAKE_GRASS, -1, -1
6978 Xfake_grassB, FALSE, FALSE,
6979 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6982 Xfake_door_1, TRUE, FALSE,
6983 EL_EM_GATE_1_GRAY, -1, -1
6986 Xfake_door_2, TRUE, FALSE,
6987 EL_EM_GATE_2_GRAY, -1, -1
6990 Xfake_door_3, TRUE, FALSE,
6991 EL_EM_GATE_3_GRAY, -1, -1
6994 Xfake_door_4, TRUE, FALSE,
6995 EL_EM_GATE_4_GRAY, -1, -1
6998 Xfake_door_5, TRUE, FALSE,
6999 EL_EMC_GATE_5_GRAY, -1, -1
7002 Xfake_door_6, TRUE, FALSE,
7003 EL_EMC_GATE_6_GRAY, -1, -1
7006 Xfake_door_7, TRUE, FALSE,
7007 EL_EMC_GATE_7_GRAY, -1, -1
7010 Xfake_door_8, TRUE, FALSE,
7011 EL_EMC_GATE_8_GRAY, -1, -1
7014 Xfake_acid_1, TRUE, FALSE,
7015 EL_EMC_FAKE_ACID, -1, -1
7018 Xfake_acid_2, FALSE, FALSE,
7019 EL_EMC_FAKE_ACID, -1, -1
7022 Xfake_acid_3, FALSE, FALSE,
7023 EL_EMC_FAKE_ACID, -1, -1
7026 Xfake_acid_4, FALSE, FALSE,
7027 EL_EMC_FAKE_ACID, -1, -1
7030 Xfake_acid_5, FALSE, FALSE,
7031 EL_EMC_FAKE_ACID, -1, -1
7034 Xfake_acid_6, FALSE, FALSE,
7035 EL_EMC_FAKE_ACID, -1, -1
7038 Xfake_acid_7, FALSE, FALSE,
7039 EL_EMC_FAKE_ACID, -1, -1
7042 Xfake_acid_8, FALSE, FALSE,
7043 EL_EMC_FAKE_ACID, -1, -1
7046 Xsteel_1, TRUE, FALSE,
7047 EL_STEELWALL, -1, -1
7050 Xsteel_2, TRUE, FALSE,
7051 EL_EMC_STEELWALL_2, -1, -1
7054 Xsteel_3, TRUE, FALSE,
7055 EL_EMC_STEELWALL_3, -1, -1
7058 Xsteel_4, TRUE, FALSE,
7059 EL_EMC_STEELWALL_4, -1, -1
7062 Xwall_1, TRUE, FALSE,
7066 Xwall_2, TRUE, FALSE,
7067 EL_EMC_WALL_14, -1, -1
7070 Xwall_3, TRUE, FALSE,
7071 EL_EMC_WALL_15, -1, -1
7074 Xwall_4, TRUE, FALSE,
7075 EL_EMC_WALL_16, -1, -1
7078 Xround_wall_1, TRUE, FALSE,
7079 EL_WALL_SLIPPERY, -1, -1
7082 Xround_wall_2, TRUE, FALSE,
7083 EL_EMC_WALL_SLIPPERY_2, -1, -1
7086 Xround_wall_3, TRUE, FALSE,
7087 EL_EMC_WALL_SLIPPERY_3, -1, -1
7090 Xround_wall_4, TRUE, FALSE,
7091 EL_EMC_WALL_SLIPPERY_4, -1, -1
7094 Xdecor_1, TRUE, FALSE,
7095 EL_EMC_WALL_8, -1, -1
7098 Xdecor_2, TRUE, FALSE,
7099 EL_EMC_WALL_6, -1, -1
7102 Xdecor_3, TRUE, FALSE,
7103 EL_EMC_WALL_4, -1, -1
7106 Xdecor_4, TRUE, FALSE,
7107 EL_EMC_WALL_7, -1, -1
7110 Xdecor_5, TRUE, FALSE,
7111 EL_EMC_WALL_5, -1, -1
7114 Xdecor_6, TRUE, FALSE,
7115 EL_EMC_WALL_9, -1, -1
7118 Xdecor_7, TRUE, FALSE,
7119 EL_EMC_WALL_10, -1, -1
7122 Xdecor_8, TRUE, FALSE,
7123 EL_EMC_WALL_1, -1, -1
7126 Xdecor_9, TRUE, FALSE,
7127 EL_EMC_WALL_2, -1, -1
7130 Xdecor_10, TRUE, FALSE,
7131 EL_EMC_WALL_3, -1, -1
7134 Xdecor_11, TRUE, FALSE,
7135 EL_EMC_WALL_11, -1, -1
7138 Xdecor_12, TRUE, FALSE,
7139 EL_EMC_WALL_12, -1, -1
7142 Xalpha_0, TRUE, FALSE,
7143 EL_CHAR('0'), -1, -1
7146 Xalpha_1, TRUE, FALSE,
7147 EL_CHAR('1'), -1, -1
7150 Xalpha_2, TRUE, FALSE,
7151 EL_CHAR('2'), -1, -1
7154 Xalpha_3, TRUE, FALSE,
7155 EL_CHAR('3'), -1, -1
7158 Xalpha_4, TRUE, FALSE,
7159 EL_CHAR('4'), -1, -1
7162 Xalpha_5, TRUE, FALSE,
7163 EL_CHAR('5'), -1, -1
7166 Xalpha_6, TRUE, FALSE,
7167 EL_CHAR('6'), -1, -1
7170 Xalpha_7, TRUE, FALSE,
7171 EL_CHAR('7'), -1, -1
7174 Xalpha_8, TRUE, FALSE,
7175 EL_CHAR('8'), -1, -1
7178 Xalpha_9, TRUE, FALSE,
7179 EL_CHAR('9'), -1, -1
7182 Xalpha_excla, TRUE, FALSE,
7183 EL_CHAR('!'), -1, -1
7186 Xalpha_quote, TRUE, FALSE,
7187 EL_CHAR('"'), -1, -1
7190 Xalpha_comma, TRUE, FALSE,
7191 EL_CHAR(','), -1, -1
7194 Xalpha_minus, TRUE, FALSE,
7195 EL_CHAR('-'), -1, -1
7198 Xalpha_perio, TRUE, FALSE,
7199 EL_CHAR('.'), -1, -1
7202 Xalpha_colon, TRUE, FALSE,
7203 EL_CHAR(':'), -1, -1
7206 Xalpha_quest, TRUE, FALSE,
7207 EL_CHAR('?'), -1, -1
7210 Xalpha_a, TRUE, FALSE,
7211 EL_CHAR('A'), -1, -1
7214 Xalpha_b, TRUE, FALSE,
7215 EL_CHAR('B'), -1, -1
7218 Xalpha_c, TRUE, FALSE,
7219 EL_CHAR('C'), -1, -1
7222 Xalpha_d, TRUE, FALSE,
7223 EL_CHAR('D'), -1, -1
7226 Xalpha_e, TRUE, FALSE,
7227 EL_CHAR('E'), -1, -1
7230 Xalpha_f, TRUE, FALSE,
7231 EL_CHAR('F'), -1, -1
7234 Xalpha_g, TRUE, FALSE,
7235 EL_CHAR('G'), -1, -1
7238 Xalpha_h, TRUE, FALSE,
7239 EL_CHAR('H'), -1, -1
7242 Xalpha_i, TRUE, FALSE,
7243 EL_CHAR('I'), -1, -1
7246 Xalpha_j, TRUE, FALSE,
7247 EL_CHAR('J'), -1, -1
7250 Xalpha_k, TRUE, FALSE,
7251 EL_CHAR('K'), -1, -1
7254 Xalpha_l, TRUE, FALSE,
7255 EL_CHAR('L'), -1, -1
7258 Xalpha_m, TRUE, FALSE,
7259 EL_CHAR('M'), -1, -1
7262 Xalpha_n, TRUE, FALSE,
7263 EL_CHAR('N'), -1, -1
7266 Xalpha_o, TRUE, FALSE,
7267 EL_CHAR('O'), -1, -1
7270 Xalpha_p, TRUE, FALSE,
7271 EL_CHAR('P'), -1, -1
7274 Xalpha_q, TRUE, FALSE,
7275 EL_CHAR('Q'), -1, -1
7278 Xalpha_r, TRUE, FALSE,
7279 EL_CHAR('R'), -1, -1
7282 Xalpha_s, TRUE, FALSE,
7283 EL_CHAR('S'), -1, -1
7286 Xalpha_t, TRUE, FALSE,
7287 EL_CHAR('T'), -1, -1
7290 Xalpha_u, TRUE, FALSE,
7291 EL_CHAR('U'), -1, -1
7294 Xalpha_v, TRUE, FALSE,
7295 EL_CHAR('V'), -1, -1
7298 Xalpha_w, TRUE, FALSE,
7299 EL_CHAR('W'), -1, -1
7302 Xalpha_x, TRUE, FALSE,
7303 EL_CHAR('X'), -1, -1
7306 Xalpha_y, TRUE, FALSE,
7307 EL_CHAR('Y'), -1, -1
7310 Xalpha_z, TRUE, FALSE,
7311 EL_CHAR('Z'), -1, -1
7314 Xalpha_arrow_e, TRUE, FALSE,
7315 EL_CHAR('>'), -1, -1
7318 Xalpha_arrow_w, TRUE, FALSE,
7319 EL_CHAR('<'), -1, -1
7322 Xalpha_copyr, TRUE, FALSE,
7323 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7327 Xboom_bug, FALSE, FALSE,
7328 EL_BUG, ACTION_EXPLODING, -1
7331 Xboom_bomb, FALSE, FALSE,
7332 EL_BOMB, ACTION_EXPLODING, -1
7335 Xboom_android, FALSE, FALSE,
7336 EL_EMC_ANDROID, ACTION_OTHER, -1
7339 Xboom_1, FALSE, FALSE,
7340 EL_DEFAULT, ACTION_EXPLODING, -1
7343 Xboom_2, FALSE, FALSE,
7344 EL_DEFAULT, ACTION_EXPLODING, -1
7347 Znormal, FALSE, FALSE,
7351 Zdynamite, FALSE, FALSE,
7355 Zplayer, FALSE, FALSE,
7359 ZBORDER, FALSE, FALSE,
7369 static struct Mapping_EM_to_RND_player
7378 em_player_mapping_list[] =
7382 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7386 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7390 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7394 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7398 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7402 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7406 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7410 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7414 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7418 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7422 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7426 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7430 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7434 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7438 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7442 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7446 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7450 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7454 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7458 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7462 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7466 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7470 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7474 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7478 EL_PLAYER_1, ACTION_DEFAULT, -1,
7482 EL_PLAYER_2, ACTION_DEFAULT, -1,
7486 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7490 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7494 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7498 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7502 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7506 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7510 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7514 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7518 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7522 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7526 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7530 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7534 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7538 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7542 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7546 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7550 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7554 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7558 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7562 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7566 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7570 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7574 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7578 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7582 EL_PLAYER_3, ACTION_DEFAULT, -1,
7586 EL_PLAYER_4, ACTION_DEFAULT, -1,
7595 int map_element_RND_to_EM(int element_rnd)
7597 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7598 static boolean mapping_initialized = FALSE;
7600 if (!mapping_initialized)
7604 // return "Xalpha_quest" for all undefined elements in mapping array
7605 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7606 mapping_RND_to_EM[i] = Xalpha_quest;
7608 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7609 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7610 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7611 em_object_mapping_list[i].element_em;
7613 mapping_initialized = TRUE;
7616 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7617 return mapping_RND_to_EM[element_rnd];
7619 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7624 int map_element_EM_to_RND(int element_em)
7626 static unsigned short mapping_EM_to_RND[TILE_MAX];
7627 static boolean mapping_initialized = FALSE;
7629 if (!mapping_initialized)
7633 // return "EL_UNKNOWN" for all undefined elements in mapping array
7634 for (i = 0; i < TILE_MAX; i++)
7635 mapping_EM_to_RND[i] = EL_UNKNOWN;
7637 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7638 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7639 em_object_mapping_list[i].element_rnd;
7641 mapping_initialized = TRUE;
7644 if (element_em >= 0 && element_em < TILE_MAX)
7645 return mapping_EM_to_RND[element_em];
7647 Error(ERR_WARN, "invalid EM level element %d", element_em);
7652 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7654 struct LevelInfo_EM *level_em = level->native_em_level;
7655 struct LEVEL *lev = level_em->lev;
7658 for (i = 0; i < TILE_MAX; i++)
7659 lev->android_array[i] = Xblank;
7661 for (i = 0; i < level->num_android_clone_elements; i++)
7663 int element_rnd = level->android_clone_element[i];
7664 int element_em = map_element_RND_to_EM(element_rnd);
7666 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7667 if (em_object_mapping_list[j].element_rnd == element_rnd)
7668 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7672 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7674 struct LevelInfo_EM *level_em = level->native_em_level;
7675 struct LEVEL *lev = level_em->lev;
7678 level->num_android_clone_elements = 0;
7680 for (i = 0; i < TILE_MAX; i++)
7682 int element_em = lev->android_array[i];
7684 boolean element_found = FALSE;
7686 if (element_em == Xblank)
7689 element_rnd = map_element_EM_to_RND(element_em);
7691 for (j = 0; j < level->num_android_clone_elements; j++)
7692 if (level->android_clone_element[j] == element_rnd)
7693 element_found = TRUE;
7697 level->android_clone_element[level->num_android_clone_elements++] =
7700 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7705 if (level->num_android_clone_elements == 0)
7707 level->num_android_clone_elements = 1;
7708 level->android_clone_element[0] = EL_EMPTY;
7712 int map_direction_RND_to_EM(int direction)
7714 return (direction == MV_UP ? 0 :
7715 direction == MV_RIGHT ? 1 :
7716 direction == MV_DOWN ? 2 :
7717 direction == MV_LEFT ? 3 :
7721 int map_direction_EM_to_RND(int direction)
7723 return (direction == 0 ? MV_UP :
7724 direction == 1 ? MV_RIGHT :
7725 direction == 2 ? MV_DOWN :
7726 direction == 3 ? MV_LEFT :
7730 int map_element_RND_to_SP(int element_rnd)
7732 int element_sp = 0x20; // map unknown elements to yellow "hardware"
7734 if (element_rnd >= EL_SP_START &&
7735 element_rnd <= EL_SP_END)
7736 element_sp = element_rnd - EL_SP_START;
7737 else if (element_rnd == EL_EMPTY_SPACE)
7739 else if (element_rnd == EL_INVISIBLE_WALL)
7745 int map_element_SP_to_RND(int element_sp)
7747 int element_rnd = EL_UNKNOWN;
7749 if (element_sp >= 0x00 &&
7751 element_rnd = EL_SP_START + element_sp;
7752 else if (element_sp == 0x28)
7753 element_rnd = EL_INVISIBLE_WALL;
7758 int map_action_SP_to_RND(int action_sp)
7762 case actActive: return ACTION_ACTIVE;
7763 case actImpact: return ACTION_IMPACT;
7764 case actExploding: return ACTION_EXPLODING;
7765 case actDigging: return ACTION_DIGGING;
7766 case actSnapping: return ACTION_SNAPPING;
7767 case actCollecting: return ACTION_COLLECTING;
7768 case actPassing: return ACTION_PASSING;
7769 case actPushing: return ACTION_PUSHING;
7770 case actDropping: return ACTION_DROPPING;
7772 default: return ACTION_DEFAULT;
7776 int map_element_RND_to_MM(int element_rnd)
7778 return (element_rnd >= EL_MM_START_1 &&
7779 element_rnd <= EL_MM_END_1 ?
7780 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7782 element_rnd >= EL_MM_START_2 &&
7783 element_rnd <= EL_MM_END_2 ?
7784 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7786 element_rnd >= EL_CHAR_START &&
7787 element_rnd <= EL_CHAR_END ?
7788 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7790 element_rnd >= EL_MM_RUNTIME_START &&
7791 element_rnd <= EL_MM_RUNTIME_END ?
7792 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7794 element_rnd >= EL_MM_DUMMY_START &&
7795 element_rnd <= EL_MM_DUMMY_END ?
7796 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7798 EL_MM_EMPTY_NATIVE);
7801 int map_element_MM_to_RND(int element_mm)
7803 return (element_mm == EL_MM_EMPTY_NATIVE ||
7804 element_mm == EL_DF_EMPTY_NATIVE ?
7807 element_mm >= EL_MM_START_1_NATIVE &&
7808 element_mm <= EL_MM_END_1_NATIVE ?
7809 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7811 element_mm >= EL_MM_START_2_NATIVE &&
7812 element_mm <= EL_MM_END_2_NATIVE ?
7813 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7815 element_mm >= EL_MM_CHAR_START_NATIVE &&
7816 element_mm <= EL_MM_CHAR_END_NATIVE ?
7817 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7819 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7820 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7821 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7823 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7824 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7825 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7830 int map_action_MM_to_RND(int action_mm)
7832 // all MM actions are defined to exactly match their RND counterparts
7836 int map_sound_MM_to_RND(int sound_mm)
7840 case SND_MM_GAME_LEVELTIME_CHARGING:
7841 return SND_GAME_LEVELTIME_CHARGING;
7843 case SND_MM_GAME_HEALTH_CHARGING:
7844 return SND_GAME_HEALTH_CHARGING;
7847 return SND_UNDEFINED;
7851 int map_mm_wall_element(int element)
7853 return (element >= EL_MM_STEEL_WALL_START &&
7854 element <= EL_MM_STEEL_WALL_END ?
7857 element >= EL_MM_WOODEN_WALL_START &&
7858 element <= EL_MM_WOODEN_WALL_END ?
7861 element >= EL_MM_ICE_WALL_START &&
7862 element <= EL_MM_ICE_WALL_END ?
7865 element >= EL_MM_AMOEBA_WALL_START &&
7866 element <= EL_MM_AMOEBA_WALL_END ?
7869 element >= EL_DF_STEEL_WALL_START &&
7870 element <= EL_DF_STEEL_WALL_END ?
7873 element >= EL_DF_WOODEN_WALL_START &&
7874 element <= EL_DF_WOODEN_WALL_END ?
7880 int map_mm_wall_element_editor(int element)
7884 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7885 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7886 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7887 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7888 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7889 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7891 default: return element;
7895 int get_next_element(int element)
7899 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7900 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7901 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7902 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7903 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7904 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7905 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7906 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7907 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7908 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7909 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7911 default: return element;
7915 int el2img_mm(int element_mm)
7917 return el2img(map_element_MM_to_RND(element_mm));
7920 int el_act_dir2img(int element, int action, int direction)
7922 element = GFX_ELEMENT(element);
7923 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7925 // direction_graphic[][] == graphic[] for undefined direction graphics
7926 return element_info[element].direction_graphic[action][direction];
7929 static int el_act_dir2crm(int element, int action, int direction)
7931 element = GFX_ELEMENT(element);
7932 direction = MV_DIR_TO_BIT(direction); // default: MV_NONE => MV_DOWN
7934 // direction_graphic[][] == graphic[] for undefined direction graphics
7935 return element_info[element].direction_crumbled[action][direction];
7938 int el_act2img(int element, int action)
7940 element = GFX_ELEMENT(element);
7942 return element_info[element].graphic[action];
7945 int el_act2crm(int element, int action)
7947 element = GFX_ELEMENT(element);
7949 return element_info[element].crumbled[action];
7952 int el_dir2img(int element, int direction)
7954 element = GFX_ELEMENT(element);
7956 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7959 int el2baseimg(int element)
7961 return element_info[element].graphic[ACTION_DEFAULT];
7964 int el2img(int element)
7966 element = GFX_ELEMENT(element);
7968 return element_info[element].graphic[ACTION_DEFAULT];
7971 int el2edimg(int element)
7973 element = GFX_ELEMENT(element);
7975 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7978 int el2preimg(int element)
7980 element = GFX_ELEMENT(element);
7982 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7985 int el2panelimg(int element)
7987 element = GFX_ELEMENT(element);
7989 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7992 int font2baseimg(int font_nr)
7994 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7997 int getBeltNrFromBeltElement(int element)
7999 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
8000 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
8001 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
8004 int getBeltNrFromBeltActiveElement(int element)
8006 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
8007 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
8008 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
8011 int getBeltNrFromBeltSwitchElement(int element)
8013 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8014 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8015 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8018 int getBeltDirNrFromBeltElement(int element)
8020 static int belt_base_element[4] =
8022 EL_CONVEYOR_BELT_1_LEFT,
8023 EL_CONVEYOR_BELT_2_LEFT,
8024 EL_CONVEYOR_BELT_3_LEFT,
8025 EL_CONVEYOR_BELT_4_LEFT
8028 int belt_nr = getBeltNrFromBeltElement(element);
8029 int belt_dir_nr = element - belt_base_element[belt_nr];
8031 return (belt_dir_nr % 3);
8034 int getBeltDirNrFromBeltSwitchElement(int element)
8036 static int belt_base_element[4] =
8038 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8039 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8040 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8041 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8044 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8045 int belt_dir_nr = element - belt_base_element[belt_nr];
8047 return (belt_dir_nr % 3);
8050 int getBeltDirFromBeltElement(int element)
8052 static int belt_move_dir[3] =
8059 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8061 return belt_move_dir[belt_dir_nr];
8064 int getBeltDirFromBeltSwitchElement(int element)
8066 static int belt_move_dir[3] =
8073 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8075 return belt_move_dir[belt_dir_nr];
8078 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8080 static int belt_base_element[4] =
8082 EL_CONVEYOR_BELT_1_LEFT,
8083 EL_CONVEYOR_BELT_2_LEFT,
8084 EL_CONVEYOR_BELT_3_LEFT,
8085 EL_CONVEYOR_BELT_4_LEFT
8088 return belt_base_element[belt_nr] + belt_dir_nr;
8091 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8093 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8095 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8098 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8100 static int belt_base_element[4] =
8102 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8103 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8104 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8105 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8108 return belt_base_element[belt_nr] + belt_dir_nr;
8111 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8113 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8115 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8118 boolean getTeamMode_EM(void)
8120 return game.team_mode || network_playing;
8123 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8125 int game_frame_delay_value;
8127 game_frame_delay_value =
8128 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8129 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8132 if (tape.playing && tape.warp_forward && !tape.pausing)
8133 game_frame_delay_value = 0;
8135 return game_frame_delay_value;
8138 unsigned int InitRND(int seed)
8140 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8141 return InitEngineRandom_EM(seed);
8142 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8143 return InitEngineRandom_SP(seed);
8144 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8145 return InitEngineRandom_MM(seed);
8147 return InitEngineRandom_RND(seed);
8150 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8151 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8153 static int get_effective_element_EM(int tile, int frame_em)
8155 int element = object_mapping[tile].element_rnd;
8156 int action = object_mapping[tile].action;
8157 boolean is_backside = object_mapping[tile].is_backside;
8158 boolean action_removing = (action == ACTION_DIGGING ||
8159 action == ACTION_SNAPPING ||
8160 action == ACTION_COLLECTING);
8166 case Yacid_splash_eB:
8167 case Yacid_splash_wB:
8168 return (frame_em > 5 ? EL_EMPTY : element);
8174 else // frame_em == 7
8178 case Yacid_splash_eB:
8179 case Yacid_splash_wB:
8182 case Yemerald_stone:
8185 case Ydiamond_stone:
8189 case Xdrip_stretchB:
8208 case Xsand_stonein_1:
8209 case Xsand_stonein_2:
8210 case Xsand_stonein_3:
8211 case Xsand_stonein_4:
8215 return (is_backside || action_removing ? EL_EMPTY : element);
8220 static boolean check_linear_animation_EM(int tile)
8224 case Xsand_stonesand_1:
8225 case Xsand_stonesand_quickout_1:
8226 case Xsand_sandstone_1:
8227 case Xsand_stonein_1:
8228 case Xsand_stoneout_1:
8247 case Yacid_splash_eB:
8248 case Yacid_splash_wB:
8249 case Yemerald_stone:
8256 static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8257 boolean has_crumbled_graphics,
8258 int crumbled, int sync_frame)
8260 // if element can be crumbled, but certain action graphics are just empty
8261 // space (like instantly snapping sand to empty space in 1 frame), do not
8262 // treat these empty space graphics as crumbled graphics in EMC engine
8263 if (crumbled == IMG_EMPTY_SPACE)
8264 has_crumbled_graphics = FALSE;
8266 if (has_crumbled_graphics)
8268 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8269 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8270 g_crumbled->anim_delay,
8271 g_crumbled->anim_mode,
8272 g_crumbled->anim_start_frame,
8275 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8276 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8278 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8279 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8281 g_em->has_crumbled_graphics = TRUE;
8285 g_em->crumbled_bitmap = NULL;
8286 g_em->crumbled_src_x = 0;
8287 g_em->crumbled_src_y = 0;
8288 g_em->crumbled_border_size = 0;
8289 g_em->crumbled_tile_size = 0;
8291 g_em->has_crumbled_graphics = FALSE;
8296 void ResetGfxAnimation_EM(int x, int y, int tile)
8302 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8303 int tile, int frame_em, int x, int y)
8305 int action = object_mapping[tile].action;
8306 int direction = object_mapping[tile].direction;
8307 int effective_element = get_effective_element_EM(tile, frame_em);
8308 int graphic = (direction == MV_NONE ?
8309 el_act2img(effective_element, action) :
8310 el_act_dir2img(effective_element, action, direction));
8311 struct GraphicInfo *g = &graphic_info[graphic];
8313 boolean action_removing = (action == ACTION_DIGGING ||
8314 action == ACTION_SNAPPING ||
8315 action == ACTION_COLLECTING);
8316 boolean action_moving = (action == ACTION_FALLING ||
8317 action == ACTION_MOVING ||
8318 action == ACTION_PUSHING ||
8319 action == ACTION_EATING ||
8320 action == ACTION_FILLING ||
8321 action == ACTION_EMPTYING);
8322 boolean action_falling = (action == ACTION_FALLING ||
8323 action == ACTION_FILLING ||
8324 action == ACTION_EMPTYING);
8326 // special case: graphic uses "2nd movement tile" and has defined
8327 // 7 frames for movement animation (or less) => use default graphic
8328 // for last (8th) frame which ends the movement animation
8329 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8331 action = ACTION_DEFAULT; // (keep action_* unchanged for now)
8332 graphic = (direction == MV_NONE ?
8333 el_act2img(effective_element, action) :
8334 el_act_dir2img(effective_element, action, direction));
8336 g = &graphic_info[graphic];
8339 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8343 else if (action_moving)
8345 boolean is_backside = object_mapping[tile].is_backside;
8349 int direction = object_mapping[tile].direction;
8350 int move_dir = (action_falling ? MV_DOWN : direction);
8355 // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
8356 if (g->double_movement && frame_em == 0)
8360 if (move_dir == MV_LEFT)
8361 GfxFrame[x - 1][y] = GfxFrame[x][y];
8362 else if (move_dir == MV_RIGHT)
8363 GfxFrame[x + 1][y] = GfxFrame[x][y];
8364 else if (move_dir == MV_UP)
8365 GfxFrame[x][y - 1] = GfxFrame[x][y];
8366 else if (move_dir == MV_DOWN)
8367 GfxFrame[x][y + 1] = GfxFrame[x][y];
8374 // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
8375 if (tile == Xsand_stonesand_quickout_1 ||
8376 tile == Xsand_stonesand_quickout_2)
8380 if (graphic_info[graphic].anim_global_sync)
8381 sync_frame = FrameCounter;
8382 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8383 sync_frame = GfxFrame[x][y];
8385 sync_frame = 0; // playfield border (pseudo steel)
8387 SetRandomAnimationValue(x, y);
8389 int frame = getAnimationFrame(g->anim_frames,
8392 g->anim_start_frame,
8395 g_em->unique_identifier =
8396 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8399 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8400 int tile, int frame_em, int x, int y)
8402 int action = object_mapping[tile].action;
8403 int direction = object_mapping[tile].direction;
8404 boolean is_backside = object_mapping[tile].is_backside;
8405 int effective_element = get_effective_element_EM(tile, frame_em);
8406 int effective_action = action;
8407 int graphic = (direction == MV_NONE ?
8408 el_act2img(effective_element, effective_action) :
8409 el_act_dir2img(effective_element, effective_action,
8411 int crumbled = (direction == MV_NONE ?
8412 el_act2crm(effective_element, effective_action) :
8413 el_act_dir2crm(effective_element, effective_action,
8415 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8416 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8417 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8418 struct GraphicInfo *g = &graphic_info[graphic];
8421 // special case: graphic uses "2nd movement tile" and has defined
8422 // 7 frames for movement animation (or less) => use default graphic
8423 // for last (8th) frame which ends the movement animation
8424 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8426 effective_action = ACTION_DEFAULT;
8427 graphic = (direction == MV_NONE ?
8428 el_act2img(effective_element, effective_action) :
8429 el_act_dir2img(effective_element, effective_action,
8431 crumbled = (direction == MV_NONE ?
8432 el_act2crm(effective_element, effective_action) :
8433 el_act_dir2crm(effective_element, effective_action,
8436 g = &graphic_info[graphic];
8439 if (graphic_info[graphic].anim_global_sync)
8440 sync_frame = FrameCounter;
8441 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8442 sync_frame = GfxFrame[x][y];
8444 sync_frame = 0; // playfield border (pseudo steel)
8446 SetRandomAnimationValue(x, y);
8448 int frame = getAnimationFrame(g->anim_frames,
8451 g->anim_start_frame,
8454 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8455 g->double_movement && is_backside);
8457 // (updating the "crumbled" graphic definitions is probably not really needed,
8458 // as animations for crumbled graphics can't be longer than one EMC cycle)
8459 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8463 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8464 int player_nr, int anim, int frame_em)
8466 int element = player_mapping[player_nr][anim].element_rnd;
8467 int action = player_mapping[player_nr][anim].action;
8468 int direction = player_mapping[player_nr][anim].direction;
8469 int graphic = (direction == MV_NONE ?
8470 el_act2img(element, action) :
8471 el_act_dir2img(element, action, direction));
8472 struct GraphicInfo *g = &graphic_info[graphic];
8475 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8477 stored_player[player_nr].StepFrame = frame_em;
8479 sync_frame = stored_player[player_nr].Frame;
8481 int frame = getAnimationFrame(g->anim_frames,
8484 g->anim_start_frame,
8487 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8488 &g_em->src_x, &g_em->src_y, FALSE);
8491 void InitGraphicInfo_EM(void)
8496 int num_em_gfx_errors = 0;
8498 if (graphic_info_em_object[0][0].bitmap == NULL)
8500 // EM graphics not yet initialized in em_open_all()
8505 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8508 // always start with reliable default values
8509 for (i = 0; i < TILE_MAX; i++)
8511 object_mapping[i].element_rnd = EL_UNKNOWN;
8512 object_mapping[i].is_backside = FALSE;
8513 object_mapping[i].action = ACTION_DEFAULT;
8514 object_mapping[i].direction = MV_NONE;
8517 // always start with reliable default values
8518 for (p = 0; p < MAX_PLAYERS; p++)
8520 for (i = 0; i < SPR_MAX; i++)
8522 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8523 player_mapping[p][i].action = ACTION_DEFAULT;
8524 player_mapping[p][i].direction = MV_NONE;
8528 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8530 int e = em_object_mapping_list[i].element_em;
8532 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8533 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8535 if (em_object_mapping_list[i].action != -1)
8536 object_mapping[e].action = em_object_mapping_list[i].action;
8538 if (em_object_mapping_list[i].direction != -1)
8539 object_mapping[e].direction =
8540 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8543 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8545 int a = em_player_mapping_list[i].action_em;
8546 int p = em_player_mapping_list[i].player_nr;
8548 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8550 if (em_player_mapping_list[i].action != -1)
8551 player_mapping[p][a].action = em_player_mapping_list[i].action;
8553 if (em_player_mapping_list[i].direction != -1)
8554 player_mapping[p][a].direction =
8555 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8558 for (i = 0; i < TILE_MAX; i++)
8560 int element = object_mapping[i].element_rnd;
8561 int action = object_mapping[i].action;
8562 int direction = object_mapping[i].direction;
8563 boolean is_backside = object_mapping[i].is_backside;
8564 boolean action_exploding = ((action == ACTION_EXPLODING ||
8565 action == ACTION_SMASHED_BY_ROCK ||
8566 action == ACTION_SMASHED_BY_SPRING) &&
8567 element != EL_DIAMOND);
8568 boolean action_active = (action == ACTION_ACTIVE);
8569 boolean action_other = (action == ACTION_OTHER);
8571 for (j = 0; j < 8; j++)
8573 int effective_element = get_effective_element_EM(i, j);
8574 int effective_action = (j < 7 ? action :
8575 i == Xdrip_stretch ? action :
8576 i == Xdrip_stretchB ? action :
8577 i == Ydrip_s1 ? action :
8578 i == Ydrip_s1B ? action :
8579 i == Xball_1B ? action :
8580 i == Xball_2 ? action :
8581 i == Xball_2B ? action :
8582 i == Yball_eat ? action :
8583 i == Ykey_1_eat ? action :
8584 i == Ykey_2_eat ? action :
8585 i == Ykey_3_eat ? action :
8586 i == Ykey_4_eat ? action :
8587 i == Ykey_5_eat ? action :
8588 i == Ykey_6_eat ? action :
8589 i == Ykey_7_eat ? action :
8590 i == Ykey_8_eat ? action :
8591 i == Ylenses_eat ? action :
8592 i == Ymagnify_eat ? action :
8593 i == Ygrass_eat ? action :
8594 i == Ydirt_eat ? action :
8595 i == Xsand_stonein_1 ? action :
8596 i == Xsand_stonein_2 ? action :
8597 i == Xsand_stonein_3 ? action :
8598 i == Xsand_stonein_4 ? action :
8599 i == Xsand_stoneout_1 ? action :
8600 i == Xsand_stoneout_2 ? action :
8601 i == Xboom_android ? ACTION_EXPLODING :
8602 action_exploding ? ACTION_EXPLODING :
8603 action_active ? action :
8604 action_other ? action :
8606 int graphic = (el_act_dir2img(effective_element, effective_action,
8608 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8610 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8611 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8612 boolean has_action_graphics = (graphic != base_graphic);
8613 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8614 struct GraphicInfo *g = &graphic_info[graphic];
8615 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8618 // ensure to get symmetric 3-frame, 2-delay animations as used in EM
8619 boolean special_animation = (action != ACTION_DEFAULT &&
8620 g->anim_frames == 3 &&
8621 g->anim_delay == 2 &&
8622 g->anim_mode & ANIM_LINEAR);
8623 int sync_frame = (i == Xdrip_stretch ? 7 :
8624 i == Xdrip_stretchB ? 7 :
8625 i == Ydrip_s2 ? j + 8 :
8626 i == Ydrip_s2B ? j + 8 :
8635 i == Xfake_acid_1 ? 0 :
8636 i == Xfake_acid_2 ? 10 :
8637 i == Xfake_acid_3 ? 20 :
8638 i == Xfake_acid_4 ? 30 :
8639 i == Xfake_acid_5 ? 40 :
8640 i == Xfake_acid_6 ? 50 :
8641 i == Xfake_acid_7 ? 60 :
8642 i == Xfake_acid_8 ? 70 :
8644 i == Xball_2B ? j + 8 :
8645 i == Yball_eat ? j + 1 :
8646 i == Ykey_1_eat ? j + 1 :
8647 i == Ykey_2_eat ? j + 1 :
8648 i == Ykey_3_eat ? j + 1 :
8649 i == Ykey_4_eat ? j + 1 :
8650 i == Ykey_5_eat ? j + 1 :
8651 i == Ykey_6_eat ? j + 1 :
8652 i == Ykey_7_eat ? j + 1 :
8653 i == Ykey_8_eat ? j + 1 :
8654 i == Ylenses_eat ? j + 1 :
8655 i == Ymagnify_eat ? j + 1 :
8656 i == Ygrass_eat ? j + 1 :
8657 i == Ydirt_eat ? j + 1 :
8658 i == Xamoeba_1 ? 0 :
8659 i == Xamoeba_2 ? 1 :
8660 i == Xamoeba_3 ? 2 :
8661 i == Xamoeba_4 ? 3 :
8662 i == Xamoeba_5 ? 0 :
8663 i == Xamoeba_6 ? 1 :
8664 i == Xamoeba_7 ? 2 :
8665 i == Xamoeba_8 ? 3 :
8666 i == Xexit_2 ? j + 8 :
8667 i == Xexit_3 ? j + 16 :
8668 i == Xdynamite_1 ? 0 :
8669 i == Xdynamite_2 ? 8 :
8670 i == Xdynamite_3 ? 16 :
8671 i == Xdynamite_4 ? 24 :
8672 i == Xsand_stonein_1 ? j + 1 :
8673 i == Xsand_stonein_2 ? j + 9 :
8674 i == Xsand_stonein_3 ? j + 17 :
8675 i == Xsand_stonein_4 ? j + 25 :
8676 i == Xsand_stoneout_1 && j == 0 ? 0 :
8677 i == Xsand_stoneout_1 && j == 1 ? 0 :
8678 i == Xsand_stoneout_1 && j == 2 ? 1 :
8679 i == Xsand_stoneout_1 && j == 3 ? 2 :
8680 i == Xsand_stoneout_1 && j == 4 ? 2 :
8681 i == Xsand_stoneout_1 && j == 5 ? 3 :
8682 i == Xsand_stoneout_1 && j == 6 ? 4 :
8683 i == Xsand_stoneout_1 && j == 7 ? 4 :
8684 i == Xsand_stoneout_2 && j == 0 ? 5 :
8685 i == Xsand_stoneout_2 && j == 1 ? 6 :
8686 i == Xsand_stoneout_2 && j == 2 ? 7 :
8687 i == Xsand_stoneout_2 && j == 3 ? 8 :
8688 i == Xsand_stoneout_2 && j == 4 ? 9 :
8689 i == Xsand_stoneout_2 && j == 5 ? 11 :
8690 i == Xsand_stoneout_2 && j == 6 ? 13 :
8691 i == Xsand_stoneout_2 && j == 7 ? 15 :
8692 i == Xboom_bug && j == 1 ? 2 :
8693 i == Xboom_bug && j == 2 ? 2 :
8694 i == Xboom_bug && j == 3 ? 4 :
8695 i == Xboom_bug && j == 4 ? 4 :
8696 i == Xboom_bug && j == 5 ? 2 :
8697 i == Xboom_bug && j == 6 ? 2 :
8698 i == Xboom_bug && j == 7 ? 0 :
8699 i == Xboom_bomb && j == 1 ? 2 :
8700 i == Xboom_bomb && j == 2 ? 2 :
8701 i == Xboom_bomb && j == 3 ? 4 :
8702 i == Xboom_bomb && j == 4 ? 4 :
8703 i == Xboom_bomb && j == 5 ? 2 :
8704 i == Xboom_bomb && j == 6 ? 2 :
8705 i == Xboom_bomb && j == 7 ? 0 :
8706 i == Xboom_android && j == 7 ? 6 :
8707 i == Xboom_1 && j == 1 ? 2 :
8708 i == Xboom_1 && j == 2 ? 2 :
8709 i == Xboom_1 && j == 3 ? 4 :
8710 i == Xboom_1 && j == 4 ? 4 :
8711 i == Xboom_1 && j == 5 ? 6 :
8712 i == Xboom_1 && j == 6 ? 6 :
8713 i == Xboom_1 && j == 7 ? 8 :
8714 i == Xboom_2 && j == 0 ? 8 :
8715 i == Xboom_2 && j == 1 ? 8 :
8716 i == Xboom_2 && j == 2 ? 10 :
8717 i == Xboom_2 && j == 3 ? 10 :
8718 i == Xboom_2 && j == 4 ? 10 :
8719 i == Xboom_2 && j == 5 ? 12 :
8720 i == Xboom_2 && j == 6 ? 12 :
8721 i == Xboom_2 && j == 7 ? 12 :
8722 special_animation && j == 4 ? 3 :
8723 effective_action != action ? 0 :
8727 Bitmap *debug_bitmap = g_em->bitmap;
8728 int debug_src_x = g_em->src_x;
8729 int debug_src_y = g_em->src_y;
8732 int frame = getAnimationFrame(g->anim_frames,
8735 g->anim_start_frame,
8738 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8739 g->double_movement && is_backside);
8741 g_em->bitmap = src_bitmap;
8742 g_em->src_x = src_x;
8743 g_em->src_y = src_y;
8744 g_em->src_offset_x = 0;
8745 g_em->src_offset_y = 0;
8746 g_em->dst_offset_x = 0;
8747 g_em->dst_offset_y = 0;
8748 g_em->width = TILEX;
8749 g_em->height = TILEY;
8751 g_em->preserve_background = FALSE;
8753 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8756 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8757 effective_action == ACTION_MOVING ||
8758 effective_action == ACTION_PUSHING ||
8759 effective_action == ACTION_EATING)) ||
8760 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8761 effective_action == ACTION_EMPTYING)))
8764 (effective_action == ACTION_FALLING ||
8765 effective_action == ACTION_FILLING ||
8766 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8767 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8768 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8769 int num_steps = (i == Ydrip_s1 ? 16 :
8770 i == Ydrip_s1B ? 16 :
8771 i == Ydrip_s2 ? 16 :
8772 i == Ydrip_s2B ? 16 :
8773 i == Xsand_stonein_1 ? 32 :
8774 i == Xsand_stonein_2 ? 32 :
8775 i == Xsand_stonein_3 ? 32 :
8776 i == Xsand_stonein_4 ? 32 :
8777 i == Xsand_stoneout_1 ? 16 :
8778 i == Xsand_stoneout_2 ? 16 : 8);
8779 int cx = ABS(dx) * (TILEX / num_steps);
8780 int cy = ABS(dy) * (TILEY / num_steps);
8781 int step_frame = (i == Ydrip_s2 ? j + 8 :
8782 i == Ydrip_s2B ? j + 8 :
8783 i == Xsand_stonein_2 ? j + 8 :
8784 i == Xsand_stonein_3 ? j + 16 :
8785 i == Xsand_stonein_4 ? j + 24 :
8786 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8787 int step = (is_backside ? step_frame : num_steps - step_frame);
8789 if (is_backside) // tile where movement starts
8791 if (dx < 0 || dy < 0)
8793 g_em->src_offset_x = cx * step;
8794 g_em->src_offset_y = cy * step;
8798 g_em->dst_offset_x = cx * step;
8799 g_em->dst_offset_y = cy * step;
8802 else // tile where movement ends
8804 if (dx < 0 || dy < 0)
8806 g_em->dst_offset_x = cx * step;
8807 g_em->dst_offset_y = cy * step;
8811 g_em->src_offset_x = cx * step;
8812 g_em->src_offset_y = cy * step;
8816 g_em->width = TILEX - cx * step;
8817 g_em->height = TILEY - cy * step;
8820 // create unique graphic identifier to decide if tile must be redrawn
8821 /* bit 31 - 16 (16 bit): EM style graphic
8822 bit 15 - 12 ( 4 bit): EM style frame
8823 bit 11 - 6 ( 6 bit): graphic width
8824 bit 5 - 0 ( 6 bit): graphic height */
8825 g_em->unique_identifier =
8826 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8830 // skip check for EMC elements not contained in original EMC artwork
8831 if (element == EL_EMC_FAKE_ACID)
8834 if (g_em->bitmap != debug_bitmap ||
8835 g_em->src_x != debug_src_x ||
8836 g_em->src_y != debug_src_y ||
8837 g_em->src_offset_x != 0 ||
8838 g_em->src_offset_y != 0 ||
8839 g_em->dst_offset_x != 0 ||
8840 g_em->dst_offset_y != 0 ||
8841 g_em->width != TILEX ||
8842 g_em->height != TILEY)
8844 static int last_i = -1;
8852 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8853 i, element, element_info[element].token_name,
8854 element_action_info[effective_action].suffix, direction);
8856 if (element != effective_element)
8857 printf(" [%d ('%s')]",
8859 element_info[effective_element].token_name);
8863 if (g_em->bitmap != debug_bitmap)
8864 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8865 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8867 if (g_em->src_x != debug_src_x ||
8868 g_em->src_y != debug_src_y)
8869 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8870 j, (is_backside ? 'B' : 'F'),
8871 g_em->src_x, g_em->src_y,
8872 g_em->src_x / 32, g_em->src_y / 32,
8873 debug_src_x, debug_src_y,
8874 debug_src_x / 32, debug_src_y / 32);
8876 if (g_em->src_offset_x != 0 ||
8877 g_em->src_offset_y != 0 ||
8878 g_em->dst_offset_x != 0 ||
8879 g_em->dst_offset_y != 0)
8880 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8882 g_em->src_offset_x, g_em->src_offset_y,
8883 g_em->dst_offset_x, g_em->dst_offset_y);
8885 if (g_em->width != TILEX ||
8886 g_em->height != TILEY)
8887 printf(" %d (%d): size %d,%d should be %d,%d\n",
8889 g_em->width, g_em->height, TILEX, TILEY);
8891 num_em_gfx_errors++;
8898 for (i = 0; i < TILE_MAX; i++)
8900 for (j = 0; j < 8; j++)
8902 int element = object_mapping[i].element_rnd;
8903 int action = object_mapping[i].action;
8904 int direction = object_mapping[i].direction;
8905 boolean is_backside = object_mapping[i].is_backside;
8906 int graphic_action = el_act_dir2img(element, action, direction);
8907 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8909 if ((action == ACTION_SMASHED_BY_ROCK ||
8910 action == ACTION_SMASHED_BY_SPRING ||
8911 action == ACTION_EATING) &&
8912 graphic_action == graphic_default)
8914 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8915 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8916 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8917 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8920 // no separate animation for "smashed by rock" -- use rock instead
8921 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8922 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8924 g_em->bitmap = g_xx->bitmap;
8925 g_em->src_x = g_xx->src_x;
8926 g_em->src_y = g_xx->src_y;
8927 g_em->src_offset_x = g_xx->src_offset_x;
8928 g_em->src_offset_y = g_xx->src_offset_y;
8929 g_em->dst_offset_x = g_xx->dst_offset_x;
8930 g_em->dst_offset_y = g_xx->dst_offset_y;
8931 g_em->width = g_xx->width;
8932 g_em->height = g_xx->height;
8933 g_em->unique_identifier = g_xx->unique_identifier;
8936 g_em->preserve_background = TRUE;
8941 for (p = 0; p < MAX_PLAYERS; p++)
8943 for (i = 0; i < SPR_MAX; i++)
8945 int element = player_mapping[p][i].element_rnd;
8946 int action = player_mapping[p][i].action;
8947 int direction = player_mapping[p][i].direction;
8949 for (j = 0; j < 8; j++)
8951 int effective_element = element;
8952 int effective_action = action;
8953 int graphic = (direction == MV_NONE ?
8954 el_act2img(effective_element, effective_action) :
8955 el_act_dir2img(effective_element, effective_action,
8957 struct GraphicInfo *g = &graphic_info[graphic];
8958 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8964 Bitmap *debug_bitmap = g_em->bitmap;
8965 int debug_src_x = g_em->src_x;
8966 int debug_src_y = g_em->src_y;
8969 int frame = getAnimationFrame(g->anim_frames,
8972 g->anim_start_frame,
8975 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8977 g_em->bitmap = src_bitmap;
8978 g_em->src_x = src_x;
8979 g_em->src_y = src_y;
8980 g_em->src_offset_x = 0;
8981 g_em->src_offset_y = 0;
8982 g_em->dst_offset_x = 0;
8983 g_em->dst_offset_y = 0;
8984 g_em->width = TILEX;
8985 g_em->height = TILEY;
8989 // skip check for EMC elements not contained in original EMC artwork
8990 if (element == EL_PLAYER_3 ||
8991 element == EL_PLAYER_4)
8994 if (g_em->bitmap != debug_bitmap ||
8995 g_em->src_x != debug_src_x ||
8996 g_em->src_y != debug_src_y)
8998 static int last_i = -1;
9006 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
9007 p, i, element, element_info[element].token_name,
9008 element_action_info[effective_action].suffix, direction);
9010 if (element != effective_element)
9011 printf(" [%d ('%s')]",
9013 element_info[effective_element].token_name);
9017 if (g_em->bitmap != debug_bitmap)
9018 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9019 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9021 if (g_em->src_x != debug_src_x ||
9022 g_em->src_y != debug_src_y)
9023 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9025 g_em->src_x, g_em->src_y,
9026 g_em->src_x / 32, g_em->src_y / 32,
9027 debug_src_x, debug_src_y,
9028 debug_src_x / 32, debug_src_y / 32);
9030 num_em_gfx_errors++;
9040 printf("::: [%d errors found]\n", num_em_gfx_errors);
9046 static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9047 boolean any_player_moving,
9048 boolean any_player_snapping,
9049 boolean any_player_dropping)
9051 if (frame == 0 && !any_player_dropping)
9053 if (!local_player->was_waiting)
9055 if (!CheckSaveEngineSnapshotToList())
9058 local_player->was_waiting = TRUE;
9061 else if (any_player_moving || any_player_snapping || any_player_dropping)
9063 local_player->was_waiting = FALSE;
9067 static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9068 boolean murphy_is_dropping)
9070 if (murphy_is_waiting)
9072 if (!local_player->was_waiting)
9074 if (!CheckSaveEngineSnapshotToList())
9077 local_player->was_waiting = TRUE;
9082 local_player->was_waiting = FALSE;
9086 static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9087 boolean button_released)
9089 if (button_released)
9091 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9092 CheckSaveEngineSnapshotToList();
9094 else if (element_clicked)
9096 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9097 CheckSaveEngineSnapshotToList();
9099 game.snapshot.changed_action = TRUE;
9103 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9104 boolean any_player_moving,
9105 boolean any_player_snapping,
9106 boolean any_player_dropping)
9108 if (tape.single_step && tape.recording && !tape.pausing)
9109 if (frame == 0 && !any_player_dropping)
9110 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9112 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9113 any_player_snapping, any_player_dropping);
9116 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9117 boolean murphy_is_dropping)
9119 boolean murphy_starts_dropping = FALSE;
9122 for (i = 0; i < MAX_PLAYERS; i++)
9123 if (stored_player[i].force_dropping)
9124 murphy_starts_dropping = TRUE;
9126 if (tape.single_step && tape.recording && !tape.pausing)
9127 if (murphy_is_waiting && !murphy_starts_dropping)
9128 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9130 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9133 void CheckSingleStepMode_MM(boolean element_clicked,
9134 boolean button_released)
9136 if (tape.single_step && tape.recording && !tape.pausing)
9137 if (button_released)
9138 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9140 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9143 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9144 int graphic, int sync_frame, int x, int y)
9146 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9148 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9151 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9153 return (IS_NEXT_FRAME(sync_frame, graphic));
9156 int getGraphicInfo_Delay(int graphic)
9158 return graphic_info[graphic].anim_delay;
9161 void PlayMenuSoundExt(int sound)
9163 if (sound == SND_UNDEFINED)
9166 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9167 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9170 if (IS_LOOP_SOUND(sound))
9171 PlaySoundLoop(sound);
9176 void PlayMenuSound(void)
9178 PlayMenuSoundExt(menu.sound[game_status]);
9181 void PlayMenuSoundStereo(int sound, int stereo_position)
9183 if (sound == SND_UNDEFINED)
9186 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9187 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9190 if (IS_LOOP_SOUND(sound))
9191 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9193 PlaySoundStereo(sound, stereo_position);
9196 void PlayMenuSoundIfLoopExt(int sound)
9198 if (sound == SND_UNDEFINED)
9201 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9202 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9205 if (IS_LOOP_SOUND(sound))
9206 PlaySoundLoop(sound);
9209 void PlayMenuSoundIfLoop(void)
9211 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9214 void PlayMenuMusicExt(int music)
9216 if (music == MUS_UNDEFINED)
9219 if (!setup.sound_music)
9222 if (IS_LOOP_MUSIC(music))
9223 PlayMusicLoop(music);
9228 void PlayMenuMusic(void)
9230 char *curr_music = getCurrentlyPlayingMusicFilename();
9231 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9233 if (!strEqual(curr_music, next_music))
9234 PlayMenuMusicExt(menu.music[game_status]);
9237 void PlayMenuSoundsAndMusic(void)
9243 static void FadeMenuSounds(void)
9248 static void FadeMenuMusic(void)
9250 char *curr_music = getCurrentlyPlayingMusicFilename();
9251 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9253 if (!strEqual(curr_music, next_music))
9257 void FadeMenuSoundsAndMusic(void)
9263 void PlaySoundActivating(void)
9266 PlaySound(SND_MENU_ITEM_ACTIVATING);
9270 void PlaySoundSelecting(void)
9273 PlaySound(SND_MENU_ITEM_SELECTING);
9277 void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
9279 boolean change_fullscreen = (setup.fullscreen !=
9280 video.fullscreen_enabled);
9281 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9282 setup.window_scaling_percent !=
9283 video.window_scaling_percent);
9285 if (change_window_scaling_percent && video.fullscreen_enabled)
9288 if (!change_window_scaling_percent && !video.fullscreen_available)
9291 if (change_window_scaling_percent)
9293 SDLSetWindowScaling(setup.window_scaling_percent);
9297 else if (change_fullscreen)
9299 SDLSetWindowFullscreen(setup.fullscreen);
9301 // set setup value according to successfully changed fullscreen mode
9302 setup.fullscreen = video.fullscreen_enabled;
9307 if (change_fullscreen ||
9308 change_window_scaling_percent)
9310 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9312 // save backbuffer content which gets lost when toggling fullscreen mode
9313 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9315 if (change_window_scaling_percent)
9317 // keep window mode, but change window scaling
9318 video.fullscreen_enabled = TRUE; // force new window scaling
9321 // toggle fullscreen
9322 ChangeVideoModeIfNeeded(setup.fullscreen);
9324 // set setup value according to successfully changed fullscreen mode
9325 setup.fullscreen = video.fullscreen_enabled;
9327 // restore backbuffer content from temporary backbuffer backup bitmap
9328 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9330 FreeBitmap(tmp_backbuffer);
9332 // update visible window/screen
9333 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9337 static void JoinRectangles(int *x, int *y, int *width, int *height,
9338 int x2, int y2, int width2, int height2)
9340 // do not join with "off-screen" rectangle
9341 if (x2 == -1 || y2 == -1)
9346 *width = MAX(*width, width2);
9347 *height = MAX(*height, height2);
9350 void SetAnimStatus(int anim_status_new)
9352 if (anim_status_new == GAME_MODE_MAIN)
9353 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9354 else if (anim_status_new == GAME_MODE_SCORES)
9355 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9357 global.anim_status_next = anim_status_new;
9359 // directly set screen modes that are entered without fading
9360 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9361 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9362 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9363 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9364 global.anim_status = global.anim_status_next;
9367 void SetGameStatus(int game_status_new)
9369 if (game_status_new != game_status)
9370 game_status_last_screen = game_status;
9372 game_status = game_status_new;
9374 SetAnimStatus(game_status_new);
9377 void SetFontStatus(int game_status_new)
9379 static int last_game_status = -1;
9381 if (game_status_new != -1)
9383 // set game status for font use after storing last game status
9384 last_game_status = game_status;
9385 game_status = game_status_new;
9389 // reset game status after font use from last stored game status
9390 game_status = last_game_status;
9394 void ResetFontStatus(void)
9399 void SetLevelSetInfo(char *identifier, int level_nr)
9401 setString(&levelset.identifier, identifier);
9403 levelset.level_nr = level_nr;
9406 boolean CheckIfPlayfieldViewportHasChanged(void)
9408 // if game status has not changed, playfield viewport has not changed either
9409 if (game_status == game_status_last)
9412 // check if playfield viewport has changed with current game status
9413 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9414 int new_real_sx = vp_playfield->x;
9415 int new_real_sy = vp_playfield->y;
9416 int new_full_sxsize = vp_playfield->width;
9417 int new_full_sysize = vp_playfield->height;
9419 return (new_real_sx != REAL_SX ||
9420 new_real_sy != REAL_SY ||
9421 new_full_sxsize != FULL_SXSIZE ||
9422 new_full_sysize != FULL_SYSIZE);
9425 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged(void)
9427 return (CheckIfGlobalBorderHasChanged() ||
9428 CheckIfPlayfieldViewportHasChanged());
9431 void ChangeViewportPropertiesIfNeeded(void)
9433 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9434 FALSE : setup.small_game_graphics);
9435 int gfx_game_mode = game_status;
9436 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9438 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9439 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9440 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9441 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9442 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9443 int new_win_xsize = vp_window->width;
9444 int new_win_ysize = vp_window->height;
9445 int border_left = vp_playfield->border_left;
9446 int border_right = vp_playfield->border_right;
9447 int border_top = vp_playfield->border_top;
9448 int border_bottom = vp_playfield->border_bottom;
9449 int new_sx = vp_playfield->x + border_left;
9450 int new_sy = vp_playfield->y + border_top;
9451 int new_sxsize = vp_playfield->width - border_left - border_right;
9452 int new_sysize = vp_playfield->height - border_top - border_bottom;
9453 int new_real_sx = vp_playfield->x;
9454 int new_real_sy = vp_playfield->y;
9455 int new_full_sxsize = vp_playfield->width;
9456 int new_full_sysize = vp_playfield->height;
9457 int new_dx = vp_door_1->x;
9458 int new_dy = vp_door_1->y;
9459 int new_dxsize = vp_door_1->width;
9460 int new_dysize = vp_door_1->height;
9461 int new_vx = vp_door_2->x;
9462 int new_vy = vp_door_2->y;
9463 int new_vxsize = vp_door_2->width;
9464 int new_vysize = vp_door_2->height;
9465 int new_ex = vp_door_3->x;
9466 int new_ey = vp_door_3->y;
9467 int new_exsize = vp_door_3->width;
9468 int new_eysize = vp_door_3->height;
9469 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9470 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9471 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9472 int new_scr_fieldx = new_sxsize / tilesize;
9473 int new_scr_fieldy = new_sysize / tilesize;
9474 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9475 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9476 boolean init_gfx_buffers = FALSE;
9477 boolean init_video_buffer = FALSE;
9478 boolean init_gadgets_and_anims = FALSE;
9479 boolean init_em_graphics = FALSE;
9481 if (new_win_xsize != WIN_XSIZE ||
9482 new_win_ysize != WIN_YSIZE)
9484 WIN_XSIZE = new_win_xsize;
9485 WIN_YSIZE = new_win_ysize;
9487 init_video_buffer = TRUE;
9488 init_gfx_buffers = TRUE;
9489 init_gadgets_and_anims = TRUE;
9491 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9494 if (new_scr_fieldx != SCR_FIELDX ||
9495 new_scr_fieldy != SCR_FIELDY)
9497 // this always toggles between MAIN and GAME when using small tile size
9499 SCR_FIELDX = new_scr_fieldx;
9500 SCR_FIELDY = new_scr_fieldy;
9502 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9513 new_sxsize != SXSIZE ||
9514 new_sysize != SYSIZE ||
9515 new_dxsize != DXSIZE ||
9516 new_dysize != DYSIZE ||
9517 new_vxsize != VXSIZE ||
9518 new_vysize != VYSIZE ||
9519 new_exsize != EXSIZE ||
9520 new_eysize != EYSIZE ||
9521 new_real_sx != REAL_SX ||
9522 new_real_sy != REAL_SY ||
9523 new_full_sxsize != FULL_SXSIZE ||
9524 new_full_sysize != FULL_SYSIZE ||
9525 new_tilesize_var != TILESIZE_VAR
9528 // ------------------------------------------------------------------------
9529 // determine next fading area for changed viewport definitions
9530 // ------------------------------------------------------------------------
9532 // start with current playfield area (default fading area)
9535 FADE_SXSIZE = FULL_SXSIZE;
9536 FADE_SYSIZE = FULL_SYSIZE;
9538 // add new playfield area if position or size has changed
9539 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9540 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9542 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9543 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9546 // add current and new door 1 area if position or size has changed
9547 if (new_dx != DX || new_dy != DY ||
9548 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9550 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9551 DX, DY, DXSIZE, DYSIZE);
9552 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9553 new_dx, new_dy, new_dxsize, new_dysize);
9556 // add current and new door 2 area if position or size has changed
9557 if (new_dx != VX || new_dy != VY ||
9558 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9560 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9561 VX, VY, VXSIZE, VYSIZE);
9562 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9563 new_vx, new_vy, new_vxsize, new_vysize);
9566 // ------------------------------------------------------------------------
9567 // handle changed tile size
9568 // ------------------------------------------------------------------------
9570 if (new_tilesize_var != TILESIZE_VAR)
9572 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9574 // changing tile size invalidates scroll values of engine snapshots
9575 FreeEngineSnapshotSingle();
9577 // changing tile size requires update of graphic mapping for EM engine
9578 init_em_graphics = TRUE;
9589 SXSIZE = new_sxsize;
9590 SYSIZE = new_sysize;
9591 DXSIZE = new_dxsize;
9592 DYSIZE = new_dysize;
9593 VXSIZE = new_vxsize;
9594 VYSIZE = new_vysize;
9595 EXSIZE = new_exsize;
9596 EYSIZE = new_eysize;
9597 REAL_SX = new_real_sx;
9598 REAL_SY = new_real_sy;
9599 FULL_SXSIZE = new_full_sxsize;
9600 FULL_SYSIZE = new_full_sysize;
9601 TILESIZE_VAR = new_tilesize_var;
9603 init_gfx_buffers = TRUE;
9604 init_gadgets_and_anims = TRUE;
9606 // printf("::: viewports: init_gfx_buffers\n");
9607 // printf("::: viewports: init_gadgets_and_anims\n");
9610 if (init_gfx_buffers)
9612 // printf("::: init_gfx_buffers\n");
9614 SCR_FIELDX = new_scr_fieldx_buffers;
9615 SCR_FIELDY = new_scr_fieldy_buffers;
9619 SCR_FIELDX = new_scr_fieldx;
9620 SCR_FIELDY = new_scr_fieldy;
9622 SetDrawDeactivationMask(REDRAW_NONE);
9623 SetDrawBackgroundMask(REDRAW_FIELD);
9626 if (init_video_buffer)
9628 // printf("::: init_video_buffer\n");
9630 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9631 InitImageTextures();
9634 if (init_gadgets_and_anims)
9636 // printf("::: init_gadgets_and_anims\n");
9639 InitGlobalAnimations();
9642 if (init_em_graphics)
9644 InitGraphicInfo_EM();