1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 static int getFieldbufferOffsetX_RND()
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND()
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 static int getLevelFromScreenX_MM(int sx)
363 int level_xsize = level.native_mm_level->fieldx;
364 int full_xsize = level_xsize * TILESIZE_VAR;
366 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
369 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
374 static int getLevelFromScreenY_MM(int sy)
376 int level_ysize = level.native_mm_level->fieldy;
377 int full_ysize = level_ysize * TILESIZE_VAR;
379 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
382 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
387 int getLevelFromScreenX(int x)
389 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
390 return getLevelFromScreenX_EM(x);
391 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
392 return getLevelFromScreenX_SP(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
394 return getLevelFromScreenX_MM(x);
396 return getLevelFromScreenX_RND(x);
399 int getLevelFromScreenY(int y)
401 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
402 return getLevelFromScreenY_EM(y);
403 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
404 return getLevelFromScreenY_SP(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
406 return getLevelFromScreenY_MM(y);
408 return getLevelFromScreenY_RND(y);
411 void DumpTile(int x, int y)
417 printf_line("-", 79);
418 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
419 printf_line("-", 79);
421 if (!IN_LEV_FIELD(x, y))
423 printf("(not in level field)\n");
429 token_name = element_info[Feld[x][y]].token_name;
431 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
432 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
433 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
434 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
435 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
436 printf(" MovPos: %d\n", MovPos[x][y]);
437 printf(" MovDir: %d\n", MovDir[x][y]);
438 printf(" MovDelay: %d\n", MovDelay[x][y]);
439 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
440 printf(" CustomValue: %d\n", CustomValue[x][y]);
441 printf(" GfxElement: %d\n", GfxElement[x][y]);
442 printf(" GfxAction: %d\n", GfxAction[x][y]);
443 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
444 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
448 void DumpTileFromScreen(int sx, int sy)
450 int lx = getLevelFromScreenX(sx);
451 int ly = getLevelFromScreenY(sy);
456 void SetDrawtoField(int mode)
458 if (mode == DRAW_TO_FIELDBUFFER)
464 BX2 = SCR_FIELDX + 1;
465 BY2 = SCR_FIELDY + 1;
467 drawto_field = fieldbuffer;
469 else /* DRAW_TO_BACKBUFFER */
475 BX2 = SCR_FIELDX - 1;
476 BY2 = SCR_FIELDY - 1;
478 drawto_field = backbuffer;
482 static void RedrawPlayfield_RND()
484 if (game.envelope_active)
487 DrawLevel(REDRAW_ALL);
491 void RedrawPlayfield()
493 if (game_status != GAME_MODE_PLAYING)
496 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
497 RedrawPlayfield_EM(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
499 RedrawPlayfield_SP(TRUE);
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
501 RedrawPlayfield_MM();
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
503 RedrawPlayfield_RND();
505 BlitScreenToBitmap(backbuffer);
507 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
511 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
514 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
515 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
517 if (x == -1 && y == -1)
520 if (draw_target == DRAW_TO_SCREEN)
521 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
523 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
526 static void DrawMaskedBorderExt_FIELD(int draw_target)
528 if (global.border_status >= GAME_MODE_MAIN &&
529 global.border_status <= GAME_MODE_PLAYING &&
530 border.draw_masked[global.border_status])
531 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
535 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
537 // when drawing to backbuffer, never draw border over open doors
538 if (draw_target == DRAW_TO_BACKBUFFER &&
539 (GetDoorState() & DOOR_OPEN_1))
542 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
543 (global.border_status != GAME_MODE_EDITOR ||
544 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
545 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
548 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
550 // when drawing to backbuffer, never draw border over open doors
551 if (draw_target == DRAW_TO_BACKBUFFER &&
552 (GetDoorState() & DOOR_OPEN_2))
555 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
556 global.border_status != GAME_MODE_EDITOR)
557 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
560 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
562 /* currently not available */
565 static void DrawMaskedBorderExt_ALL(int draw_target)
567 DrawMaskedBorderExt_FIELD(draw_target);
568 DrawMaskedBorderExt_DOOR_1(draw_target);
569 DrawMaskedBorderExt_DOOR_2(draw_target);
570 DrawMaskedBorderExt_DOOR_3(draw_target);
573 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
575 /* never draw masked screen borders on borderless screens */
576 if (global.border_status == GAME_MODE_LOADING ||
577 global.border_status == GAME_MODE_TITLE)
580 if (redraw_mask & REDRAW_ALL)
581 DrawMaskedBorderExt_ALL(draw_target);
584 if (redraw_mask & REDRAW_FIELD)
585 DrawMaskedBorderExt_FIELD(draw_target);
586 if (redraw_mask & REDRAW_DOOR_1)
587 DrawMaskedBorderExt_DOOR_1(draw_target);
588 if (redraw_mask & REDRAW_DOOR_2)
589 DrawMaskedBorderExt_DOOR_2(draw_target);
590 if (redraw_mask & REDRAW_DOOR_3)
591 DrawMaskedBorderExt_DOOR_3(draw_target);
595 void DrawMaskedBorder_FIELD()
597 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
600 void DrawMaskedBorder(int redraw_mask)
602 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
605 void DrawMaskedBorderToTarget(int draw_target)
607 if (draw_target == DRAW_TO_BACKBUFFER ||
608 draw_target == DRAW_TO_SCREEN)
610 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
614 int last_border_status = global.border_status;
616 if (draw_target == DRAW_TO_FADE_SOURCE)
618 global.border_status = gfx.fade_border_source_status;
619 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
621 else if (draw_target == DRAW_TO_FADE_TARGET)
623 global.border_status = gfx.fade_border_target_status;
624 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
627 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
629 global.border_status = last_border_status;
630 gfx.masked_border_bitmap_ptr = backbuffer;
634 void DrawTileCursor(int draw_target)
640 int graphic = IMG_GLOBAL_TILE_CURSOR;
642 int tilesize = TILESIZE_VAR;
643 int width = tilesize;
644 int height = tilesize;
646 if (game_status != GAME_MODE_PLAYING)
649 if (!tile_cursor.enabled ||
653 if (tile_cursor.moving)
655 int step = TILESIZE_VAR / 4;
656 int dx = tile_cursor.target_x - tile_cursor.x;
657 int dy = tile_cursor.target_y - tile_cursor.y;
660 tile_cursor.x = tile_cursor.target_x;
662 tile_cursor.x += SIGN(dx) * step;
665 tile_cursor.y = tile_cursor.target_y;
667 tile_cursor.y += SIGN(dy) * step;
669 if (tile_cursor.x == tile_cursor.target_x &&
670 tile_cursor.y == tile_cursor.target_y)
671 tile_cursor.moving = FALSE;
674 dst_x = tile_cursor.x;
675 dst_y = tile_cursor.y;
677 frame = getGraphicAnimationFrame(graphic, -1);
679 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
682 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
683 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
685 if (draw_target == DRAW_TO_SCREEN)
686 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
688 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
692 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
694 int fx = getFieldbufferOffsetX_RND();
695 int fy = getFieldbufferOffsetY_RND();
697 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
700 void BlitScreenToBitmap(Bitmap *target_bitmap)
702 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
703 BlitScreenToBitmap_EM(target_bitmap);
704 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
705 BlitScreenToBitmap_SP(target_bitmap);
706 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
707 BlitScreenToBitmap_MM(target_bitmap);
708 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
709 BlitScreenToBitmap_RND(target_bitmap);
711 redraw_mask |= REDRAW_FIELD;
714 void DrawFramesPerSecond()
717 int font_nr = FONT_TEXT_2;
718 int font_width = getFontWidth(font_nr);
719 int draw_deactivation_mask = GetDrawDeactivationMask();
720 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
722 /* draw FPS with leading space (needed if field buffer deactivated) */
723 sprintf(text, " %04.1f fps", global.frames_per_second);
725 /* override draw deactivation mask (required for invisible warp mode) */
726 SetDrawDeactivationMask(REDRAW_NONE);
728 /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
729 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
730 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
732 /* set draw deactivation mask to previous value */
733 SetDrawDeactivationMask(draw_deactivation_mask);
735 /* force full-screen redraw in this frame */
736 redraw_mask = REDRAW_ALL;
740 static void PrintFrameTimeDebugging()
742 static unsigned int last_counter = 0;
743 unsigned int counter = Counter();
744 int diff_1 = counter - last_counter;
745 int diff_2 = diff_1 - GAME_FRAME_DELAY;
747 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
748 char diff_bar[2 * diff_2_max + 5];
752 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
754 for (i = 0; i < diff_2_max; i++)
755 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
756 i >= diff_2_max - diff_2_cut ? '-' : ' ');
758 diff_bar[pos++] = '|';
760 for (i = 0; i < diff_2_max; i++)
761 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
763 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
765 diff_bar[pos++] = '\0';
767 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
770 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
773 last_counter = counter;
777 static int unifiedRedrawMask(int mask)
779 if (mask & REDRAW_ALL)
782 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
788 static boolean equalRedrawMasks(int mask_1, int mask_2)
790 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
795 static int last_redraw_mask = REDRAW_NONE;
797 // force screen redraw in every frame to continue drawing global animations
798 // (but always use the last redraw mask to prevent unwanted side effects)
799 if (redraw_mask == REDRAW_NONE)
800 redraw_mask = last_redraw_mask;
802 last_redraw_mask = redraw_mask;
805 // masked border now drawn immediately when blitting backbuffer to window
807 // draw masked border to all viewports, if defined
808 DrawMaskedBorder(redraw_mask);
811 // draw frames per second (only if debug mode is enabled)
812 if (redraw_mask & REDRAW_FPS)
813 DrawFramesPerSecond();
815 // remove playfield redraw before potentially merging with doors redraw
816 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
817 redraw_mask &= ~REDRAW_FIELD;
819 // redraw complete window if both playfield and (some) doors need redraw
820 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
821 redraw_mask = REDRAW_ALL;
823 /* although redrawing the whole window would be fine for normal gameplay,
824 being able to only redraw the playfield is required for deactivating
825 certain drawing areas (mainly playfield) to work, which is needed for
826 warp-forward to be fast enough (by skipping redraw of most frames) */
828 if (redraw_mask & REDRAW_ALL)
830 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
832 else if (redraw_mask & REDRAW_FIELD)
834 BlitBitmap(backbuffer, window,
835 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
837 else if (redraw_mask & REDRAW_DOORS)
839 // merge door areas to prevent calling screen redraw more than once
845 if (redraw_mask & REDRAW_DOOR_1)
849 x2 = MAX(x2, DX + DXSIZE);
850 y2 = MAX(y2, DY + DYSIZE);
853 if (redraw_mask & REDRAW_DOOR_2)
857 x2 = MAX(x2, VX + VXSIZE);
858 y2 = MAX(y2, VY + VYSIZE);
861 if (redraw_mask & REDRAW_DOOR_3)
865 x2 = MAX(x2, EX + EXSIZE);
866 y2 = MAX(y2, EY + EYSIZE);
869 // make sure that at least one pixel is blitted, and inside the screen
870 // (else nothing is blitted, causing the animations not to be updated)
871 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
872 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
873 x2 = MIN(MAX(1, x2), WIN_XSIZE);
874 y2 = MIN(MAX(1, y2), WIN_YSIZE);
876 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
879 redraw_mask = REDRAW_NONE;
882 PrintFrameTimeDebugging();
886 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
888 unsigned int frame_delay_value_old = GetVideoFrameDelay();
890 SetVideoFrameDelay(frame_delay_value);
894 SetVideoFrameDelay(frame_delay_value_old);
897 static int fade_type_skip = FADE_TYPE_NONE;
899 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
901 void (*draw_border_function)(void) = NULL;
902 int x, y, width, height;
903 int fade_delay, post_delay;
905 if (fade_type == FADE_TYPE_FADE_OUT)
907 if (fade_type_skip != FADE_TYPE_NONE)
909 /* skip all fade operations until specified fade operation */
910 if (fade_type & fade_type_skip)
911 fade_type_skip = FADE_TYPE_NONE;
916 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
920 redraw_mask |= fade_mask;
922 if (fade_type == FADE_TYPE_SKIP)
924 fade_type_skip = fade_mode;
929 fade_delay = fading.fade_delay;
930 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
932 if (fade_type_skip != FADE_TYPE_NONE)
934 /* skip all fade operations until specified fade operation */
935 if (fade_type & fade_type_skip)
936 fade_type_skip = FADE_TYPE_NONE;
941 if (global.autoplay_leveldir)
946 if (fade_mask == REDRAW_FIELD)
951 height = FADE_SYSIZE;
953 if (border.draw_masked_when_fading)
954 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
956 DrawMaskedBorder_FIELD(); /* draw once */
958 else /* REDRAW_ALL */
966 if (!setup.fade_screens ||
968 fading.fade_mode == FADE_MODE_NONE)
970 if (fade_mode == FADE_MODE_FADE_OUT)
973 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
975 redraw_mask &= ~fade_mask;
980 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
981 draw_border_function);
983 redraw_mask &= ~fade_mask;
986 static void SetScreenStates_BeforeFadingIn()
988 // temporarily set screen mode for animations to screen after fading in
989 global.anim_status = global.anim_status_next;
991 // store backbuffer with all animations that will be started after fading in
992 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
993 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
995 // set screen mode for animations back to fading
996 global.anim_status = GAME_MODE_PSEUDO_FADING;
999 static void SetScreenStates_AfterFadingIn()
1001 // store new source screen (to use correct masked border for fading)
1002 gfx.fade_border_source_status = global.border_status;
1004 global.anim_status = global.anim_status_next;
1007 static void SetScreenStates_BeforeFadingOut()
1009 // store new target screen (to use correct masked border for fading)
1010 gfx.fade_border_target_status = game_status;
1012 // set screen mode for animations to fading
1013 global.anim_status = GAME_MODE_PSEUDO_FADING;
1015 // store backbuffer with all animations that will be stopped for fading out
1016 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1017 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1020 static void SetScreenStates_AfterFadingOut()
1022 global.border_status = game_status;
1025 void FadeIn(int fade_mask)
1027 SetScreenStates_BeforeFadingIn();
1030 DrawMaskedBorder(REDRAW_ALL);
1033 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1034 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1036 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1040 FADE_SXSIZE = FULL_SXSIZE;
1041 FADE_SYSIZE = FULL_SYSIZE;
1043 if (game_status == GAME_MODE_PLAYING &&
1044 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1045 SetOverlayActive(TRUE);
1047 SetScreenStates_AfterFadingIn();
1049 // force update of global animation status in case of rapid screen changes
1050 redraw_mask = REDRAW_ALL;
1054 void FadeOut(int fade_mask)
1056 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1057 if (!equalRedrawMasks(fade_mask, redraw_mask))
1060 SetScreenStates_BeforeFadingOut();
1062 SetTileCursorActive(FALSE);
1063 SetOverlayActive(FALSE);
1066 DrawMaskedBorder(REDRAW_ALL);
1069 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1070 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1072 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1074 SetScreenStates_AfterFadingOut();
1077 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1079 static struct TitleFadingInfo fading_leave_stored;
1082 fading_leave_stored = fading_leave;
1084 fading = fading_leave_stored;
1087 void FadeSetEnterMenu()
1089 fading = menu.enter_menu;
1091 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1094 void FadeSetLeaveMenu()
1096 fading = menu.leave_menu;
1098 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1101 void FadeSetEnterScreen()
1103 fading = menu.enter_screen[game_status];
1105 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1108 void FadeSetNextScreen()
1110 fading = menu.next_screen[game_status];
1112 // (do not overwrite fade mode set by FadeSetEnterScreen)
1113 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1116 void FadeSetLeaveScreen()
1118 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1121 void FadeSetFromType(int type)
1123 if (type & TYPE_ENTER_SCREEN)
1124 FadeSetEnterScreen();
1125 else if (type & TYPE_ENTER)
1127 else if (type & TYPE_LEAVE)
1131 void FadeSetDisabled()
1133 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1135 fading = fading_none;
1138 void FadeSkipNextFadeIn()
1140 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1143 void FadeSkipNextFadeOut()
1145 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1148 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1150 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1152 return (graphic == IMG_UNDEFINED ? NULL :
1153 graphic_info[graphic].bitmap != NULL || redefined ?
1154 graphic_info[graphic].bitmap :
1155 graphic_info[default_graphic].bitmap);
1158 Bitmap *getBackgroundBitmap(int graphic)
1160 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1163 Bitmap *getGlobalBorderBitmap(int graphic)
1165 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1168 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1171 (status == GAME_MODE_MAIN ||
1172 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1173 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1174 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1175 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1178 return getGlobalBorderBitmap(graphic);
1181 void SetWindowBackgroundImageIfDefined(int graphic)
1183 if (graphic_info[graphic].bitmap)
1184 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1187 void SetMainBackgroundImageIfDefined(int graphic)
1189 if (graphic_info[graphic].bitmap)
1190 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1193 void SetDoorBackgroundImageIfDefined(int graphic)
1195 if (graphic_info[graphic].bitmap)
1196 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1199 void SetWindowBackgroundImage(int graphic)
1201 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1204 void SetMainBackgroundImage(int graphic)
1206 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1209 void SetDoorBackgroundImage(int graphic)
1211 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1214 void SetPanelBackground()
1216 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1218 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1219 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1221 SetDoorBackgroundBitmap(bitmap_db_panel);
1224 void DrawBackground(int x, int y, int width, int height)
1226 /* "drawto" might still point to playfield buffer here (hall of fame) */
1227 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1229 if (IN_GFX_FIELD_FULL(x, y))
1230 redraw_mask |= REDRAW_FIELD;
1231 else if (IN_GFX_DOOR_1(x, y))
1232 redraw_mask |= REDRAW_DOOR_1;
1233 else if (IN_GFX_DOOR_2(x, y))
1234 redraw_mask |= REDRAW_DOOR_2;
1235 else if (IN_GFX_DOOR_3(x, y))
1236 redraw_mask |= REDRAW_DOOR_3;
1239 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1241 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1243 if (font->bitmap == NULL)
1246 DrawBackground(x, y, width, height);
1249 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1251 struct GraphicInfo *g = &graphic_info[graphic];
1253 if (g->bitmap == NULL)
1256 DrawBackground(x, y, width, height);
1259 static int game_status_last = -1;
1260 static Bitmap *global_border_bitmap_last = NULL;
1261 static Bitmap *global_border_bitmap = NULL;
1262 static int real_sx_last = -1, real_sy_last = -1;
1263 static int full_sxsize_last = -1, full_sysize_last = -1;
1264 static int dx_last = -1, dy_last = -1;
1265 static int dxsize_last = -1, dysize_last = -1;
1266 static int vx_last = -1, vy_last = -1;
1267 static int vxsize_last = -1, vysize_last = -1;
1268 static int ex_last = -1, ey_last = -1;
1269 static int exsize_last = -1, eysize_last = -1;
1271 boolean CheckIfGlobalBorderHasChanged()
1273 // if game status has not changed, global border has not changed either
1274 if (game_status == game_status_last)
1277 // determine and store new global border bitmap for current game status
1278 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1280 return (global_border_bitmap_last != global_border_bitmap);
1283 boolean CheckIfGlobalBorderRedrawIsNeeded()
1285 // if game status has not changed, nothing has to be redrawn
1286 if (game_status == game_status_last)
1289 // redraw if last screen was title screen
1290 if (game_status_last == GAME_MODE_TITLE)
1293 // redraw if global screen border has changed
1294 if (CheckIfGlobalBorderHasChanged())
1297 // redraw if position or size of playfield area has changed
1298 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1299 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1302 // redraw if position or size of door area has changed
1303 if (dx_last != DX || dy_last != DY ||
1304 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1307 // redraw if position or size of tape area has changed
1308 if (vx_last != VX || vy_last != VY ||
1309 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1312 // redraw if position or size of editor area has changed
1313 if (ex_last != EX || ey_last != EY ||
1314 exsize_last != EXSIZE || eysize_last != EYSIZE)
1320 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1323 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1325 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1328 void RedrawGlobalBorder()
1330 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1332 RedrawGlobalBorderFromBitmap(bitmap);
1334 redraw_mask = REDRAW_ALL;
1337 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1339 static void RedrawGlobalBorderIfNeeded()
1341 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1342 if (game_status == game_status_last)
1346 // copy current draw buffer to later copy back areas that have not changed
1347 if (game_status_last != GAME_MODE_TITLE)
1348 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1350 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1351 if (CheckIfGlobalBorderRedrawIsNeeded())
1354 // redraw global screen border (or clear, if defined to be empty)
1355 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1357 if (game_status == GAME_MODE_EDITOR)
1358 DrawSpecialEditorDoor();
1360 // copy previous playfield and door areas, if they are defined on both
1361 // previous and current screen and if they still have the same size
1363 if (real_sx_last != -1 && real_sy_last != -1 &&
1364 REAL_SX != -1 && REAL_SY != -1 &&
1365 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1366 BlitBitmap(bitmap_db_store_1, backbuffer,
1367 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1370 if (dx_last != -1 && dy_last != -1 &&
1371 DX != -1 && DY != -1 &&
1372 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1373 BlitBitmap(bitmap_db_store_1, backbuffer,
1374 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1376 if (game_status != GAME_MODE_EDITOR)
1378 if (vx_last != -1 && vy_last != -1 &&
1379 VX != -1 && VY != -1 &&
1380 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1381 BlitBitmap(bitmap_db_store_1, backbuffer,
1382 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1386 if (ex_last != -1 && ey_last != -1 &&
1387 EX != -1 && EY != -1 &&
1388 exsize_last == EXSIZE && eysize_last == EYSIZE)
1389 BlitBitmap(bitmap_db_store_1, backbuffer,
1390 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1393 redraw_mask = REDRAW_ALL;
1396 game_status_last = game_status;
1398 global_border_bitmap_last = global_border_bitmap;
1400 real_sx_last = REAL_SX;
1401 real_sy_last = REAL_SY;
1402 full_sxsize_last = FULL_SXSIZE;
1403 full_sysize_last = FULL_SYSIZE;
1406 dxsize_last = DXSIZE;
1407 dysize_last = DYSIZE;
1410 vxsize_last = VXSIZE;
1411 vysize_last = VYSIZE;
1414 exsize_last = EXSIZE;
1415 eysize_last = EYSIZE;
1420 RedrawGlobalBorderIfNeeded();
1422 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1423 /* (when entering hall of fame after playing) */
1424 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1426 /* !!! maybe this should be done before clearing the background !!! */
1427 if (game_status == GAME_MODE_PLAYING)
1429 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1430 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1434 SetDrawtoField(DRAW_TO_BACKBUFFER);
1438 void MarkTileDirty(int x, int y)
1440 redraw_mask |= REDRAW_FIELD;
1443 void SetBorderElement()
1447 BorderElement = EL_EMPTY;
1449 /* the MM game engine does not use a visible border element */
1450 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1453 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1455 for (x = 0; x < lev_fieldx; x++)
1457 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1458 BorderElement = EL_STEELWALL;
1460 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1466 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1467 int max_array_fieldx, int max_array_fieldy,
1468 short field[max_array_fieldx][max_array_fieldy],
1469 int max_fieldx, int max_fieldy)
1473 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1474 static int safety = 0;
1476 /* check if starting field still has the desired content */
1477 if (field[from_x][from_y] == fill_element)
1482 if (safety > max_fieldx * max_fieldy)
1483 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1485 old_element = field[from_x][from_y];
1486 field[from_x][from_y] = fill_element;
1488 for (i = 0; i < 4; i++)
1490 x = from_x + check[i][0];
1491 y = from_y + check[i][1];
1493 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1494 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1495 field, max_fieldx, max_fieldy);
1501 void FloodFillLevel(int from_x, int from_y, int fill_element,
1502 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1503 int max_fieldx, int max_fieldy)
1505 FloodFillLevelExt(from_x, from_y, fill_element,
1506 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1507 max_fieldx, max_fieldy);
1510 void SetRandomAnimationValue(int x, int y)
1512 gfx.anim_random_frame = GfxRandom[x][y];
1515 int getGraphicAnimationFrame(int graphic, int sync_frame)
1517 /* animation synchronized with global frame counter, not move position */
1518 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1519 sync_frame = FrameCounter;
1521 return getAnimationFrame(graphic_info[graphic].anim_frames,
1522 graphic_info[graphic].anim_delay,
1523 graphic_info[graphic].anim_mode,
1524 graphic_info[graphic].anim_start_frame,
1528 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1530 struct GraphicInfo *g = &graphic_info[graphic];
1531 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1533 if (tilesize == gfx.standard_tile_size)
1534 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1535 else if (tilesize == game.tile_size)
1536 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1538 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1541 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1542 boolean get_backside)
1544 struct GraphicInfo *g = &graphic_info[graphic];
1545 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1546 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1548 if (g->offset_y == 0) /* frames are ordered horizontally */
1550 int max_width = g->anim_frames_per_line * g->width;
1551 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1553 *x = pos % max_width;
1554 *y = src_y % g->height + pos / max_width * g->height;
1556 else if (g->offset_x == 0) /* frames are ordered vertically */
1558 int max_height = g->anim_frames_per_line * g->height;
1559 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1561 *x = src_x % g->width + pos / max_height * g->width;
1562 *y = pos % max_height;
1564 else /* frames are ordered diagonally */
1566 *x = src_x + frame * g->offset_x;
1567 *y = src_y + frame * g->offset_y;
1571 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1572 Bitmap **bitmap, int *x, int *y,
1573 boolean get_backside)
1575 struct GraphicInfo *g = &graphic_info[graphic];
1577 // if no graphics defined at all, use fallback graphics
1578 if (g->bitmaps == NULL)
1579 *g = graphic_info[IMG_CHAR_EXCLAM];
1581 // if no in-game graphics defined, always use standard graphic size
1582 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1583 tilesize = TILESIZE;
1585 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1586 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1588 *x = *x * tilesize / g->tile_size;
1589 *y = *y * tilesize / g->tile_size;
1592 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1593 Bitmap **bitmap, int *x, int *y)
1595 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1598 void getFixedGraphicSource(int graphic, int frame,
1599 Bitmap **bitmap, int *x, int *y)
1601 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1604 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1606 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1609 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1610 int *x, int *y, boolean get_backside)
1612 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1616 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1618 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1621 void DrawGraphic(int x, int y, int graphic, int frame)
1624 if (!IN_SCR_FIELD(x, y))
1626 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1627 printf("DrawGraphic(): This should never happen!\n");
1632 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1635 MarkTileDirty(x, y);
1638 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1641 if (!IN_SCR_FIELD(x, y))
1643 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1644 printf("DrawGraphic(): This should never happen!\n");
1649 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1651 MarkTileDirty(x, y);
1654 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1660 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1662 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1665 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1671 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1672 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1675 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1678 if (!IN_SCR_FIELD(x, y))
1680 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1681 printf("DrawGraphicThruMask(): This should never happen!\n");
1686 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1689 MarkTileDirty(x, y);
1692 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1695 if (!IN_SCR_FIELD(x, y))
1697 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1698 printf("DrawGraphicThruMask(): This should never happen!\n");
1703 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1705 MarkTileDirty(x, y);
1708 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1714 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1716 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1720 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1721 int graphic, int frame)
1726 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1728 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1732 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1734 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1736 MarkTileDirty(x / tilesize, y / tilesize);
1739 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1742 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1743 graphic, frame, tilesize);
1744 MarkTileDirty(x / tilesize, y / tilesize);
1747 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1753 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1754 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1757 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1758 int frame, int tilesize)
1763 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1764 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1767 void DrawMiniGraphic(int x, int y, int graphic)
1769 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1770 MarkTileDirty(x / 2, y / 2);
1773 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1778 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1779 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1782 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1783 int graphic, int frame,
1784 int cut_mode, int mask_mode)
1789 int width = TILEX, height = TILEY;
1792 if (dx || dy) /* shifted graphic */
1794 if (x < BX1) /* object enters playfield from the left */
1801 else if (x > BX2) /* object enters playfield from the right */
1807 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1813 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1815 else if (dx) /* general horizontal movement */
1816 MarkTileDirty(x + SIGN(dx), y);
1818 if (y < BY1) /* object enters playfield from the top */
1820 if (cut_mode == CUT_BELOW) /* object completely above top border */
1828 else if (y > BY2) /* object enters playfield from the bottom */
1834 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1840 else if (dy > 0 && cut_mode == CUT_ABOVE)
1842 if (y == BY2) /* object completely above bottom border */
1848 MarkTileDirty(x, y + 1);
1849 } /* object leaves playfield to the bottom */
1850 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1852 else if (dy) /* general vertical movement */
1853 MarkTileDirty(x, y + SIGN(dy));
1857 if (!IN_SCR_FIELD(x, y))
1859 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1860 printf("DrawGraphicShifted(): This should never happen!\n");
1865 width = width * TILESIZE_VAR / TILESIZE;
1866 height = height * TILESIZE_VAR / TILESIZE;
1867 cx = cx * TILESIZE_VAR / TILESIZE;
1868 cy = cy * TILESIZE_VAR / TILESIZE;
1869 dx = dx * TILESIZE_VAR / TILESIZE;
1870 dy = dy * TILESIZE_VAR / TILESIZE;
1872 if (width > 0 && height > 0)
1874 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1879 dst_x = FX + x * TILEX_VAR + dx;
1880 dst_y = FY + y * TILEY_VAR + dy;
1882 if (mask_mode == USE_MASKING)
1883 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1886 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1889 MarkTileDirty(x, y);
1893 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1894 int graphic, int frame,
1895 int cut_mode, int mask_mode)
1900 int width = TILEX_VAR, height = TILEY_VAR;
1903 int x2 = x + SIGN(dx);
1904 int y2 = y + SIGN(dy);
1906 /* movement with two-tile animations must be sync'ed with movement position,
1907 not with current GfxFrame (which can be higher when using slow movement) */
1908 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1909 int anim_frames = graphic_info[graphic].anim_frames;
1911 /* (we also need anim_delay here for movement animations with less frames) */
1912 int anim_delay = graphic_info[graphic].anim_delay;
1913 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1915 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1916 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1918 /* re-calculate animation frame for two-tile movement animation */
1919 frame = getGraphicAnimationFrame(graphic, sync_frame);
1921 /* check if movement start graphic inside screen area and should be drawn */
1922 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1924 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1926 dst_x = FX + x1 * TILEX_VAR;
1927 dst_y = FY + y1 * TILEY_VAR;
1929 if (mask_mode == USE_MASKING)
1930 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1933 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1936 MarkTileDirty(x1, y1);
1939 /* check if movement end graphic inside screen area and should be drawn */
1940 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1942 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1944 dst_x = FX + x2 * TILEX_VAR;
1945 dst_y = FY + y2 * TILEY_VAR;
1947 if (mask_mode == USE_MASKING)
1948 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1951 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1954 MarkTileDirty(x2, y2);
1958 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1959 int graphic, int frame,
1960 int cut_mode, int mask_mode)
1964 DrawGraphic(x, y, graphic, frame);
1969 if (graphic_info[graphic].double_movement) /* EM style movement images */
1970 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1972 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1975 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1976 int frame, int cut_mode)
1978 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1981 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1982 int cut_mode, int mask_mode)
1984 int lx = LEVELX(x), ly = LEVELY(y);
1988 if (IN_LEV_FIELD(lx, ly))
1990 SetRandomAnimationValue(lx, ly);
1992 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1993 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1995 /* do not use double (EM style) movement graphic when not moving */
1996 if (graphic_info[graphic].double_movement && !dx && !dy)
1998 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1999 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2002 else /* border element */
2004 graphic = el2img(element);
2005 frame = getGraphicAnimationFrame(graphic, -1);
2008 if (element == EL_EXPANDABLE_WALL)
2010 boolean left_stopped = FALSE, right_stopped = FALSE;
2012 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2013 left_stopped = TRUE;
2014 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2015 right_stopped = TRUE;
2017 if (left_stopped && right_stopped)
2019 else if (left_stopped)
2021 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2022 frame = graphic_info[graphic].anim_frames - 1;
2024 else if (right_stopped)
2026 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2027 frame = graphic_info[graphic].anim_frames - 1;
2032 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2033 else if (mask_mode == USE_MASKING)
2034 DrawGraphicThruMask(x, y, graphic, frame);
2036 DrawGraphic(x, y, graphic, frame);
2039 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2040 int cut_mode, int mask_mode)
2042 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2043 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2044 cut_mode, mask_mode);
2047 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2050 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2053 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2056 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2059 void DrawLevelElementThruMask(int x, int y, int element)
2061 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2064 void DrawLevelFieldThruMask(int x, int y)
2066 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2069 /* !!! implementation of quicksand is totally broken !!! */
2070 #define IS_CRUMBLED_TILE(x, y, e) \
2071 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2072 !IS_MOVING(x, y) || \
2073 (e) == EL_QUICKSAND_EMPTYING || \
2074 (e) == EL_QUICKSAND_FAST_EMPTYING))
2076 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2081 int width, height, cx, cy;
2082 int sx = SCREENX(x), sy = SCREENY(y);
2083 int crumbled_border_size = graphic_info[graphic].border_size;
2084 int crumbled_tile_size = graphic_info[graphic].tile_size;
2085 int crumbled_border_size_var =
2086 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2089 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2091 for (i = 1; i < 4; i++)
2093 int dxx = (i & 1 ? dx : 0);
2094 int dyy = (i & 2 ? dy : 0);
2097 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2100 /* check if neighbour field is of same crumble type */
2101 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2102 graphic_info[graphic].class ==
2103 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2105 /* return if check prevents inner corner */
2106 if (same == (dxx == dx && dyy == dy))
2110 /* if we reach this point, we have an inner corner */
2112 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2114 width = crumbled_border_size_var;
2115 height = crumbled_border_size_var;
2116 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2117 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2119 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2120 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2123 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2128 int width, height, bx, by, cx, cy;
2129 int sx = SCREENX(x), sy = SCREENY(y);
2130 int crumbled_border_size = graphic_info[graphic].border_size;
2131 int crumbled_tile_size = graphic_info[graphic].tile_size;
2132 int crumbled_border_size_var =
2133 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2134 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2137 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2139 /* draw simple, sloppy, non-corner-accurate crumbled border */
2141 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2142 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2143 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2144 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2146 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2147 FX + sx * TILEX_VAR + cx,
2148 FY + sy * TILEY_VAR + cy);
2150 /* (remaining middle border part must be at least as big as corner part) */
2151 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2152 crumbled_border_size_var >= TILESIZE_VAR / 3)
2155 /* correct corners of crumbled border, if needed */
2157 for (i = -1; i <= 1; i += 2)
2159 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2160 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2161 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2164 /* check if neighbour field is of same crumble type */
2165 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2166 graphic_info[graphic].class ==
2167 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2169 /* no crumbled corner, but continued crumbled border */
2171 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2172 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2173 int b1 = (i == 1 ? crumbled_border_size_var :
2174 TILESIZE_VAR - 2 * crumbled_border_size_var);
2176 width = crumbled_border_size_var;
2177 height = crumbled_border_size_var;
2179 if (dir == 1 || dir == 2)
2194 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2196 FX + sx * TILEX_VAR + cx,
2197 FY + sy * TILEY_VAR + cy);
2202 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2204 int sx = SCREENX(x), sy = SCREENY(y);
2207 static int xy[4][2] =
2215 if (!IN_LEV_FIELD(x, y))
2218 element = TILE_GFX_ELEMENT(x, y);
2220 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2222 if (!IN_SCR_FIELD(sx, sy))
2225 /* crumble field borders towards direct neighbour fields */
2226 for (i = 0; i < 4; i++)
2228 int xx = x + xy[i][0];
2229 int yy = y + xy[i][1];
2231 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2234 /* check if neighbour field is of same crumble type */
2235 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2236 graphic_info[graphic].class ==
2237 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2240 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2243 /* crumble inner field corners towards corner neighbour fields */
2244 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2245 graphic_info[graphic].anim_frames == 2)
2247 for (i = 0; i < 4; i++)
2249 int dx = (i & 1 ? +1 : -1);
2250 int dy = (i & 2 ? +1 : -1);
2252 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2256 MarkTileDirty(sx, sy);
2258 else /* center field is not crumbled -- crumble neighbour fields */
2260 /* crumble field borders of direct neighbour fields */
2261 for (i = 0; i < 4; i++)
2263 int xx = x + xy[i][0];
2264 int yy = y + xy[i][1];
2265 int sxx = sx + xy[i][0];
2266 int syy = sy + xy[i][1];
2268 if (!IN_LEV_FIELD(xx, yy) ||
2269 !IN_SCR_FIELD(sxx, syy))
2272 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2275 element = TILE_GFX_ELEMENT(xx, yy);
2277 if (!IS_CRUMBLED_TILE(xx, yy, element))
2280 graphic = el_act2crm(element, ACTION_DEFAULT);
2282 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2284 MarkTileDirty(sxx, syy);
2287 /* crumble inner field corners of corner neighbour fields */
2288 for (i = 0; i < 4; i++)
2290 int dx = (i & 1 ? +1 : -1);
2291 int dy = (i & 2 ? +1 : -1);
2297 if (!IN_LEV_FIELD(xx, yy) ||
2298 !IN_SCR_FIELD(sxx, syy))
2301 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2304 element = TILE_GFX_ELEMENT(xx, yy);
2306 if (!IS_CRUMBLED_TILE(xx, yy, element))
2309 graphic = el_act2crm(element, ACTION_DEFAULT);
2311 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2312 graphic_info[graphic].anim_frames == 2)
2313 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2315 MarkTileDirty(sxx, syy);
2320 void DrawLevelFieldCrumbled(int x, int y)
2324 if (!IN_LEV_FIELD(x, y))
2327 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2328 GfxElement[x][y] != EL_UNDEFINED &&
2329 GFX_CRUMBLED(GfxElement[x][y]))
2331 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2336 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2338 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2341 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2344 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2345 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2346 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2347 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2348 int sx = SCREENX(x), sy = SCREENY(y);
2350 DrawGraphic(sx, sy, graphic1, frame1);
2351 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2354 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2356 int sx = SCREENX(x), sy = SCREENY(y);
2357 static int xy[4][2] =
2366 /* crumble direct neighbour fields (required for field borders) */
2367 for (i = 0; i < 4; i++)
2369 int xx = x + xy[i][0];
2370 int yy = y + xy[i][1];
2371 int sxx = sx + xy[i][0];
2372 int syy = sy + xy[i][1];
2374 if (!IN_LEV_FIELD(xx, yy) ||
2375 !IN_SCR_FIELD(sxx, syy) ||
2376 !GFX_CRUMBLED(Feld[xx][yy]) ||
2380 DrawLevelField(xx, yy);
2383 /* crumble corner neighbour fields (required for inner field corners) */
2384 for (i = 0; i < 4; i++)
2386 int dx = (i & 1 ? +1 : -1);
2387 int dy = (i & 2 ? +1 : -1);
2393 if (!IN_LEV_FIELD(xx, yy) ||
2394 !IN_SCR_FIELD(sxx, syy) ||
2395 !GFX_CRUMBLED(Feld[xx][yy]) ||
2399 int element = TILE_GFX_ELEMENT(xx, yy);
2400 int graphic = el_act2crm(element, ACTION_DEFAULT);
2402 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2403 graphic_info[graphic].anim_frames == 2)
2404 DrawLevelField(xx, yy);
2408 static int getBorderElement(int x, int y)
2412 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2413 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2414 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2415 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2416 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2417 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2418 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2420 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2421 int steel_position = (x == -1 && y == -1 ? 0 :
2422 x == lev_fieldx && y == -1 ? 1 :
2423 x == -1 && y == lev_fieldy ? 2 :
2424 x == lev_fieldx && y == lev_fieldy ? 3 :
2425 x == -1 || x == lev_fieldx ? 4 :
2426 y == -1 || y == lev_fieldy ? 5 : 6);
2428 return border[steel_position][steel_type];
2431 void DrawScreenElement(int x, int y, int element)
2433 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2434 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2437 void DrawLevelElement(int x, int y, int element)
2439 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2440 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2443 void DrawScreenField(int x, int y)
2445 int lx = LEVELX(x), ly = LEVELY(y);
2446 int element, content;
2448 if (!IN_LEV_FIELD(lx, ly))
2450 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2453 element = getBorderElement(lx, ly);
2455 DrawScreenElement(x, y, element);
2460 element = Feld[lx][ly];
2461 content = Store[lx][ly];
2463 if (IS_MOVING(lx, ly))
2465 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2466 boolean cut_mode = NO_CUTTING;
2468 if (element == EL_QUICKSAND_EMPTYING ||
2469 element == EL_QUICKSAND_FAST_EMPTYING ||
2470 element == EL_MAGIC_WALL_EMPTYING ||
2471 element == EL_BD_MAGIC_WALL_EMPTYING ||
2472 element == EL_DC_MAGIC_WALL_EMPTYING ||
2473 element == EL_AMOEBA_DROPPING)
2474 cut_mode = CUT_ABOVE;
2475 else if (element == EL_QUICKSAND_FILLING ||
2476 element == EL_QUICKSAND_FAST_FILLING ||
2477 element == EL_MAGIC_WALL_FILLING ||
2478 element == EL_BD_MAGIC_WALL_FILLING ||
2479 element == EL_DC_MAGIC_WALL_FILLING)
2480 cut_mode = CUT_BELOW;
2482 if (cut_mode == CUT_ABOVE)
2483 DrawScreenElement(x, y, element);
2485 DrawScreenElement(x, y, EL_EMPTY);
2488 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2489 else if (cut_mode == NO_CUTTING)
2490 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2493 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2495 if (cut_mode == CUT_BELOW &&
2496 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2497 DrawLevelElement(lx, ly + 1, element);
2500 if (content == EL_ACID)
2502 int dir = MovDir[lx][ly];
2503 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2504 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2506 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2508 // prevent target field from being drawn again (but without masking)
2509 // (this would happen if target field is scanned after moving element)
2510 Stop[newlx][newly] = TRUE;
2513 else if (IS_BLOCKED(lx, ly))
2518 boolean cut_mode = NO_CUTTING;
2519 int element_old, content_old;
2521 Blocked2Moving(lx, ly, &oldx, &oldy);
2524 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2525 MovDir[oldx][oldy] == MV_RIGHT);
2527 element_old = Feld[oldx][oldy];
2528 content_old = Store[oldx][oldy];
2530 if (element_old == EL_QUICKSAND_EMPTYING ||
2531 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2532 element_old == EL_MAGIC_WALL_EMPTYING ||
2533 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2534 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2535 element_old == EL_AMOEBA_DROPPING)
2536 cut_mode = CUT_ABOVE;
2538 DrawScreenElement(x, y, EL_EMPTY);
2541 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2543 else if (cut_mode == NO_CUTTING)
2544 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2547 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2550 else if (IS_DRAWABLE(element))
2551 DrawScreenElement(x, y, element);
2553 DrawScreenElement(x, y, EL_EMPTY);
2556 void DrawLevelField(int x, int y)
2558 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2559 DrawScreenField(SCREENX(x), SCREENY(y));
2560 else if (IS_MOVING(x, y))
2564 Moving2Blocked(x, y, &newx, &newy);
2565 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2566 DrawScreenField(SCREENX(newx), SCREENY(newy));
2568 else if (IS_BLOCKED(x, y))
2572 Blocked2Moving(x, y, &oldx, &oldy);
2573 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2574 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2578 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2579 int (*el2img_function)(int), boolean masked,
2580 int element_bits_draw)
2582 int element_base = map_mm_wall_element(element);
2583 int element_bits = (IS_DF_WALL(element) ?
2584 element - EL_DF_WALL_START :
2585 IS_MM_WALL(element) ?
2586 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2587 int graphic = el2img_function(element_base);
2588 int tilesize_draw = tilesize / 2;
2593 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2595 for (i = 0; i < 4; i++)
2597 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2598 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2600 if (!(element_bits_draw & (1 << i)))
2603 if (element_bits & (1 << i))
2606 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2607 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2609 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2610 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2615 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2616 tilesize_draw, tilesize_draw);
2621 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2622 boolean masked, int element_bits_draw)
2624 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2625 element, tilesize, el2edimg, masked, element_bits_draw);
2628 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2629 int (*el2img_function)(int))
2631 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2635 void DrawSizedElementExt(int x, int y, int element, int tilesize,
2638 if (IS_MM_WALL(element))
2640 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2641 element, tilesize, el2edimg, masked, 0x000f);
2645 int graphic = el2edimg(element);
2648 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2650 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2654 void DrawSizedElement(int x, int y, int element, int tilesize)
2656 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2659 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2661 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2664 void DrawMiniElement(int x, int y, int element)
2668 graphic = el2edimg(element);
2669 DrawMiniGraphic(x, y, graphic);
2672 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2675 int x = sx + scroll_x, y = sy + scroll_y;
2677 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2678 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2679 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2680 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2682 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2685 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2687 int x = sx + scroll_x, y = sy + scroll_y;
2689 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2690 DrawMiniElement(sx, sy, EL_EMPTY);
2691 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2692 DrawMiniElement(sx, sy, Feld[x][y]);
2694 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2697 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2698 int x, int y, int xsize, int ysize,
2699 int tile_width, int tile_height)
2703 int dst_x = startx + x * tile_width;
2704 int dst_y = starty + y * tile_height;
2705 int width = graphic_info[graphic].width;
2706 int height = graphic_info[graphic].height;
2707 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2708 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2709 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2710 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2711 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2712 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2713 boolean draw_masked = graphic_info[graphic].draw_masked;
2715 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2717 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2719 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2723 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2724 inner_sx + (x - 1) * tile_width % inner_width);
2725 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2726 inner_sy + (y - 1) * tile_height % inner_height);
2729 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2732 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2736 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2737 int x, int y, int xsize, int ysize, int font_nr)
2739 int font_width = getFontWidth(font_nr);
2740 int font_height = getFontHeight(font_nr);
2742 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2743 font_width, font_height);
2746 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2748 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2749 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2750 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2751 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2752 boolean no_delay = (tape.warp_forward);
2753 unsigned int anim_delay = 0;
2754 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2755 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2756 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2757 int font_width = getFontWidth(font_nr);
2758 int font_height = getFontHeight(font_nr);
2759 int max_xsize = level.envelope[envelope_nr].xsize;
2760 int max_ysize = level.envelope[envelope_nr].ysize;
2761 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2762 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2763 int xend = max_xsize;
2764 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2765 int xstep = (xstart < xend ? 1 : 0);
2766 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2768 int end = MAX(xend - xstart, yend - ystart);
2771 for (i = start; i <= end; i++)
2773 int last_frame = end; // last frame of this "for" loop
2774 int x = xstart + i * xstep;
2775 int y = ystart + i * ystep;
2776 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2777 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2778 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2779 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2782 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2784 BlitScreenToBitmap(backbuffer);
2786 SetDrawtoField(DRAW_TO_BACKBUFFER);
2788 for (yy = 0; yy < ysize; yy++)
2789 for (xx = 0; xx < xsize; xx++)
2790 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2792 DrawTextBuffer(sx + font_width, sy + font_height,
2793 level.envelope[envelope_nr].text, font_nr, max_xsize,
2794 xsize - 2, ysize - 2, 0, mask_mode,
2795 level.envelope[envelope_nr].autowrap,
2796 level.envelope[envelope_nr].centered, FALSE);
2798 redraw_mask |= REDRAW_FIELD;
2801 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2805 void ShowEnvelope(int envelope_nr)
2807 int element = EL_ENVELOPE_1 + envelope_nr;
2808 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2809 int sound_opening = element_info[element].sound[ACTION_OPENING];
2810 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2811 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2812 boolean no_delay = (tape.warp_forward);
2813 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2814 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2815 int anim_mode = graphic_info[graphic].anim_mode;
2816 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2817 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2819 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2821 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2823 if (anim_mode == ANIM_DEFAULT)
2824 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2826 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2829 Delay(wait_delay_value);
2831 WaitForEventToContinue();
2833 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2835 if (anim_mode != ANIM_NONE)
2836 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2838 if (anim_mode == ANIM_DEFAULT)
2839 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2841 game.envelope_active = FALSE;
2843 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2845 redraw_mask |= REDRAW_FIELD;
2849 static void setRequestBasePosition(int *x, int *y)
2851 int sx_base, sy_base;
2853 if (request.x != -1)
2854 sx_base = request.x;
2855 else if (request.align == ALIGN_LEFT)
2857 else if (request.align == ALIGN_RIGHT)
2858 sx_base = SX + SXSIZE;
2860 sx_base = SX + SXSIZE / 2;
2862 if (request.y != -1)
2863 sy_base = request.y;
2864 else if (request.valign == VALIGN_TOP)
2866 else if (request.valign == VALIGN_BOTTOM)
2867 sy_base = SY + SYSIZE;
2869 sy_base = SY + SYSIZE / 2;
2875 static void setRequestPositionExt(int *x, int *y, int width, int height,
2876 boolean add_border_size)
2878 int border_size = request.border_size;
2879 int sx_base, sy_base;
2882 setRequestBasePosition(&sx_base, &sy_base);
2884 if (request.align == ALIGN_LEFT)
2886 else if (request.align == ALIGN_RIGHT)
2887 sx = sx_base - width;
2889 sx = sx_base - width / 2;
2891 if (request.valign == VALIGN_TOP)
2893 else if (request.valign == VALIGN_BOTTOM)
2894 sy = sy_base - height;
2896 sy = sy_base - height / 2;
2898 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2899 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2901 if (add_border_size)
2911 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2913 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2916 void DrawEnvelopeRequest(char *text)
2918 char *text_final = text;
2919 char *text_door_style = NULL;
2920 int graphic = IMG_BACKGROUND_REQUEST;
2921 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2922 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2923 int font_nr = FONT_REQUEST;
2924 int font_width = getFontWidth(font_nr);
2925 int font_height = getFontHeight(font_nr);
2926 int border_size = request.border_size;
2927 int line_spacing = request.line_spacing;
2928 int line_height = font_height + line_spacing;
2929 int max_text_width = request.width - 2 * border_size;
2930 int max_text_height = request.height - 2 * border_size;
2931 int line_length = max_text_width / font_width;
2932 int max_lines = max_text_height / line_height;
2933 int text_width = line_length * font_width;
2934 int width = request.width;
2935 int height = request.height;
2936 int tile_size = MAX(request.step_offset, 1);
2937 int x_steps = width / tile_size;
2938 int y_steps = height / tile_size;
2939 int sx_offset = border_size;
2940 int sy_offset = border_size;
2944 if (request.centered)
2945 sx_offset = (request.width - text_width) / 2;
2947 if (request.wrap_single_words && !request.autowrap)
2949 char *src_text_ptr, *dst_text_ptr;
2951 text_door_style = checked_malloc(2 * strlen(text) + 1);
2953 src_text_ptr = text;
2954 dst_text_ptr = text_door_style;
2956 while (*src_text_ptr)
2958 if (*src_text_ptr == ' ' ||
2959 *src_text_ptr == '?' ||
2960 *src_text_ptr == '!')
2961 *dst_text_ptr++ = '\n';
2963 if (*src_text_ptr != ' ')
2964 *dst_text_ptr++ = *src_text_ptr;
2969 *dst_text_ptr = '\0';
2971 text_final = text_door_style;
2974 setRequestPosition(&sx, &sy, FALSE);
2976 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2978 for (y = 0; y < y_steps; y++)
2979 for (x = 0; x < x_steps; x++)
2980 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2981 x, y, x_steps, y_steps,
2982 tile_size, tile_size);
2984 /* force DOOR font inside door area */
2985 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2987 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2988 line_length, -1, max_lines, line_spacing, mask_mode,
2989 request.autowrap, request.centered, FALSE);
2993 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2994 RedrawGadget(tool_gadget[i]);
2996 // store readily prepared envelope request for later use when animating
2997 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2999 if (text_door_style)
3000 free(text_door_style);
3003 void AnimateEnvelopeRequest(int anim_mode, int action)
3005 int graphic = IMG_BACKGROUND_REQUEST;
3006 boolean draw_masked = graphic_info[graphic].draw_masked;
3007 int delay_value_normal = request.step_delay;
3008 int delay_value_fast = delay_value_normal / 2;
3009 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3010 boolean no_delay = (tape.warp_forward);
3011 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3012 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3013 unsigned int anim_delay = 0;
3015 int tile_size = MAX(request.step_offset, 1);
3016 int max_xsize = request.width / tile_size;
3017 int max_ysize = request.height / tile_size;
3018 int max_xsize_inner = max_xsize - 2;
3019 int max_ysize_inner = max_ysize - 2;
3021 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3022 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3023 int xend = max_xsize_inner;
3024 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3025 int xstep = (xstart < xend ? 1 : 0);
3026 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3028 int end = MAX(xend - xstart, yend - ystart);
3031 if (setup.quick_doors)
3038 for (i = start; i <= end; i++)
3040 int last_frame = end; // last frame of this "for" loop
3041 int x = xstart + i * xstep;
3042 int y = ystart + i * ystep;
3043 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3044 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3045 int xsize_size_left = (xsize - 1) * tile_size;
3046 int ysize_size_top = (ysize - 1) * tile_size;
3047 int max_xsize_pos = (max_xsize - 1) * tile_size;
3048 int max_ysize_pos = (max_ysize - 1) * tile_size;
3049 int width = xsize * tile_size;
3050 int height = ysize * tile_size;
3055 setRequestPosition(&src_x, &src_y, FALSE);
3056 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3058 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3060 for (yy = 0; yy < 2; yy++)
3062 for (xx = 0; xx < 2; xx++)
3064 int src_xx = src_x + xx * max_xsize_pos;
3065 int src_yy = src_y + yy * max_ysize_pos;
3066 int dst_xx = dst_x + xx * xsize_size_left;
3067 int dst_yy = dst_y + yy * ysize_size_top;
3068 int xx_size = (xx ? tile_size : xsize_size_left);
3069 int yy_size = (yy ? tile_size : ysize_size_top);
3072 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3073 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3075 BlitBitmap(bitmap_db_store_2, backbuffer,
3076 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3080 redraw_mask |= REDRAW_FIELD;
3084 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3088 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3090 int graphic = IMG_BACKGROUND_REQUEST;
3091 int sound_opening = SND_REQUEST_OPENING;
3092 int sound_closing = SND_REQUEST_CLOSING;
3093 int anim_mode_1 = request.anim_mode; /* (higher priority) */
3094 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
3095 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3096 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3097 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3099 if (game_status == GAME_MODE_PLAYING)
3100 BlitScreenToBitmap(backbuffer);
3102 SetDrawtoField(DRAW_TO_BACKBUFFER);
3104 // SetDrawBackgroundMask(REDRAW_NONE);
3106 if (action == ACTION_OPENING)
3108 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3110 if (req_state & REQ_ASK)
3112 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3113 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3115 else if (req_state & REQ_CONFIRM)
3117 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3119 else if (req_state & REQ_PLAYER)
3121 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3122 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3123 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3124 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3127 DrawEnvelopeRequest(text);
3130 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
3132 if (action == ACTION_OPENING)
3134 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3136 if (anim_mode == ANIM_DEFAULT)
3137 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3139 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3143 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3145 if (anim_mode != ANIM_NONE)
3146 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3148 if (anim_mode == ANIM_DEFAULT)
3149 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3152 game.envelope_active = FALSE;
3154 if (action == ACTION_CLOSING)
3155 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3157 // SetDrawBackgroundMask(last_draw_background_mask);
3159 redraw_mask |= REDRAW_FIELD;
3163 if (action == ACTION_CLOSING &&
3164 game_status == GAME_MODE_PLAYING &&
3165 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3166 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3169 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3171 if (IS_MM_WALL(element))
3173 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3179 int graphic = el2preimg(element);
3181 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3182 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3187 void DrawLevel(int draw_background_mask)
3191 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3192 SetDrawBackgroundMask(draw_background_mask);
3196 for (x = BX1; x <= BX2; x++)
3197 for (y = BY1; y <= BY2; y++)
3198 DrawScreenField(x, y);
3200 redraw_mask |= REDRAW_FIELD;
3203 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3208 for (x = 0; x < size_x; x++)
3209 for (y = 0; y < size_y; y++)
3210 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3212 redraw_mask |= REDRAW_FIELD;
3215 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3219 for (x = 0; x < size_x; x++)
3220 for (y = 0; y < size_y; y++)
3221 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3223 redraw_mask |= REDRAW_FIELD;
3226 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3228 boolean show_level_border = (BorderElement != EL_EMPTY);
3229 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3230 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3231 int tile_size = preview.tile_size;
3232 int preview_width = preview.xsize * tile_size;
3233 int preview_height = preview.ysize * tile_size;
3234 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3235 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3236 int real_preview_width = real_preview_xsize * tile_size;
3237 int real_preview_height = real_preview_ysize * tile_size;
3238 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3239 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3242 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3245 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3247 dst_x += (preview_width - real_preview_width) / 2;
3248 dst_y += (preview_height - real_preview_height) / 2;
3250 for (x = 0; x < real_preview_xsize; x++)
3252 for (y = 0; y < real_preview_ysize; y++)
3254 int lx = from_x + x + (show_level_border ? -1 : 0);
3255 int ly = from_y + y + (show_level_border ? -1 : 0);
3256 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3257 getBorderElement(lx, ly));
3259 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3260 element, tile_size);
3264 redraw_mask |= REDRAW_FIELD;
3267 #define MICROLABEL_EMPTY 0
3268 #define MICROLABEL_LEVEL_NAME 1
3269 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3270 #define MICROLABEL_LEVEL_AUTHOR 3
3271 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3272 #define MICROLABEL_IMPORTED_FROM 5
3273 #define MICROLABEL_IMPORTED_BY_HEAD 6
3274 #define MICROLABEL_IMPORTED_BY 7
3276 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3278 int max_text_width = SXSIZE;
3279 int font_width = getFontWidth(font_nr);
3281 if (pos->align == ALIGN_CENTER)
3282 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3283 else if (pos->align == ALIGN_RIGHT)
3284 max_text_width = pos->x;
3286 max_text_width = SXSIZE - pos->x;
3288 return max_text_width / font_width;
3291 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3293 char label_text[MAX_OUTPUT_LINESIZE + 1];
3294 int max_len_label_text;
3295 int font_nr = pos->font;
3298 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3301 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3302 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3303 mode == MICROLABEL_IMPORTED_BY_HEAD)
3304 font_nr = pos->font_alt;
3306 max_len_label_text = getMaxTextLength(pos, font_nr);
3308 if (pos->size != -1)
3309 max_len_label_text = pos->size;
3311 for (i = 0; i < max_len_label_text; i++)
3312 label_text[i] = ' ';
3313 label_text[max_len_label_text] = '\0';
3315 if (strlen(label_text) > 0)
3316 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3319 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3320 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3321 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3322 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3323 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3324 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3325 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3326 max_len_label_text);
3327 label_text[max_len_label_text] = '\0';
3329 if (strlen(label_text) > 0)
3330 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3332 redraw_mask |= REDRAW_FIELD;
3335 static void DrawPreviewLevelLabel(int mode)
3337 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3340 static void DrawPreviewLevelInfo(int mode)
3342 if (mode == MICROLABEL_LEVEL_NAME)
3343 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3344 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3345 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3348 static void DrawPreviewLevelExt(boolean restart)
3350 static unsigned int scroll_delay = 0;
3351 static unsigned int label_delay = 0;
3352 static int from_x, from_y, scroll_direction;
3353 static int label_state, label_counter;
3354 unsigned int scroll_delay_value = preview.step_delay;
3355 boolean show_level_border = (BorderElement != EL_EMPTY);
3356 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3357 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3364 if (preview.anim_mode == ANIM_CENTERED)
3366 if (level_xsize > preview.xsize)
3367 from_x = (level_xsize - preview.xsize) / 2;
3368 if (level_ysize > preview.ysize)
3369 from_y = (level_ysize - preview.ysize) / 2;
3372 from_x += preview.xoffset;
3373 from_y += preview.yoffset;
3375 scroll_direction = MV_RIGHT;
3379 DrawPreviewLevelPlayfield(from_x, from_y);
3380 DrawPreviewLevelLabel(label_state);
3382 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3383 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3385 /* initialize delay counters */
3386 DelayReached(&scroll_delay, 0);
3387 DelayReached(&label_delay, 0);
3389 if (leveldir_current->name)
3391 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3392 char label_text[MAX_OUTPUT_LINESIZE + 1];
3393 int font_nr = pos->font;
3394 int max_len_label_text = getMaxTextLength(pos, font_nr);
3396 if (pos->size != -1)
3397 max_len_label_text = pos->size;
3399 strncpy(label_text, leveldir_current->name, max_len_label_text);
3400 label_text[max_len_label_text] = '\0';
3402 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3403 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3409 /* scroll preview level, if needed */
3410 if (preview.anim_mode != ANIM_NONE &&
3411 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3412 DelayReached(&scroll_delay, scroll_delay_value))
3414 switch (scroll_direction)
3419 from_x -= preview.step_offset;
3420 from_x = (from_x < 0 ? 0 : from_x);
3423 scroll_direction = MV_UP;
3427 if (from_x < level_xsize - preview.xsize)
3429 from_x += preview.step_offset;
3430 from_x = (from_x > level_xsize - preview.xsize ?
3431 level_xsize - preview.xsize : from_x);
3434 scroll_direction = MV_DOWN;
3440 from_y -= preview.step_offset;
3441 from_y = (from_y < 0 ? 0 : from_y);
3444 scroll_direction = MV_RIGHT;
3448 if (from_y < level_ysize - preview.ysize)
3450 from_y += preview.step_offset;
3451 from_y = (from_y > level_ysize - preview.ysize ?
3452 level_ysize - preview.ysize : from_y);
3455 scroll_direction = MV_LEFT;
3462 DrawPreviewLevelPlayfield(from_x, from_y);
3465 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3466 /* redraw micro level label, if needed */
3467 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3468 !strEqual(level.author, ANONYMOUS_NAME) &&
3469 !strEqual(level.author, leveldir_current->name) &&
3470 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3472 int max_label_counter = 23;
3474 if (leveldir_current->imported_from != NULL &&
3475 strlen(leveldir_current->imported_from) > 0)
3476 max_label_counter += 14;
3477 if (leveldir_current->imported_by != NULL &&
3478 strlen(leveldir_current->imported_by) > 0)
3479 max_label_counter += 14;
3481 label_counter = (label_counter + 1) % max_label_counter;
3482 label_state = (label_counter >= 0 && label_counter <= 7 ?
3483 MICROLABEL_LEVEL_NAME :
3484 label_counter >= 9 && label_counter <= 12 ?
3485 MICROLABEL_LEVEL_AUTHOR_HEAD :
3486 label_counter >= 14 && label_counter <= 21 ?
3487 MICROLABEL_LEVEL_AUTHOR :
3488 label_counter >= 23 && label_counter <= 26 ?
3489 MICROLABEL_IMPORTED_FROM_HEAD :
3490 label_counter >= 28 && label_counter <= 35 ?
3491 MICROLABEL_IMPORTED_FROM :
3492 label_counter >= 37 && label_counter <= 40 ?
3493 MICROLABEL_IMPORTED_BY_HEAD :
3494 label_counter >= 42 && label_counter <= 49 ?
3495 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3497 if (leveldir_current->imported_from == NULL &&
3498 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3499 label_state == MICROLABEL_IMPORTED_FROM))
3500 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3501 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3503 DrawPreviewLevelLabel(label_state);
3507 void DrawPreviewPlayers()
3509 if (game_status != GAME_MODE_MAIN)
3512 if (!network.enabled && !setup.team_mode)
3515 boolean player_found[MAX_PLAYERS];
3516 int num_players = 0;
3519 for (i = 0; i < MAX_PLAYERS; i++)
3520 player_found[i] = FALSE;
3522 /* check which players can be found in the level (simple approach) */
3523 for (x = 0; x < lev_fieldx; x++)
3525 for (y = 0; y < lev_fieldy; y++)
3527 int element = level.field[x][y];
3529 if (ELEM_IS_PLAYER(element))
3531 int player_nr = GET_PLAYER_NR(element);
3533 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3535 if (!player_found[player_nr])
3538 player_found[player_nr] = TRUE;
3543 struct TextPosInfo *pos = &menu.main.preview_players;
3544 int tile_size = pos->tile_size;
3545 int border_size = pos->border_size;
3546 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3547 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3548 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3549 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3550 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3551 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3552 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3553 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3554 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3555 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3556 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3557 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3559 /* clear area in which the players will be drawn */
3560 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3561 max_players_width, max_players_height);
3563 /* only draw players if level is suited for team mode */
3564 if (num_players < 2)
3567 /* draw all players that were found in the level */
3568 for (i = 0; i < MAX_PLAYERS; i++)
3570 if (player_found[i])
3572 int graphic = el2img(EL_PLAYER_1 + i);
3574 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3576 xpos += player_xoffset;
3577 ypos += player_yoffset;
3582 void DrawPreviewLevelInitial()
3584 DrawPreviewLevelExt(TRUE);
3585 DrawPreviewPlayers();
3588 void DrawPreviewLevelAnimation()
3590 DrawPreviewLevelExt(FALSE);
3593 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3594 int border_size, int font_nr)
3596 int graphic = el2img(EL_PLAYER_1 + player_nr);
3597 int font_height = getFontHeight(font_nr);
3598 int player_height = MAX(tile_size, font_height);
3599 int xoffset_text = tile_size + border_size;
3600 int yoffset_text = (player_height - font_height) / 2;
3601 int yoffset_graphic = (player_height - tile_size) / 2;
3602 char *player_name = getNetworkPlayerName(player_nr + 1);
3604 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3606 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3609 void DrawNetworkPlayersExt(boolean force)
3611 if (game_status != GAME_MODE_MAIN)
3614 if (!network.connected && !force)
3617 int num_players = 0;
3620 for (i = 0; i < MAX_PLAYERS; i++)
3621 if (stored_player[i].connected_network)
3624 struct TextPosInfo *pos = &menu.main.network_players;
3625 int tile_size = pos->tile_size;
3626 int border_size = 2;
3627 int xoffset_text = tile_size + border_size;
3628 int font_nr = pos->font;
3629 int font_width = getFontWidth(font_nr);
3630 int font_height = getFontHeight(font_nr);
3631 int player_height = MAX(tile_size, font_height);
3632 int player_yoffset = player_height + border_size;
3633 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3634 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3635 int all_players_height = num_players * player_yoffset - border_size;
3636 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3637 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3638 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3640 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3641 max_players_width, max_players_height);
3643 /* first draw local network player ... */
3644 for (i = 0; i < MAX_PLAYERS; i++)
3646 if (stored_player[i].connected_network &&
3647 stored_player[i].connected_locally)
3649 char *player_name = getNetworkPlayerName(i + 1);
3650 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3651 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3653 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3655 ypos += player_yoffset;
3659 /* ... then draw all other network players */
3660 for (i = 0; i < MAX_PLAYERS; i++)
3662 if (stored_player[i].connected_network &&
3663 !stored_player[i].connected_locally)
3665 char *player_name = getNetworkPlayerName(i + 1);
3666 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3667 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3669 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3671 ypos += player_yoffset;
3676 void DrawNetworkPlayers()
3678 DrawNetworkPlayersExt(FALSE);
3681 void ClearNetworkPlayers()
3683 DrawNetworkPlayersExt(TRUE);
3686 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3687 int graphic, int sync_frame,
3690 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3692 if (mask_mode == USE_MASKING)
3693 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3695 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3698 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3699 int graphic, int sync_frame, int mask_mode)
3701 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3703 if (mask_mode == USE_MASKING)
3704 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3706 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3709 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3711 int lx = LEVELX(x), ly = LEVELY(y);
3713 if (!IN_SCR_FIELD(x, y))
3716 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3717 graphic, GfxFrame[lx][ly], NO_MASKING);
3719 MarkTileDirty(x, y);
3722 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3724 int lx = LEVELX(x), ly = LEVELY(y);
3726 if (!IN_SCR_FIELD(x, y))
3729 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3730 graphic, GfxFrame[lx][ly], NO_MASKING);
3731 MarkTileDirty(x, y);
3734 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3736 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3739 void DrawLevelElementAnimation(int x, int y, int element)
3741 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3743 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3746 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3748 int sx = SCREENX(x), sy = SCREENY(y);
3750 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3753 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3756 DrawGraphicAnimation(sx, sy, graphic);
3759 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3760 DrawLevelFieldCrumbled(x, y);
3762 if (GFX_CRUMBLED(Feld[x][y]))
3763 DrawLevelFieldCrumbled(x, y);
3767 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3769 int sx = SCREENX(x), sy = SCREENY(y);
3772 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3775 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3777 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3780 DrawGraphicAnimation(sx, sy, graphic);
3782 if (GFX_CRUMBLED(element))
3783 DrawLevelFieldCrumbled(x, y);
3786 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3788 if (player->use_murphy)
3790 /* this works only because currently only one player can be "murphy" ... */
3791 static int last_horizontal_dir = MV_LEFT;
3792 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3794 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3795 last_horizontal_dir = move_dir;
3797 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3799 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3801 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3807 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3810 static boolean equalGraphics(int graphic1, int graphic2)
3812 struct GraphicInfo *g1 = &graphic_info[graphic1];
3813 struct GraphicInfo *g2 = &graphic_info[graphic2];
3815 return (g1->bitmap == g2->bitmap &&
3816 g1->src_x == g2->src_x &&
3817 g1->src_y == g2->src_y &&
3818 g1->anim_frames == g2->anim_frames &&
3819 g1->anim_delay == g2->anim_delay &&
3820 g1->anim_mode == g2->anim_mode);
3823 void DrawAllPlayers()
3827 for (i = 0; i < MAX_PLAYERS; i++)
3828 if (stored_player[i].active)
3829 DrawPlayer(&stored_player[i]);
3832 void DrawPlayerField(int x, int y)
3834 if (!IS_PLAYER(x, y))
3837 DrawPlayer(PLAYERINFO(x, y));
3840 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3842 void DrawPlayer(struct PlayerInfo *player)
3844 int jx = player->jx;
3845 int jy = player->jy;
3846 int move_dir = player->MovDir;
3847 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3848 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3849 int last_jx = (player->is_moving ? jx - dx : jx);
3850 int last_jy = (player->is_moving ? jy - dy : jy);
3851 int next_jx = jx + dx;
3852 int next_jy = jy + dy;
3853 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3854 boolean player_is_opaque = FALSE;
3855 int sx = SCREENX(jx), sy = SCREENY(jy);
3856 int sxx = 0, syy = 0;
3857 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3859 int action = ACTION_DEFAULT;
3860 int last_player_graphic = getPlayerGraphic(player, move_dir);
3861 int last_player_frame = player->Frame;
3864 /* GfxElement[][] is set to the element the player is digging or collecting;
3865 remove also for off-screen player if the player is not moving anymore */
3866 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3867 GfxElement[jx][jy] = EL_UNDEFINED;
3869 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3873 if (!IN_LEV_FIELD(jx, jy))
3875 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3876 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3877 printf("DrawPlayerField(): This should never happen!\n");
3882 if (element == EL_EXPLOSION)
3885 action = (player->is_pushing ? ACTION_PUSHING :
3886 player->is_digging ? ACTION_DIGGING :
3887 player->is_collecting ? ACTION_COLLECTING :
3888 player->is_moving ? ACTION_MOVING :
3889 player->is_snapping ? ACTION_SNAPPING :
3890 player->is_dropping ? ACTION_DROPPING :
3891 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3893 if (player->is_waiting)
3894 move_dir = player->dir_waiting;
3896 InitPlayerGfxAnimation(player, action, move_dir);
3898 /* ----------------------------------------------------------------------- */
3899 /* draw things in the field the player is leaving, if needed */
3900 /* ----------------------------------------------------------------------- */
3902 if (player->is_moving)
3904 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3906 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3908 if (last_element == EL_DYNAMITE_ACTIVE ||
3909 last_element == EL_EM_DYNAMITE_ACTIVE ||
3910 last_element == EL_SP_DISK_RED_ACTIVE)
3911 DrawDynamite(last_jx, last_jy);
3913 DrawLevelFieldThruMask(last_jx, last_jy);
3915 else if (last_element == EL_DYNAMITE_ACTIVE ||
3916 last_element == EL_EM_DYNAMITE_ACTIVE ||
3917 last_element == EL_SP_DISK_RED_ACTIVE)
3918 DrawDynamite(last_jx, last_jy);
3920 /* !!! this is not enough to prevent flickering of players which are
3921 moving next to each others without a free tile between them -- this
3922 can only be solved by drawing all players layer by layer (first the
3923 background, then the foreground etc.) !!! => TODO */
3924 else if (!IS_PLAYER(last_jx, last_jy))
3925 DrawLevelField(last_jx, last_jy);
3928 DrawLevelField(last_jx, last_jy);
3931 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3932 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3935 if (!IN_SCR_FIELD(sx, sy))
3938 /* ----------------------------------------------------------------------- */
3939 /* draw things behind the player, if needed */
3940 /* ----------------------------------------------------------------------- */
3943 DrawLevelElement(jx, jy, Back[jx][jy]);
3944 else if (IS_ACTIVE_BOMB(element))
3945 DrawLevelElement(jx, jy, EL_EMPTY);
3948 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3950 int old_element = GfxElement[jx][jy];
3951 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3952 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3954 if (GFX_CRUMBLED(old_element))
3955 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3957 DrawGraphic(sx, sy, old_graphic, frame);
3959 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3960 player_is_opaque = TRUE;
3964 GfxElement[jx][jy] = EL_UNDEFINED;
3966 /* make sure that pushed elements are drawn with correct frame rate */
3967 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3969 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3970 GfxFrame[jx][jy] = player->StepFrame;
3972 DrawLevelField(jx, jy);
3976 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3977 /* ----------------------------------------------------------------------- */
3978 /* draw player himself */
3979 /* ----------------------------------------------------------------------- */
3981 graphic = getPlayerGraphic(player, move_dir);
3983 /* in the case of changed player action or direction, prevent the current
3984 animation frame from being restarted for identical animations */
3985 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3986 player->Frame = last_player_frame;
3988 frame = getGraphicAnimationFrame(graphic, player->Frame);
3992 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3993 sxx = player->GfxPos;
3995 syy = player->GfxPos;
3998 if (player_is_opaque)
3999 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4001 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4003 if (SHIELD_ON(player))
4005 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4006 IMG_SHIELD_NORMAL_ACTIVE);
4007 int frame = getGraphicAnimationFrame(graphic, -1);
4009 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4013 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4016 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4017 sxx = player->GfxPos;
4019 syy = player->GfxPos;
4023 /* ----------------------------------------------------------------------- */
4024 /* draw things the player is pushing, if needed */
4025 /* ----------------------------------------------------------------------- */
4027 if (player->is_pushing && player->is_moving)
4029 int px = SCREENX(jx), py = SCREENY(jy);
4030 int pxx = (TILEX - ABS(sxx)) * dx;
4031 int pyy = (TILEY - ABS(syy)) * dy;
4032 int gfx_frame = GfxFrame[jx][jy];
4038 if (!IS_MOVING(jx, jy)) /* push movement already finished */
4040 element = Feld[next_jx][next_jy];
4041 gfx_frame = GfxFrame[next_jx][next_jy];
4044 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4046 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4047 frame = getGraphicAnimationFrame(graphic, sync_frame);
4049 /* draw background element under pushed element (like the Sokoban field) */
4050 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4052 /* this allows transparent pushing animation over non-black background */
4055 DrawLevelElement(jx, jy, Back[jx][jy]);
4057 DrawLevelElement(jx, jy, EL_EMPTY);
4059 if (Back[next_jx][next_jy])
4060 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4062 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4064 else if (Back[next_jx][next_jy])
4065 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4068 /* do not draw (EM style) pushing animation when pushing is finished */
4069 /* (two-tile animations usually do not contain start and end frame) */
4070 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4071 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4073 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4075 /* masked drawing is needed for EMC style (double) movement graphics */
4076 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
4077 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4081 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4082 /* ----------------------------------------------------------------------- */
4083 /* draw player himself */
4084 /* ----------------------------------------------------------------------- */
4086 graphic = getPlayerGraphic(player, move_dir);
4088 /* in the case of changed player action or direction, prevent the current
4089 animation frame from being restarted for identical animations */
4090 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4091 player->Frame = last_player_frame;
4093 frame = getGraphicAnimationFrame(graphic, player->Frame);
4097 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4098 sxx = player->GfxPos;
4100 syy = player->GfxPos;
4103 if (player_is_opaque)
4104 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4106 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4108 if (SHIELD_ON(player))
4110 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4111 IMG_SHIELD_NORMAL_ACTIVE);
4112 int frame = getGraphicAnimationFrame(graphic, -1);
4114 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4118 /* ----------------------------------------------------------------------- */
4119 /* draw things in front of player (active dynamite or dynabombs) */
4120 /* ----------------------------------------------------------------------- */
4122 if (IS_ACTIVE_BOMB(element))
4124 graphic = el2img(element);
4125 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4127 if (game.emulation == EMU_SUPAPLEX)
4128 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4130 DrawGraphicThruMask(sx, sy, graphic, frame);
4133 if (player_is_moving && last_element == EL_EXPLOSION)
4135 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4136 GfxElement[last_jx][last_jy] : EL_EMPTY);
4137 int graphic = el_act2img(element, ACTION_EXPLODING);
4138 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4139 int phase = ExplodePhase[last_jx][last_jy] - 1;
4140 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4143 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4146 /* ----------------------------------------------------------------------- */
4147 /* draw elements the player is just walking/passing through/under */
4148 /* ----------------------------------------------------------------------- */
4150 if (player_is_moving)
4152 /* handle the field the player is leaving ... */
4153 if (IS_ACCESSIBLE_INSIDE(last_element))
4154 DrawLevelField(last_jx, last_jy);
4155 else if (IS_ACCESSIBLE_UNDER(last_element))
4156 DrawLevelFieldThruMask(last_jx, last_jy);
4159 /* do not redraw accessible elements if the player is just pushing them */
4160 if (!player_is_moving || !player->is_pushing)
4162 /* ... and the field the player is entering */
4163 if (IS_ACCESSIBLE_INSIDE(element))
4164 DrawLevelField(jx, jy);
4165 else if (IS_ACCESSIBLE_UNDER(element))
4166 DrawLevelFieldThruMask(jx, jy);
4169 MarkTileDirty(sx, sy);
4172 /* ------------------------------------------------------------------------- */
4174 void WaitForEventToContinue()
4176 boolean still_wait = TRUE;
4178 if (program.headless)
4181 /* simulate releasing mouse button over last gadget, if still pressed */
4183 HandleGadgets(-1, -1, 0);
4185 button_status = MB_RELEASED;
4193 if (NextValidEvent(&event))
4197 case EVENT_BUTTONPRESS:
4198 case EVENT_KEYPRESS:
4199 #if defined(TARGET_SDL2)
4200 case SDL_CONTROLLERBUTTONDOWN:
4202 case SDL_JOYBUTTONDOWN:
4206 case EVENT_KEYRELEASE:
4207 ClearPlayerAction();
4211 HandleOtherEvents(&event);
4215 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4224 #define MAX_REQUEST_LINES 13
4225 #define MAX_REQUEST_LINE_FONT1_LEN 7
4226 #define MAX_REQUEST_LINE_FONT2_LEN 10
4228 static int RequestHandleEvents(unsigned int req_state)
4230 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
4231 local_player->LevelSolved_GameEnd);
4232 int width = request.width;
4233 int height = request.height;
4237 setRequestPosition(&sx, &sy, FALSE);
4239 button_status = MB_RELEASED;
4241 request_gadget_id = -1;
4248 /* the MM game engine does not use a special (scrollable) field buffer */
4249 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4250 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4252 HandleGameActions();
4254 SetDrawtoField(DRAW_TO_BACKBUFFER);
4256 if (global.use_envelope_request)
4258 /* copy current state of request area to middle of playfield area */
4259 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4267 while (NextValidEvent(&event))
4271 case EVENT_BUTTONPRESS:
4272 case EVENT_BUTTONRELEASE:
4273 case EVENT_MOTIONNOTIFY:
4277 if (event.type == EVENT_MOTIONNOTIFY)
4282 motion_status = TRUE;
4283 mx = ((MotionEvent *) &event)->x;
4284 my = ((MotionEvent *) &event)->y;
4288 motion_status = FALSE;
4289 mx = ((ButtonEvent *) &event)->x;
4290 my = ((ButtonEvent *) &event)->y;
4291 if (event.type == EVENT_BUTTONPRESS)
4292 button_status = ((ButtonEvent *) &event)->button;
4294 button_status = MB_RELEASED;
4297 /* this sets 'request_gadget_id' */
4298 HandleGadgets(mx, my, button_status);
4300 switch (request_gadget_id)
4302 case TOOL_CTRL_ID_YES:
4305 case TOOL_CTRL_ID_NO:
4308 case TOOL_CTRL_ID_CONFIRM:
4309 result = TRUE | FALSE;
4312 case TOOL_CTRL_ID_PLAYER_1:
4315 case TOOL_CTRL_ID_PLAYER_2:
4318 case TOOL_CTRL_ID_PLAYER_3:
4321 case TOOL_CTRL_ID_PLAYER_4:
4332 #if defined(TARGET_SDL2)
4333 case SDL_WINDOWEVENT:
4334 HandleWindowEvent((WindowEvent *) &event);
4337 case SDL_APP_WILLENTERBACKGROUND:
4338 case SDL_APP_DIDENTERBACKGROUND:
4339 case SDL_APP_WILLENTERFOREGROUND:
4340 case SDL_APP_DIDENTERFOREGROUND:
4341 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4345 case EVENT_KEYPRESS:
4347 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4352 if (req_state & REQ_CONFIRM)
4358 #if defined(TARGET_SDL2)
4362 #if defined(KSYM_Rewind)
4363 case KSYM_Rewind: /* for Amazon Fire TV remote */
4371 #if defined(TARGET_SDL2)
4374 #if defined(KSYM_FastForward)
4375 case KSYM_FastForward: /* for Amazon Fire TV remote */
4382 HandleKeysDebug(key);
4386 if (req_state & REQ_PLAYER)
4388 int old_player_nr = setup.network_player_nr;
4391 result = old_player_nr + 1;
4396 result = old_player_nr + 1;
4427 case EVENT_KEYRELEASE:
4428 ClearPlayerAction();
4431 #if defined(TARGET_SDL2)
4432 case SDL_CONTROLLERBUTTONDOWN:
4433 switch (event.cbutton.button)
4435 case SDL_CONTROLLER_BUTTON_A:
4436 case SDL_CONTROLLER_BUTTON_X:
4437 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4438 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4442 case SDL_CONTROLLER_BUTTON_B:
4443 case SDL_CONTROLLER_BUTTON_Y:
4444 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4445 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4446 case SDL_CONTROLLER_BUTTON_BACK:
4451 if (req_state & REQ_PLAYER)
4453 int old_player_nr = setup.network_player_nr;
4456 result = old_player_nr + 1;
4458 switch (event.cbutton.button)
4460 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4461 case SDL_CONTROLLER_BUTTON_Y:
4465 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4466 case SDL_CONTROLLER_BUTTON_B:
4470 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4471 case SDL_CONTROLLER_BUTTON_A:
4475 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4476 case SDL_CONTROLLER_BUTTON_X:
4487 case SDL_CONTROLLERBUTTONUP:
4488 HandleJoystickEvent(&event);
4489 ClearPlayerAction();
4494 HandleOtherEvents(&event);
4499 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4501 int joy = AnyJoystick();
4503 if (joy & JOY_BUTTON_1)
4505 else if (joy & JOY_BUTTON_2)
4508 else if (AnyJoystick())
4510 int joy = AnyJoystick();
4512 if (req_state & REQ_PLAYER)
4516 else if (joy & JOY_RIGHT)
4518 else if (joy & JOY_DOWN)
4520 else if (joy & JOY_LEFT)
4527 if (global.use_envelope_request)
4529 /* copy back current state of pressed buttons inside request area */
4530 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4540 static boolean RequestDoor(char *text, unsigned int req_state)
4542 unsigned int old_door_state;
4543 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4544 int font_nr = FONT_TEXT_2;
4549 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4551 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4552 font_nr = FONT_TEXT_1;
4555 if (game_status == GAME_MODE_PLAYING)
4556 BlitScreenToBitmap(backbuffer);
4558 /* disable deactivated drawing when quick-loading level tape recording */
4559 if (tape.playing && tape.deactivate_display)
4560 TapeDeactivateDisplayOff(TRUE);
4562 SetMouseCursor(CURSOR_DEFAULT);
4564 /* pause network game while waiting for request to answer */
4565 if (network.enabled &&
4566 game_status == GAME_MODE_PLAYING &&
4567 req_state & REQUEST_WAIT_FOR_INPUT)
4568 SendToServer_PausePlaying();
4570 old_door_state = GetDoorState();
4572 /* simulate releasing mouse button over last gadget, if still pressed */
4574 HandleGadgets(-1, -1, 0);
4578 /* draw released gadget before proceeding */
4581 if (old_door_state & DOOR_OPEN_1)
4583 CloseDoor(DOOR_CLOSE_1);
4585 /* save old door content */
4586 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4587 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4590 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4591 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4593 /* clear door drawing field */
4594 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4596 /* force DOOR font inside door area */
4597 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4599 /* write text for request */
4600 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4602 char text_line[max_request_line_len + 1];
4608 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4610 tc = *(text_ptr + tx);
4611 // if (!tc || tc == ' ')
4612 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4616 if ((tc == '?' || tc == '!') && tl == 0)
4626 strncpy(text_line, text_ptr, tl);
4629 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4630 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4631 text_line, font_nr);
4633 text_ptr += tl + (tc == ' ' ? 1 : 0);
4634 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4639 if (req_state & REQ_ASK)
4641 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4642 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4644 else if (req_state & REQ_CONFIRM)
4646 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4648 else if (req_state & REQ_PLAYER)
4650 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4651 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4652 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4653 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4656 /* copy request gadgets to door backbuffer */
4657 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4659 OpenDoor(DOOR_OPEN_1);
4661 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4663 if (game_status == GAME_MODE_PLAYING)
4665 SetPanelBackground();
4666 SetDrawBackgroundMask(REDRAW_DOOR_1);
4670 SetDrawBackgroundMask(REDRAW_FIELD);
4676 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4678 // ---------- handle request buttons ----------
4679 result = RequestHandleEvents(req_state);
4683 if (!(req_state & REQ_STAY_OPEN))
4685 CloseDoor(DOOR_CLOSE_1);
4687 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4688 (req_state & REQ_REOPEN))
4689 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4694 if (game_status == GAME_MODE_PLAYING)
4696 SetPanelBackground();
4697 SetDrawBackgroundMask(REDRAW_DOOR_1);
4701 SetDrawBackgroundMask(REDRAW_FIELD);
4704 /* continue network game after request */
4705 if (network.enabled &&
4706 game_status == GAME_MODE_PLAYING &&
4707 req_state & REQUEST_WAIT_FOR_INPUT)
4708 SendToServer_ContinuePlaying();
4710 /* restore deactivated drawing when quick-loading level tape recording */
4711 if (tape.playing && tape.deactivate_display)
4712 TapeDeactivateDisplayOn();
4717 static boolean RequestEnvelope(char *text, unsigned int req_state)
4721 if (game_status == GAME_MODE_PLAYING)
4722 BlitScreenToBitmap(backbuffer);
4724 /* disable deactivated drawing when quick-loading level tape recording */
4725 if (tape.playing && tape.deactivate_display)
4726 TapeDeactivateDisplayOff(TRUE);
4728 SetMouseCursor(CURSOR_DEFAULT);
4730 /* pause network game while waiting for request to answer */
4731 if (network.enabled &&
4732 game_status == GAME_MODE_PLAYING &&
4733 req_state & REQUEST_WAIT_FOR_INPUT)
4734 SendToServer_PausePlaying();
4736 /* simulate releasing mouse button over last gadget, if still pressed */
4738 HandleGadgets(-1, -1, 0);
4742 // (replace with setting corresponding request background)
4743 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4744 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4746 /* clear door drawing field */
4747 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4749 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4751 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4753 if (game_status == GAME_MODE_PLAYING)
4755 SetPanelBackground();
4756 SetDrawBackgroundMask(REDRAW_DOOR_1);
4760 SetDrawBackgroundMask(REDRAW_FIELD);
4766 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4768 // ---------- handle request buttons ----------
4769 result = RequestHandleEvents(req_state);
4773 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4777 if (game_status == GAME_MODE_PLAYING)
4779 SetPanelBackground();
4780 SetDrawBackgroundMask(REDRAW_DOOR_1);
4784 SetDrawBackgroundMask(REDRAW_FIELD);
4787 /* continue network game after request */
4788 if (network.enabled &&
4789 game_status == GAME_MODE_PLAYING &&
4790 req_state & REQUEST_WAIT_FOR_INPUT)
4791 SendToServer_ContinuePlaying();
4793 /* restore deactivated drawing when quick-loading level tape recording */
4794 if (tape.playing && tape.deactivate_display)
4795 TapeDeactivateDisplayOn();
4800 boolean Request(char *text, unsigned int req_state)
4802 boolean overlay_active = GetOverlayActive();
4805 SetOverlayActive(FALSE);
4807 if (global.use_envelope_request)
4808 result = RequestEnvelope(text, req_state);
4810 result = RequestDoor(text, req_state);
4812 SetOverlayActive(overlay_active);
4817 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4819 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4820 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4823 if (dpo1->sort_priority != dpo2->sort_priority)
4824 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4826 compare_result = dpo1->nr - dpo2->nr;
4828 return compare_result;
4831 void InitGraphicCompatibilityInfo_Doors()
4837 struct DoorInfo *door;
4841 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4842 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4844 { -1, -1, -1, NULL }
4846 struct Rect door_rect_list[] =
4848 { DX, DY, DXSIZE, DYSIZE },
4849 { VX, VY, VXSIZE, VYSIZE }
4853 for (i = 0; doors[i].door_token != -1; i++)
4855 int door_token = doors[i].door_token;
4856 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4857 int part_1 = doors[i].part_1;
4858 int part_8 = doors[i].part_8;
4859 int part_2 = part_1 + 1;
4860 int part_3 = part_1 + 2;
4861 struct DoorInfo *door = doors[i].door;
4862 struct Rect *door_rect = &door_rect_list[door_index];
4863 boolean door_gfx_redefined = FALSE;
4865 /* check if any door part graphic definitions have been redefined */
4867 for (j = 0; door_part_controls[j].door_token != -1; j++)
4869 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4870 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4872 if (dpc->door_token == door_token && fi->redefined)
4873 door_gfx_redefined = TRUE;
4876 /* check for old-style door graphic/animation modifications */
4878 if (!door_gfx_redefined)
4880 if (door->anim_mode & ANIM_STATIC_PANEL)
4882 door->panel.step_xoffset = 0;
4883 door->panel.step_yoffset = 0;
4886 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4888 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4889 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4890 int num_door_steps, num_panel_steps;
4892 /* remove door part graphics other than the two default wings */
4894 for (j = 0; door_part_controls[j].door_token != -1; j++)
4896 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4897 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4899 if (dpc->graphic >= part_3 &&
4900 dpc->graphic <= part_8)
4904 /* set graphics and screen positions of the default wings */
4906 g_part_1->width = door_rect->width;
4907 g_part_1->height = door_rect->height;
4908 g_part_2->width = door_rect->width;
4909 g_part_2->height = door_rect->height;
4910 g_part_2->src_x = door_rect->width;
4911 g_part_2->src_y = g_part_1->src_y;
4913 door->part_2.x = door->part_1.x;
4914 door->part_2.y = door->part_1.y;
4916 if (door->width != -1)
4918 g_part_1->width = door->width;
4919 g_part_2->width = door->width;
4921 // special treatment for graphics and screen position of right wing
4922 g_part_2->src_x += door_rect->width - door->width;
4923 door->part_2.x += door_rect->width - door->width;
4926 if (door->height != -1)
4928 g_part_1->height = door->height;
4929 g_part_2->height = door->height;
4931 // special treatment for graphics and screen position of bottom wing
4932 g_part_2->src_y += door_rect->height - door->height;
4933 door->part_2.y += door_rect->height - door->height;
4936 /* set animation delays for the default wings and panels */
4938 door->part_1.step_delay = door->step_delay;
4939 door->part_2.step_delay = door->step_delay;
4940 door->panel.step_delay = door->step_delay;
4942 /* set animation draw order for the default wings */
4944 door->part_1.sort_priority = 2; /* draw left wing over ... */
4945 door->part_2.sort_priority = 1; /* ... right wing */
4947 /* set animation draw offset for the default wings */
4949 if (door->anim_mode & ANIM_HORIZONTAL)
4951 door->part_1.step_xoffset = door->step_offset;
4952 door->part_1.step_yoffset = 0;
4953 door->part_2.step_xoffset = door->step_offset * -1;
4954 door->part_2.step_yoffset = 0;
4956 num_door_steps = g_part_1->width / door->step_offset;
4958 else // ANIM_VERTICAL
4960 door->part_1.step_xoffset = 0;
4961 door->part_1.step_yoffset = door->step_offset;
4962 door->part_2.step_xoffset = 0;
4963 door->part_2.step_yoffset = door->step_offset * -1;
4965 num_door_steps = g_part_1->height / door->step_offset;
4968 /* set animation draw offset for the default panels */
4970 if (door->step_offset > 1)
4972 num_panel_steps = 2 * door_rect->height / door->step_offset;
4973 door->panel.start_step = num_panel_steps - num_door_steps;
4974 door->panel.start_step_closing = door->panel.start_step;
4978 num_panel_steps = door_rect->height / door->step_offset;
4979 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4980 door->panel.start_step_closing = door->panel.start_step;
4981 door->panel.step_delay *= 2;
4992 for (i = 0; door_part_controls[i].door_token != -1; i++)
4994 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4995 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4997 /* initialize "start_step_opening" and "start_step_closing", if needed */
4998 if (dpc->pos->start_step_opening == 0 &&
4999 dpc->pos->start_step_closing == 0)
5001 // dpc->pos->start_step_opening = dpc->pos->start_step;
5002 dpc->pos->start_step_closing = dpc->pos->start_step;
5005 /* fill structure for door part draw order (sorted below) */
5007 dpo->sort_priority = dpc->pos->sort_priority;
5010 /* sort door part controls according to sort_priority and graphic number */
5011 qsort(door_part_order, MAX_DOOR_PARTS,
5012 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
5015 unsigned int OpenDoor(unsigned int door_state)
5017 if (door_state & DOOR_COPY_BACK)
5019 if (door_state & DOOR_OPEN_1)
5020 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
5021 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
5023 if (door_state & DOOR_OPEN_2)
5024 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
5025 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
5027 door_state &= ~DOOR_COPY_BACK;
5030 return MoveDoor(door_state);
5033 unsigned int CloseDoor(unsigned int door_state)
5035 unsigned int old_door_state = GetDoorState();
5037 if (!(door_state & DOOR_NO_COPY_BACK))
5039 if (old_door_state & DOOR_OPEN_1)
5040 BlitBitmap(backbuffer, bitmap_db_door_1,
5041 DX, DY, DXSIZE, DYSIZE, 0, 0);
5043 if (old_door_state & DOOR_OPEN_2)
5044 BlitBitmap(backbuffer, bitmap_db_door_2,
5045 VX, VY, VXSIZE, VYSIZE, 0, 0);
5047 door_state &= ~DOOR_NO_COPY_BACK;
5050 return MoveDoor(door_state);
5053 unsigned int GetDoorState()
5055 return MoveDoor(DOOR_GET_STATE);
5058 unsigned int SetDoorState(unsigned int door_state)
5060 return MoveDoor(door_state | DOOR_SET_STATE);
5063 int euclid(int a, int b)
5065 return (b ? euclid(b, a % b) : a);
5068 unsigned int MoveDoor(unsigned int door_state)
5070 struct Rect door_rect_list[] =
5072 { DX, DY, DXSIZE, DYSIZE },
5073 { VX, VY, VXSIZE, VYSIZE }
5075 static int door1 = DOOR_CLOSE_1;
5076 static int door2 = DOOR_CLOSE_2;
5077 unsigned int door_delay = 0;
5078 unsigned int door_delay_value;
5081 if (door_state == DOOR_GET_STATE)
5082 return (door1 | door2);
5084 if (door_state & DOOR_SET_STATE)
5086 if (door_state & DOOR_ACTION_1)
5087 door1 = door_state & DOOR_ACTION_1;
5088 if (door_state & DOOR_ACTION_2)
5089 door2 = door_state & DOOR_ACTION_2;
5091 return (door1 | door2);
5094 if (!(door_state & DOOR_FORCE_REDRAW))
5096 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
5097 door_state &= ~DOOR_OPEN_1;
5098 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
5099 door_state &= ~DOOR_CLOSE_1;
5100 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
5101 door_state &= ~DOOR_OPEN_2;
5102 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
5103 door_state &= ~DOOR_CLOSE_2;
5106 if (global.autoplay_leveldir)
5108 door_state |= DOOR_NO_DELAY;
5109 door_state &= ~DOOR_CLOSE_ALL;
5112 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
5113 door_state |= DOOR_NO_DELAY;
5115 if (door_state & DOOR_ACTION)
5117 boolean door_panel_drawn[NUM_DOORS];
5118 boolean panel_has_doors[NUM_DOORS];
5119 boolean door_part_skip[MAX_DOOR_PARTS];
5120 boolean door_part_done[MAX_DOOR_PARTS];
5121 boolean door_part_done_all;
5122 int num_steps[MAX_DOOR_PARTS];
5123 int max_move_delay = 0; // delay for complete animations of all doors
5124 int max_step_delay = 0; // delay (ms) between two animation frames
5125 int num_move_steps = 0; // number of animation steps for all doors
5126 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
5127 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
5128 int current_move_delay = 0;
5132 for (i = 0; i < NUM_DOORS; i++)
5133 panel_has_doors[i] = FALSE;
5135 for (i = 0; i < MAX_DOOR_PARTS; i++)
5137 struct DoorPartControlInfo *dpc = &door_part_controls[i];
5138 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5139 int door_token = dpc->door_token;
5141 door_part_done[i] = FALSE;
5142 door_part_skip[i] = (!(door_state & door_token) ||
5146 for (i = 0; i < MAX_DOOR_PARTS; i++)
5148 int nr = door_part_order[i].nr;
5149 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5150 struct DoorPartPosInfo *pos = dpc->pos;
5151 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5152 int door_token = dpc->door_token;
5153 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5154 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5155 int step_xoffset = ABS(pos->step_xoffset);
5156 int step_yoffset = ABS(pos->step_yoffset);
5157 int step_delay = pos->step_delay;
5158 int current_door_state = door_state & door_token;
5159 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5160 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
5161 boolean part_opening = (is_panel ? door_closing : door_opening);
5162 int start_step = (part_opening ? pos->start_step_opening :
5163 pos->start_step_closing);
5164 float move_xsize = (step_xoffset ? g->width : 0);
5165 float move_ysize = (step_yoffset ? g->height : 0);
5166 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
5167 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
5168 int move_steps = (move_xsteps && move_ysteps ?
5169 MIN(move_xsteps, move_ysteps) :
5170 move_xsteps ? move_xsteps : move_ysteps) - start_step;
5171 int move_delay = move_steps * step_delay;
5173 if (door_part_skip[nr])
5176 max_move_delay = MAX(max_move_delay, move_delay);
5177 max_step_delay = (max_step_delay == 0 ? step_delay :
5178 euclid(max_step_delay, step_delay));
5179 num_steps[nr] = move_steps;
5183 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
5185 panel_has_doors[door_index] = TRUE;
5189 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
5191 num_move_steps = max_move_delay / max_step_delay;
5192 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
5194 door_delay_value = max_step_delay;
5196 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
5198 start = num_move_steps - 1;
5202 /* opening door sound has priority over simultaneously closing door */
5203 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
5205 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
5207 if (door_state & DOOR_OPEN_1)
5208 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
5209 if (door_state & DOOR_OPEN_2)
5210 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
5212 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
5214 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
5216 if (door_state & DOOR_CLOSE_1)
5217 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
5218 if (door_state & DOOR_CLOSE_2)
5219 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
5223 for (k = start; k < num_move_steps; k++)
5225 int last_frame = num_move_steps - 1; // last frame of this "for" loop
5227 door_part_done_all = TRUE;
5229 for (i = 0; i < NUM_DOORS; i++)
5230 door_panel_drawn[i] = FALSE;
5232 for (i = 0; i < MAX_DOOR_PARTS; i++)
5234 int nr = door_part_order[i].nr;
5235 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
5236 struct DoorPartPosInfo *pos = dpc->pos;
5237 struct GraphicInfo *g = &graphic_info[dpc->graphic];
5238 int door_token = dpc->door_token;
5239 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
5240 boolean is_panel = DOOR_PART_IS_PANEL(nr);
5241 boolean is_panel_and_door_has_closed = FALSE;
5242 struct Rect *door_rect = &door_rect_list[door_index];
5243 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
5245 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
5246 int current_door_state = door_state & door_token;
5247 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
5248 boolean door_closing = !door_opening;
5249 boolean part_opening = (is_panel ? door_closing : door_opening);
5250 boolean part_closing = !part_opening;
5251 int start_step = (part_opening ? pos->start_step_opening :
5252 pos->start_step_closing);
5253 int step_delay = pos->step_delay;
5254 int step_factor = step_delay / max_step_delay;
5255 int k1 = (step_factor ? k / step_factor + 1 : k);
5256 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
5257 int kk = MAX(0, k2);
5260 int src_x, src_y, src_xx, src_yy;
5261 int dst_x, dst_y, dst_xx, dst_yy;
5264 if (door_part_skip[nr])
5267 if (!(door_state & door_token))
5275 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
5276 int kk_door = MAX(0, k2_door);
5277 int sync_frame = kk_door * door_delay_value;
5278 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
5280 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
5281 &g_src_x, &g_src_y);
5286 if (!door_panel_drawn[door_index])
5288 ClearRectangle(drawto, door_rect->x, door_rect->y,
5289 door_rect->width, door_rect->height);
5291 door_panel_drawn[door_index] = TRUE;
5294 // draw opening or closing door parts
5296 if (pos->step_xoffset < 0) // door part on right side
5299 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
5302 if (dst_xx + width > door_rect->width)
5303 width = door_rect->width - dst_xx;
5305 else // door part on left side
5308 dst_xx = pos->x - kk * pos->step_xoffset;
5312 src_xx = ABS(dst_xx);
5316 width = g->width - src_xx;
5318 if (width > door_rect->width)
5319 width = door_rect->width;
5321 // printf("::: k == %d [%d] \n", k, start_step);
5324 if (pos->step_yoffset < 0) // door part on bottom side
5327 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
5330 if (dst_yy + height > door_rect->height)
5331 height = door_rect->height - dst_yy;
5333 else // door part on top side
5336 dst_yy = pos->y - kk * pos->step_yoffset;
5340 src_yy = ABS(dst_yy);
5344 height = g->height - src_yy;
5347 src_x = g_src_x + src_xx;
5348 src_y = g_src_y + src_yy;
5350 dst_x = door_rect->x + dst_xx;
5351 dst_y = door_rect->y + dst_yy;
5353 is_panel_and_door_has_closed =
5356 panel_has_doors[door_index] &&
5357 k >= num_move_steps_doors_only - 1);
5359 if (width >= 0 && width <= g->width &&
5360 height >= 0 && height <= g->height &&
5361 !is_panel_and_door_has_closed)
5363 if (is_panel || !pos->draw_masked)
5364 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5367 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5371 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5373 if ((part_opening && (width < 0 || height < 0)) ||
5374 (part_closing && (width >= g->width && height >= g->height)))
5375 door_part_done[nr] = TRUE;
5377 // continue door part animations, but not panel after door has closed
5378 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5379 door_part_done_all = FALSE;
5382 if (!(door_state & DOOR_NO_DELAY))
5386 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5388 current_move_delay += max_step_delay;
5390 /* prevent OS (Windows) from complaining about program not responding */
5394 if (door_part_done_all)
5398 if (!(door_state & DOOR_NO_DELAY))
5400 /* wait for specified door action post delay */
5401 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5402 door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
5403 else if (door_state & DOOR_ACTION_1)
5404 door_delay_value = door_1.post_delay;
5405 else if (door_state & DOOR_ACTION_2)
5406 door_delay_value = door_2.post_delay;
5408 while (!DelayReached(&door_delay, door_delay_value))
5413 if (door_state & DOOR_ACTION_1)
5414 door1 = door_state & DOOR_ACTION_1;
5415 if (door_state & DOOR_ACTION_2)
5416 door2 = door_state & DOOR_ACTION_2;
5418 // draw masked border over door area
5419 DrawMaskedBorder(REDRAW_DOOR_1);
5420 DrawMaskedBorder(REDRAW_DOOR_2);
5422 return (door1 | door2);
5425 static boolean useSpecialEditorDoor()
5427 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5428 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5430 // do not draw special editor door if editor border defined or redefined
5431 if (graphic_info[graphic].bitmap != NULL || redefined)
5434 // do not draw special editor door if global border defined to be empty
5435 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5438 // do not draw special editor door if viewport definitions do not match
5442 EY + EYSIZE != VY + VYSIZE)
5448 void DrawSpecialEditorDoor()
5450 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5451 int top_border_width = gfx1->width;
5452 int top_border_height = gfx1->height;
5453 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5454 int ex = EX - outer_border;
5455 int ey = EY - outer_border;
5456 int vy = VY - outer_border;
5457 int exsize = EXSIZE + 2 * outer_border;
5459 if (!useSpecialEditorDoor())
5462 /* draw bigger level editor toolbox window */
5463 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5464 top_border_width, top_border_height, ex, ey - top_border_height);
5465 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5466 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5468 redraw_mask |= REDRAW_ALL;
5471 void UndrawSpecialEditorDoor()
5473 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5474 int top_border_width = gfx1->width;
5475 int top_border_height = gfx1->height;
5476 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5477 int ex = EX - outer_border;
5478 int ey = EY - outer_border;
5479 int ey_top = ey - top_border_height;
5480 int exsize = EXSIZE + 2 * outer_border;
5481 int eysize = EYSIZE + 2 * outer_border;
5483 if (!useSpecialEditorDoor())
5486 /* draw normal tape recorder window */
5487 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5489 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5490 ex, ey_top, top_border_width, top_border_height,
5492 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5493 ex, ey, exsize, eysize, ex, ey);
5497 // if screen background is set to "[NONE]", clear editor toolbox window
5498 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5499 ClearRectangle(drawto, ex, ey, exsize, eysize);
5502 redraw_mask |= REDRAW_ALL;
5506 /* ---------- new tool button stuff ---------------------------------------- */
5511 struct TextPosInfo *pos;
5514 } toolbutton_info[NUM_TOOL_BUTTONS] =
5517 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5518 TOOL_CTRL_ID_YES, "yes"
5521 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5522 TOOL_CTRL_ID_NO, "no"
5525 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5526 TOOL_CTRL_ID_CONFIRM, "confirm"
5529 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5530 TOOL_CTRL_ID_PLAYER_1, "player 1"
5533 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5534 TOOL_CTRL_ID_PLAYER_2, "player 2"
5537 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5538 TOOL_CTRL_ID_PLAYER_3, "player 3"
5541 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5542 TOOL_CTRL_ID_PLAYER_4, "player 4"
5546 void CreateToolButtons()
5550 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5552 int graphic = toolbutton_info[i].graphic;
5553 struct GraphicInfo *gfx = &graphic_info[graphic];
5554 struct TextPosInfo *pos = toolbutton_info[i].pos;
5555 struct GadgetInfo *gi;
5556 Bitmap *deco_bitmap = None;
5557 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5558 unsigned int event_mask = GD_EVENT_RELEASED;
5561 int gd_x = gfx->src_x;
5562 int gd_y = gfx->src_y;
5563 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5564 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5569 if (global.use_envelope_request)
5571 setRequestPosition(&dx, &dy, TRUE);
5573 // check if request buttons are outside of envelope and fix, if needed
5574 if (x < 0 || x + gfx->width > request.width ||
5575 y < 0 || y + gfx->height > request.height)
5577 if (id == TOOL_CTRL_ID_YES)
5580 y = request.height - 2 * request.border_size - gfx->height;
5582 else if (id == TOOL_CTRL_ID_NO)
5584 x = request.width - 2 * request.border_size - gfx->width;
5585 y = request.height - 2 * request.border_size - gfx->height;
5587 else if (id == TOOL_CTRL_ID_CONFIRM)
5589 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5590 y = request.height - 2 * request.border_size - gfx->height;
5592 else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5594 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5596 x = (request.width - 2 * request.border_size - gfx->width) / 2;
5597 y = request.height - 2 * request.border_size - gfx->height * 2;
5599 x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
5600 y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
5605 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5607 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5609 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5610 pos->size, &deco_bitmap, &deco_x, &deco_y);
5611 deco_xpos = (gfx->width - pos->size) / 2;
5612 deco_ypos = (gfx->height - pos->size) / 2;
5615 gi = CreateGadget(GDI_CUSTOM_ID, id,
5616 GDI_IMAGE_ID, graphic,
5617 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5620 GDI_WIDTH, gfx->width,
5621 GDI_HEIGHT, gfx->height,
5622 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5623 GDI_STATE, GD_BUTTON_UNPRESSED,
5624 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5625 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5626 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5627 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5628 GDI_DECORATION_SIZE, pos->size, pos->size,
5629 GDI_DECORATION_SHIFTING, 1, 1,
5630 GDI_DIRECT_DRAW, FALSE,
5631 GDI_EVENT_MASK, event_mask,
5632 GDI_CALLBACK_ACTION, HandleToolButtons,
5636 Error(ERR_EXIT, "cannot create gadget");
5638 tool_gadget[id] = gi;
5642 void FreeToolButtons()
5646 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5647 FreeGadget(tool_gadget[i]);
5650 static void UnmapToolButtons()
5654 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5655 UnmapGadget(tool_gadget[i]);
5658 static void HandleToolButtons(struct GadgetInfo *gi)
5660 request_gadget_id = gi->custom_id;
5663 static struct Mapping_EM_to_RND_object
5666 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5667 boolean is_backside; /* backside of moving element */
5673 em_object_mapping_list[] =
5676 Xblank, TRUE, FALSE,
5680 Yacid_splash_eB, FALSE, FALSE,
5681 EL_ACID_SPLASH_RIGHT, -1, -1
5684 Yacid_splash_wB, FALSE, FALSE,
5685 EL_ACID_SPLASH_LEFT, -1, -1
5688 #ifdef EM_ENGINE_BAD_ROLL
5690 Xstone_force_e, FALSE, FALSE,
5691 EL_ROCK, -1, MV_BIT_RIGHT
5694 Xstone_force_w, FALSE, FALSE,
5695 EL_ROCK, -1, MV_BIT_LEFT
5698 Xnut_force_e, FALSE, FALSE,
5699 EL_NUT, -1, MV_BIT_RIGHT
5702 Xnut_force_w, FALSE, FALSE,
5703 EL_NUT, -1, MV_BIT_LEFT
5706 Xspring_force_e, FALSE, FALSE,
5707 EL_SPRING, -1, MV_BIT_RIGHT
5710 Xspring_force_w, FALSE, FALSE,
5711 EL_SPRING, -1, MV_BIT_LEFT
5714 Xemerald_force_e, FALSE, FALSE,
5715 EL_EMERALD, -1, MV_BIT_RIGHT
5718 Xemerald_force_w, FALSE, FALSE,
5719 EL_EMERALD, -1, MV_BIT_LEFT
5722 Xdiamond_force_e, FALSE, FALSE,
5723 EL_DIAMOND, -1, MV_BIT_RIGHT
5726 Xdiamond_force_w, FALSE, FALSE,
5727 EL_DIAMOND, -1, MV_BIT_LEFT
5730 Xbomb_force_e, FALSE, FALSE,
5731 EL_BOMB, -1, MV_BIT_RIGHT
5734 Xbomb_force_w, FALSE, FALSE,
5735 EL_BOMB, -1, MV_BIT_LEFT
5737 #endif /* EM_ENGINE_BAD_ROLL */
5740 Xstone, TRUE, FALSE,
5744 Xstone_pause, FALSE, FALSE,
5748 Xstone_fall, FALSE, FALSE,
5752 Ystone_s, FALSE, FALSE,
5753 EL_ROCK, ACTION_FALLING, -1
5756 Ystone_sB, FALSE, TRUE,
5757 EL_ROCK, ACTION_FALLING, -1
5760 Ystone_e, FALSE, FALSE,
5761 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5764 Ystone_eB, FALSE, TRUE,
5765 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5768 Ystone_w, FALSE, FALSE,
5769 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5772 Ystone_wB, FALSE, TRUE,
5773 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5780 Xnut_pause, FALSE, FALSE,
5784 Xnut_fall, FALSE, FALSE,
5788 Ynut_s, FALSE, FALSE,
5789 EL_NUT, ACTION_FALLING, -1
5792 Ynut_sB, FALSE, TRUE,
5793 EL_NUT, ACTION_FALLING, -1
5796 Ynut_e, FALSE, FALSE,
5797 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5800 Ynut_eB, FALSE, TRUE,
5801 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5804 Ynut_w, FALSE, FALSE,
5805 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5808 Ynut_wB, FALSE, TRUE,
5809 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5812 Xbug_n, TRUE, FALSE,
5816 Xbug_e, TRUE, FALSE,
5817 EL_BUG_RIGHT, -1, -1
5820 Xbug_s, TRUE, FALSE,
5824 Xbug_w, TRUE, FALSE,
5828 Xbug_gon, FALSE, FALSE,
5832 Xbug_goe, FALSE, FALSE,
5833 EL_BUG_RIGHT, -1, -1
5836 Xbug_gos, FALSE, FALSE,
5840 Xbug_gow, FALSE, FALSE,
5844 Ybug_n, FALSE, FALSE,
5845 EL_BUG, ACTION_MOVING, MV_BIT_UP
5848 Ybug_nB, FALSE, TRUE,
5849 EL_BUG, ACTION_MOVING, MV_BIT_UP
5852 Ybug_e, FALSE, FALSE,
5853 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5856 Ybug_eB, FALSE, TRUE,
5857 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5860 Ybug_s, FALSE, FALSE,
5861 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5864 Ybug_sB, FALSE, TRUE,
5865 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5868 Ybug_w, FALSE, FALSE,
5869 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5872 Ybug_wB, FALSE, TRUE,
5873 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5876 Ybug_w_n, FALSE, FALSE,
5877 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5880 Ybug_n_e, FALSE, FALSE,
5881 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5884 Ybug_e_s, FALSE, FALSE,
5885 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5888 Ybug_s_w, FALSE, FALSE,
5889 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5892 Ybug_e_n, FALSE, FALSE,
5893 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5896 Ybug_s_e, FALSE, FALSE,
5897 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5900 Ybug_w_s, FALSE, FALSE,
5901 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5904 Ybug_n_w, FALSE, FALSE,
5905 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5908 Ybug_stone, FALSE, FALSE,
5909 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5912 Ybug_spring, FALSE, FALSE,
5913 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5916 Xtank_n, TRUE, FALSE,
5917 EL_SPACESHIP_UP, -1, -1
5920 Xtank_e, TRUE, FALSE,
5921 EL_SPACESHIP_RIGHT, -1, -1
5924 Xtank_s, TRUE, FALSE,
5925 EL_SPACESHIP_DOWN, -1, -1
5928 Xtank_w, TRUE, FALSE,
5929 EL_SPACESHIP_LEFT, -1, -1
5932 Xtank_gon, FALSE, FALSE,
5933 EL_SPACESHIP_UP, -1, -1
5936 Xtank_goe, FALSE, FALSE,
5937 EL_SPACESHIP_RIGHT, -1, -1
5940 Xtank_gos, FALSE, FALSE,
5941 EL_SPACESHIP_DOWN, -1, -1
5944 Xtank_gow, FALSE, FALSE,
5945 EL_SPACESHIP_LEFT, -1, -1
5948 Ytank_n, FALSE, FALSE,
5949 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5952 Ytank_nB, FALSE, TRUE,
5953 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5956 Ytank_e, FALSE, FALSE,
5957 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5960 Ytank_eB, FALSE, TRUE,
5961 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5964 Ytank_s, FALSE, FALSE,
5965 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5968 Ytank_sB, FALSE, TRUE,
5969 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5972 Ytank_w, FALSE, FALSE,
5973 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5976 Ytank_wB, FALSE, TRUE,
5977 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5980 Ytank_w_n, FALSE, FALSE,
5981 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5984 Ytank_n_e, FALSE, FALSE,
5985 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5988 Ytank_e_s, FALSE, FALSE,
5989 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5992 Ytank_s_w, FALSE, FALSE,
5993 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5996 Ytank_e_n, FALSE, FALSE,
5997 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
6000 Ytank_s_e, FALSE, FALSE,
6001 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
6004 Ytank_w_s, FALSE, FALSE,
6005 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
6008 Ytank_n_w, FALSE, FALSE,
6009 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
6012 Ytank_stone, FALSE, FALSE,
6013 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
6016 Ytank_spring, FALSE, FALSE,
6017 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
6020 Xandroid, TRUE, FALSE,
6021 EL_EMC_ANDROID, ACTION_ACTIVE, -1
6024 Xandroid_1_n, FALSE, FALSE,
6025 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6028 Xandroid_2_n, FALSE, FALSE,
6029 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
6032 Xandroid_1_e, FALSE, FALSE,
6033 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6036 Xandroid_2_e, FALSE, FALSE,
6037 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
6040 Xandroid_1_w, FALSE, FALSE,
6041 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6044 Xandroid_2_w, FALSE, FALSE,
6045 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
6048 Xandroid_1_s, FALSE, FALSE,
6049 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6052 Xandroid_2_s, FALSE, FALSE,
6053 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
6056 Yandroid_n, FALSE, FALSE,
6057 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6060 Yandroid_nB, FALSE, TRUE,
6061 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
6064 Yandroid_ne, FALSE, FALSE,
6065 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
6068 Yandroid_neB, FALSE, TRUE,
6069 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
6072 Yandroid_e, FALSE, FALSE,
6073 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6076 Yandroid_eB, FALSE, TRUE,
6077 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
6080 Yandroid_se, FALSE, FALSE,
6081 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
6084 Yandroid_seB, FALSE, TRUE,
6085 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
6088 Yandroid_s, FALSE, FALSE,
6089 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6092 Yandroid_sB, FALSE, TRUE,
6093 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
6096 Yandroid_sw, FALSE, FALSE,
6097 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
6100 Yandroid_swB, FALSE, TRUE,
6101 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
6104 Yandroid_w, FALSE, FALSE,
6105 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6108 Yandroid_wB, FALSE, TRUE,
6109 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
6112 Yandroid_nw, FALSE, FALSE,
6113 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
6116 Yandroid_nwB, FALSE, TRUE,
6117 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
6120 Xspring, TRUE, FALSE,
6124 Xspring_pause, FALSE, FALSE,
6128 Xspring_e, FALSE, FALSE,
6132 Xspring_w, FALSE, FALSE,
6136 Xspring_fall, FALSE, FALSE,
6140 Yspring_s, FALSE, FALSE,
6141 EL_SPRING, ACTION_FALLING, -1
6144 Yspring_sB, FALSE, TRUE,
6145 EL_SPRING, ACTION_FALLING, -1
6148 Yspring_e, FALSE, FALSE,
6149 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6152 Yspring_eB, FALSE, TRUE,
6153 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
6156 Yspring_w, FALSE, FALSE,
6157 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6160 Yspring_wB, FALSE, TRUE,
6161 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
6164 Yspring_kill_e, FALSE, FALSE,
6165 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6168 Yspring_kill_eB, FALSE, TRUE,
6169 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
6172 Yspring_kill_w, FALSE, FALSE,
6173 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6176 Yspring_kill_wB, FALSE, TRUE,
6177 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
6180 Xeater_n, TRUE, FALSE,
6181 EL_YAMYAM_UP, -1, -1
6184 Xeater_e, TRUE, FALSE,
6185 EL_YAMYAM_RIGHT, -1, -1
6188 Xeater_w, TRUE, FALSE,
6189 EL_YAMYAM_LEFT, -1, -1
6192 Xeater_s, TRUE, FALSE,
6193 EL_YAMYAM_DOWN, -1, -1
6196 Yeater_n, FALSE, FALSE,
6197 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6200 Yeater_nB, FALSE, TRUE,
6201 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
6204 Yeater_e, FALSE, FALSE,
6205 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6208 Yeater_eB, FALSE, TRUE,
6209 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
6212 Yeater_s, FALSE, FALSE,
6213 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6216 Yeater_sB, FALSE, TRUE,
6217 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
6220 Yeater_w, FALSE, FALSE,
6221 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6224 Yeater_wB, FALSE, TRUE,
6225 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
6228 Yeater_stone, FALSE, FALSE,
6229 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
6232 Yeater_spring, FALSE, FALSE,
6233 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
6236 Xalien, TRUE, FALSE,
6240 Xalien_pause, FALSE, FALSE,
6244 Yalien_n, FALSE, FALSE,
6245 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6248 Yalien_nB, FALSE, TRUE,
6249 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
6252 Yalien_e, FALSE, FALSE,
6253 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6256 Yalien_eB, FALSE, TRUE,
6257 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
6260 Yalien_s, FALSE, FALSE,
6261 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6264 Yalien_sB, FALSE, TRUE,
6265 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
6268 Yalien_w, FALSE, FALSE,
6269 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6272 Yalien_wB, FALSE, TRUE,
6273 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
6276 Yalien_stone, FALSE, FALSE,
6277 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
6280 Yalien_spring, FALSE, FALSE,
6281 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
6284 Xemerald, TRUE, FALSE,
6288 Xemerald_pause, FALSE, FALSE,
6292 Xemerald_fall, FALSE, FALSE,
6296 Xemerald_shine, FALSE, FALSE,
6297 EL_EMERALD, ACTION_TWINKLING, -1
6300 Yemerald_s, FALSE, FALSE,
6301 EL_EMERALD, ACTION_FALLING, -1
6304 Yemerald_sB, FALSE, TRUE,
6305 EL_EMERALD, ACTION_FALLING, -1
6308 Yemerald_e, FALSE, FALSE,
6309 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6312 Yemerald_eB, FALSE, TRUE,
6313 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
6316 Yemerald_w, FALSE, FALSE,
6317 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6320 Yemerald_wB, FALSE, TRUE,
6321 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
6324 Yemerald_eat, FALSE, FALSE,
6325 EL_EMERALD, ACTION_COLLECTING, -1
6328 Yemerald_stone, FALSE, FALSE,
6329 EL_NUT, ACTION_BREAKING, -1
6332 Xdiamond, TRUE, FALSE,
6336 Xdiamond_pause, FALSE, FALSE,
6340 Xdiamond_fall, FALSE, FALSE,
6344 Xdiamond_shine, FALSE, FALSE,
6345 EL_DIAMOND, ACTION_TWINKLING, -1
6348 Ydiamond_s, FALSE, FALSE,
6349 EL_DIAMOND, ACTION_FALLING, -1
6352 Ydiamond_sB, FALSE, TRUE,
6353 EL_DIAMOND, ACTION_FALLING, -1
6356 Ydiamond_e, FALSE, FALSE,
6357 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6360 Ydiamond_eB, FALSE, TRUE,
6361 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
6364 Ydiamond_w, FALSE, FALSE,
6365 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6368 Ydiamond_wB, FALSE, TRUE,
6369 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
6372 Ydiamond_eat, FALSE, FALSE,
6373 EL_DIAMOND, ACTION_COLLECTING, -1
6376 Ydiamond_stone, FALSE, FALSE,
6377 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6380 Xdrip_fall, TRUE, FALSE,
6381 EL_AMOEBA_DROP, -1, -1
6384 Xdrip_stretch, FALSE, FALSE,
6385 EL_AMOEBA_DROP, ACTION_FALLING, -1
6388 Xdrip_stretchB, FALSE, TRUE,
6389 EL_AMOEBA_DROP, ACTION_FALLING, -1
6392 Xdrip_eat, FALSE, FALSE,
6393 EL_AMOEBA_DROP, ACTION_GROWING, -1
6396 Ydrip_s1, FALSE, FALSE,
6397 EL_AMOEBA_DROP, ACTION_FALLING, -1
6400 Ydrip_s1B, FALSE, TRUE,
6401 EL_AMOEBA_DROP, ACTION_FALLING, -1
6404 Ydrip_s2, FALSE, FALSE,
6405 EL_AMOEBA_DROP, ACTION_FALLING, -1
6408 Ydrip_s2B, FALSE, TRUE,
6409 EL_AMOEBA_DROP, ACTION_FALLING, -1
6416 Xbomb_pause, FALSE, FALSE,
6420 Xbomb_fall, FALSE, FALSE,
6424 Ybomb_s, FALSE, FALSE,
6425 EL_BOMB, ACTION_FALLING, -1
6428 Ybomb_sB, FALSE, TRUE,
6429 EL_BOMB, ACTION_FALLING, -1
6432 Ybomb_e, FALSE, FALSE,
6433 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6436 Ybomb_eB, FALSE, TRUE,
6437 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6440 Ybomb_w, FALSE, FALSE,
6441 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6444 Ybomb_wB, FALSE, TRUE,
6445 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6448 Ybomb_eat, FALSE, FALSE,
6449 EL_BOMB, ACTION_ACTIVATING, -1
6452 Xballoon, TRUE, FALSE,
6456 Yballoon_n, FALSE, FALSE,
6457 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6460 Yballoon_nB, FALSE, TRUE,
6461 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6464 Yballoon_e, FALSE, FALSE,
6465 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6468 Yballoon_eB, FALSE, TRUE,
6469 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6472 Yballoon_s, FALSE, FALSE,
6473 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6476 Yballoon_sB, FALSE, TRUE,
6477 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6480 Yballoon_w, FALSE, FALSE,
6481 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6484 Yballoon_wB, FALSE, TRUE,
6485 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6488 Xgrass, TRUE, FALSE,
6489 EL_EMC_GRASS, -1, -1
6492 Ygrass_nB, FALSE, FALSE,
6493 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6496 Ygrass_eB, FALSE, FALSE,
6497 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6500 Ygrass_sB, FALSE, FALSE,
6501 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6504 Ygrass_wB, FALSE, FALSE,
6505 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6512 Ydirt_nB, FALSE, FALSE,
6513 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6516 Ydirt_eB, FALSE, FALSE,
6517 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6520 Ydirt_sB, FALSE, FALSE,
6521 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6524 Ydirt_wB, FALSE, FALSE,
6525 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6528 Xacid_ne, TRUE, FALSE,
6529 EL_ACID_POOL_TOPRIGHT, -1, -1
6532 Xacid_se, TRUE, FALSE,
6533 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6536 Xacid_s, TRUE, FALSE,
6537 EL_ACID_POOL_BOTTOM, -1, -1
6540 Xacid_sw, TRUE, FALSE,
6541 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6544 Xacid_nw, TRUE, FALSE,
6545 EL_ACID_POOL_TOPLEFT, -1, -1
6548 Xacid_1, TRUE, FALSE,
6552 Xacid_2, FALSE, FALSE,
6556 Xacid_3, FALSE, FALSE,
6560 Xacid_4, FALSE, FALSE,
6564 Xacid_5, FALSE, FALSE,
6568 Xacid_6, FALSE, FALSE,
6572 Xacid_7, FALSE, FALSE,
6576 Xacid_8, FALSE, FALSE,
6580 Xball_1, TRUE, FALSE,
6581 EL_EMC_MAGIC_BALL, -1, -1
6584 Xball_1B, FALSE, FALSE,
6585 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6588 Xball_2, FALSE, FALSE,
6589 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6592 Xball_2B, FALSE, FALSE,
6593 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6596 Yball_eat, FALSE, FALSE,
6597 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6600 Ykey_1_eat, FALSE, FALSE,
6601 EL_EM_KEY_1, ACTION_COLLECTING, -1
6604 Ykey_2_eat, FALSE, FALSE,
6605 EL_EM_KEY_2, ACTION_COLLECTING, -1
6608 Ykey_3_eat, FALSE, FALSE,
6609 EL_EM_KEY_3, ACTION_COLLECTING, -1
6612 Ykey_4_eat, FALSE, FALSE,
6613 EL_EM_KEY_4, ACTION_COLLECTING, -1
6616 Ykey_5_eat, FALSE, FALSE,
6617 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6620 Ykey_6_eat, FALSE, FALSE,
6621 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6624 Ykey_7_eat, FALSE, FALSE,
6625 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6628 Ykey_8_eat, FALSE, FALSE,
6629 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6632 Ylenses_eat, FALSE, FALSE,
6633 EL_EMC_LENSES, ACTION_COLLECTING, -1
6636 Ymagnify_eat, FALSE, FALSE,
6637 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6640 Ygrass_eat, FALSE, FALSE,
6641 EL_EMC_GRASS, ACTION_SNAPPING, -1
6644 Ydirt_eat, FALSE, FALSE,
6645 EL_SAND, ACTION_SNAPPING, -1
6648 Xgrow_ns, TRUE, FALSE,
6649 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6652 Ygrow_ns_eat, FALSE, FALSE,
6653 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6656 Xgrow_ew, TRUE, FALSE,
6657 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6660 Ygrow_ew_eat, FALSE, FALSE,
6661 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6664 Xwonderwall, TRUE, FALSE,
6665 EL_MAGIC_WALL, -1, -1
6668 XwonderwallB, FALSE, FALSE,
6669 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6672 Xamoeba_1, TRUE, FALSE,
6673 EL_AMOEBA_DRY, ACTION_OTHER, -1
6676 Xamoeba_2, FALSE, FALSE,
6677 EL_AMOEBA_DRY, ACTION_OTHER, -1
6680 Xamoeba_3, FALSE, FALSE,
6681 EL_AMOEBA_DRY, ACTION_OTHER, -1
6684 Xamoeba_4, FALSE, FALSE,
6685 EL_AMOEBA_DRY, ACTION_OTHER, -1
6688 Xamoeba_5, TRUE, FALSE,
6689 EL_AMOEBA_WET, ACTION_OTHER, -1
6692 Xamoeba_6, FALSE, FALSE,
6693 EL_AMOEBA_WET, ACTION_OTHER, -1
6696 Xamoeba_7, FALSE, FALSE,
6697 EL_AMOEBA_WET, ACTION_OTHER, -1
6700 Xamoeba_8, FALSE, FALSE,
6701 EL_AMOEBA_WET, ACTION_OTHER, -1
6704 Xdoor_1, TRUE, FALSE,
6705 EL_EM_GATE_1, -1, -1
6708 Xdoor_2, TRUE, FALSE,
6709 EL_EM_GATE_2, -1, -1
6712 Xdoor_3, TRUE, FALSE,
6713 EL_EM_GATE_3, -1, -1
6716 Xdoor_4, TRUE, FALSE,
6717 EL_EM_GATE_4, -1, -1
6720 Xdoor_5, TRUE, FALSE,
6721 EL_EMC_GATE_5, -1, -1
6724 Xdoor_6, TRUE, FALSE,
6725 EL_EMC_GATE_6, -1, -1
6728 Xdoor_7, TRUE, FALSE,
6729 EL_EMC_GATE_7, -1, -1
6732 Xdoor_8, TRUE, FALSE,
6733 EL_EMC_GATE_8, -1, -1
6736 Xkey_1, TRUE, FALSE,
6740 Xkey_2, TRUE, FALSE,
6744 Xkey_3, TRUE, FALSE,
6748 Xkey_4, TRUE, FALSE,
6752 Xkey_5, TRUE, FALSE,
6753 EL_EMC_KEY_5, -1, -1
6756 Xkey_6, TRUE, FALSE,
6757 EL_EMC_KEY_6, -1, -1
6760 Xkey_7, TRUE, FALSE,
6761 EL_EMC_KEY_7, -1, -1
6764 Xkey_8, TRUE, FALSE,
6765 EL_EMC_KEY_8, -1, -1
6768 Xwind_n, TRUE, FALSE,
6769 EL_BALLOON_SWITCH_UP, -1, -1
6772 Xwind_e, TRUE, FALSE,
6773 EL_BALLOON_SWITCH_RIGHT, -1, -1
6776 Xwind_s, TRUE, FALSE,
6777 EL_BALLOON_SWITCH_DOWN, -1, -1
6780 Xwind_w, TRUE, FALSE,
6781 EL_BALLOON_SWITCH_LEFT, -1, -1
6784 Xwind_nesw, TRUE, FALSE,
6785 EL_BALLOON_SWITCH_ANY, -1, -1
6788 Xwind_stop, TRUE, FALSE,
6789 EL_BALLOON_SWITCH_NONE, -1, -1
6793 EL_EM_EXIT_CLOSED, -1, -1
6796 Xexit_1, TRUE, FALSE,
6797 EL_EM_EXIT_OPEN, -1, -1
6800 Xexit_2, FALSE, FALSE,
6801 EL_EM_EXIT_OPEN, -1, -1
6804 Xexit_3, FALSE, FALSE,
6805 EL_EM_EXIT_OPEN, -1, -1
6808 Xdynamite, TRUE, FALSE,
6809 EL_EM_DYNAMITE, -1, -1
6812 Ydynamite_eat, FALSE, FALSE,
6813 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6816 Xdynamite_1, TRUE, FALSE,
6817 EL_EM_DYNAMITE_ACTIVE, -1, -1
6820 Xdynamite_2, FALSE, FALSE,
6821 EL_EM_DYNAMITE_ACTIVE, -1, -1
6824 Xdynamite_3, FALSE, FALSE,
6825 EL_EM_DYNAMITE_ACTIVE, -1, -1
6828 Xdynamite_4, FALSE, FALSE,
6829 EL_EM_DYNAMITE_ACTIVE, -1, -1
6832 Xbumper, TRUE, FALSE,
6833 EL_EMC_SPRING_BUMPER, -1, -1
6836 XbumperB, FALSE, FALSE,
6837 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6840 Xwheel, TRUE, FALSE,
6841 EL_ROBOT_WHEEL, -1, -1
6844 XwheelB, FALSE, FALSE,
6845 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6848 Xswitch, TRUE, FALSE,
6849 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6852 XswitchB, FALSE, FALSE,
6853 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6857 EL_QUICKSAND_EMPTY, -1, -1
6860 Xsand_stone, TRUE, FALSE,
6861 EL_QUICKSAND_FULL, -1, -1
6864 Xsand_stonein_1, FALSE, TRUE,
6865 EL_ROCK, ACTION_FILLING, -1
6868 Xsand_stonein_2, FALSE, TRUE,
6869 EL_ROCK, ACTION_FILLING, -1
6872 Xsand_stonein_3, FALSE, TRUE,
6873 EL_ROCK, ACTION_FILLING, -1
6876 Xsand_stonein_4, FALSE, TRUE,
6877 EL_ROCK, ACTION_FILLING, -1
6880 Xsand_stonesand_1, FALSE, FALSE,
6881 EL_QUICKSAND_EMPTYING, -1, -1
6884 Xsand_stonesand_2, FALSE, FALSE,
6885 EL_QUICKSAND_EMPTYING, -1, -1
6888 Xsand_stonesand_3, FALSE, FALSE,
6889 EL_QUICKSAND_EMPTYING, -1, -1
6892 Xsand_stonesand_4, FALSE, FALSE,
6893 EL_QUICKSAND_EMPTYING, -1, -1
6896 Xsand_stonesand_quickout_1, FALSE, FALSE,
6897 EL_QUICKSAND_EMPTYING, -1, -1
6900 Xsand_stonesand_quickout_2, FALSE, FALSE,
6901 EL_QUICKSAND_EMPTYING, -1, -1
6904 Xsand_stoneout_1, FALSE, FALSE,
6905 EL_ROCK, ACTION_EMPTYING, -1
6908 Xsand_stoneout_2, FALSE, FALSE,
6909 EL_ROCK, ACTION_EMPTYING, -1
6912 Xsand_sandstone_1, FALSE, FALSE,
6913 EL_QUICKSAND_FILLING, -1, -1
6916 Xsand_sandstone_2, FALSE, FALSE,
6917 EL_QUICKSAND_FILLING, -1, -1
6920 Xsand_sandstone_3, FALSE, FALSE,
6921 EL_QUICKSAND_FILLING, -1, -1
6924 Xsand_sandstone_4, FALSE, FALSE,
6925 EL_QUICKSAND_FILLING, -1, -1
6928 Xplant, TRUE, FALSE,
6929 EL_EMC_PLANT, -1, -1
6932 Yplant, FALSE, FALSE,
6933 EL_EMC_PLANT, -1, -1
6936 Xlenses, TRUE, FALSE,
6937 EL_EMC_LENSES, -1, -1
6940 Xmagnify, TRUE, FALSE,
6941 EL_EMC_MAGNIFIER, -1, -1
6944 Xdripper, TRUE, FALSE,
6945 EL_EMC_DRIPPER, -1, -1
6948 XdripperB, FALSE, FALSE,
6949 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6952 Xfake_blank, TRUE, FALSE,
6953 EL_INVISIBLE_WALL, -1, -1
6956 Xfake_blankB, FALSE, FALSE,
6957 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6960 Xfake_grass, TRUE, FALSE,
6961 EL_EMC_FAKE_GRASS, -1, -1
6964 Xfake_grassB, FALSE, FALSE,
6965 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6968 Xfake_door_1, TRUE, FALSE,
6969 EL_EM_GATE_1_GRAY, -1, -1
6972 Xfake_door_2, TRUE, FALSE,
6973 EL_EM_GATE_2_GRAY, -1, -1
6976 Xfake_door_3, TRUE, FALSE,
6977 EL_EM_GATE_3_GRAY, -1, -1
6980 Xfake_door_4, TRUE, FALSE,
6981 EL_EM_GATE_4_GRAY, -1, -1
6984 Xfake_door_5, TRUE, FALSE,
6985 EL_EMC_GATE_5_GRAY, -1, -1
6988 Xfake_door_6, TRUE, FALSE,
6989 EL_EMC_GATE_6_GRAY, -1, -1
6992 Xfake_door_7, TRUE, FALSE,
6993 EL_EMC_GATE_7_GRAY, -1, -1
6996 Xfake_door_8, TRUE, FALSE,
6997 EL_EMC_GATE_8_GRAY, -1, -1
7000 Xfake_acid_1, TRUE, FALSE,
7001 EL_EMC_FAKE_ACID, -1, -1
7004 Xfake_acid_2, FALSE, FALSE,
7005 EL_EMC_FAKE_ACID, -1, -1
7008 Xfake_acid_3, FALSE, FALSE,
7009 EL_EMC_FAKE_ACID, -1, -1
7012 Xfake_acid_4, FALSE, FALSE,
7013 EL_EMC_FAKE_ACID, -1, -1
7016 Xfake_acid_5, FALSE, FALSE,
7017 EL_EMC_FAKE_ACID, -1, -1
7020 Xfake_acid_6, FALSE, FALSE,
7021 EL_EMC_FAKE_ACID, -1, -1
7024 Xfake_acid_7, FALSE, FALSE,
7025 EL_EMC_FAKE_ACID, -1, -1
7028 Xfake_acid_8, FALSE, FALSE,
7029 EL_EMC_FAKE_ACID, -1, -1
7032 Xsteel_1, TRUE, FALSE,
7033 EL_STEELWALL, -1, -1
7036 Xsteel_2, TRUE, FALSE,
7037 EL_EMC_STEELWALL_2, -1, -1
7040 Xsteel_3, TRUE, FALSE,
7041 EL_EMC_STEELWALL_3, -1, -1
7044 Xsteel_4, TRUE, FALSE,
7045 EL_EMC_STEELWALL_4, -1, -1
7048 Xwall_1, TRUE, FALSE,
7052 Xwall_2, TRUE, FALSE,
7053 EL_EMC_WALL_14, -1, -1
7056 Xwall_3, TRUE, FALSE,
7057 EL_EMC_WALL_15, -1, -1
7060 Xwall_4, TRUE, FALSE,
7061 EL_EMC_WALL_16, -1, -1
7064 Xround_wall_1, TRUE, FALSE,
7065 EL_WALL_SLIPPERY, -1, -1
7068 Xround_wall_2, TRUE, FALSE,
7069 EL_EMC_WALL_SLIPPERY_2, -1, -1
7072 Xround_wall_3, TRUE, FALSE,
7073 EL_EMC_WALL_SLIPPERY_3, -1, -1
7076 Xround_wall_4, TRUE, FALSE,
7077 EL_EMC_WALL_SLIPPERY_4, -1, -1
7080 Xdecor_1, TRUE, FALSE,
7081 EL_EMC_WALL_8, -1, -1
7084 Xdecor_2, TRUE, FALSE,
7085 EL_EMC_WALL_6, -1, -1
7088 Xdecor_3, TRUE, FALSE,
7089 EL_EMC_WALL_4, -1, -1
7092 Xdecor_4, TRUE, FALSE,
7093 EL_EMC_WALL_7, -1, -1
7096 Xdecor_5, TRUE, FALSE,
7097 EL_EMC_WALL_5, -1, -1
7100 Xdecor_6, TRUE, FALSE,
7101 EL_EMC_WALL_9, -1, -1
7104 Xdecor_7, TRUE, FALSE,
7105 EL_EMC_WALL_10, -1, -1
7108 Xdecor_8, TRUE, FALSE,
7109 EL_EMC_WALL_1, -1, -1
7112 Xdecor_9, TRUE, FALSE,
7113 EL_EMC_WALL_2, -1, -1
7116 Xdecor_10, TRUE, FALSE,
7117 EL_EMC_WALL_3, -1, -1
7120 Xdecor_11, TRUE, FALSE,
7121 EL_EMC_WALL_11, -1, -1
7124 Xdecor_12, TRUE, FALSE,
7125 EL_EMC_WALL_12, -1, -1
7128 Xalpha_0, TRUE, FALSE,
7129 EL_CHAR('0'), -1, -1
7132 Xalpha_1, TRUE, FALSE,
7133 EL_CHAR('1'), -1, -1
7136 Xalpha_2, TRUE, FALSE,
7137 EL_CHAR('2'), -1, -1
7140 Xalpha_3, TRUE, FALSE,
7141 EL_CHAR('3'), -1, -1
7144 Xalpha_4, TRUE, FALSE,
7145 EL_CHAR('4'), -1, -1
7148 Xalpha_5, TRUE, FALSE,
7149 EL_CHAR('5'), -1, -1
7152 Xalpha_6, TRUE, FALSE,
7153 EL_CHAR('6'), -1, -1
7156 Xalpha_7, TRUE, FALSE,
7157 EL_CHAR('7'), -1, -1
7160 Xalpha_8, TRUE, FALSE,
7161 EL_CHAR('8'), -1, -1
7164 Xalpha_9, TRUE, FALSE,
7165 EL_CHAR('9'), -1, -1
7168 Xalpha_excla, TRUE, FALSE,
7169 EL_CHAR('!'), -1, -1
7172 Xalpha_quote, TRUE, FALSE,
7173 EL_CHAR('"'), -1, -1
7176 Xalpha_comma, TRUE, FALSE,
7177 EL_CHAR(','), -1, -1
7180 Xalpha_minus, TRUE, FALSE,
7181 EL_CHAR('-'), -1, -1
7184 Xalpha_perio, TRUE, FALSE,
7185 EL_CHAR('.'), -1, -1
7188 Xalpha_colon, TRUE, FALSE,
7189 EL_CHAR(':'), -1, -1
7192 Xalpha_quest, TRUE, FALSE,
7193 EL_CHAR('?'), -1, -1
7196 Xalpha_a, TRUE, FALSE,
7197 EL_CHAR('A'), -1, -1
7200 Xalpha_b, TRUE, FALSE,
7201 EL_CHAR('B'), -1, -1
7204 Xalpha_c, TRUE, FALSE,
7205 EL_CHAR('C'), -1, -1
7208 Xalpha_d, TRUE, FALSE,
7209 EL_CHAR('D'), -1, -1
7212 Xalpha_e, TRUE, FALSE,
7213 EL_CHAR('E'), -1, -1
7216 Xalpha_f, TRUE, FALSE,
7217 EL_CHAR('F'), -1, -1
7220 Xalpha_g, TRUE, FALSE,
7221 EL_CHAR('G'), -1, -1
7224 Xalpha_h, TRUE, FALSE,
7225 EL_CHAR('H'), -1, -1
7228 Xalpha_i, TRUE, FALSE,
7229 EL_CHAR('I'), -1, -1
7232 Xalpha_j, TRUE, FALSE,
7233 EL_CHAR('J'), -1, -1
7236 Xalpha_k, TRUE, FALSE,
7237 EL_CHAR('K'), -1, -1
7240 Xalpha_l, TRUE, FALSE,
7241 EL_CHAR('L'), -1, -1
7244 Xalpha_m, TRUE, FALSE,
7245 EL_CHAR('M'), -1, -1
7248 Xalpha_n, TRUE, FALSE,
7249 EL_CHAR('N'), -1, -1
7252 Xalpha_o, TRUE, FALSE,
7253 EL_CHAR('O'), -1, -1
7256 Xalpha_p, TRUE, FALSE,
7257 EL_CHAR('P'), -1, -1
7260 Xalpha_q, TRUE, FALSE,
7261 EL_CHAR('Q'), -1, -1
7264 Xalpha_r, TRUE, FALSE,
7265 EL_CHAR('R'), -1, -1
7268 Xalpha_s, TRUE, FALSE,
7269 EL_CHAR('S'), -1, -1
7272 Xalpha_t, TRUE, FALSE,
7273 EL_CHAR('T'), -1, -1
7276 Xalpha_u, TRUE, FALSE,
7277 EL_CHAR('U'), -1, -1
7280 Xalpha_v, TRUE, FALSE,
7281 EL_CHAR('V'), -1, -1
7284 Xalpha_w, TRUE, FALSE,
7285 EL_CHAR('W'), -1, -1
7288 Xalpha_x, TRUE, FALSE,
7289 EL_CHAR('X'), -1, -1
7292 Xalpha_y, TRUE, FALSE,
7293 EL_CHAR('Y'), -1, -1
7296 Xalpha_z, TRUE, FALSE,
7297 EL_CHAR('Z'), -1, -1
7300 Xalpha_arrow_e, TRUE, FALSE,
7301 EL_CHAR('>'), -1, -1
7304 Xalpha_arrow_w, TRUE, FALSE,
7305 EL_CHAR('<'), -1, -1
7308 Xalpha_copyr, TRUE, FALSE,
7309 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
7313 Xboom_bug, FALSE, FALSE,
7314 EL_BUG, ACTION_EXPLODING, -1
7317 Xboom_bomb, FALSE, FALSE,
7318 EL_BOMB, ACTION_EXPLODING, -1
7321 Xboom_android, FALSE, FALSE,
7322 EL_EMC_ANDROID, ACTION_OTHER, -1
7325 Xboom_1, FALSE, FALSE,
7326 EL_DEFAULT, ACTION_EXPLODING, -1
7329 Xboom_2, FALSE, FALSE,
7330 EL_DEFAULT, ACTION_EXPLODING, -1
7333 Znormal, FALSE, FALSE,
7337 Zdynamite, FALSE, FALSE,
7341 Zplayer, FALSE, FALSE,
7345 ZBORDER, FALSE, FALSE,
7355 static struct Mapping_EM_to_RND_player
7364 em_player_mapping_list[] =
7368 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
7372 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
7376 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7380 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7384 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7388 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7392 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7396 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7400 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7404 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7408 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7412 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7416 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7420 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7424 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7428 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7432 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7436 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7440 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7444 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7448 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7452 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7456 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7460 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7464 EL_PLAYER_1, ACTION_DEFAULT, -1,
7468 EL_PLAYER_2, ACTION_DEFAULT, -1,
7472 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7476 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7480 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7484 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7488 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7492 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7496 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7500 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7504 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7508 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7512 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7516 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7520 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7524 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7528 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7532 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7536 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7540 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7544 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7548 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7552 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7556 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7560 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7564 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7568 EL_PLAYER_3, ACTION_DEFAULT, -1,
7572 EL_PLAYER_4, ACTION_DEFAULT, -1,
7581 int map_element_RND_to_EM(int element_rnd)
7583 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7584 static boolean mapping_initialized = FALSE;
7586 if (!mapping_initialized)
7590 /* return "Xalpha_quest" for all undefined elements in mapping array */
7591 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7592 mapping_RND_to_EM[i] = Xalpha_quest;
7594 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7595 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7596 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7597 em_object_mapping_list[i].element_em;
7599 mapping_initialized = TRUE;
7602 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7603 return mapping_RND_to_EM[element_rnd];
7605 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7610 int map_element_EM_to_RND(int element_em)
7612 static unsigned short mapping_EM_to_RND[TILE_MAX];
7613 static boolean mapping_initialized = FALSE;
7615 if (!mapping_initialized)
7619 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7620 for (i = 0; i < TILE_MAX; i++)
7621 mapping_EM_to_RND[i] = EL_UNKNOWN;
7623 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7624 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7625 em_object_mapping_list[i].element_rnd;
7627 mapping_initialized = TRUE;
7630 if (element_em >= 0 && element_em < TILE_MAX)
7631 return mapping_EM_to_RND[element_em];
7633 Error(ERR_WARN, "invalid EM level element %d", element_em);
7638 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7640 struct LevelInfo_EM *level_em = level->native_em_level;
7641 struct LEVEL *lev = level_em->lev;
7644 for (i = 0; i < TILE_MAX; i++)
7645 lev->android_array[i] = Xblank;
7647 for (i = 0; i < level->num_android_clone_elements; i++)
7649 int element_rnd = level->android_clone_element[i];
7650 int element_em = map_element_RND_to_EM(element_rnd);
7652 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7653 if (em_object_mapping_list[j].element_rnd == element_rnd)
7654 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7658 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7660 struct LevelInfo_EM *level_em = level->native_em_level;
7661 struct LEVEL *lev = level_em->lev;
7664 level->num_android_clone_elements = 0;
7666 for (i = 0; i < TILE_MAX; i++)
7668 int element_em = lev->android_array[i];
7670 boolean element_found = FALSE;
7672 if (element_em == Xblank)
7675 element_rnd = map_element_EM_to_RND(element_em);
7677 for (j = 0; j < level->num_android_clone_elements; j++)
7678 if (level->android_clone_element[j] == element_rnd)
7679 element_found = TRUE;
7683 level->android_clone_element[level->num_android_clone_elements++] =
7686 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7691 if (level->num_android_clone_elements == 0)
7693 level->num_android_clone_elements = 1;
7694 level->android_clone_element[0] = EL_EMPTY;
7698 int map_direction_RND_to_EM(int direction)
7700 return (direction == MV_UP ? 0 :
7701 direction == MV_RIGHT ? 1 :
7702 direction == MV_DOWN ? 2 :
7703 direction == MV_LEFT ? 3 :
7707 int map_direction_EM_to_RND(int direction)
7709 return (direction == 0 ? MV_UP :
7710 direction == 1 ? MV_RIGHT :
7711 direction == 2 ? MV_DOWN :
7712 direction == 3 ? MV_LEFT :
7716 int map_element_RND_to_SP(int element_rnd)
7718 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7720 if (element_rnd >= EL_SP_START &&
7721 element_rnd <= EL_SP_END)
7722 element_sp = element_rnd - EL_SP_START;
7723 else if (element_rnd == EL_EMPTY_SPACE)
7725 else if (element_rnd == EL_INVISIBLE_WALL)
7731 int map_element_SP_to_RND(int element_sp)
7733 int element_rnd = EL_UNKNOWN;
7735 if (element_sp >= 0x00 &&
7737 element_rnd = EL_SP_START + element_sp;
7738 else if (element_sp == 0x28)
7739 element_rnd = EL_INVISIBLE_WALL;
7744 int map_action_SP_to_RND(int action_sp)
7748 case actActive: return ACTION_ACTIVE;
7749 case actImpact: return ACTION_IMPACT;
7750 case actExploding: return ACTION_EXPLODING;
7751 case actDigging: return ACTION_DIGGING;
7752 case actSnapping: return ACTION_SNAPPING;
7753 case actCollecting: return ACTION_COLLECTING;
7754 case actPassing: return ACTION_PASSING;
7755 case actPushing: return ACTION_PUSHING;
7756 case actDropping: return ACTION_DROPPING;
7758 default: return ACTION_DEFAULT;
7762 int map_element_RND_to_MM(int element_rnd)
7764 return (element_rnd >= EL_MM_START_1 &&
7765 element_rnd <= EL_MM_END_1 ?
7766 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7768 element_rnd >= EL_MM_START_2 &&
7769 element_rnd <= EL_MM_END_2 ?
7770 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7772 element_rnd >= EL_CHAR_START &&
7773 element_rnd <= EL_CHAR_END ?
7774 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7776 element_rnd >= EL_MM_RUNTIME_START &&
7777 element_rnd <= EL_MM_RUNTIME_END ?
7778 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7780 element_rnd >= EL_MM_DUMMY_START &&
7781 element_rnd <= EL_MM_DUMMY_END ?
7782 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7784 EL_MM_EMPTY_NATIVE);
7787 int map_element_MM_to_RND(int element_mm)
7789 return (element_mm == EL_MM_EMPTY_NATIVE ||
7790 element_mm == EL_DF_EMPTY_NATIVE ?
7793 element_mm >= EL_MM_START_1_NATIVE &&
7794 element_mm <= EL_MM_END_1_NATIVE ?
7795 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7797 element_mm >= EL_MM_START_2_NATIVE &&
7798 element_mm <= EL_MM_END_2_NATIVE ?
7799 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7801 element_mm >= EL_MM_CHAR_START_NATIVE &&
7802 element_mm <= EL_MM_CHAR_END_NATIVE ?
7803 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7805 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7806 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7807 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7809 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7810 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7811 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7816 int map_action_MM_to_RND(int action_mm)
7818 /* all MM actions are defined to exactly match their RND counterparts */
7822 int map_sound_MM_to_RND(int sound_mm)
7826 case SND_MM_GAME_LEVELTIME_CHARGING:
7827 return SND_GAME_LEVELTIME_CHARGING;
7829 case SND_MM_GAME_HEALTH_CHARGING:
7830 return SND_GAME_HEALTH_CHARGING;
7833 return SND_UNDEFINED;
7837 int map_mm_wall_element(int element)
7839 return (element >= EL_MM_STEEL_WALL_START &&
7840 element <= EL_MM_STEEL_WALL_END ?
7843 element >= EL_MM_WOODEN_WALL_START &&
7844 element <= EL_MM_WOODEN_WALL_END ?
7847 element >= EL_MM_ICE_WALL_START &&
7848 element <= EL_MM_ICE_WALL_END ?
7851 element >= EL_MM_AMOEBA_WALL_START &&
7852 element <= EL_MM_AMOEBA_WALL_END ?
7855 element >= EL_DF_STEEL_WALL_START &&
7856 element <= EL_DF_STEEL_WALL_END ?
7859 element >= EL_DF_WOODEN_WALL_START &&
7860 element <= EL_DF_WOODEN_WALL_END ?
7866 int map_mm_wall_element_editor(int element)
7870 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7871 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7872 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7873 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7874 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7875 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7877 default: return element;
7881 int get_next_element(int element)
7885 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7886 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7887 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7888 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7889 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7890 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7891 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7892 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7893 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7894 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7895 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7897 default: return element;
7901 int el2img_mm(int element_mm)
7903 return el2img(map_element_MM_to_RND(element_mm));
7906 int el_act_dir2img(int element, int action, int direction)
7908 element = GFX_ELEMENT(element);
7909 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7911 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7912 return element_info[element].direction_graphic[action][direction];
7915 static int el_act_dir2crm(int element, int action, int direction)
7917 element = GFX_ELEMENT(element);
7918 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7920 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7921 return element_info[element].direction_crumbled[action][direction];
7924 int el_act2img(int element, int action)
7926 element = GFX_ELEMENT(element);
7928 return element_info[element].graphic[action];
7931 int el_act2crm(int element, int action)
7933 element = GFX_ELEMENT(element);
7935 return element_info[element].crumbled[action];
7938 int el_dir2img(int element, int direction)
7940 element = GFX_ELEMENT(element);
7942 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7945 int el2baseimg(int element)
7947 return element_info[element].graphic[ACTION_DEFAULT];
7950 int el2img(int element)
7952 element = GFX_ELEMENT(element);
7954 return element_info[element].graphic[ACTION_DEFAULT];
7957 int el2edimg(int element)
7959 element = GFX_ELEMENT(element);
7961 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7964 int el2preimg(int element)
7966 element = GFX_ELEMENT(element);
7968 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7971 int el2panelimg(int element)
7973 element = GFX_ELEMENT(element);
7975 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7978 int font2baseimg(int font_nr)
7980 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7983 int getBeltNrFromBeltElement(int element)
7985 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7986 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7987 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7990 int getBeltNrFromBeltActiveElement(int element)
7992 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7993 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7994 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7997 int getBeltNrFromBeltSwitchElement(int element)
7999 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
8000 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
8001 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
8004 int getBeltDirNrFromBeltElement(int element)
8006 static int belt_base_element[4] =
8008 EL_CONVEYOR_BELT_1_LEFT,
8009 EL_CONVEYOR_BELT_2_LEFT,
8010 EL_CONVEYOR_BELT_3_LEFT,
8011 EL_CONVEYOR_BELT_4_LEFT
8014 int belt_nr = getBeltNrFromBeltElement(element);
8015 int belt_dir_nr = element - belt_base_element[belt_nr];
8017 return (belt_dir_nr % 3);
8020 int getBeltDirNrFromBeltSwitchElement(int element)
8022 static int belt_base_element[4] =
8024 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8025 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8026 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8027 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8030 int belt_nr = getBeltNrFromBeltSwitchElement(element);
8031 int belt_dir_nr = element - belt_base_element[belt_nr];
8033 return (belt_dir_nr % 3);
8036 int getBeltDirFromBeltElement(int element)
8038 static int belt_move_dir[3] =
8045 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
8047 return belt_move_dir[belt_dir_nr];
8050 int getBeltDirFromBeltSwitchElement(int element)
8052 static int belt_move_dir[3] =
8059 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
8061 return belt_move_dir[belt_dir_nr];
8064 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8066 static int belt_base_element[4] =
8068 EL_CONVEYOR_BELT_1_LEFT,
8069 EL_CONVEYOR_BELT_2_LEFT,
8070 EL_CONVEYOR_BELT_3_LEFT,
8071 EL_CONVEYOR_BELT_4_LEFT
8074 return belt_base_element[belt_nr] + belt_dir_nr;
8077 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8079 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8081 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8084 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
8086 static int belt_base_element[4] =
8088 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
8089 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
8090 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
8091 EL_CONVEYOR_BELT_4_SWITCH_LEFT
8094 return belt_base_element[belt_nr] + belt_dir_nr;
8097 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
8099 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
8101 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
8104 boolean getTeamMode_EM()
8106 return game.team_mode;
8109 int getGameFrameDelay_EM(int native_em_game_frame_delay)
8111 int game_frame_delay_value;
8113 game_frame_delay_value =
8114 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
8115 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
8118 if (tape.playing && tape.warp_forward && !tape.pausing)
8119 game_frame_delay_value = 0;
8121 return game_frame_delay_value;
8124 unsigned int InitRND(int seed)
8126 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
8127 return InitEngineRandom_EM(seed);
8128 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
8129 return InitEngineRandom_SP(seed);
8130 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
8131 return InitEngineRandom_MM(seed);
8133 return InitEngineRandom_RND(seed);
8136 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
8137 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
8139 inline static int get_effective_element_EM(int tile, int frame_em)
8141 int element = object_mapping[tile].element_rnd;
8142 int action = object_mapping[tile].action;
8143 boolean is_backside = object_mapping[tile].is_backside;
8144 boolean action_removing = (action == ACTION_DIGGING ||
8145 action == ACTION_SNAPPING ||
8146 action == ACTION_COLLECTING);
8152 case Yacid_splash_eB:
8153 case Yacid_splash_wB:
8154 return (frame_em > 5 ? EL_EMPTY : element);
8160 else /* frame_em == 7 */
8164 case Yacid_splash_eB:
8165 case Yacid_splash_wB:
8168 case Yemerald_stone:
8171 case Ydiamond_stone:
8175 case Xdrip_stretchB:
8194 case Xsand_stonein_1:
8195 case Xsand_stonein_2:
8196 case Xsand_stonein_3:
8197 case Xsand_stonein_4:
8201 return (is_backside || action_removing ? EL_EMPTY : element);
8206 inline static boolean check_linear_animation_EM(int tile)
8210 case Xsand_stonesand_1:
8211 case Xsand_stonesand_quickout_1:
8212 case Xsand_sandstone_1:
8213 case Xsand_stonein_1:
8214 case Xsand_stoneout_1:
8233 case Yacid_splash_eB:
8234 case Yacid_splash_wB:
8235 case Yemerald_stone:
8242 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
8243 boolean has_crumbled_graphics,
8244 int crumbled, int sync_frame)
8246 /* if element can be crumbled, but certain action graphics are just empty
8247 space (like instantly snapping sand to empty space in 1 frame), do not
8248 treat these empty space graphics as crumbled graphics in EMC engine */
8249 if (crumbled == IMG_EMPTY_SPACE)
8250 has_crumbled_graphics = FALSE;
8252 if (has_crumbled_graphics)
8254 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
8255 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
8256 g_crumbled->anim_delay,
8257 g_crumbled->anim_mode,
8258 g_crumbled->anim_start_frame,
8261 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
8262 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
8264 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
8265 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
8267 g_em->has_crumbled_graphics = TRUE;
8271 g_em->crumbled_bitmap = NULL;
8272 g_em->crumbled_src_x = 0;
8273 g_em->crumbled_src_y = 0;
8274 g_em->crumbled_border_size = 0;
8275 g_em->crumbled_tile_size = 0;
8277 g_em->has_crumbled_graphics = FALSE;
8281 void ResetGfxAnimation_EM(int x, int y, int tile)
8286 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
8287 int tile, int frame_em, int x, int y)
8289 int action = object_mapping[tile].action;
8290 int direction = object_mapping[tile].direction;
8291 int effective_element = get_effective_element_EM(tile, frame_em);
8292 int graphic = (direction == MV_NONE ?
8293 el_act2img(effective_element, action) :
8294 el_act_dir2img(effective_element, action, direction));
8295 struct GraphicInfo *g = &graphic_info[graphic];
8297 boolean action_removing = (action == ACTION_DIGGING ||
8298 action == ACTION_SNAPPING ||
8299 action == ACTION_COLLECTING);
8300 boolean action_moving = (action == ACTION_FALLING ||
8301 action == ACTION_MOVING ||
8302 action == ACTION_PUSHING ||
8303 action == ACTION_EATING ||
8304 action == ACTION_FILLING ||
8305 action == ACTION_EMPTYING);
8306 boolean action_falling = (action == ACTION_FALLING ||
8307 action == ACTION_FILLING ||
8308 action == ACTION_EMPTYING);
8310 /* special case: graphic uses "2nd movement tile" and has defined
8311 7 frames for movement animation (or less) => use default graphic
8312 for last (8th) frame which ends the movement animation */
8313 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8315 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
8316 graphic = (direction == MV_NONE ?
8317 el_act2img(effective_element, action) :
8318 el_act_dir2img(effective_element, action, direction));
8320 g = &graphic_info[graphic];
8323 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
8327 else if (action_moving)
8329 boolean is_backside = object_mapping[tile].is_backside;
8333 int direction = object_mapping[tile].direction;
8334 int move_dir = (action_falling ? MV_DOWN : direction);
8339 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
8340 if (g->double_movement && frame_em == 0)
8344 if (move_dir == MV_LEFT)
8345 GfxFrame[x - 1][y] = GfxFrame[x][y];
8346 else if (move_dir == MV_RIGHT)
8347 GfxFrame[x + 1][y] = GfxFrame[x][y];
8348 else if (move_dir == MV_UP)
8349 GfxFrame[x][y - 1] = GfxFrame[x][y];
8350 else if (move_dir == MV_DOWN)
8351 GfxFrame[x][y + 1] = GfxFrame[x][y];
8358 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
8359 if (tile == Xsand_stonesand_quickout_1 ||
8360 tile == Xsand_stonesand_quickout_2)
8364 if (graphic_info[graphic].anim_global_sync)
8365 sync_frame = FrameCounter;
8366 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8367 sync_frame = GfxFrame[x][y];
8369 sync_frame = 0; /* playfield border (pseudo steel) */
8371 SetRandomAnimationValue(x, y);
8373 int frame = getAnimationFrame(g->anim_frames,
8376 g->anim_start_frame,
8379 g_em->unique_identifier =
8380 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8383 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8384 int tile, int frame_em, int x, int y)
8386 int action = object_mapping[tile].action;
8387 int direction = object_mapping[tile].direction;
8388 boolean is_backside = object_mapping[tile].is_backside;
8389 int effective_element = get_effective_element_EM(tile, frame_em);
8390 int effective_action = action;
8391 int graphic = (direction == MV_NONE ?
8392 el_act2img(effective_element, effective_action) :
8393 el_act_dir2img(effective_element, effective_action,
8395 int crumbled = (direction == MV_NONE ?
8396 el_act2crm(effective_element, effective_action) :
8397 el_act_dir2crm(effective_element, effective_action,
8399 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8400 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8401 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8402 struct GraphicInfo *g = &graphic_info[graphic];
8405 /* special case: graphic uses "2nd movement tile" and has defined
8406 7 frames for movement animation (or less) => use default graphic
8407 for last (8th) frame which ends the movement animation */
8408 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8410 effective_action = ACTION_DEFAULT;
8411 graphic = (direction == MV_NONE ?
8412 el_act2img(effective_element, effective_action) :
8413 el_act_dir2img(effective_element, effective_action,
8415 crumbled = (direction == MV_NONE ?
8416 el_act2crm(effective_element, effective_action) :
8417 el_act_dir2crm(effective_element, effective_action,
8420 g = &graphic_info[graphic];
8423 if (graphic_info[graphic].anim_global_sync)
8424 sync_frame = FrameCounter;
8425 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8426 sync_frame = GfxFrame[x][y];
8428 sync_frame = 0; /* playfield border (pseudo steel) */
8430 SetRandomAnimationValue(x, y);
8432 int frame = getAnimationFrame(g->anim_frames,
8435 g->anim_start_frame,
8438 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8439 g->double_movement && is_backside);
8441 /* (updating the "crumbled" graphic definitions is probably not really needed,
8442 as animations for crumbled graphics can't be longer than one EMC cycle) */
8443 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8447 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8448 int player_nr, int anim, int frame_em)
8450 int element = player_mapping[player_nr][anim].element_rnd;
8451 int action = player_mapping[player_nr][anim].action;
8452 int direction = player_mapping[player_nr][anim].direction;
8453 int graphic = (direction == MV_NONE ?
8454 el_act2img(element, action) :
8455 el_act_dir2img(element, action, direction));
8456 struct GraphicInfo *g = &graphic_info[graphic];
8459 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8461 stored_player[player_nr].StepFrame = frame_em;
8463 sync_frame = stored_player[player_nr].Frame;
8465 int frame = getAnimationFrame(g->anim_frames,
8468 g->anim_start_frame,
8471 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8472 &g_em->src_x, &g_em->src_y, FALSE);
8475 void InitGraphicInfo_EM(void)
8480 int num_em_gfx_errors = 0;
8482 if (graphic_info_em_object[0][0].bitmap == NULL)
8484 /* EM graphics not yet initialized in em_open_all() */
8489 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8492 /* always start with reliable default values */
8493 for (i = 0; i < TILE_MAX; i++)
8495 object_mapping[i].element_rnd = EL_UNKNOWN;
8496 object_mapping[i].is_backside = FALSE;
8497 object_mapping[i].action = ACTION_DEFAULT;
8498 object_mapping[i].direction = MV_NONE;
8501 /* always start with reliable default values */
8502 for (p = 0; p < MAX_PLAYERS; p++)
8504 for (i = 0; i < SPR_MAX; i++)
8506 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8507 player_mapping[p][i].action = ACTION_DEFAULT;
8508 player_mapping[p][i].direction = MV_NONE;
8512 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8514 int e = em_object_mapping_list[i].element_em;
8516 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8517 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8519 if (em_object_mapping_list[i].action != -1)
8520 object_mapping[e].action = em_object_mapping_list[i].action;
8522 if (em_object_mapping_list[i].direction != -1)
8523 object_mapping[e].direction =
8524 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8527 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8529 int a = em_player_mapping_list[i].action_em;
8530 int p = em_player_mapping_list[i].player_nr;
8532 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8534 if (em_player_mapping_list[i].action != -1)
8535 player_mapping[p][a].action = em_player_mapping_list[i].action;
8537 if (em_player_mapping_list[i].direction != -1)
8538 player_mapping[p][a].direction =
8539 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8542 for (i = 0; i < TILE_MAX; i++)
8544 int element = object_mapping[i].element_rnd;
8545 int action = object_mapping[i].action;
8546 int direction = object_mapping[i].direction;
8547 boolean is_backside = object_mapping[i].is_backside;
8548 boolean action_exploding = ((action == ACTION_EXPLODING ||
8549 action == ACTION_SMASHED_BY_ROCK ||
8550 action == ACTION_SMASHED_BY_SPRING) &&
8551 element != EL_DIAMOND);
8552 boolean action_active = (action == ACTION_ACTIVE);
8553 boolean action_other = (action == ACTION_OTHER);
8555 for (j = 0; j < 8; j++)
8557 int effective_element = get_effective_element_EM(i, j);
8558 int effective_action = (j < 7 ? action :
8559 i == Xdrip_stretch ? action :
8560 i == Xdrip_stretchB ? action :
8561 i == Ydrip_s1 ? action :
8562 i == Ydrip_s1B ? action :
8563 i == Xball_1B ? action :
8564 i == Xball_2 ? action :
8565 i == Xball_2B ? action :
8566 i == Yball_eat ? action :
8567 i == Ykey_1_eat ? action :
8568 i == Ykey_2_eat ? action :
8569 i == Ykey_3_eat ? action :
8570 i == Ykey_4_eat ? action :
8571 i == Ykey_5_eat ? action :
8572 i == Ykey_6_eat ? action :
8573 i == Ykey_7_eat ? action :
8574 i == Ykey_8_eat ? action :
8575 i == Ylenses_eat ? action :
8576 i == Ymagnify_eat ? action :
8577 i == Ygrass_eat ? action :
8578 i == Ydirt_eat ? action :
8579 i == Xsand_stonein_1 ? action :
8580 i == Xsand_stonein_2 ? action :
8581 i == Xsand_stonein_3 ? action :
8582 i == Xsand_stonein_4 ? action :
8583 i == Xsand_stoneout_1 ? action :
8584 i == Xsand_stoneout_2 ? action :
8585 i == Xboom_android ? ACTION_EXPLODING :
8586 action_exploding ? ACTION_EXPLODING :
8587 action_active ? action :
8588 action_other ? action :
8590 int graphic = (el_act_dir2img(effective_element, effective_action,
8592 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8594 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8595 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8596 boolean has_action_graphics = (graphic != base_graphic);
8597 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8598 struct GraphicInfo *g = &graphic_info[graphic];
8599 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8602 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8603 boolean special_animation = (action != ACTION_DEFAULT &&
8604 g->anim_frames == 3 &&
8605 g->anim_delay == 2 &&
8606 g->anim_mode & ANIM_LINEAR);
8607 int sync_frame = (i == Xdrip_stretch ? 7 :
8608 i == Xdrip_stretchB ? 7 :
8609 i == Ydrip_s2 ? j + 8 :
8610 i == Ydrip_s2B ? j + 8 :
8619 i == Xfake_acid_1 ? 0 :
8620 i == Xfake_acid_2 ? 10 :
8621 i == Xfake_acid_3 ? 20 :
8622 i == Xfake_acid_4 ? 30 :
8623 i == Xfake_acid_5 ? 40 :
8624 i == Xfake_acid_6 ? 50 :
8625 i == Xfake_acid_7 ? 60 :
8626 i == Xfake_acid_8 ? 70 :
8628 i == Xball_2B ? j + 8 :
8629 i == Yball_eat ? j + 1 :
8630 i == Ykey_1_eat ? j + 1 :
8631 i == Ykey_2_eat ? j + 1 :
8632 i == Ykey_3_eat ? j + 1 :
8633 i == Ykey_4_eat ? j + 1 :
8634 i == Ykey_5_eat ? j + 1 :
8635 i == Ykey_6_eat ? j + 1 :
8636 i == Ykey_7_eat ? j + 1 :
8637 i == Ykey_8_eat ? j + 1 :
8638 i == Ylenses_eat ? j + 1 :
8639 i == Ymagnify_eat ? j + 1 :
8640 i == Ygrass_eat ? j + 1 :
8641 i == Ydirt_eat ? j + 1 :
8642 i == Xamoeba_1 ? 0 :
8643 i == Xamoeba_2 ? 1 :
8644 i == Xamoeba_3 ? 2 :
8645 i == Xamoeba_4 ? 3 :
8646 i == Xamoeba_5 ? 0 :
8647 i == Xamoeba_6 ? 1 :
8648 i == Xamoeba_7 ? 2 :
8649 i == Xamoeba_8 ? 3 :
8650 i == Xexit_2 ? j + 8 :
8651 i == Xexit_3 ? j + 16 :
8652 i == Xdynamite_1 ? 0 :
8653 i == Xdynamite_2 ? 8 :
8654 i == Xdynamite_3 ? 16 :
8655 i == Xdynamite_4 ? 24 :
8656 i == Xsand_stonein_1 ? j + 1 :
8657 i == Xsand_stonein_2 ? j + 9 :
8658 i == Xsand_stonein_3 ? j + 17 :
8659 i == Xsand_stonein_4 ? j + 25 :
8660 i == Xsand_stoneout_1 && j == 0 ? 0 :
8661 i == Xsand_stoneout_1 && j == 1 ? 0 :
8662 i == Xsand_stoneout_1 && j == 2 ? 1 :
8663 i == Xsand_stoneout_1 && j == 3 ? 2 :
8664 i == Xsand_stoneout_1 && j == 4 ? 2 :
8665 i == Xsand_stoneout_1 && j == 5 ? 3 :
8666 i == Xsand_stoneout_1 && j == 6 ? 4 :
8667 i == Xsand_stoneout_1 && j == 7 ? 4 :
8668 i == Xsand_stoneout_2 && j == 0 ? 5 :
8669 i == Xsand_stoneout_2 && j == 1 ? 6 :
8670 i == Xsand_stoneout_2 && j == 2 ? 7 :
8671 i == Xsand_stoneout_2 && j == 3 ? 8 :
8672 i == Xsand_stoneout_2 && j == 4 ? 9 :
8673 i == Xsand_stoneout_2 && j == 5 ? 11 :
8674 i == Xsand_stoneout_2 && j == 6 ? 13 :
8675 i == Xsand_stoneout_2 && j == 7 ? 15 :
8676 i == Xboom_bug && j == 1 ? 2 :
8677 i == Xboom_bug && j == 2 ? 2 :
8678 i == Xboom_bug && j == 3 ? 4 :
8679 i == Xboom_bug && j == 4 ? 4 :
8680 i == Xboom_bug && j == 5 ? 2 :
8681 i == Xboom_bug && j == 6 ? 2 :
8682 i == Xboom_bug && j == 7 ? 0 :
8683 i == Xboom_bomb && j == 1 ? 2 :
8684 i == Xboom_bomb && j == 2 ? 2 :
8685 i == Xboom_bomb && j == 3 ? 4 :
8686 i == Xboom_bomb && j == 4 ? 4 :
8687 i == Xboom_bomb && j == 5 ? 2 :
8688 i == Xboom_bomb && j == 6 ? 2 :
8689 i == Xboom_bomb && j == 7 ? 0 :
8690 i == Xboom_android && j == 7 ? 6 :
8691 i == Xboom_1 && j == 1 ? 2 :
8692 i == Xboom_1 && j == 2 ? 2 :
8693 i == Xboom_1 && j == 3 ? 4 :
8694 i == Xboom_1 && j == 4 ? 4 :
8695 i == Xboom_1 && j == 5 ? 6 :
8696 i == Xboom_1 && j == 6 ? 6 :
8697 i == Xboom_1 && j == 7 ? 8 :
8698 i == Xboom_2 && j == 0 ? 8 :
8699 i == Xboom_2 && j == 1 ? 8 :
8700 i == Xboom_2 && j == 2 ? 10 :
8701 i == Xboom_2 && j == 3 ? 10 :
8702 i == Xboom_2 && j == 4 ? 10 :
8703 i == Xboom_2 && j == 5 ? 12 :
8704 i == Xboom_2 && j == 6 ? 12 :
8705 i == Xboom_2 && j == 7 ? 12 :
8706 special_animation && j == 4 ? 3 :
8707 effective_action != action ? 0 :
8711 Bitmap *debug_bitmap = g_em->bitmap;
8712 int debug_src_x = g_em->src_x;
8713 int debug_src_y = g_em->src_y;
8716 int frame = getAnimationFrame(g->anim_frames,
8719 g->anim_start_frame,
8722 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8723 g->double_movement && is_backside);
8725 g_em->bitmap = src_bitmap;
8726 g_em->src_x = src_x;
8727 g_em->src_y = src_y;
8728 g_em->src_offset_x = 0;
8729 g_em->src_offset_y = 0;
8730 g_em->dst_offset_x = 0;
8731 g_em->dst_offset_y = 0;
8732 g_em->width = TILEX;
8733 g_em->height = TILEY;
8735 g_em->preserve_background = FALSE;
8737 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8740 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8741 effective_action == ACTION_MOVING ||
8742 effective_action == ACTION_PUSHING ||
8743 effective_action == ACTION_EATING)) ||
8744 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8745 effective_action == ACTION_EMPTYING)))
8748 (effective_action == ACTION_FALLING ||
8749 effective_action == ACTION_FILLING ||
8750 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8751 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8752 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8753 int num_steps = (i == Ydrip_s1 ? 16 :
8754 i == Ydrip_s1B ? 16 :
8755 i == Ydrip_s2 ? 16 :
8756 i == Ydrip_s2B ? 16 :
8757 i == Xsand_stonein_1 ? 32 :
8758 i == Xsand_stonein_2 ? 32 :
8759 i == Xsand_stonein_3 ? 32 :
8760 i == Xsand_stonein_4 ? 32 :
8761 i == Xsand_stoneout_1 ? 16 :
8762 i == Xsand_stoneout_2 ? 16 : 8);
8763 int cx = ABS(dx) * (TILEX / num_steps);
8764 int cy = ABS(dy) * (TILEY / num_steps);
8765 int step_frame = (i == Ydrip_s2 ? j + 8 :
8766 i == Ydrip_s2B ? j + 8 :
8767 i == Xsand_stonein_2 ? j + 8 :
8768 i == Xsand_stonein_3 ? j + 16 :
8769 i == Xsand_stonein_4 ? j + 24 :
8770 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8771 int step = (is_backside ? step_frame : num_steps - step_frame);
8773 if (is_backside) /* tile where movement starts */
8775 if (dx < 0 || dy < 0)
8777 g_em->src_offset_x = cx * step;
8778 g_em->src_offset_y = cy * step;
8782 g_em->dst_offset_x = cx * step;
8783 g_em->dst_offset_y = cy * step;
8786 else /* tile where movement ends */
8788 if (dx < 0 || dy < 0)
8790 g_em->dst_offset_x = cx * step;
8791 g_em->dst_offset_y = cy * step;
8795 g_em->src_offset_x = cx * step;
8796 g_em->src_offset_y = cy * step;
8800 g_em->width = TILEX - cx * step;
8801 g_em->height = TILEY - cy * step;
8804 /* create unique graphic identifier to decide if tile must be redrawn */
8805 /* bit 31 - 16 (16 bit): EM style graphic
8806 bit 15 - 12 ( 4 bit): EM style frame
8807 bit 11 - 6 ( 6 bit): graphic width
8808 bit 5 - 0 ( 6 bit): graphic height */
8809 g_em->unique_identifier =
8810 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8814 /* skip check for EMC elements not contained in original EMC artwork */
8815 if (element == EL_EMC_FAKE_ACID)
8818 if (g_em->bitmap != debug_bitmap ||
8819 g_em->src_x != debug_src_x ||
8820 g_em->src_y != debug_src_y ||
8821 g_em->src_offset_x != 0 ||
8822 g_em->src_offset_y != 0 ||
8823 g_em->dst_offset_x != 0 ||
8824 g_em->dst_offset_y != 0 ||
8825 g_em->width != TILEX ||
8826 g_em->height != TILEY)
8828 static int last_i = -1;
8836 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8837 i, element, element_info[element].token_name,
8838 element_action_info[effective_action].suffix, direction);
8840 if (element != effective_element)
8841 printf(" [%d ('%s')]",
8843 element_info[effective_element].token_name);
8847 if (g_em->bitmap != debug_bitmap)
8848 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8849 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8851 if (g_em->src_x != debug_src_x ||
8852 g_em->src_y != debug_src_y)
8853 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8854 j, (is_backside ? 'B' : 'F'),
8855 g_em->src_x, g_em->src_y,
8856 g_em->src_x / 32, g_em->src_y / 32,
8857 debug_src_x, debug_src_y,
8858 debug_src_x / 32, debug_src_y / 32);
8860 if (g_em->src_offset_x != 0 ||
8861 g_em->src_offset_y != 0 ||
8862 g_em->dst_offset_x != 0 ||
8863 g_em->dst_offset_y != 0)
8864 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8866 g_em->src_offset_x, g_em->src_offset_y,
8867 g_em->dst_offset_x, g_em->dst_offset_y);
8869 if (g_em->width != TILEX ||
8870 g_em->height != TILEY)
8871 printf(" %d (%d): size %d,%d should be %d,%d\n",
8873 g_em->width, g_em->height, TILEX, TILEY);
8875 num_em_gfx_errors++;
8882 for (i = 0; i < TILE_MAX; i++)
8884 for (j = 0; j < 8; j++)
8886 int element = object_mapping[i].element_rnd;
8887 int action = object_mapping[i].action;
8888 int direction = object_mapping[i].direction;
8889 boolean is_backside = object_mapping[i].is_backside;
8890 int graphic_action = el_act_dir2img(element, action, direction);
8891 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8893 if ((action == ACTION_SMASHED_BY_ROCK ||
8894 action == ACTION_SMASHED_BY_SPRING ||
8895 action == ACTION_EATING) &&
8896 graphic_action == graphic_default)
8898 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8899 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8900 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8901 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8904 /* no separate animation for "smashed by rock" -- use rock instead */
8905 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8906 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8908 g_em->bitmap = g_xx->bitmap;
8909 g_em->src_x = g_xx->src_x;
8910 g_em->src_y = g_xx->src_y;
8911 g_em->src_offset_x = g_xx->src_offset_x;
8912 g_em->src_offset_y = g_xx->src_offset_y;
8913 g_em->dst_offset_x = g_xx->dst_offset_x;
8914 g_em->dst_offset_y = g_xx->dst_offset_y;
8915 g_em->width = g_xx->width;
8916 g_em->height = g_xx->height;
8917 g_em->unique_identifier = g_xx->unique_identifier;
8920 g_em->preserve_background = TRUE;
8925 for (p = 0; p < MAX_PLAYERS; p++)
8927 for (i = 0; i < SPR_MAX; i++)
8929 int element = player_mapping[p][i].element_rnd;
8930 int action = player_mapping[p][i].action;
8931 int direction = player_mapping[p][i].direction;
8933 for (j = 0; j < 8; j++)
8935 int effective_element = element;
8936 int effective_action = action;
8937 int graphic = (direction == MV_NONE ?
8938 el_act2img(effective_element, effective_action) :
8939 el_act_dir2img(effective_element, effective_action,
8941 struct GraphicInfo *g = &graphic_info[graphic];
8942 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8948 Bitmap *debug_bitmap = g_em->bitmap;
8949 int debug_src_x = g_em->src_x;
8950 int debug_src_y = g_em->src_y;
8953 int frame = getAnimationFrame(g->anim_frames,
8956 g->anim_start_frame,
8959 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8961 g_em->bitmap = src_bitmap;
8962 g_em->src_x = src_x;
8963 g_em->src_y = src_y;
8964 g_em->src_offset_x = 0;
8965 g_em->src_offset_y = 0;
8966 g_em->dst_offset_x = 0;
8967 g_em->dst_offset_y = 0;
8968 g_em->width = TILEX;
8969 g_em->height = TILEY;
8973 /* skip check for EMC elements not contained in original EMC artwork */
8974 if (element == EL_PLAYER_3 ||
8975 element == EL_PLAYER_4)
8978 if (g_em->bitmap != debug_bitmap ||
8979 g_em->src_x != debug_src_x ||
8980 g_em->src_y != debug_src_y)
8982 static int last_i = -1;
8990 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8991 p, i, element, element_info[element].token_name,
8992 element_action_info[effective_action].suffix, direction);
8994 if (element != effective_element)
8995 printf(" [%d ('%s')]",
8997 element_info[effective_element].token_name);
9001 if (g_em->bitmap != debug_bitmap)
9002 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
9003 j, (int)(g_em->bitmap), (int)(debug_bitmap));
9005 if (g_em->src_x != debug_src_x ||
9006 g_em->src_y != debug_src_y)
9007 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
9009 g_em->src_x, g_em->src_y,
9010 g_em->src_x / 32, g_em->src_y / 32,
9011 debug_src_x, debug_src_y,
9012 debug_src_x / 32, debug_src_y / 32);
9014 num_em_gfx_errors++;
9024 printf("::: [%d errors found]\n", num_em_gfx_errors);
9030 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
9031 boolean any_player_moving,
9032 boolean any_player_snapping,
9033 boolean any_player_dropping)
9035 if (frame == 0 && !any_player_dropping)
9037 if (!local_player->was_waiting)
9039 if (!CheckSaveEngineSnapshotToList())
9042 local_player->was_waiting = TRUE;
9045 else if (any_player_moving || any_player_snapping || any_player_dropping)
9047 local_player->was_waiting = FALSE;
9051 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
9052 boolean murphy_is_dropping)
9054 if (murphy_is_waiting)
9056 if (!local_player->was_waiting)
9058 if (!CheckSaveEngineSnapshotToList())
9061 local_player->was_waiting = TRUE;
9066 local_player->was_waiting = FALSE;
9070 void CheckSaveEngineSnapshot_MM(boolean element_clicked,
9071 boolean button_released)
9073 if (button_released)
9075 if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
9076 CheckSaveEngineSnapshotToList();
9078 else if (element_clicked)
9080 if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
9081 CheckSaveEngineSnapshotToList();
9083 game.snapshot.changed_action = TRUE;
9087 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
9088 boolean any_player_moving,
9089 boolean any_player_snapping,
9090 boolean any_player_dropping)
9092 if (tape.single_step && tape.recording && !tape.pausing)
9093 if (frame == 0 && !any_player_dropping)
9094 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9096 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
9097 any_player_snapping, any_player_dropping);
9100 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
9101 boolean murphy_is_dropping)
9103 boolean murphy_starts_dropping = FALSE;
9106 for (i = 0; i < MAX_PLAYERS; i++)
9107 if (stored_player[i].force_dropping)
9108 murphy_starts_dropping = TRUE;
9110 if (tape.single_step && tape.recording && !tape.pausing)
9111 if (murphy_is_waiting && !murphy_starts_dropping)
9112 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9114 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
9117 void CheckSingleStepMode_MM(boolean element_clicked,
9118 boolean button_released)
9120 if (tape.single_step && tape.recording && !tape.pausing)
9121 if (button_released)
9122 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
9124 CheckSaveEngineSnapshot_MM(element_clicked, button_released);
9127 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
9128 int graphic, int sync_frame, int x, int y)
9130 int frame = getGraphicAnimationFrame(graphic, sync_frame);
9132 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
9135 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
9137 return (IS_NEXT_FRAME(sync_frame, graphic));
9140 int getGraphicInfo_Delay(int graphic)
9142 return graphic_info[graphic].anim_delay;
9145 void PlayMenuSoundExt(int sound)
9147 if (sound == SND_UNDEFINED)
9150 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9151 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9154 if (IS_LOOP_SOUND(sound))
9155 PlaySoundLoop(sound);
9160 void PlayMenuSound()
9162 PlayMenuSoundExt(menu.sound[game_status]);
9165 void PlayMenuSoundStereo(int sound, int stereo_position)
9167 if (sound == SND_UNDEFINED)
9170 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9171 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9174 if (IS_LOOP_SOUND(sound))
9175 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
9177 PlaySoundStereo(sound, stereo_position);
9180 void PlayMenuSoundIfLoopExt(int sound)
9182 if (sound == SND_UNDEFINED)
9185 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
9186 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
9189 if (IS_LOOP_SOUND(sound))
9190 PlaySoundLoop(sound);
9193 void PlayMenuSoundIfLoop()
9195 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
9198 void PlayMenuMusicExt(int music)
9200 if (music == MUS_UNDEFINED)
9203 if (!setup.sound_music)
9209 void PlayMenuMusic()
9211 char *curr_music = getCurrentlyPlayingMusicFilename();
9212 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9214 if (!strEqual(curr_music, next_music))
9215 PlayMenuMusicExt(menu.music[game_status]);
9218 void PlayMenuSoundsAndMusic()
9224 static void FadeMenuSounds()
9229 static void FadeMenuMusic()
9231 char *curr_music = getCurrentlyPlayingMusicFilename();
9232 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
9234 if (!strEqual(curr_music, next_music))
9238 void FadeMenuSoundsAndMusic()
9244 void PlaySoundActivating()
9247 PlaySound(SND_MENU_ITEM_ACTIVATING);
9251 void PlaySoundSelecting()
9254 PlaySound(SND_MENU_ITEM_SELECTING);
9258 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
9260 boolean change_fullscreen = (setup.fullscreen !=
9261 video.fullscreen_enabled);
9262 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
9263 setup.window_scaling_percent !=
9264 video.window_scaling_percent);
9266 if (change_window_scaling_percent && video.fullscreen_enabled)
9269 if (!change_window_scaling_percent && !video.fullscreen_available)
9272 #if defined(TARGET_SDL2)
9273 if (change_window_scaling_percent)
9275 SDLSetWindowScaling(setup.window_scaling_percent);
9279 else if (change_fullscreen)
9281 SDLSetWindowFullscreen(setup.fullscreen);
9283 /* set setup value according to successfully changed fullscreen mode */
9284 setup.fullscreen = video.fullscreen_enabled;
9290 if (change_fullscreen ||
9291 change_window_scaling_percent)
9293 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
9295 /* save backbuffer content which gets lost when toggling fullscreen mode */
9296 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9298 if (change_window_scaling_percent)
9300 /* keep window mode, but change window scaling */
9301 video.fullscreen_enabled = TRUE; /* force new window scaling */
9304 /* toggle fullscreen */
9305 ChangeVideoModeIfNeeded(setup.fullscreen);
9307 /* set setup value according to successfully changed fullscreen mode */
9308 setup.fullscreen = video.fullscreen_enabled;
9310 /* restore backbuffer content from temporary backbuffer backup bitmap */
9311 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9313 FreeBitmap(tmp_backbuffer);
9315 /* update visible window/screen */
9316 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
9320 void JoinRectangles(int *x, int *y, int *width, int *height,
9321 int x2, int y2, int width2, int height2)
9323 // do not join with "off-screen" rectangle
9324 if (x2 == -1 || y2 == -1)
9329 *width = MAX(*width, width2);
9330 *height = MAX(*height, height2);
9333 void SetAnimStatus(int anim_status_new)
9335 if (anim_status_new == GAME_MODE_MAIN)
9336 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
9337 else if (anim_status_new == GAME_MODE_SCORES)
9338 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
9340 global.anim_status_next = anim_status_new;
9342 // directly set screen modes that are entered without fading
9343 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
9344 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
9345 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
9346 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
9347 global.anim_status = global.anim_status_next;
9350 void SetGameStatus(int game_status_new)
9352 if (game_status_new != game_status)
9353 game_status_last_screen = game_status;
9355 game_status = game_status_new;
9357 SetAnimStatus(game_status_new);
9360 void SetFontStatus(int game_status_new)
9362 static int last_game_status = -1;
9364 if (game_status_new != -1)
9366 // set game status for font use after storing last game status
9367 last_game_status = game_status;
9368 game_status = game_status_new;
9372 // reset game status after font use from last stored game status
9373 game_status = last_game_status;
9377 void ResetFontStatus()
9382 boolean CheckIfPlayfieldViewportHasChanged()
9384 // if game status has not changed, playfield viewport has not changed either
9385 if (game_status == game_status_last)
9388 // check if playfield viewport has changed with current game status
9389 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
9390 int new_real_sx = vp_playfield->x;
9391 int new_real_sy = vp_playfield->y;
9392 int new_full_sxsize = vp_playfield->width;
9393 int new_full_sysize = vp_playfield->height;
9395 return (new_real_sx != REAL_SX ||
9396 new_real_sy != REAL_SY ||
9397 new_full_sxsize != FULL_SXSIZE ||
9398 new_full_sysize != FULL_SYSIZE);
9401 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
9403 return (CheckIfGlobalBorderHasChanged() ||
9404 CheckIfPlayfieldViewportHasChanged());
9407 void ChangeViewportPropertiesIfNeeded()
9409 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9410 FALSE : setup.small_game_graphics);
9411 int gfx_game_mode = game_status;
9412 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9414 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9415 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9416 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9417 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9418 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9419 int new_win_xsize = vp_window->width;
9420 int new_win_ysize = vp_window->height;
9421 int border_size = vp_playfield->border_size;
9422 int new_sx = vp_playfield->x + border_size;
9423 int new_sy = vp_playfield->y + border_size;
9424 int new_sxsize = vp_playfield->width - 2 * border_size;
9425 int new_sysize = vp_playfield->height - 2 * border_size;
9426 int new_real_sx = vp_playfield->x;
9427 int new_real_sy = vp_playfield->y;
9428 int new_full_sxsize = vp_playfield->width;
9429 int new_full_sysize = vp_playfield->height;
9430 int new_dx = vp_door_1->x;
9431 int new_dy = vp_door_1->y;
9432 int new_dxsize = vp_door_1->width;
9433 int new_dysize = vp_door_1->height;
9434 int new_vx = vp_door_2->x;
9435 int new_vy = vp_door_2->y;
9436 int new_vxsize = vp_door_2->width;
9437 int new_vysize = vp_door_2->height;
9438 int new_ex = vp_door_3->x;
9439 int new_ey = vp_door_3->y;
9440 int new_exsize = vp_door_3->width;
9441 int new_eysize = vp_door_3->height;
9442 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9443 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9444 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9445 int new_scr_fieldx = new_sxsize / tilesize;
9446 int new_scr_fieldy = new_sysize / tilesize;
9447 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9448 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9449 boolean init_gfx_buffers = FALSE;
9450 boolean init_video_buffer = FALSE;
9451 boolean init_gadgets_and_anims = FALSE;
9452 boolean init_em_graphics = FALSE;
9454 if (new_win_xsize != WIN_XSIZE ||
9455 new_win_ysize != WIN_YSIZE)
9457 WIN_XSIZE = new_win_xsize;
9458 WIN_YSIZE = new_win_ysize;
9460 init_video_buffer = TRUE;
9461 init_gfx_buffers = TRUE;
9462 init_gadgets_and_anims = TRUE;
9464 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9467 if (new_scr_fieldx != SCR_FIELDX ||
9468 new_scr_fieldy != SCR_FIELDY)
9470 /* this always toggles between MAIN and GAME when using small tile size */
9472 SCR_FIELDX = new_scr_fieldx;
9473 SCR_FIELDY = new_scr_fieldy;
9475 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9486 new_sxsize != SXSIZE ||
9487 new_sysize != SYSIZE ||
9488 new_dxsize != DXSIZE ||
9489 new_dysize != DYSIZE ||
9490 new_vxsize != VXSIZE ||
9491 new_vysize != VYSIZE ||
9492 new_exsize != EXSIZE ||
9493 new_eysize != EYSIZE ||
9494 new_real_sx != REAL_SX ||
9495 new_real_sy != REAL_SY ||
9496 new_full_sxsize != FULL_SXSIZE ||
9497 new_full_sysize != FULL_SYSIZE ||
9498 new_tilesize_var != TILESIZE_VAR
9501 // ------------------------------------------------------------------------
9502 // determine next fading area for changed viewport definitions
9503 // ------------------------------------------------------------------------
9505 // start with current playfield area (default fading area)
9508 FADE_SXSIZE = FULL_SXSIZE;
9509 FADE_SYSIZE = FULL_SYSIZE;
9511 // add new playfield area if position or size has changed
9512 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9513 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9515 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9516 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9519 // add current and new door 1 area if position or size has changed
9520 if (new_dx != DX || new_dy != DY ||
9521 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9523 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9524 DX, DY, DXSIZE, DYSIZE);
9525 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9526 new_dx, new_dy, new_dxsize, new_dysize);
9529 // add current and new door 2 area if position or size has changed
9530 if (new_dx != VX || new_dy != VY ||
9531 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9533 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9534 VX, VY, VXSIZE, VYSIZE);
9535 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9536 new_vx, new_vy, new_vxsize, new_vysize);
9539 // ------------------------------------------------------------------------
9540 // handle changed tile size
9541 // ------------------------------------------------------------------------
9543 if (new_tilesize_var != TILESIZE_VAR)
9545 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9547 // changing tile size invalidates scroll values of engine snapshots
9548 FreeEngineSnapshotSingle();
9550 // changing tile size requires update of graphic mapping for EM engine
9551 init_em_graphics = TRUE;
9562 SXSIZE = new_sxsize;
9563 SYSIZE = new_sysize;
9564 DXSIZE = new_dxsize;
9565 DYSIZE = new_dysize;
9566 VXSIZE = new_vxsize;
9567 VYSIZE = new_vysize;
9568 EXSIZE = new_exsize;
9569 EYSIZE = new_eysize;
9570 REAL_SX = new_real_sx;
9571 REAL_SY = new_real_sy;
9572 FULL_SXSIZE = new_full_sxsize;
9573 FULL_SYSIZE = new_full_sysize;
9574 TILESIZE_VAR = new_tilesize_var;
9576 init_gfx_buffers = TRUE;
9577 init_gadgets_and_anims = TRUE;
9579 // printf("::: viewports: init_gfx_buffers\n");
9580 // printf("::: viewports: init_gadgets_and_anims\n");
9583 if (init_gfx_buffers)
9585 // printf("::: init_gfx_buffers\n");
9587 SCR_FIELDX = new_scr_fieldx_buffers;
9588 SCR_FIELDY = new_scr_fieldy_buffers;
9592 SCR_FIELDX = new_scr_fieldx;
9593 SCR_FIELDY = new_scr_fieldy;
9595 SetDrawDeactivationMask(REDRAW_NONE);
9596 SetDrawBackgroundMask(REDRAW_FIELD);
9599 if (init_video_buffer)
9601 // printf("::: init_video_buffer\n");
9603 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9604 InitImageTextures();
9607 if (init_gadgets_and_anims)
9609 // printf("::: init_gadgets_and_anims\n");
9612 InitGlobalAnimations();
9615 if (init_em_graphics)
9617 InitGraphicInfo_EM();